cvsimport
[findutils.git] / locate / locate.c
blob069aba87f67d441011e8b017108778179e8e1bec
1 /* locate -- search databases for filenames that match patterns
2 Copyright (C) 1994, 1996, 1998, 1999, 2000, 2003,
3 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 /* Usage: locate [options] pattern...
21 Scan a pathname list for the full pathname of a file, given only
22 a piece of the name (possibly containing shell globbing metacharacters).
23 The list has been processed with front-compression, which reduces
24 the list size by a factor of 4-5.
25 Recognizes two database formats, old and new. The old format is
26 bigram coded, which reduces space by a further 20-25% and uses the
27 following encoding of the database bytes:
29 0-28 likeliest differential counts + offset (14) to make nonnegative
30 30 escape code for out-of-range count to follow in next halfword
31 128-255 bigram codes (the 128 most common, as determined by `updatedb')
32 32-127 single character (printable) ASCII remainder
34 Earlier versions of GNU locate used to use a novel two-tiered
35 string search technique, which was described in Usenix ;login:, Vol
36 8, No 1, February/March, 1983, p. 8.
38 However, latterly code changes to provide additional functionality
39 became dificult to make with the existing reading scheme, and so
40 we no longer perform the matching as efficiently as we used to (that is,
41 we no longer use the same algorithm).
43 The old algorithm was:
45 First, match a metacharacter-free subpattern and a partial
46 pathname BACKWARDS to avoid full expansion of the pathname list.
47 The time savings is 40-50% over forward matching, which cannot
48 efficiently handle overlapped search patterns and compressed
49 path remainders.
51 Then, match the actual shell glob pattern (if in this form)
52 against the candidate pathnames using the slower shell filename
53 matching routines.
56 Written by James A. Woods <jwoods@adobe.com>.
57 Modified by David MacKenzie <djm@gnu.org>.
58 Additional work by James Youngman and Bas van Gompel.
61 #include <config.h>
63 #include <stdio.h>
64 #include <signal.h>
65 #include <ctype.h>
66 #include <sys/types.h>
67 #include <grp.h> /* for setgroups() */
68 #include <sys/stat.h>
69 #include <time.h>
70 #include <fnmatch.h>
71 #include <getopt.h>
72 #include <xstrtol.h>
74 #include <stdbool.h> /* for bool/boolean */
76 /* The presence of unistd.h is assumed by gnulib these days, so we
77 * might as well assume it too.
79 /* We need <unistd.h> for isatty(). */
80 #include <unistd.h>
82 #include <fcntl.h>
84 #define NDEBUG
85 #include <assert.h>
86 #include <string.h>
89 #ifdef STDC_HEADERS
90 #include <stdlib.h>
91 #endif
93 #include <errno.h>
95 #ifdef HAVE_LOCALE_H
96 #include <locale.h>
97 #endif
99 #if ENABLE_NLS
100 # include <libintl.h>
101 # define _(Text) gettext (Text)
102 #else
103 # define _(Text) Text
104 #define textdomain(Domain)
105 #define bindtextdomain(Package, Directory)
106 #endif
107 #ifdef gettext_noop
108 # define N_(String) gettext_noop (String)
109 #else
110 /* We used to use (String) instead of just String, but apparently ISO C
111 * doesn't allow this (at least, that's what HP said when someone reported
112 * this as a compiler bug). This is HP case number 1205608192. See
113 * also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11250 (which references
114 * ANSI 3.5.7p14-15). The Intel icc compiler also rejects constructs
115 * like: static const char buf[] = ("string");
117 # define N_(String) String
118 #endif
120 #include "locatedb.h"
121 #include <getline.h>
122 #include "xalloc.h"
123 #include "error.h"
124 #include "human.h"
125 #include "dirname.h"
126 #include "closeout.h"
127 #include "nextelem.h"
128 #include "regex.h"
129 #include "quote.h"
130 #include "quotearg.h"
131 #include "printquoted.h"
132 #include "regextype.h"
133 #include "findutils-version.h"
135 /* Note that this evaluates Ch many times. */
136 #ifdef _LIBC
137 # define TOUPPER(Ch) toupper (Ch)
138 # define TOLOWER(Ch) tolower (Ch)
139 #else
140 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
141 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
142 #endif
144 /* typedef enum {false, true} boolean; */
146 /* Warn if a database is older than this. 8 days allows for a weekly
147 update that takes up to a day to perform. */
148 static unsigned int warn_number_units = 8;
150 /* Printable name of units used in WARN_SECONDS */
151 static const char warn_name_units[] = N_("days");
152 #define SECONDS_PER_UNIT (60 * 60 * 24)
154 enum visit_result
156 VISIT_CONTINUE = 1, /* please call the next visitor */
157 VISIT_ACCEPTED = 2, /* accepted, call no futher callbacks for this file */
158 VISIT_REJECTED = 4, /* rejected, process next file. */
159 VISIT_ABORT = 8 /* rejected, process no more files. */
162 enum ExistenceCheckType
164 ACCEPT_EITHER, /* Corresponds to lack of -E/-e option */
165 ACCEPT_EXISTING, /* Corresponds to option -e */
166 ACCEPT_NON_EXISTING /* Corresponds to option -E */
169 /* Check for existence of files before printing them out? */
170 enum ExistenceCheckType check_existence = ACCEPT_EITHER;
172 static int follow_symlinks = 1;
174 /* What to separate the results with. */
175 static int separator = '\n';
177 static struct quoting_options * quote_opts = NULL;
178 static bool stdout_is_a_tty;
179 static bool print_quoted_filename;
180 static bool results_were_filtered;
182 static const char *selected_secure_db = NULL;
185 /* Change the number of days old the database can be
186 * before we complain about it.
188 static void
189 set_max_db_age(const char *s)
191 char *end;
192 unsigned long int val;
193 /* XXX: we ignore the case where the input is negative, which is allowed(!). */
195 if (0 == *s)
197 error(1, 0,
198 _("The argument for option --max-database-age must not be empty"));
202 /* We have to set errno here, otherwise when the function returns ULONG_MAX,
203 * we would not be able to tell if that is the correct answer, or whether it
204 * signifies an error.
206 errno = 0;
207 val = strtoul(s, &end, 10);
209 /* Diagnose number too large, non-numbes and trailing junk. */
210 if ((ULONG_MAX == val && ERANGE == errno) ||
211 (0 == val && EINVAL == errno))
213 error(1, errno,
214 _("Invalid argument %s for option --max-database-age"),
215 quotearg_n_style(0, locale_quoting_style, s));
217 else if (*end)
219 /* errno wasn't set, don't print its message */
220 error(1, 0,
221 _("Invalid argument %s for option --max-database-age"),
222 quotearg_n_style(0, locale_quoting_style, s));
224 else
226 warn_number_units = val;
232 /* Read in a 16-bit int, high byte first (network byte order). */
234 static short
235 get_short (FILE *fp)
238 register short x;
240 x = (signed char) fgetc (fp) << 8;
241 x |= (fgetc (fp) & 0xff);
242 return x;
245 const char * const metacharacters = "*?[]\\";
247 /* Return nonzero if S contains any shell glob characters.
249 static int
250 contains_metacharacter(const char *s)
252 if (NULL == strpbrk(s, metacharacters))
253 return 0;
254 else
255 return 1;
258 /* locate_read_str()
260 * Read bytes from FP into the buffer at offset OFFSET in (*BUF),
261 * until we reach DELIMITER or end-of-file. We reallocate the buffer
262 * as necessary, altering (*BUF) and (*SIZ) as appropriate. No assumption
263 * is made regarding the content of the data (i.e. the implementation is
264 * 8-bit clean, the only delimiter is DELIMITER).
266 * Written Fri May 23 18:41:16 2003 by James Youngman, because getstr()
267 * has been removed from gnulib.
269 * We call the function locate_read_str() to avoid a name clash with the curses
270 * function getstr().
272 static int
273 locate_read_str(char **buf, size_t *siz, FILE *fp, int delimiter, int offs)
275 char * p = NULL;
276 size_t sz = 0;
277 int nread;
278 size_t needed;
280 nread = getdelim(&p, &sz, delimiter, fp);
281 if (nread >= 0)
283 assert(p != NULL);
285 needed = offs + nread + 1u;
286 if (needed > (*siz))
288 char *pnew = realloc(*buf, needed);
289 if (NULL == pnew)
291 return -1; /* FAIL */
293 else
295 *siz = needed;
296 *buf = pnew;
299 memcpy((*buf)+offs, p, nread);
300 free(p);
302 return nread;
306 struct locate_limits
308 uintmax_t limit;
309 uintmax_t items_accepted;
311 static struct locate_limits limits;
314 struct locate_stats
316 uintmax_t compressed_bytes;
317 uintmax_t total_filename_count;
318 uintmax_t total_filename_length;
319 uintmax_t whitespace_count;
320 uintmax_t newline_count;
321 uintmax_t highbit_filename_count;
323 static struct locate_stats statistics;
326 struct regular_expression
328 struct re_pattern_buffer regex; /* for --regex */
332 struct process_data
334 int c; /* An input byte. */
335 char itemcount; /* Indicates we're at the beginning of an slocate db. */
336 int count; /* The length of the prefix shared with the previous database entry. */
337 int len;
338 char *original_filename; /* The current input database entry. */
339 size_t pathsize; /* Amount allocated for it. */
340 char *munged_filename; /* path or basename(path) */
341 FILE *fp; /* The pathname database. */
342 const char *dbfile; /* Its name, or "<stdin>" */
343 int slocatedb_format; /* Allows us to cope with slocate's format variant */
344 GetwordEndianState endian_state;
345 /* for the old database format,
346 the first and second characters of the most common bigrams. */
347 char bigram1[128];
348 char bigram2[128];
352 typedef int (*visitfunc)(struct process_data *procdata,
353 void *context);
355 struct visitor
357 visitfunc inspector;
358 void * context;
359 struct visitor *next;
363 static struct visitor *inspectors = NULL;
364 static struct visitor *lastinspector = NULL;
365 static struct visitor *past_pat_inspector = NULL;
367 static inline int visit(const struct visitor *p,
368 int accept_flags,
369 struct process_data *procdata,
370 const struct visitor * const stop)
372 register int result = accept_flags;
373 while ( (accept_flags & result) && (stop != p) )
375 result = (p->inspector)(procdata, p->context);
376 p = p->next;
378 return result;
381 /* 0 or 1 pattern(s) */
382 static int
383 process_simple(struct process_data *procdata)
385 return visit(inspectors, (VISIT_CONTINUE|VISIT_ACCEPTED), procdata, NULL);
388 /* Accept if any pattern matches. */
389 static int
390 process_or (struct process_data *procdata)
392 int result;
394 result = visit(inspectors, (VISIT_CONTINUE|VISIT_REJECTED), procdata, past_pat_inspector);
395 if (result == VISIT_CONTINUE)
396 result = VISIT_REJECTED;
397 if (result & (VISIT_ABORT | VISIT_REJECTED))
398 return result;
400 result = visit(past_pat_inspector, VISIT_CONTINUE, procdata, NULL);
401 if (VISIT_CONTINUE == result)
402 return VISIT_ACCEPTED;
403 else
404 return result;
407 /* Accept if all pattern match. */
408 static int
409 process_and (struct process_data *procdata)
411 int result;
413 result = visit(inspectors, (VISIT_CONTINUE|VISIT_ACCEPTED), procdata, past_pat_inspector);
414 if (result == VISIT_CONTINUE)
415 result = VISIT_REJECTED;
416 if (result & (VISIT_ABORT | VISIT_REJECTED))
417 return result;
419 result = visit(past_pat_inspector, VISIT_CONTINUE, procdata, NULL);
420 if (VISIT_CONTINUE == result)
421 return VISIT_ACCEPTED;
422 else
423 return result;
426 typedef int (*processfunc)(struct process_data *procdata);
428 static processfunc mainprocessor = NULL;
430 static void
431 add_visitor(visitfunc fn, void *context)
433 struct visitor *p = xmalloc(sizeof(struct visitor));
434 p->inspector = fn;
435 p->context = context;
436 p->next = NULL;
438 if (NULL == lastinspector)
440 lastinspector = inspectors = p;
442 else
444 lastinspector->next = p;
445 lastinspector = p;
449 static int
450 visit_justprint_quoted(struct process_data *procdata, void *context)
452 (void) context;
453 print_quoted (stdout, quote_opts, stdout_is_a_tty,
454 "%s",
455 procdata->original_filename);
456 putchar(separator);
457 return VISIT_CONTINUE;
460 static int
461 visit_justprint_unquoted(struct process_data *procdata, void *context)
463 (void) context;
464 fputs(procdata->original_filename, stdout);
465 putchar(separator);
466 return VISIT_CONTINUE;
469 static void
470 toolong (struct process_data *procdata)
472 error (1, 0,
473 _("locate database %s contains a "
474 "filename longer than locate can handle"),
475 procdata->dbfile);
478 static void
479 extend (struct process_data *procdata, size_t siz1, size_t siz2)
481 /* Figure out if the addition operation is safe before performing it. */
482 if (SIZE_MAX - siz1 < siz2)
484 toolong (procdata);
486 else if (procdata->pathsize < (siz1+siz2))
488 procdata->pathsize = siz1+siz2;
489 procdata->original_filename = x2nrealloc (procdata->original_filename,
490 &procdata->pathsize,
495 static int
496 visit_old_format(struct process_data *procdata, void *context)
498 register size_t i;
499 (void) context;
501 if (EOF == procdata->c)
502 return VISIT_ABORT;
504 /* Get the offset in the path where this path info starts. */
505 if (procdata->c == LOCATEDB_OLD_ESCAPE)
507 int minval, maxval;
508 int word;
510 procdata->count -= LOCATEDB_OLD_OFFSET;
511 minval = (0 - procdata->count);
512 if (procdata->count >= 0)
513 maxval = (procdata->len - procdata->count);
514 else
515 maxval = (procdata->len - 0);
516 word = getword(procdata->fp, procdata->dbfile,
517 minval, maxval, &procdata->endian_state);
518 procdata->count += word;
519 assert(procdata->count >= 0);
521 else
523 procdata->count += (procdata->c - LOCATEDB_OLD_OFFSET);
524 assert(procdata->count >= 0);
527 /* Overlay the old path with the remainder of the new. Read
528 * more data until we get to the next filename.
530 for (i=procdata->count;
531 (procdata->c = getc (procdata->fp)) > LOCATEDB_OLD_ESCAPE;)
533 if (EOF == procdata->c)
534 break;
536 if (procdata->c < 0200)
538 /* An ordinary character. */
539 extend (procdata, i, 1u);
540 procdata->original_filename[i++] = procdata->c;
542 else
544 /* Bigram markers have the high bit set. */
545 extend (procdata, i, 2u);
546 procdata->c &= 0177;
547 procdata->original_filename[i++] = procdata->bigram1[procdata->c];
548 procdata->original_filename[i++] = procdata->bigram2[procdata->c];
552 /* Consider the case where we executed the loop body zero times; we
553 * still need space for the terminating null byte.
555 extend (procdata, i, 1u);
556 procdata->original_filename[i] = 0;
557 procdata->len = i;
558 procdata->munged_filename = procdata->original_filename;
560 return VISIT_CONTINUE;
563 static int
564 visit_locate02_format(struct process_data *procdata, void *context)
566 register char *s;
567 int nread;
568 (void) context;
570 if (procdata->slocatedb_format)
572 if (procdata->itemcount == 0)
574 ungetc(procdata->c, procdata->fp);
575 procdata->count = 0;
576 procdata->len = 0;
578 else if (procdata->itemcount == 1)
580 procdata->count = procdata->len-1;
582 else
584 if (procdata->c == LOCATEDB_ESCAPE)
585 procdata->count += (short)get_short (procdata->fp);
586 else if (procdata->c > 127)
587 procdata->count += procdata->c - 256;
588 else
589 procdata->count += procdata->c;
592 else
594 if (procdata->c == LOCATEDB_ESCAPE)
595 procdata->count += (short)get_short (procdata->fp);
596 else if (procdata->c > 127)
597 procdata->count += procdata->c - 256;
598 else
599 procdata->count += procdata->c;
602 if (procdata->count > procdata->len || procdata->count < 0)
604 /* This should not happen generally , but since we're
605 * reading in data which is outside our control, we
606 * cannot prevent it.
608 error(1, 0, _("locate database %s is corrupt or invalid"),
609 quotearg_n_style(0, locale_quoting_style, procdata->dbfile));
612 /* Overlay the old path with the remainder of the new. */
613 nread = locate_read_str (&procdata->original_filename,
614 &procdata->pathsize,
615 procdata->fp, 0, procdata->count);
616 if (nread < 0)
617 return VISIT_ABORT;
618 procdata->c = getc (procdata->fp);
619 procdata->len = procdata->count + nread;
620 s = procdata->original_filename + procdata->len - 1; /* Move to the last char in path. */
621 assert (s[0] != '\0');
622 assert (s[1] == '\0'); /* Our terminator. */
623 assert (s[2] == '\0'); /* Added by locate_read_str. */
625 procdata->munged_filename = procdata->original_filename;
627 if (procdata->slocatedb_format)
629 /* Don't increment indefinitely, it might overflow. */
630 if (procdata->itemcount < 6)
632 ++(procdata->itemcount);
637 return VISIT_CONTINUE;
640 static int
641 visit_basename(struct process_data *procdata, void *context)
643 (void) context;
644 procdata->munged_filename = last_component (procdata->original_filename);
646 return VISIT_CONTINUE;
650 /* visit_existing_follow implements -L -e */
651 static int
652 visit_existing_follow(struct process_data *procdata, void *context)
654 struct stat st;
655 (void) context;
657 /* munged_filename has been converted in some way (to lower case,
658 * or is just the base name of the file), and original_filename has not.
659 * Hence only original_filename is still actually the name of the file
660 * whose existence we would need to check.
662 if (stat(procdata->original_filename, &st) != 0)
664 return VISIT_REJECTED;
666 else
668 return VISIT_CONTINUE;
672 /* visit_non_existing_follow implements -L -E */
673 static int
674 visit_non_existing_follow(struct process_data *procdata, void *context)
676 struct stat st;
677 (void) context;
679 /* munged_filename has been converted in some way (to lower case,
680 * or is just the base name of the file), and original_filename has not.
681 * Hence only original_filename is still actually the name of the file
682 * whose existence we would need to check.
684 if (stat(procdata->original_filename, &st) == 0)
686 return VISIT_REJECTED;
688 else
690 return VISIT_CONTINUE;
694 /* visit_existing_nofollow implements -P -e */
695 static int
696 visit_existing_nofollow(struct process_data *procdata, void *context)
698 struct stat st;
699 (void) context;
701 /* munged_filename has been converted in some way (to lower case,
702 * or is just the base name of the file), and original_filename has not.
703 * Hence only original_filename is still actually the name of the file
704 * whose existence we would need to check.
706 if (lstat(procdata->original_filename, &st) != 0)
708 return VISIT_REJECTED;
710 else
712 return VISIT_CONTINUE;
716 /* visit_non_existing_nofollow implements -P -E */
717 static int
718 visit_non_existing_nofollow(struct process_data *procdata, void *context)
720 struct stat st;
721 (void) context;
723 /* munged_filename has been converted in some way (to lower case,
724 * or is just the base name of the file), and original_filename has not.
725 * Hence only original_filename is still actually the name of the file
726 * whose existence we would need to check.
728 if (lstat(procdata->original_filename, &st) == 0)
730 return VISIT_REJECTED;
732 else
734 return VISIT_CONTINUE;
738 static int
739 visit_substring_match_nocasefold_wide(struct process_data *procdata, void *context)
741 const char *pattern = context;
743 if (NULL != mbsstr(procdata->munged_filename, pattern))
744 return VISIT_ACCEPTED;
745 else
746 return VISIT_REJECTED;
749 static int
750 visit_substring_match_nocasefold_narrow(struct process_data *procdata, void *context)
752 const char *pattern = context;
753 assert(MB_CUR_MAX == 1);
754 if (NULL != strstr(procdata->munged_filename, pattern))
755 return VISIT_ACCEPTED;
756 else
757 return VISIT_REJECTED;
760 static int
761 visit_substring_match_casefold_wide(struct process_data *procdata, void *context)
763 const char *pattern = context;
765 if (NULL != mbscasestr(procdata->munged_filename, pattern))
766 return VISIT_ACCEPTED;
767 else
768 return VISIT_REJECTED;
772 static int
773 visit_substring_match_casefold_narrow(struct process_data *procdata, void *context)
775 const char *pattern = context;
777 assert(MB_CUR_MAX == 1);
778 if (NULL != strcasestr(procdata->munged_filename, pattern))
779 return VISIT_ACCEPTED;
780 else
781 return VISIT_REJECTED;
785 static int
786 visit_globmatch_nofold(struct process_data *procdata, void *context)
788 const char *glob = context;
789 if (fnmatch(glob, procdata->munged_filename, 0) != 0)
790 return VISIT_REJECTED;
791 else
792 return VISIT_ACCEPTED;
796 static int
797 visit_globmatch_casefold(struct process_data *procdata, void *context)
799 const char *glob = context;
800 if (fnmatch(glob, procdata->munged_filename, FNM_CASEFOLD) != 0)
801 return VISIT_REJECTED;
802 else
803 return VISIT_ACCEPTED;
807 static int
808 visit_regex(struct process_data *procdata, void *context)
810 struct regular_expression *p = context;
811 const size_t len = strlen(procdata->munged_filename);
813 int rv = re_search (&p->regex, procdata->munged_filename,
814 len, 0, len,
815 (struct re_registers *) NULL);
816 if (rv < 0)
818 return VISIT_REJECTED; /* no match (-1), or internal error (-2) */
820 else
822 return VISIT_ACCEPTED; /* match */
827 static int
828 visit_stats(struct process_data *procdata, void *context)
830 struct locate_stats *p = context;
831 size_t len = strlen(procdata->original_filename);
832 const char *s;
833 int highbit, whitespace, newline;
835 ++(p->total_filename_count);
836 p->total_filename_length += len;
838 highbit = whitespace = newline = 0;
839 for (s=procdata->original_filename; *s; ++s)
841 if ( (int)(*s) & 128 )
842 highbit = 1;
843 if ('\n' == *s)
845 newline = whitespace = 1;
847 else if (isspace((unsigned char)*s))
849 whitespace = 1;
853 if (highbit)
854 ++(p->highbit_filename_count);
855 if (whitespace)
856 ++(p->whitespace_count);
857 if (newline)
858 ++(p->newline_count);
860 return VISIT_CONTINUE;
864 static int
865 visit_limit(struct process_data *procdata, void *context)
867 struct locate_limits *p = context;
869 (void) procdata;
871 if (++p->items_accepted >= p->limit)
872 return VISIT_ABORT;
873 else
874 return VISIT_CONTINUE;
877 static int
878 visit_count(struct process_data *procdata, void *context)
880 struct locate_limits *p = context;
882 (void) procdata;
884 ++p->items_accepted;
885 return VISIT_CONTINUE;
888 /* Emit the statistics.
890 static void
891 print_stats(int argc, size_t database_file_size)
893 char hbuf[LONGEST_HUMAN_READABLE + 1];
895 printf(_("Locate database size: %s bytes\n"),
896 human_readable ((uintmax_t) database_file_size,
897 hbuf, human_ceiling, 1, 1));
899 printf( (results_were_filtered ?
900 _("Matching Filenames: %s ") :
901 _("All Filenames: %s ")),
902 human_readable (statistics.total_filename_count,
903 hbuf, human_ceiling, 1, 1));
904 printf(_("with a cumulative length of %s bytes"),
905 human_readable (statistics.total_filename_length,
906 hbuf, human_ceiling, 1, 1));
908 printf(_("\n\tof which %s contain whitespace, "),
909 human_readable (statistics.whitespace_count,
910 hbuf, human_ceiling, 1, 1));
911 printf(_("\n\t%s contain newline characters, "),
912 human_readable (statistics.newline_count,
913 hbuf, human_ceiling, 1, 1));
914 printf(_("\n\tand %s contain characters with the high bit set.\n"),
915 human_readable (statistics.highbit_filename_count,
916 hbuf, human_ceiling, 1, 1));
918 if (!argc)
920 if (results_were_filtered)
922 printf(_("Some filenames may have been filtered out, "
923 "so we cannot compute the compression ratio.\n"));
925 else
927 if (statistics.total_filename_length)
929 /* A negative compression ratio just means that the
930 * compressed database is larger than the list of
931 * filenames. This can happen for example for
932 * old-format databases containing a small list of short
933 * filenames, because the bigram list is 256 bytes.
935 printf(_("Compression ratio %4.2f%% (higher is better)\n"),
936 100.0 * ((double)statistics.total_filename_length
937 - (double) database_file_size)
938 / (double) statistics.total_filename_length);
940 else
942 printf(_("Compression ratio is undefined\n"));
946 printf("\n");
950 * Return nonzero if the data we read in indicates that we are
951 * looking at a LOCATE02 locate database.
953 static int
954 looking_at_gnu_locatedb (const char *data, size_t len)
956 if (len < sizeof (LOCATEDB_MAGIC))
957 return 0;
958 else if (0 == memcmp (data, LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC)))
959 return 1; /* We saw the magic byte sequence */
960 else
961 return 0;
965 * Return nonzero if the data we read in indicates that we are
966 * looking at an slocate database.
968 static int
969 looking_at_slocate_locatedb (const char *filename,
970 const char *data,
971 size_t len,
972 int *seclevel)
974 assert(len <= 2);
976 if (len < 2)
978 return 0;
980 else
982 /* Check that the magic number is a one-byte string */
983 if (0 == data[1])
985 if (isdigit((unsigned char)data[0]))
987 /* looks promising. */
988 *seclevel = (data[0] - '0');
990 if (*seclevel > 1)
992 /* Hmm, well it's probably an slocate database
993 * of some awsomely huge security level, like 2.
994 * We don't know how to handle those.
996 error(0, 0,
997 _("locate database %s looks like an slocate "
998 "database but it seems to have security level %c, "
999 "which GNU findutils does not currently support"),
1000 quotearg_n_style(0, locale_quoting_style, filename),
1001 data[1]);
1002 return 1;
1004 else
1006 return 1;
1009 else
1011 /* Not a digit. */
1012 return 0;
1015 else
1017 /* Definitely not slocate. */
1018 return 0;
1024 static int
1025 i_am_little_endian(void)
1027 union
1029 unsigned char uch[4];
1030 unsigned int ui;
1031 } u;
1032 u.ui = 0u;
1033 u.uch[0] = 1;
1034 u.uch[1] = u.uch[2] = u.uch[3] = 0;
1035 return u.ui == 1;
1041 /* Print or count the entries in DBFILE that match shell globbing patterns in
1042 ARGV. Return the number of entries matched. */
1044 static unsigned long
1045 search_one_database (int argc,
1046 char **argv,
1047 const char *dbfile,
1048 FILE *fp,
1049 off_t filesize,
1050 int ignore_case,
1051 int enable_print,
1052 int basename_only,
1053 int use_limit,
1054 struct locate_limits *plimit,
1055 int stats,
1056 int op_and,
1057 int regex,
1058 int regex_options)
1060 char *pathpart; /* A pattern to consider. */
1061 int argn; /* Index to current pattern in argv. */
1062 int nread; /* number of bytes read from an entry. */
1063 struct process_data procdata; /* Storage for data shared with visitors. */
1064 int slocate_seclevel;
1065 int oldformat;
1066 struct visitor* pvis; /* temp for determining past_pat_inspector. */
1067 const char *format_name;
1068 enum ExistenceCheckType do_check_existence;
1071 /* We may turn on existence checking for a given database.
1072 * We ensure that we can return to the previous behaviour
1073 * by using two variables, do_check_existence (which we act on)
1074 * and check_existence (whcih indicates the default before we
1075 * adjust it on the bassis of what kind of database we;re using
1077 do_check_existence = check_existence;
1080 if (ignore_case)
1081 regex_options |= RE_ICASE;
1083 oldformat = 0;
1084 procdata.endian_state = GetwordEndianStateInitial;
1085 procdata.len = procdata.count = 0;
1086 procdata.slocatedb_format = 0;
1087 procdata.itemcount = 0;
1089 procdata.dbfile = dbfile;
1090 procdata.fp = fp;
1092 /* Set up the inspection regime */
1093 inspectors = NULL;
1094 lastinspector = NULL;
1095 past_pat_inspector = NULL;
1096 results_were_filtered = false;
1097 #if 0
1098 procdata.pathsize = 1026; /* Increased as necessary by locate_read_str. */
1099 #else
1100 procdata.pathsize = 128; /* Increased as necessary by locate_read_str. */
1101 #endif
1102 procdata.original_filename = xmalloc (procdata.pathsize);
1105 nread = fread (procdata.original_filename, 1, SLOCATE_DB_MAGIC_LEN,
1106 procdata.fp);
1107 slocate_seclevel = 0;
1108 if (looking_at_slocate_locatedb(procdata.dbfile,
1109 procdata.original_filename,
1110 nread,
1111 &slocate_seclevel))
1113 error(0, 0,
1114 _("%s is an slocate database. "
1115 "Support for these is new, expect problems for now."),
1116 quotearg_n_style(0, locale_quoting_style, procdata.dbfile));
1118 /* slocate also uses frcode, but with a different header.
1119 * We handle the header here and then work with the data
1120 * in the normal way.
1122 if (slocate_seclevel > 1)
1124 /* We don't know what those security levels mean,
1125 * so do nothing further
1127 error(0, 0,
1128 _("%s is an slocate database of unsupported security level %d; skipping it."),
1129 quotearg_n_style(0, locale_quoting_style, procdata.dbfile),
1130 slocate_seclevel);
1131 return 0;
1133 else if (slocate_seclevel > 0)
1135 /* Don't show the filenames to the user if they don't exist.
1136 * Showing stats is safe since filenames are only counted
1137 * after the existence check
1139 if (ACCEPT_NON_EXISTING == check_existence)
1141 /* Do not allow the user to see a list of filenames that they
1142 * cannot stat().
1144 error(0, 0,
1145 _("You specified the -E option, but that option "
1146 "cannot be used with slocate-format databases "
1147 "with a non-zero security level. No results will be "
1148 "generated for this database.\n"));
1149 return 0;
1151 if (ACCEPT_EXISTING != do_check_existence)
1153 if (enable_print || stats)
1155 error(0, 0,
1156 _("%s is an slocate database. "
1157 "Turning on the '-e' option."),
1158 quotearg_n_style(0, locale_quoting_style, procdata.dbfile));
1160 do_check_existence = ACCEPT_EXISTING;
1163 add_visitor(visit_locate02_format, NULL);
1164 format_name = "slocate";
1165 procdata.slocatedb_format = 1;
1167 else
1169 int nread2;
1171 procdata.slocatedb_format = 0;
1172 extend (&procdata, sizeof(LOCATEDB_MAGIC), 0u);
1173 nread2 = fread (procdata.original_filename+nread, 1, sizeof (LOCATEDB_MAGIC)-nread,
1174 procdata.fp);
1175 if (looking_at_gnu_locatedb(procdata.original_filename, nread+nread2))
1177 add_visitor(visit_locate02_format, NULL);
1178 format_name = "GNU LOCATE02";
1180 else /* Use the old format */
1182 int i;
1184 nread += nread2;
1185 extend (&procdata, 256u, 0u);
1186 /* Read the list of the most common bigrams in the database. */
1187 if (nread < 256)
1189 int more_read = fread (procdata.original_filename + nread, 1,
1190 256 - nread, procdata.fp);
1191 if ( (more_read + nread) != 256 )
1193 error(1, 0,
1194 _("Old-format locate database %s is "
1195 "too short to be valid"),
1196 quotearg_n_style(0, locale_quoting_style, dbfile));
1201 for (i = 0; i < 128; i++)
1203 procdata.bigram1[i] = procdata.original_filename[i << 1];
1204 procdata.bigram2[i] = procdata.original_filename[(i << 1) + 1];
1206 format_name = "old";
1207 oldformat = 1;
1208 add_visitor(visit_old_format, NULL);
1212 if (basename_only)
1213 add_visitor(visit_basename, NULL);
1215 /* Add an inspector for each pattern we're looking for. */
1216 for ( argn = 0; argn < argc; argn++ )
1218 results_were_filtered = true;
1219 pathpart = argv[argn];
1220 if (regex)
1222 struct regular_expression *p = xmalloc(sizeof(*p));
1223 const char *error_message = NULL;
1225 memset (&p->regex, 0, sizeof (p->regex));
1227 re_set_syntax(regex_options);
1228 p->regex.allocated = 100;
1229 p->regex.buffer = xmalloc (p->regex.allocated);
1230 p->regex.fastmap = NULL;
1231 p->regex.syntax = regex_options;
1232 p->regex.translate = NULL;
1234 error_message = re_compile_pattern (pathpart, strlen (pathpart),
1235 &p->regex);
1236 if (error_message)
1238 error (1, 0, "%s", error_message);
1240 else
1242 add_visitor(visit_regex, p);
1245 else if (contains_metacharacter(pathpart))
1247 if (ignore_case)
1248 add_visitor(visit_globmatch_casefold, pathpart);
1249 else
1250 add_visitor(visit_globmatch_nofold, pathpart);
1252 else
1254 /* No glob characters used. Hence we match on
1255 * _any part_ of the filename, not just the
1256 * basename. This seems odd to me, but it is the
1257 * traditional behaviour.
1258 * James Youngman <jay@gnu.org>
1260 visitfunc matcher;
1261 if (1 == MB_CUR_MAX)
1263 /* As an optimisation, use a strstr() matcher if we are
1264 * in a unibyte locale. This can give a x2 speedup in
1265 * the C locale. Some light testing reveals that
1266 * glibc's strstr() is somewhere around 40% faster than
1267 * gnulib's, so we just use strstr().
1269 matcher = ignore_case ?
1270 visit_substring_match_casefold_narrow :
1271 visit_substring_match_nocasefold_narrow;
1273 else
1275 matcher = ignore_case ?
1276 visit_substring_match_casefold_wide :
1277 visit_substring_match_nocasefold_wide;
1279 add_visitor(matcher, pathpart);
1283 pvis = lastinspector;
1285 /* We add visit_existing_*() as late as possible to reduce the
1286 * number of stat() calls.
1288 switch (do_check_existence)
1290 case ACCEPT_EXISTING:
1291 results_were_filtered = true;
1292 if (follow_symlinks) /* -L, default */
1293 add_visitor(visit_existing_follow, NULL);
1294 else /* -P */
1295 add_visitor(visit_existing_nofollow, NULL);
1296 break;
1298 case ACCEPT_NON_EXISTING:
1299 results_were_filtered = true;
1300 if (follow_symlinks) /* -L, default */
1301 add_visitor(visit_non_existing_follow, NULL);
1302 else /* -P */
1303 add_visitor(visit_non_existing_nofollow, NULL);
1304 break;
1306 case ACCEPT_EITHER: /* Default, neither -E nor -e */
1307 /* do nothing; no extra processing. */
1308 break;
1311 /* Security issue: The stats visitor must be added immediately
1312 * before the print visitor, because otherwise the -S option would
1313 * leak information about files that the caller cannot see.
1315 if (stats)
1316 add_visitor(visit_stats, &statistics);
1318 if (enable_print)
1320 if (print_quoted_filename)
1321 add_visitor(visit_justprint_quoted, NULL);
1322 else
1323 add_visitor(visit_justprint_unquoted, NULL);
1327 if (use_limit)
1328 add_visitor(visit_limit, plimit);
1329 else
1330 add_visitor(visit_count, plimit);
1333 if (argc > 1)
1335 past_pat_inspector = pvis->next;
1336 if (op_and)
1337 mainprocessor = process_and;
1338 else
1339 mainprocessor = process_or;
1341 else
1342 mainprocessor = process_simple;
1344 if (stats)
1346 printf(_("Database %s is in the %s format.\n"),
1347 procdata.dbfile,
1348 format_name);
1352 procdata.c = getc (procdata.fp);
1353 /* If we are searching for filename patterns, the inspector list
1354 * will contain an entry for each pattern for which we are searching.
1356 while ( (procdata.c != EOF) &&
1357 (VISIT_ABORT != (mainprocessor)(&procdata)) )
1359 /* Do nothing; all the work is done in the visitor functions. */
1362 if (stats)
1364 if (oldformat)
1366 int host_little_endian = i_am_little_endian();
1367 const char *little = _("The database has little-endian "
1368 "machine-word encoding.\n");
1369 const char *big = _("The database has big-endian "
1370 "machine-word encoding.\n");
1372 if (GetwordEndianStateNative == procdata.endian_state)
1374 printf("%s", (host_little_endian ? little : big));
1376 else if (GetwordEndianStateSwab == procdata.endian_state)
1378 printf("%s", (host_little_endian ? big : little));
1380 else
1382 printf(_("The database machine-word encoding order "
1383 "is not obvious.\n"));
1386 if (filesize)
1387 print_stats(argc, filesize);
1390 if (ferror (procdata.fp))
1392 error (0, errno, "%s",
1393 quotearg_n_style(0, locale_quoting_style, procdata.dbfile));
1394 return 0;
1396 return plimit->items_accepted;
1402 extern char *version_string;
1404 /* The name this program was run with. */
1405 char *program_name;
1407 static void
1408 usage (FILE *stream)
1410 fprintf (stream, _("\
1411 Usage: %s [-d path | --database=path] [-e | -E | --[non-]existing]\n\
1412 [-i | --ignore-case] [-w | --wholename] [-b | --basename] \n\
1413 [--limit=N | -l N] [-S | --statistics] [-0 | --null] [-c | --count]\n\
1414 [-P | -H | --nofollow] [-L | --follow] [-m | --mmap ] [ -s | --stdio ]\n\
1415 [-A | --all] [-p | --print] [-r | --regex ] [--regextype=TYPE]\n\
1416 [--max-database-age D] [--version] [--help]\n\
1417 pattern...\n"),
1418 program_name);
1419 fputs (_("\nReport bugs to <bug-findutils@gnu.org>.\n"), stream);
1421 enum
1423 REGEXTYPE_OPTION = CHAR_MAX + 1,
1424 MAX_DB_AGE
1428 static struct option const longopts[] =
1430 {"database", required_argument, NULL, 'd'},
1431 {"existing", no_argument, NULL, 'e'},
1432 {"non-existing", no_argument, NULL, 'E'},
1433 {"ignore-case", no_argument, NULL, 'i'},
1434 {"all", no_argument, NULL, 'A'},
1435 {"help", no_argument, NULL, 'h'},
1436 {"version", no_argument, NULL, 'v'},
1437 {"null", no_argument, NULL, '0'},
1438 {"count", no_argument, NULL, 'c'},
1439 {"wholename", no_argument, NULL, 'w'},
1440 {"wholepath", no_argument, NULL, 'w'}, /* Synonym. */
1441 {"basename", no_argument, NULL, 'b'},
1442 {"print", no_argument, NULL, 'p'},
1443 {"stdio", no_argument, NULL, 's'},
1444 {"mmap", no_argument, NULL, 'm'},
1445 {"limit", required_argument, NULL, 'l'},
1446 {"regex", no_argument, NULL, 'r'},
1447 {"regextype", required_argument, NULL, REGEXTYPE_OPTION},
1448 {"statistics", no_argument, NULL, 'S'},
1449 {"follow", no_argument, NULL, 'L'},
1450 {"nofollow", no_argument, NULL, 'P'},
1451 {"max-database-age", required_argument, NULL, MAX_DB_AGE},
1452 {NULL, no_argument, NULL, 0}
1456 static int
1457 drop_privs(void)
1459 const char * what = "failed";
1460 const uid_t orig_euid = geteuid();
1461 const uid_t uid = getuid();
1462 const gid_t gid = getgid();
1464 #if HAVE_SETGROUPS
1465 /* Use of setgroups() is restricted to root only. */
1466 if (0 == orig_euid)
1468 /* We're either root or running setuid-root. */
1469 gid_t groups[1];
1470 groups[0] = gid;
1471 if (0 != setgroups(1u, groups))
1473 what = _("failed to drop group privileges");
1474 goto fail;
1477 #endif
1479 /* Drop any setuid privileges */
1480 if (uid != orig_euid)
1482 if (0 == uid)
1484 /* We're really root anyway, but are setuid to something else. Leave it. */
1486 else
1488 errno = 0;
1489 if (0 != setuid(getuid()))
1491 what = _("failed to drop setuid privileges");
1492 goto fail;
1495 /* Defend against the case where the attacker runs us with the
1496 * capability to call setuid() turned off, which on some systems
1497 * will cause the above attempt to drop privileges fail (leaving us
1498 * privileged).
1500 else
1502 /* Check that we can no longer switch bask to root */
1503 if (0 == setuid(0))
1505 what = _("Failed to fully drop privileges");
1506 /* The errno value here is not interesting (since
1507 * the system call we are complaining about
1508 * succeeded when we wanted it to fail). Arrange
1509 * for the call to error() not to print the errno
1510 * value by setting errno=0.
1512 errno = 0;
1513 goto fail;
1519 /* Drop any setgid privileges */
1520 errno = 0;
1521 if (0 != setgid(gid))
1523 what = _("failed to drop setgid privileges");
1524 goto fail;
1527 /* success. */
1528 return 0;
1530 fail:
1531 error(1, errno, "%s",
1532 quotearg_n_style(0, locale_quoting_style, what));
1533 abort();
1534 kill(0, SIGKILL);
1535 _exit(1);
1536 /*NOTREACHED*/
1537 /* ... we hope. */
1538 for (;;)
1540 /* deliberate infinite loop */
1544 static int
1545 opendb(const char *name)
1547 int fd = open(name, O_RDONLY
1548 #if defined O_LARGEFILE
1549 |O_LARGEFILE
1550 #endif
1552 if (fd >= 0)
1554 /* Make sure it won't survive an exec */
1555 if (0 != fcntl(fd, F_SETFD, FD_CLOEXEC))
1557 close(fd);
1558 fd = -1;
1561 return fd;
1565 dolocate (int argc, char **argv, int secure_db_fd)
1567 char *dbpath;
1568 unsigned long int found = 0uL;
1569 int ignore_case = 0;
1570 int print = 0;
1571 int just_count = 0;
1572 int basename_only = 0;
1573 int use_limit = 0;
1574 int regex = 0;
1575 int regex_options = RE_SYNTAX_EMACS;
1576 int stats = 0;
1577 int op_and = 0;
1578 const char *e;
1579 FILE *fp;
1580 int they_chose_db = 0;
1581 bool did_stdin = false; /* Set to prevent rereading stdin. */
1583 program_name = argv[0];
1585 #ifdef HAVE_SETLOCALE
1586 setlocale (LC_ALL, "");
1587 #endif
1588 bindtextdomain (PACKAGE, LOCALEDIR);
1589 textdomain (PACKAGE);
1590 atexit (close_stdout);
1592 limits.limit = 0;
1593 limits.items_accepted = 0;
1595 quote_opts = clone_quoting_options (NULL);
1596 print_quoted_filename = true;
1598 /* We cannot simultaneously trust $LOCATE_PATH and use the
1599 * setuid-access-controlled database,, since that could cause a leak
1600 * of private data.
1602 dbpath = getenv ("LOCATE_PATH");
1603 if (dbpath)
1605 they_chose_db = 1;
1608 check_existence = ACCEPT_EITHER;
1610 for (;;)
1612 int opti = -1;
1613 int optc = getopt_long (argc, argv, "Abcd:eEil:prsm0SwHPL", longopts,
1614 &opti);
1615 if (optc == -1)
1616 break;
1618 switch (optc)
1620 case '0':
1621 separator = 0;
1622 print_quoted_filename = false; /* print filename 'raw'. */
1623 break;
1625 case 'A':
1626 op_and = 1;
1627 break;
1629 case 'b':
1630 basename_only = 1;
1631 break;
1633 case 'c':
1634 just_count = 1;
1635 break;
1637 case 'd':
1638 dbpath = optarg;
1639 they_chose_db = 1;
1640 break;
1642 case 'e':
1643 check_existence = ACCEPT_EXISTING;
1644 break;
1646 case 'E':
1647 check_existence = ACCEPT_NON_EXISTING;
1648 break;
1650 case 'i':
1651 ignore_case = 1;
1652 break;
1654 case 'h':
1655 usage (stdout);
1656 return 0;
1658 case MAX_DB_AGE:
1659 /* XXX: nothing in the test suite for this option. */
1660 set_max_db_age (optarg);
1661 break;
1663 case 'p':
1664 print = 1;
1665 break;
1667 case 'v':
1668 display_findutils_version ("locate");
1669 return 0;
1671 case 'w':
1672 basename_only = 0;
1673 break;
1675 case 'r':
1676 regex = 1;
1677 break;
1679 case REGEXTYPE_OPTION:
1680 regex_options = get_regex_type (optarg);
1681 break;
1683 case 'S':
1684 stats = 1;
1685 break;
1687 case 'L':
1688 follow_symlinks = 1;
1689 break;
1691 /* In find, -P and -H differ in the way they handle paths
1692 * given on the command line. This is not relevant for
1693 * locate, but the -H option is supported because it is
1694 * probably more intuitive to do so.
1696 case 'P':
1697 case 'H':
1698 follow_symlinks = 0;
1699 break;
1701 case 'l':
1703 char *end = optarg;
1704 strtol_error err = xstrtoumax (optarg, &end, 10, &limits.limit,
1705 NULL);
1706 if (LONGINT_OK != err)
1707 xstrtol_fatal (err, opti, optc, longopts, optarg);
1708 use_limit = 1;
1710 break;
1712 case 's': /* use stdio */
1713 case 'm': /* use mmap */
1714 /* These options are implemented simply for
1715 * compatibility with FreeBSD
1717 break;
1719 default:
1720 usage (stderr);
1721 return 1;
1726 /* If the user gave the -d option or set LOCATE_PATH,
1727 * relinquish access to the secure database.
1729 if (they_chose_db)
1731 if (secure_db_fd >= 0)
1733 close(secure_db_fd);
1734 secure_db_fd = -1;
1738 if (!just_count && !stats)
1739 print = 1;
1741 if (stats)
1743 if (optind == argc)
1744 use_limit = 0;
1746 else
1748 if (!just_count && optind == argc)
1750 usage (stderr);
1751 return 1;
1756 if (1 == isatty(STDOUT_FILENO))
1757 stdout_is_a_tty = true;
1758 else
1759 stdout_is_a_tty = false;
1761 if (they_chose_db)
1762 next_element (dbpath, 0); /* Initialize. */
1764 /* Bail out early if limit already reached. */
1765 while (!use_limit || limits.limit > limits.items_accepted)
1767 struct stat st;
1768 int fd;
1769 off_t filesize;
1771 statistics.compressed_bytes =
1772 statistics.total_filename_count =
1773 statistics.total_filename_length =
1774 statistics.whitespace_count =
1775 statistics.newline_count =
1776 statistics.highbit_filename_count = 0u;
1778 if (they_chose_db)
1780 /* Take the next element from the list of databases */
1781 e = next_element ((char *) NULL, 0);
1782 if (NULL == e)
1783 break;
1785 if (0 == strcmp (e, "-"))
1787 if (did_stdin)
1789 error (0, 0,
1790 _("warning: the locate database can only be read from stdin once."));
1791 return 0;
1793 else
1795 e = "<stdin>";
1796 fd = 0;
1797 did_stdin = true;
1800 else
1802 if (0 == strlen(e) || 0 == strcmp(e, "."))
1804 e = LOCATE_DB;
1807 /* open the database */
1808 fd = opendb(e);
1809 if (fd < 0)
1811 error (0, errno, "%s",
1812 quotearg_n_style(0, locale_quoting_style, e));
1813 return 0;
1817 else
1819 if (-1 == secure_db_fd)
1821 /* Already searched the database, it's time to exit the loop */
1822 break;
1824 else
1826 e = selected_secure_db;
1827 fd = secure_db_fd;
1828 secure_db_fd = -1;
1832 /* Check the database to see if it is old. */
1833 if (fstat(fd, &st))
1835 error (0, errno, "%s",
1836 quotearg_n_style(0, locale_quoting_style, e));
1837 /* continue anyway */
1838 filesize = (off_t)0;
1840 else
1842 time_t now;
1844 filesize = st.st_size;
1846 if ((time_t)-1 == time(&now))
1848 /* If we can't tell the time, we don't know how old the
1849 * database is. But since the message is just advisory,
1850 * we continue anyway.
1852 error (0, errno, _("time system call failed"));
1854 else
1856 double age = difftime(now, st.st_mtime);
1857 double warn_seconds = SECONDS_PER_UNIT * warn_number_units;
1858 if (age > warn_seconds)
1860 /* For example:
1861 warning: database `fred' is more than 8 days old (actual age is 10 days)*/
1862 error (0, 0,
1863 _("warning: database %s is more than %d %s old (actual age is %.1f %s)"),
1864 quotearg_n_style(0, locale_quoting_style, e),
1865 warn_number_units, _(warn_name_units),
1866 (age/(double)SECONDS_PER_UNIT), _(warn_name_units));
1871 fp = fdopen(fd, "r");
1872 if (NULL == fp)
1874 error (0, errno, "%s",
1875 quotearg_n_style(0, locale_quoting_style, e));
1876 return 0;
1879 /* Search this database for all patterns simultaneously */
1880 found = search_one_database (argc - optind, &argv[optind],
1881 e, fp, filesize,
1882 ignore_case, print, basename_only,
1883 use_limit, &limits, stats,
1884 op_and, regex, regex_options);
1886 /* Close the databsase (even if it is stdin) */
1887 if (fclose (fp) == EOF)
1889 error (0, errno, "%s",
1890 quotearg_n_style(0, locale_quoting_style, e));
1891 return 0;
1895 if (just_count)
1897 printf("%ld\n", found);
1900 if (found || (use_limit && (limits.limit==0)) || stats )
1901 return 0;
1902 else
1903 return 1;
1906 #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
1907 static int
1908 open_secure_db(void)
1910 int fd, i;
1912 const char * secure_db_list[] =
1914 LOCATE_DB,
1915 "/var/lib/slocate/slocate.db",
1916 NULL
1918 for (i=0; secure_db_list[i]; ++i)
1920 fd = opendb(secure_db_list[i]);
1921 if (fd >= 0)
1923 selected_secure_db = secure_db_list[i];
1924 return fd;
1927 return -1;
1931 main (int argc, char **argv)
1933 int dbfd = open_secure_db();
1934 drop_privs();
1936 return dolocate(argc, argv, dbfd);