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 2, or (at your option)
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, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
21 /* Usage: locate [options] pattern...
23 Scan a pathname list for the full pathname of a file, given only
24 a piece of the name (possibly containing shell globbing metacharacters).
25 The list has been processed with front-compression, which reduces
26 the list size by a factor of 4-5.
27 Recognizes two database formats, old and new. The old format is
28 bigram coded, which reduces space by a further 20-25% and uses the
29 following encoding of the database bytes:
31 0-28 likeliest differential counts + offset (14) to make nonnegative
32 30 escape code for out-of-range count to follow in next halfword
33 128-255 bigram codes (the 128 most common, as determined by `updatedb')
34 32-127 single character (printable) ASCII remainder
36 Earlier versions of GNU locate used to use a novel two-tiered
37 string search technique, which was described in Usenix ;login:, Vol
38 8, No 1, February/March, 1983, p. 8.
40 However, latterly code changes to provide additional functionality
41 became dificult to make with the existing reading scheme, and so
42 we no longer perform the matching as efficiently as we used to (that is,
43 we no longer use the same algorithm).
45 The old algorithm was:
47 First, match a metacharacter-free subpattern and a partial
48 pathname BACKWARDS to avoid full expansion of the pathname list.
49 The time savings is 40-50% over forward matching, which cannot
50 efficiently handle overlapped search patterns and compressed
53 Then, match the actual shell glob pattern (if in this form)
54 against the candidate pathnames using the slower shell filename
58 Written by James A. Woods <jwoods@adobe.com>.
59 Modified by David MacKenzie <djm@gnu.org>.
60 Additional work by James Youngman and Bas van Gompel.
67 #include <sys/types.h>
74 /* The presence of unistd.h is assumed by gnulib these days, so we
75 * might as well assume it too.
77 /* We need <unistd.h> for isatty(). */
88 #if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
110 # include <libintl.h>
111 # define _(Text) gettext (Text)
113 # define _(Text) Text
114 #define textdomain(Domain)
115 #define bindtextdomain(Package, Directory)
118 # define N_(String) gettext_noop (String)
120 /* We used to use (String) instead of just String, but apparentl;y ISO C
121 * doesn't allow this (at least, that's what HP said when someone reported
122 * this as a compiler bug). This is HP case number 1205608192. See
123 * also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11250 (which references
124 * ANSI 3.5.7p14-15). The Intel icc compiler also rejects constructs
125 * like: static const char buf[] = ("string");
127 # define N_(String) String
130 #include "locatedb.h"
132 #include "../gnulib/lib/xalloc.h"
133 #include "../gnulib/lib/error.h"
134 #include "../gnulib/lib/human.h"
136 #include "closeout.h"
137 #include "nextelem.h"
140 #include "quotearg.h"
141 #include "printquoted.h"
142 #include "regextype.h"
145 /* Note that this evaluates C many times. */
147 # define TOUPPER(Ch) toupper (Ch)
148 # define TOLOWER(Ch) tolower (Ch)
150 # define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
151 # define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
154 /* typedef enum {false, true} boolean; */
156 /* Warn if a database is older than this. 8 days allows for a weekly
157 update that takes up to a day to perform. */
158 static unsigned int warn_number_units
= 8;
160 /* Printable name of units used in WARN_SECONDS */
161 static const char warn_name_units
[] = N_("days");
162 #define SECONDS_PER_UNIT (60 * 60 * 24)
166 VISIT_CONTINUE
= 1, /* please call the next visitor */
167 VISIT_ACCEPTED
= 2, /* accepted, call no futher callbacks for this file */
168 VISIT_REJECTED
= 4, /* rejected, process next file. */
169 VISIT_ABORT
= 8 /* rejected, process no more files. */
172 enum ExistenceCheckType
174 ACCEPT_EITHER
, /* Corresponds to lack of -E/-e option */
175 ACCEPT_EXISTING
, /* Corresponds to option -e */
176 ACCEPT_NON_EXISTING
/* Corresponds to option -E */
179 /* Check for existence of files before printing them out? */
180 enum ExistenceCheckType check_existence
= ACCEPT_EITHER
;
182 static int follow_symlinks
= 1;
184 /* What to separate the results with. */
185 static int separator
= '\n';
187 static struct quoting_options
* quote_opts
= NULL
;
188 static bool stdout_is_a_tty
;
189 static bool print_quoted_filename
;
190 static bool results_were_filtered
;
192 static char* slocate_db_pathname
= "/var/lib/slocate/slocate.db";
194 static const char *selected_secure_db
= NULL
;
197 /* Change the number of days old the database can be
198 * before we complain about it.
201 set_max_db_age(const char *s
)
204 unsigned long int val
;
205 /* XXX: we ignore the case where the input is negative, which is allowed(!). */
210 _("The argument argument for option --max-database-age must not be empty"),
215 /* We have to set errno here, otherwise when the function returns ULONG_MAX,
216 * we would not be able to tell if that is the correct answer, or whether it
217 * signifies an error.
220 val
= strtoul(s
, &end
, 10);
222 /* Diagnose number too large, non-numbes and trailing junk. */
223 if ((ULONG_MAX
== val
&& ERANGE
== errno
) ||
224 (0 == val
&& EINVAL
== errno
))
227 _("Invalid argument `%s' for option --max-database-age"),
232 /* errno wasn't set, don't print its message */
234 _("Invalid argument `%s' for option --max-database-age"),
239 warn_number_units
= val
;
245 /* Read in a 16-bit int, high byte first (network byte order). */
253 x
= (signed char) fgetc (fp
) << 8;
254 x
|= (fgetc (fp
) & 0xff);
258 const char * const metacharacters
= "*?[]\\";
260 /* Return nonzero if S contains any shell glob characters.
263 contains_metacharacter(const char *s
)
265 if (NULL
== strpbrk(s
, metacharacters
))
273 * Read bytes from FP into the buffer at offset OFFSET in (*BUF),
274 * until we reach DELIMITER or end-of-file. We reallocate the buffer
275 * as necessary, altering (*BUF) and (*SIZ) as appropriate. No assumption
276 * is made regarding the content of the data (i.e. the implementation is
277 * 8-bit clean, the only delimiter is DELIMITER).
279 * Written Fri May 23 18:41:16 2003 by James Youngman, because getstr()
280 * has been removed from gnulib.
282 * We call the function locate_read_str() to avoid a name clash with the curses
286 locate_read_str(char **buf
, size_t *siz
, FILE *fp
, int delimiter
, int offs
)
293 nread
= getdelim(&p
, &sz
, delimiter
, fp
);
298 needed
= offs
+ nread
+ 1u;
301 char *pnew
= realloc(*buf
, needed
);
304 return -1; /* FAIL */
312 memcpy((*buf
)+offs
, p
, nread
);
320 lc_strcpy(char *dest
, const char *src
)
324 *dest
++ = TOLOWER(*src
);
333 uintmax_t items_accepted
;
335 static struct locate_limits limits
;
340 uintmax_t compressed_bytes
;
341 uintmax_t total_filename_count
;
342 uintmax_t total_filename_length
;
343 uintmax_t whitespace_count
;
344 uintmax_t newline_count
;
345 uintmax_t highbit_filename_count
;
347 static struct locate_stats statistics
;
356 static struct stringbuf casebuf
;
362 struct stringbuf
*pbuf
;
365 struct regular_expression
367 struct re_pattern_buffer regex
; /* for --regex */
373 int c
; /* An input byte. */
374 char itemcount
; /* Indicates we're at the beginning of an slocate db. */
375 int count
; /* The length of the prefix shared with the previous database entry. */
377 char *original_filename
; /* The current input database entry. */
378 size_t pathsize
; /* Amount allocated for it. */
379 char *munged_filename
; /* path or base_name(path) */
380 FILE *fp
; /* The pathname database. */
381 const char *dbfile
; /* Its name, or "<stdin>" */
382 int slocatedb_format
; /* Allows us to cope with slocate's format variant */
383 /* for the old database format,
384 the first and second characters of the most common bigrams. */
390 typedef int (*visitfunc
)(struct process_data
*procdata
,
397 struct visitor
*next
;
401 static struct visitor
*inspectors
= NULL
;
402 static struct visitor
*lastinspector
= NULL
;
403 static struct visitor
*past_pat_inspector
= NULL
;
405 /* 0 or 1 pattern(s) */
407 process_simple(struct process_data
*procdata
)
409 int result
= VISIT_CONTINUE
;
410 const struct visitor
*p
= inspectors
;
412 while ( ((VISIT_CONTINUE
| VISIT_ACCEPTED
) & result
) && (NULL
!= p
) )
414 result
= (p
->inspector
)(procdata
, p
->context
);
421 /* Accept if any pattern matches. */
423 process_or (struct process_data
*procdata
)
425 int result
= VISIT_CONTINUE
;
426 const struct visitor
*p
= inspectors
;
428 while ( ((VISIT_CONTINUE
| VISIT_REJECTED
) & result
) && (past_pat_inspector
!= p
) )
430 result
= (p
->inspector
)(procdata
, p
->context
);
434 if (result
== VISIT_CONTINUE
)
435 result
= VISIT_REJECTED
;
436 if (result
& (VISIT_ABORT
| VISIT_REJECTED
))
439 p
= past_pat_inspector
;
440 result
= VISIT_CONTINUE
;
442 while ( (VISIT_CONTINUE
== result
) && (NULL
!= p
) )
444 result
= (p
->inspector
)(procdata
, p
->context
);
448 if (VISIT_CONTINUE
== result
)
449 return VISIT_ACCEPTED
;
454 /* Accept if all pattern match. */
456 process_and (struct process_data
*procdata
)
458 int result
= VISIT_CONTINUE
;
459 const struct visitor
*p
= inspectors
;
461 while ( ((VISIT_CONTINUE
| VISIT_ACCEPTED
) & result
) && (past_pat_inspector
!= p
) )
463 result
= (p
->inspector
)(procdata
, p
->context
);
467 if (result
== VISIT_CONTINUE
)
468 result
= VISIT_REJECTED
;
469 if (result
& (VISIT_ABORT
| VISIT_REJECTED
))
472 p
= past_pat_inspector
;
473 result
= VISIT_CONTINUE
;
475 while ( (VISIT_CONTINUE
== result
) && (NULL
!= p
) )
477 result
= (p
->inspector
)(procdata
, p
->context
);
481 if (VISIT_CONTINUE
== result
)
482 return VISIT_ACCEPTED
;
487 typedef int (*processfunc
)(struct process_data
*procdata
);
489 static processfunc mainprocessor
= NULL
;
492 add_visitor(visitfunc fn
, void *context
)
494 struct visitor
*p
= xmalloc(sizeof(struct visitor
));
496 p
->context
= context
;
499 if (NULL
== lastinspector
)
501 lastinspector
= inspectors
= p
;
505 lastinspector
->next
= p
;
513 visit_justprint_quoted(struct process_data
*procdata
, void *context
)
516 print_quoted (stdout
, quote_opts
, stdout_is_a_tty
,
518 procdata
->original_filename
);
520 return VISIT_CONTINUE
;
524 visit_justprint_unquoted(struct process_data
*procdata
, void *context
)
527 fputs(procdata
->original_filename
, stdout
);
529 return VISIT_CONTINUE
;
533 visit_old_format(struct process_data
*procdata
, void *context
)
538 /* Get the offset in the path where this path info starts. */
539 if (procdata
->c
== LOCATEDB_OLD_ESCAPE
)
540 procdata
->count
+= getw (procdata
->fp
) - LOCATEDB_OLD_OFFSET
;
542 procdata
->count
+= procdata
->c
- LOCATEDB_OLD_OFFSET
;
544 /* Overlay the old path with the remainder of the new. */
545 for (s
= procdata
->original_filename
+ procdata
->count
;
546 (procdata
->c
= getc (procdata
->fp
)) > LOCATEDB_OLD_ESCAPE
;)
547 if (procdata
->c
< 0200)
548 *s
++ = procdata
->c
; /* An ordinary character. */
551 /* Bigram markers have the high bit set. */
553 *s
++ = procdata
->bigram1
[procdata
->c
];
554 *s
++ = procdata
->bigram2
[procdata
->c
];
558 procdata
->munged_filename
= procdata
->original_filename
;
560 return VISIT_CONTINUE
;
565 visit_locate02_format(struct process_data
*procdata
, void *context
)
571 if (procdata
->slocatedb_format
)
573 if (procdata
->itemcount
== 0)
575 ungetc(procdata
->c
, procdata
->fp
);
579 else if (procdata
->itemcount
== 1)
581 procdata
->count
= procdata
->len
-1;
585 if (procdata
->c
== LOCATEDB_ESCAPE
)
586 procdata
->count
+= (short)get_short (procdata
->fp
);
587 else if (procdata
->c
> 127)
588 procdata
->count
+= procdata
->c
- 256;
590 procdata
->count
+= procdata
->c
;
595 if (procdata
->c
== LOCATEDB_ESCAPE
)
596 procdata
->count
+= (short)get_short (procdata
->fp
);
597 else if (procdata
->c
> 127)
598 procdata
->count
+= procdata
->c
- 256;
600 procdata
->count
+= procdata
->c
;
603 if (procdata
->count
> procdata
->len
|| procdata
->count
< 0)
605 /* This should not happen generally , but since we're
606 * reading in data which is outside our control, we
609 error(1, 0, _("locate database `%s' is corrupt or invalid"), procdata
->dbfile
);
612 /* Overlay the old path with the remainder of the new. */
613 nread
= locate_read_str (&procdata
->original_filename
, &procdata
->pathsize
,
614 procdata
->fp
, 0, procdata
->count
);
617 procdata
->c
= getc (procdata
->fp
);
618 procdata
->len
= procdata
->count
+ nread
;
619 s
= procdata
->original_filename
+ procdata
->len
- 1; /* Move to the last char in path. */
620 assert (s
[0] != '\0');
621 assert (s
[1] == '\0'); /* Our terminator. */
622 assert (s
[2] == '\0'); /* Added by locate_read_str. */
624 procdata
->munged_filename
= procdata
->original_filename
;
626 if (procdata
->slocatedb_format
)
628 /* Don't increment indefinitely, it might overflow. */
629 if (procdata
->itemcount
< 6)
631 ++(procdata
->itemcount
);
636 return VISIT_CONTINUE
;
640 visit_basename(struct process_data
*procdata
, void *context
)
643 procdata
->munged_filename
= base_name(procdata
->original_filename
);
645 return VISIT_CONTINUE
;
650 visit_casefold(struct process_data
*procdata
, void *context
)
652 struct stringbuf
*b
= context
;
654 if (*b
->preqlen
+1 > b
->buffersize
)
656 b
->buffer
= xrealloc(b
->buffer
, *b
->preqlen
+1); /* XXX: consider using extendbuf(). */
657 b
->buffersize
= *b
->preqlen
+1;
659 lc_strcpy(b
->buffer
, procdata
->munged_filename
);
661 return VISIT_CONTINUE
;
664 /* visit_existing_follow implements -L -e */
666 visit_existing_follow(struct process_data
*procdata
, void *context
)
671 /* munged_filename has been converted in some way (to lower case,
672 * or is just the base name of the file), and original_filename has not.
673 * Hence only original_filename is still actually the name of the file
674 * whose existence we would need to check.
676 if (stat(procdata
->original_filename
, &st
) != 0)
678 return VISIT_REJECTED
;
682 return VISIT_CONTINUE
;
686 /* visit_non_existing_follow implements -L -E */
688 visit_non_existing_follow(struct process_data
*procdata
, void *context
)
693 /* munged_filename has been converted in some way (to lower case,
694 * or is just the base name of the file), and original_filename has not.
695 * Hence only original_filename is still actually the name of the file
696 * whose existence we would need to check.
698 if (stat(procdata
->original_filename
, &st
) == 0)
700 return VISIT_REJECTED
;
704 return VISIT_CONTINUE
;
708 /* visit_existing_nofollow implements -P -e */
710 visit_existing_nofollow(struct process_data
*procdata
, void *context
)
715 /* munged_filename has been converted in some way (to lower case,
716 * or is just the base name of the file), and original_filename has not.
717 * Hence only original_filename is still actually the name of the file
718 * whose existence we would need to check.
720 if (lstat(procdata
->original_filename
, &st
) != 0)
722 return VISIT_REJECTED
;
726 return VISIT_CONTINUE
;
730 /* visit_non_existing_nofollow implements -P -E */
732 visit_non_existing_nofollow(struct process_data
*procdata
, void *context
)
737 /* munged_filename has been converted in some way (to lower case,
738 * or is just the base name of the file), and original_filename has not.
739 * Hence only original_filename is still actually the name of the file
740 * whose existence we would need to check.
742 if (lstat(procdata
->original_filename
, &st
) == 0)
744 return VISIT_REJECTED
;
748 return VISIT_CONTINUE
;
753 visit_substring_match_nocasefold(struct process_data
*procdata
, void *context
)
755 const char *pattern
= context
;
757 if (NULL
!= strstr(procdata
->munged_filename
, pattern
))
758 return VISIT_ACCEPTED
;
760 return VISIT_REJECTED
;
764 visit_substring_match_casefold(struct process_data
*procdata
, void *context
)
766 const struct casefolder
* p
= context
;
767 const struct stringbuf
* b
= p
->pbuf
;
770 if (NULL
!= strstr(b
->buffer
, p
->pattern
))
771 return VISIT_ACCEPTED
;
773 return VISIT_REJECTED
;
778 visit_globmatch_nofold(struct process_data
*procdata
, void *context
)
780 const char *glob
= context
;
781 if (fnmatch(glob
, procdata
->munged_filename
, 0) != 0)
782 return VISIT_REJECTED
;
784 return VISIT_ACCEPTED
;
789 visit_globmatch_casefold(struct process_data
*procdata
, void *context
)
791 const char *glob
= context
;
792 if (fnmatch(glob
, procdata
->munged_filename
, FNM_CASEFOLD
) != 0)
793 return VISIT_REJECTED
;
795 return VISIT_ACCEPTED
;
800 visit_regex(struct process_data
*procdata
, void *context
)
802 struct regular_expression
*p
= context
;
803 const size_t len
= strlen(procdata
->munged_filename
);
805 int rv
= re_search (&p
->regex
, procdata
->munged_filename
,
807 (struct re_registers
*) NULL
);
810 return VISIT_REJECTED
; /* no match (-1), or internal error (-2) */
814 return VISIT_ACCEPTED
; /* match */
820 visit_stats(struct process_data
*procdata
, void *context
)
822 struct locate_stats
*p
= context
;
823 size_t len
= strlen(procdata
->original_filename
);
825 int highbit
, whitespace
, newline
;
827 ++(p
->total_filename_count
);
828 p
->total_filename_length
+= len
;
830 highbit
= whitespace
= newline
= 0;
831 for (s
=procdata
->original_filename
; *s
; ++s
)
833 if ( (int)(*s
) & 128 )
837 newline
= whitespace
= 1;
839 else if (isspace((unsigned char)*s
))
846 ++(p
->highbit_filename_count
);
848 ++(p
->whitespace_count
);
850 ++(p
->newline_count
);
852 return VISIT_CONTINUE
;
857 visit_limit(struct process_data
*procdata
, void *context
)
859 struct locate_limits
*p
= context
;
863 if (++p
->items_accepted
>= p
->limit
)
866 return VISIT_CONTINUE
;
870 visit_count(struct process_data
*procdata
, void *context
)
872 struct locate_limits
*p
= context
;
877 return VISIT_CONTINUE
;
880 /* Emit the statistics.
883 print_stats(int argc
, size_t database_file_size
)
885 char hbuf
[LONGEST_HUMAN_READABLE
+ 1];
887 printf(_("Locate database size: %s bytes\n"),
888 human_readable ((uintmax_t) database_file_size
,
889 hbuf
, human_ceiling
, 1, 1));
891 printf( (results_were_filtered
?
892 _("Matching Filenames: %s ") :
893 _("All Filenames: %s ")),
894 human_readable (statistics
.total_filename_count
,
895 hbuf
, human_ceiling
, 1, 1));
896 printf(_("with a cumulative length of %s bytes"),
897 human_readable (statistics
.total_filename_length
,
898 hbuf
, human_ceiling
, 1, 1));
900 printf(_("\n\tof which %s contain whitespace, "),
901 human_readable (statistics
.whitespace_count
,
902 hbuf
, human_ceiling
, 1, 1));
903 printf(_("\n\t%s contain newline characters, "),
904 human_readable (statistics
.newline_count
,
905 hbuf
, human_ceiling
, 1, 1));
906 printf(_("\n\tand %s contain characters with the high bit set.\n"),
907 human_readable (statistics
.highbit_filename_count
,
908 hbuf
, human_ceiling
, 1, 1));
912 if (results_were_filtered
)
914 printf(_("Some filenames may have been filtered out, "
915 "so we cannot compute the compression ratio.\n"));
919 if (statistics
.total_filename_length
)
921 printf(_("Compression ratio %4.2f%%\n"),
922 100.0 * ((double)statistics
.total_filename_length
923 - (double) database_file_size
)
924 / (double) statistics
.total_filename_length
);
928 printf(_("Compression ratio is undefined\n"));
936 * Return nonzero if the data we read in indicates that we are
937 * looking at a LOCATE02 locate database.
940 looking_at_gnu_locatedb (const char *data
, size_t len
)
942 if (len
< sizeof (LOCATEDB_MAGIC
))
944 else if (0 == memcmp (data
, LOCATEDB_MAGIC
, sizeof (LOCATEDB_MAGIC
)))
945 return 1; /* We saw the magic byte sequence */
951 * Return nonzero if the data we read in indicates that we are
952 * looking at an slocate database.
955 looking_at_slocate_locatedb (const char *filename
,
960 char slocate_magic
[] = "1";
961 size_t lenwanted
= sizeof(slocate_magic
);
970 /* Check that the magic number is a one-byte string */
973 if (isdigit((unsigned char)data
[0]))
975 /* looks promising. */
976 *seclevel
= (data
[0] - '0');
980 /* Hmm, well it's probably an slocate database
981 * of some awsomely huge security level, like 2.
982 * We don't know how to handle those.
985 _("locate database `%s' looks like an slocate "
986 "database but it seems to have security level %c, "
987 "which GNU findutils does not currently support"),
999 /* Definitely not slocate. */
1005 /* Print or count the entries in DBFILE that match shell globbing patterns in
1006 ARGV. Return the number of entries matched. */
1008 static unsigned long
1009 search_one_database (int argc
,
1018 struct locate_limits
*plimit
,
1024 char *pathpart
; /* A pattern to consider. */
1025 int argn
; /* Index to current pattern in argv. */
1026 int need_fold
; /* Set when folding and any pattern is non-glob. */
1027 int nread
; /* number of bytes read from an entry. */
1028 struct process_data procdata
; /* Storage for data shared with visitors. */
1029 int slocate_seclevel
;
1030 struct visitor
* pvis
; /* temp for determining past_pat_inspector. */
1031 const char *format_name
;
1032 enum ExistenceCheckType do_check_existence
;
1035 /* We may turn on existence checking for a given database.
1036 * We ensure that we can return to the previous behaviour
1037 * by using two variables, do_check_existence (which we act on)
1038 * and check_existence (whcih indicates the default before we
1039 * adjust it on the bassis of what kind of database we;re using
1041 do_check_existence
= check_existence
;
1045 regex_options
|= RE_ICASE
;
1047 procdata
.len
= procdata
.count
= 0;
1048 procdata
.slocatedb_format
= 0;
1049 procdata
.itemcount
= 0;
1051 procdata
.dbfile
= dbfile
;
1054 /* Set up the inspection regime */
1056 lastinspector
= NULL
;
1057 past_pat_inspector
= NULL
;
1058 results_were_filtered
= false;
1060 procdata
.pathsize
= 1026; /* Increased as necessary by locate_read_str. */
1061 procdata
.original_filename
= xmalloc (procdata
.pathsize
);
1064 nread
= fread (procdata
.original_filename
, 1, SLOCATE_DB_MAGIC_LEN
,
1066 if (looking_at_slocate_locatedb(procdata
.dbfile
,
1067 procdata
.original_filename
,
1072 _("`%s' is an slocate database. "
1073 "Support for these is new, expect problems for now "
1074 "(you are, after all, using the CVS code)."),
1077 /* slocate also uses frcode, but with a different header.
1078 * We handle the header here and then work with the data
1079 * in the normal way.
1081 if (slocate_seclevel
> 1)
1083 /* We don't know what those security levels mean,
1084 * so do nothing further
1088 else if (slocate_seclevel
> 0)
1090 /* Don't show the filenames to the user if they don't exist.
1091 * Showing stats is safe since filenames are only counted
1092 * after the existence check
1094 if (ACCEPT_NON_EXISTING
== check_existence
)
1096 /* Do not allow the user to see a list of filenames that they
1100 _("You specified the -E option, but that option "
1101 "cannot be used with slocate-format databases "
1102 "with a non-zero security level. No results will be "
1103 "generated for this database.\n"));
1106 if (ACCEPT_EXISTING
!= do_check_existence
)
1108 if (enable_print
|| stats
)
1111 _("`%s' is an slocate database. "
1112 "Turning on the '-e' option."),
1115 do_check_existence
= ACCEPT_EXISTING
;
1118 add_visitor(visit_locate02_format
, NULL
);
1119 format_name
= "slocate";
1120 procdata
.slocatedb_format
= 1;
1126 procdata
.slocatedb_format
= 0;
1127 nread2
= fread (procdata
.original_filename
+nread
, 1, sizeof (LOCATEDB_MAGIC
)-nread
,
1129 if (looking_at_gnu_locatedb(procdata
.original_filename
, nread
+nread2
))
1131 add_visitor(visit_locate02_format
, NULL
);
1132 format_name
= "GNU LOCATE02";
1134 else /* Use the old format */
1139 /* Read the list of the most common bigrams in the database. */
1142 int more_read
= fread (procdata
.original_filename
+ nread
, 1,
1143 256 - nread
, procdata
.fp
);
1144 /* XXX: check more_read+nread! */
1147 for (i
= 0; i
< 128; i
++)
1149 procdata
.bigram1
[i
] = procdata
.original_filename
[i
<< 1];
1150 procdata
.bigram2
[i
] = procdata
.original_filename
[(i
<< 1) + 1];
1152 format_name
= "old";
1153 add_visitor(visit_old_format
, NULL
);
1158 add_visitor(visit_basename
, NULL
);
1160 /* See if we need fold. */
1161 if (ignore_case
&& !regex
)
1162 for ( argn
= 0; argn
< argc
; argn
++ )
1164 pathpart
= argv
[argn
];
1165 if (!contains_metacharacter(pathpart
))
1174 add_visitor(visit_casefold
, &casebuf
);
1175 casebuf
.preqlen
= &procdata
.pathsize
;
1178 /* Add an inspector for each pattern we're looking for. */
1179 for ( argn
= 0; argn
< argc
; argn
++ )
1181 results_were_filtered
= true;
1182 pathpart
= argv
[argn
];
1185 struct regular_expression
*p
= xmalloc(sizeof(*p
));
1186 const char *error_message
= NULL
;
1188 memset (&p
->regex
, 0, sizeof (p
->regex
));
1190 re_set_syntax(regex_options
);
1191 p
->regex
.allocated
= 100;
1192 p
->regex
.buffer
= (unsigned char *) xmalloc (p
->regex
.allocated
);
1193 p
->regex
.fastmap
= NULL
;
1194 p
->regex
.syntax
= regex_options
;
1195 p
->regex
.translate
= NULL
;
1197 error_message
= re_compile_pattern (pathpart
, strlen (pathpart
),
1201 error (1, 0, "%s", error_message
);
1205 add_visitor(visit_regex
, p
);
1208 else if (contains_metacharacter(pathpart
))
1211 add_visitor(visit_globmatch_casefold
, pathpart
);
1213 add_visitor(visit_globmatch_nofold
, pathpart
);
1217 /* No glob characters used. Hence we match on
1218 * _any part_ of the filename, not just the
1219 * basename. This seems odd to me, but it is the
1220 * traditional behaviour.
1221 * James Youngman <jay@gnu.org>
1225 struct casefolder
* cf
= xmalloc(sizeof(*cf
));
1226 cf
->pattern
= pathpart
;
1227 cf
->pbuf
= &casebuf
;
1228 add_visitor(visit_substring_match_casefold
, cf
);
1229 /* If we ignore case, convert it to lower now so we don't have to
1232 lc_strcpy(pathpart
, pathpart
);
1236 add_visitor(visit_substring_match_nocasefold
, pathpart
);
1241 pvis
= lastinspector
;
1243 /* We add visit_existing_*() as late as possible to reduce the
1244 * number of stat() calls.
1246 switch (do_check_existence
)
1248 case ACCEPT_EXISTING
:
1249 results_were_filtered
= true;
1250 if (follow_symlinks
) /* -L, default */
1251 add_visitor(visit_existing_follow
, NULL
);
1253 add_visitor(visit_existing_nofollow
, NULL
);
1256 case ACCEPT_NON_EXISTING
:
1257 results_were_filtered
= true;
1258 if (follow_symlinks
) /* -L, default */
1259 add_visitor(visit_non_existing_follow
, NULL
);
1261 add_visitor(visit_non_existing_nofollow
, NULL
);
1264 case ACCEPT_EITHER
: /* Default, neither -E nor -e */
1265 /* do nothing; no extra processing. */
1269 /* Security issue: The stats visitor must be added immediately
1270 * before the print visitor, because otherwise the -S option would
1271 * leak information about files that the caller cannot see.
1274 add_visitor(visit_stats
, &statistics
);
1278 if (print_quoted_filename
)
1279 add_visitor(visit_justprint_quoted
, NULL
);
1281 add_visitor(visit_justprint_unquoted
, NULL
);
1286 add_visitor(visit_limit
, plimit
);
1288 add_visitor(visit_count
, plimit
);
1293 past_pat_inspector
= pvis
->next
;
1295 mainprocessor
= process_and
;
1297 mainprocessor
= process_or
;
1300 mainprocessor
= process_simple
;
1304 printf(_("Database %s is in the %s format.\n"),
1310 procdata
.c
= getc (procdata
.fp
);
1311 /* If we are searching for filename patterns, the inspector list
1312 * will contain an entry for each pattern for which we are searching.
1314 while ( (procdata
.c
!= EOF
) &&
1315 (VISIT_ABORT
!= (mainprocessor
)(&procdata
)) )
1317 /* Do nothing; all the work is done in the visitor functions. */
1323 print_stats(argc
, filesize
);
1326 if (ferror (procdata
.fp
))
1328 error (0, errno
, "%s", procdata
.dbfile
);
1331 return plimit
->items_accepted
;
1337 extern char *version_string
;
1339 /* The name this program was run with. */
1343 usage (FILE *stream
)
1345 fprintf (stream
, _("\
1346 Usage: %s [-d path | --database=path] [-e | -E | --[non-]existing]\n\
1347 [-i | --ignore-case] [-w | --wholename] [-b | --basename] \n\
1348 [--limit=N | -l N] [-S | --statistics] [-0 | --null] [-c | --count]\n\
1349 [-P | -H | --nofollow] [-L | --follow] [-m | --mmap ] [ -s | --stdio ]\n\
1350 [-A | --all] [-p | --print] [-r | --regex ] [--regextype=TYPE]\n\
1351 [--max-database-age D] [-version] [--help]\n\
1354 fputs (_("\nReport bugs to <bug-findutils@gnu.org>.\n"), stream
);
1358 REGEXTYPE_OPTION
= CHAR_MAX
+ 1,
1363 static struct option
const longopts
[] =
1365 {"database", required_argument
, NULL
, 'd'},
1366 {"existing", no_argument
, NULL
, 'e'},
1367 {"non-existing", no_argument
, NULL
, 'E'},
1368 {"ignore-case", no_argument
, NULL
, 'i'},
1369 {"all", no_argument
, NULL
, 'A'},
1370 {"help", no_argument
, NULL
, 'h'},
1371 {"version", no_argument
, NULL
, 'v'},
1372 {"null", no_argument
, NULL
, '0'},
1373 {"count", no_argument
, NULL
, 'c'},
1374 {"wholename", no_argument
, NULL
, 'w'},
1375 {"wholepath", no_argument
, NULL
, 'w'}, /* Synonym. */
1376 {"basename", no_argument
, NULL
, 'b'},
1377 {"print", no_argument
, NULL
, 'p'},
1378 {"stdio", no_argument
, NULL
, 's'},
1379 {"mmap", no_argument
, NULL
, 'm'},
1380 {"limit", required_argument
, NULL
, 'l'},
1381 {"regex", no_argument
, NULL
, 'r'},
1382 {"regextype", required_argument
, NULL
, REGEXTYPE_OPTION
},
1383 {"statistics", no_argument
, NULL
, 'S'},
1384 {"follow", no_argument
, NULL
, 'L'},
1385 {"nofollow", no_argument
, NULL
, 'P'},
1386 {"max-database-age", required_argument
, NULL
, MAX_DB_AGE
},
1387 {NULL
, no_argument
, NULL
, 0}
1394 const char * what
= "failed";
1395 uid_t orig_euid
= geteuid();
1397 /* Use of setgroups() is restrcted to root only. */
1401 groups
[1] = getgid();
1402 if (0 != setgroups(1, groups
))
1404 what
= _("failed to drop group privileges");
1409 if (0 != setuid(getuid()))
1411 what
= _("failed to drop setuid privileges");
1415 /* Defend against the case where the attacker runs us with the
1416 * capability to call setuid() turned off, which on some systems
1417 * will cause the above attempt to drop privileges fail (leaving us
1422 what
= _("Failed to drop privileges");
1430 error(1, errno
, "%s", what
);
1438 /* deliberate infinite loop */
1443 opendb(const char *name
)
1445 int fd
= open(name
, O_RDONLY
1446 #if defined(O_LARGEFILE)
1452 /* Make sure it won't survive an exec */
1453 if (0 != fcntl(fd
, F_SETFD
, FD_CLOEXEC
))
1463 dolocate (int argc
, char **argv
, int secure_db_fd
)
1466 unsigned long int found
= 0uL;
1468 int ignore_case
= 0;
1471 int basename_only
= 0;
1474 int regex_options
= RE_SYNTAX_EMACS
;
1479 int they_chose_db
= 0;
1480 bool did_stdin
= false; /* Set to prevent rereading stdin. */
1482 program_name
= argv
[0];
1484 #ifdef HAVE_SETLOCALE
1485 setlocale (LC_ALL
, "");
1487 bindtextdomain (PACKAGE
, LOCALEDIR
);
1488 textdomain (PACKAGE
);
1489 atexit (close_stdout
);
1492 limits
.items_accepted
= 0;
1494 quote_opts
= clone_quoting_options (NULL
);
1495 print_quoted_filename
= true;
1497 /* We cannot simultaneously trust $LOCATE_PATH and use the
1498 * setuid-access-controlled database,, since that could cause a leak
1501 dbpath
= getenv ("LOCATE_PATH");
1507 check_existence
= ACCEPT_EITHER
;
1509 while ((optc
= getopt_long (argc
, argv
, "Abcd:eEil:prsm0SwHPL", longopts
, (int *) 0)) != -1)
1514 print_quoted_filename
= false; /* print filename 'raw'. */
1535 check_existence
= ACCEPT_EXISTING
;
1539 check_existence
= ACCEPT_NON_EXISTING
;
1551 /* XXX: nothing in the test suite for this option. */
1552 set_max_db_age(optarg
);
1560 printf (_("GNU locate version %s\n"), version_string
);
1571 case REGEXTYPE_OPTION
:
1572 regex_options
= get_regex_type(optarg
);
1580 follow_symlinks
= 1;
1583 /* In find, -P and -H differ in the way they handle paths
1584 * given on the command line. This is not relevant for
1585 * locate, but the -H option is supported because it is
1586 * probably more intuitive to do so.
1590 follow_symlinks
= 0;
1596 strtol_error err
= xstrtoumax(optarg
, &end
, 10, &limits
.limit
, NULL
);
1597 if (LONGINT_OK
!= err
)
1599 STRTOL_FATAL_ERROR(optarg
, _("argument to --limit"), err
);
1605 case 's': /* use stdio */
1606 case 'm': /* use mmap */
1607 /* These options are implemented simply for
1608 * compatibility with FreeBSD
1618 /* If the user gave the -d option or set LOCATE_PATH,
1619 * relinquish access to the secure database.
1623 if (secure_db_fd
>= 0)
1625 close(secure_db_fd
);
1630 if (!just_count
&& !stats
)
1640 if (!just_count
&& optind
== argc
)
1648 if (1 == isatty(STDOUT_FILENO
))
1649 stdout_is_a_tty
= true;
1651 stdout_is_a_tty
= false;
1654 next_element (dbpath
, 0); /* Initialize. */
1656 /* Bail out early if limit already reached. */
1657 while (!use_limit
|| limits
.limit
> limits
.items_accepted
)
1663 statistics
.compressed_bytes
=
1664 statistics
.total_filename_count
=
1665 statistics
.total_filename_length
=
1666 statistics
.whitespace_count
=
1667 statistics
.newline_count
=
1668 statistics
.highbit_filename_count
= 0u;
1672 /* Take the next element from the list of databases */
1673 e
= next_element ((char *) NULL
, 0);
1677 if (0 == strcmp (e
, "-"))
1682 _("warning: the locate database can only be read from stdin once."));
1694 if (0 == strlen(e
) || 0 == strcmp(e
, "."))
1699 /* open the database */
1703 error (0, errno
, "%s", e
);
1710 if (-1 == secure_db_fd
)
1712 /* Already searched the database, it's time to exit the loop */
1717 e
= selected_secure_db
;
1723 /* Check the database to see if it is old. */
1726 error (0, errno
, "%s", e
);
1727 /* continue anyway */
1728 filesize
= (off_t
)0;
1734 filesize
= st
.st_size
;
1736 if ((time_t)-1 == time(&now
))
1738 /* If we can't tell the time, we don't know how old the
1739 * database is. But since the message is just advisory,
1740 * we continue anyway.
1742 error (0, errno
, "time system call");
1746 double age
= difftime(now
, st
.st_mtime
);
1747 double warn_seconds
= SECONDS_PER_UNIT
* warn_number_units
;
1748 if (age
> warn_seconds
)
1751 warning: database `fred' is more than 8 days old (actual age is 10 days)*/
1753 _("warning: database `%s' is more than %d %s old (actual age is %.1f %s)"),
1755 warn_number_units
, _(warn_name_units
),
1756 (age
/(double)SECONDS_PER_UNIT
), _(warn_name_units
));
1761 fp
= fdopen(fd
, "r");
1764 error (0, errno
, "%s", e
);
1768 /* Search this database for all patterns simultaneously */
1769 found
= search_one_database (argc
- optind
, &argv
[optind
],
1771 ignore_case
, print
, basename_only
,
1772 use_limit
, &limits
, stats
,
1773 op_and
, regex
, regex_options
);
1775 /* Close the databsase (even if it is stdin) */
1776 if (fclose (fp
) == EOF
)
1778 error (0, errno
, "%s", e
);
1785 printf("%ld\n", found
);
1788 if (found
|| (use_limit
&& (limits
.limit
==0)) || stats
)
1794 #define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
1796 open_secure_db(void)
1800 const char * secure_db_list
[] =
1803 "/var/lib/slocate/slocate.db",
1806 for (i
=0; secure_db_list
[i
]; ++i
)
1808 fd
= opendb(secure_db_list
[i
]);
1811 selected_secure_db
= secure_db_list
[i
];
1819 main (int argc
, char **argv
)
1821 int dbfd
= open_secure_db();
1824 return dolocate(argc
, argv
, dbfd
);