source: trunk/essentials/sys-apps/findutils/locate/locate.c

Last change on this file was 3178, checked in by bird, 19 years ago

the logic doesn't work for use yet.

File size: 41.1 KB
Line 
1/* locate -- search databases for filenames that match patterns
2 Copyright (C) 1994, 1996, 1998, 1999, 2000, 2003,
3 2004, 2005 Free Software Foundation, Inc.
4
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)
8 any later version.
9
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.
14
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,
18 USA.
19*/
20
21/* Usage: locate [options] pattern...
22
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:
30
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
35
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.
39
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).
44
45 The old algorithm was:
46
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
51 path remainders.
52
53 Then, match the actual shell glob pattern (if in this form)
54 against the candidate pathnames using the slower shell filename
55 matching routines.
56
57
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.
61*/
62
63#include <config.h>
64#include <stdio.h>
65#include <signal.h>
66#include <ctype.h>
67#include <sys/types.h>
68#include <sys/stat.h>
69#include <time.h>
70#include <fnmatch.h>
71#include <getopt.h>
72#include <xstrtol.h>
73
74/* The presence of unistd.h is assumed by gnulib these days, so we
75 * might as well assume it too.
76 */
77/* We need <unistd.h> for isatty(). */
78#include <unistd.h>
79
80#if HAVE_FCNTL_H
81/* We use fcntl() */
82#include <fcntl.h>
83#endif
84
85#define NDEBUG
86#include <assert.h>
87
88#if defined(HAVE_STRING_H) || defined(STDC_HEADERS)
89#include <string.h>
90#else
91#include <strings.h>
92#define strchr index
93#endif
94
95#ifdef STDC_HEADERS
96#include <stdlib.h>
97#endif
98
99#ifdef HAVE_ERRNO_H
100#include <errno.h>
101#else
102extern int errno;
103#endif
104
105#ifdef HAVE_LOCALE_H
106#include <locale.h>
107#endif
108
109#if ENABLE_NLS
110# include <libintl.h>
111# define _(Text) gettext (Text)
112#else
113# define _(Text) Text
114#define textdomain(Domain)
115#define bindtextdomain(Package, Directory)
116#endif
117#ifdef gettext_noop
118# define N_(String) gettext_noop (String)
119#else
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");
126 */
127# define N_(String) String
128#endif
129
130#include "locatedb.h"
131#include <getline.h>
132#include "../gnulib/lib/xalloc.h"
133#include "../gnulib/lib/error.h"
134#include "../gnulib/lib/human.h"
135#include "dirname.h"
136#include "closeout.h"
137#include "nextelem.h"
138#include "regex.h"
139#include "quote.h"
140#include "quotearg.h"
141#include "printquoted.h"
142#include "regextype.h"
143
144
145/* Note that this evaluates C many times. */
146#ifdef _LIBC
147# define TOUPPER(Ch) toupper (Ch)
148# define TOLOWER(Ch) tolower (Ch)
149#else
150# define TOUPPER(Ch) (islower (Ch) ? toupper (Ch) : (Ch))
151# define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
152#endif
153
154/* typedef enum {false, true} boolean; */
155
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#define WARN_NUMBER_UNITS (8)
159/* Printable name of units used in WARN_SECONDS */
160static const char warn_name_units[] = N_("days");
161#define SECONDS_PER_UNIT (60 * 60 * 24)
162
163#define WARN_SECONDS ((SECONDS_PER_UNIT) * (WARN_NUMBER_UNITS))
164
165enum visit_result
166 {
167 VISIT_CONTINUE = 1, /* please call the next visitor */
168 VISIT_ACCEPTED = 2, /* accepted, call no futher callbacks for this file */
169 VISIT_REJECTED = 4, /* rejected, process next file. */
170 VISIT_ABORT = 8 /* rejected, process no more files. */
171 };
172
173enum ExistenceCheckType
174 {
175 ACCEPT_EITHER, /* Corresponds to lack of -E/-e option */
176 ACCEPT_EXISTING, /* Corresponds to option -e */
177 ACCEPT_NON_EXISTING /* Corresponds to option -E */
178 };
179
180/* Check for existence of files before printing them out? */
181enum ExistenceCheckType check_existence = ACCEPT_EITHER;
182
183static int follow_symlinks = 1;
184
185/* What to separate the results with. */
186static int separator = '\n';
187
188static struct quoting_options * quote_opts = NULL;
189static bool stdout_is_a_tty;
190static bool print_quoted_filename;
191static bool results_were_filtered;
192
193static char* slocate_db_pathname = "/var/lib/slocate/slocate.db";
194
195static const char *selected_secure_db = NULL;
196
197
198/* Read in a 16-bit int, high byte first (network byte order). */
199
200static short
201get_short (FILE *fp)
202{
203
204 register short x;
205
206 x = (signed char) fgetc (fp) << 8;
207 x |= (fgetc (fp) & 0xff);
208 return x;
209}
210
211const char * const metacharacters = "*?[]\\";
212
213/* Return nonzero if S contains any shell glob characters.
214 */
215static int
216contains_metacharacter(const char *s)
217{
218 if (NULL == strpbrk(s, metacharacters))
219 return 0;
220 else
221 return 1;
222}
223
224/* locate_read_str()
225 *
226 * Read bytes from FP into the buffer at offset OFFSET in (*BUF),
227 * until we reach DELIMITER or end-of-file. We reallocate the buffer
228 * as necessary, altering (*BUF) and (*SIZ) as appropriate. No assumption
229 * is made regarding the content of the data (i.e. the implementation is
230 * 8-bit clean, the only delimiter is DELIMITER).
231 *
232 * Written Fri May 23 18:41:16 2003 by James Youngman, because getstr()
233 * has been removed from gnulib.
234 *
235 * We call the function locate_read_str() to avoid a name clash with the curses
236 * function getstr().
237 */
238static int
239locate_read_str(char **buf, size_t *siz, FILE *fp, int delimiter, int offs)
240{
241 char * p = NULL;
242 size_t sz = 0;
243 int nread;
244 size_t needed;
245
246 nread = getdelim(&p, &sz, delimiter, fp);
247 if (nread >= 0)
248 {
249 assert(p != NULL);
250
251 needed = offs + nread + 1u;
252 if (needed > (*siz))
253 {
254 char *pnew = realloc(*buf, needed);
255 if (NULL == pnew)
256 {
257 return -1; /* FAIL */
258 }
259 else
260 {
261 *siz = needed;
262 *buf = pnew;
263 }
264 }
265 memcpy((*buf)+offs, p, nread);
266 free(p);
267 }
268 return nread;
269}
270
271
272static void
273lc_strcpy(char *dest, const char *src)
274{
275 while (*src)
276 {
277 *dest++ = TOLOWER(*src);
278 ++src;
279 }
280 *dest = 0;
281}
282
283struct locate_limits
284{
285 uintmax_t limit;
286 uintmax_t items_accepted;
287};
288static struct locate_limits limits;
289
290
291struct locate_stats
292{
293 uintmax_t compressed_bytes;
294 uintmax_t total_filename_count;
295 uintmax_t total_filename_length;
296 uintmax_t whitespace_count;
297 uintmax_t newline_count;
298 uintmax_t highbit_filename_count;
299};
300static struct locate_stats statistics;
301
302
303struct stringbuf
304{
305 char *buffer;
306 size_t buffersize;
307 size_t *preqlen;
308};
309static struct stringbuf casebuf;
310
311
312struct casefolder
313{
314 const char *pattern;
315 struct stringbuf *pbuf;
316};
317
318struct regular_expression
319{
320 struct re_pattern_buffer regex; /* for --regex */
321};
322
323
324struct process_data
325{
326 int c; /* An input byte. */
327 char itemcount; /* Indicates we're at the beginning of an slocate db. */
328 int count; /* The length of the prefix shared with the previous database entry. */
329 int len;
330 char *original_filename; /* The current input database entry. */
331 size_t pathsize; /* Amount allocated for it. */
332 char *munged_filename; /* path or base_name(path) */
333 FILE *fp; /* The pathname database. */
334 const char *dbfile; /* Its name, or "<stdin>" */
335 int slocatedb_format; /* Allows us to cope with slocate's format variant */
336 /* for the old database format,
337 the first and second characters of the most common bigrams. */
338 char bigram1[128];
339 char bigram2[128];
340};
341
342
343typedef int (*visitfunc)(struct process_data *procdata,
344 void *context);
345
346struct visitor
347{
348 visitfunc inspector;
349 void * context;
350 struct visitor *next;
351};
352
353
354static struct visitor *inspectors = NULL;
355static struct visitor *lastinspector = NULL;
356static struct visitor *past_pat_inspector = NULL;
357
358/* 0 or 1 pattern(s) */
359static int
360process_simple(struct process_data *procdata)
361{
362 int result = VISIT_CONTINUE;
363 const struct visitor *p = inspectors;
364
365 while ( ((VISIT_CONTINUE | VISIT_ACCEPTED) & result) && (NULL != p) )
366 {
367 result = (p->inspector)(procdata, p->context);
368 p = p->next;
369 }
370
371 return result;
372}
373
374/* Accept if any pattern matches. */
375static int
376process_or (struct process_data *procdata)
377{
378 int result = VISIT_CONTINUE;
379 const struct visitor *p = inspectors;
380
381 while ( ((VISIT_CONTINUE | VISIT_REJECTED) & result) && (past_pat_inspector != p) )
382 {
383 result = (p->inspector)(procdata, p->context);
384 p = p->next;
385 }
386
387 if (result == VISIT_CONTINUE)
388 result = VISIT_REJECTED;
389 if (result & (VISIT_ABORT | VISIT_REJECTED))
390 return result;
391
392 p = past_pat_inspector;
393 result = VISIT_CONTINUE;
394
395 while ( (VISIT_CONTINUE == result) && (NULL != p) )
396 {
397 result = (p->inspector)(procdata, p->context);
398 p = p->next;
399 }
400
401 if (VISIT_CONTINUE == result)
402 return VISIT_ACCEPTED;
403 else
404 return result;
405}
406
407/* Accept if all pattern match. */
408static int
409process_and (struct process_data *procdata)
410{
411 int result = VISIT_CONTINUE;
412 const struct visitor *p = inspectors;
413
414 while ( ((VISIT_CONTINUE | VISIT_ACCEPTED) & result) && (past_pat_inspector != p) )
415 {
416 result = (p->inspector)(procdata, p->context);
417 p = p->next;
418 }
419
420 if (result == VISIT_CONTINUE)
421 result = VISIT_REJECTED;
422 if (result & (VISIT_ABORT | VISIT_REJECTED))
423 return result;
424
425 p = past_pat_inspector;
426 result = VISIT_CONTINUE;
427
428 while ( (VISIT_CONTINUE == result) && (NULL != p) )
429 {
430 result = (p->inspector)(procdata, p->context);
431 p = p->next;
432 }
433
434 if (VISIT_CONTINUE == result)
435 return VISIT_ACCEPTED;
436 else
437 return result;
438}
439
440typedef int (*processfunc)(struct process_data *procdata);
441
442static processfunc mainprocessor = NULL;
443
444static void
445add_visitor(visitfunc fn, void *context)
446{
447 struct visitor *p = xmalloc(sizeof(struct visitor));
448 p->inspector = fn;
449 p->context = context;
450 p->next = NULL;
451
452 if (NULL == lastinspector)
453 {
454 lastinspector = inspectors = p;
455 }
456 else
457 {
458 lastinspector->next = p;
459 lastinspector = p;
460 }
461}
462
463
464
465static int
466visit_justprint_quoted(struct process_data *procdata, void *context)
467{
468 (void) context;
469 print_quoted (stdout, quote_opts, stdout_is_a_tty,
470 "%s",
471 procdata->original_filename);
472 putchar(separator);
473 return VISIT_CONTINUE;
474}
475
476static int
477visit_justprint_unquoted(struct process_data *procdata, void *context)
478{
479 (void) context;
480 fputs(procdata->original_filename, stdout);
481 putchar(separator);
482 return VISIT_CONTINUE;
483}
484
485static int
486visit_old_format(struct process_data *procdata, void *context)
487{
488 register char *s;
489 (void) context;
490
491 /* Get the offset in the path where this path info starts. */
492 if (procdata->c == LOCATEDB_OLD_ESCAPE)
493 procdata->count += getw (procdata->fp) - LOCATEDB_OLD_OFFSET;
494 else
495 procdata->count += procdata->c - LOCATEDB_OLD_OFFSET;
496
497 /* Overlay the old path with the remainder of the new. */
498 for (s = procdata->original_filename + procdata->count;
499 (procdata->c = getc (procdata->fp)) > LOCATEDB_OLD_ESCAPE;)
500 if (procdata->c < 0200)
501 *s++ = procdata->c; /* An ordinary character. */
502 else
503 {
504 /* Bigram markers have the high bit set. */
505 procdata->c &= 0177;
506 *s++ = procdata->bigram1[procdata->c];
507 *s++ = procdata->bigram2[procdata->c];
508 }
509 *s-- = '\0';
510
511 procdata->munged_filename = procdata->original_filename;
512
513 return VISIT_CONTINUE;
514}
515
516
517static int
518visit_locate02_format(struct process_data *procdata, void *context)
519{
520 register char *s;
521 int nread;
522 (void) context;
523
524 if (procdata->slocatedb_format)
525 {
526 if (procdata->itemcount == 0)
527 {
528 ungetc(procdata->c, procdata->fp);
529 procdata->count = 0;
530 procdata->len = 0;
531 }
532 else if (procdata->itemcount == 1)
533 {
534 procdata->count = procdata->len-1;
535 }
536 else
537 {
538 if (procdata->c == LOCATEDB_ESCAPE)
539 procdata->count += (short)get_short (procdata->fp);
540 else if (procdata->c > 127)
541 procdata->count += procdata->c - 256;
542 else
543 procdata->count += procdata->c;
544 }
545 }
546 else
547 {
548 if (procdata->c == LOCATEDB_ESCAPE)
549 procdata->count += (short)get_short (procdata->fp);
550 else if (procdata->c > 127)
551 procdata->count += procdata->c - 256;
552 else
553 procdata->count += procdata->c;
554 }
555
556 if (procdata->count > procdata->len || procdata->count < 0)
557 {
558 /* This should not happen generally , but since we're
559 * reading in data which is outside our control, we
560 * cannot prevent it.
561 */
562 error(1, 0, _("locate database `%s' is corrupt or invalid"), procdata->dbfile);
563 }
564
565 /* Overlay the old path with the remainder of the new. */
566 nread = locate_read_str (&procdata->original_filename, &procdata->pathsize,
567 procdata->fp, 0, procdata->count);
568 if (nread < 0)
569 return VISIT_ABORT;
570 procdata->c = getc (procdata->fp);
571 procdata->len = procdata->count + nread;
572 s = procdata->original_filename + procdata->len - 1; /* Move to the last char in path. */
573 assert (s[0] != '\0');
574 assert (s[1] == '\0'); /* Our terminator. */
575 assert (s[2] == '\0'); /* Added by locate_read_str. */
576
577 procdata->munged_filename = procdata->original_filename;
578
579 if (procdata->slocatedb_format)
580 {
581 /* Don't increment indefinitely, it might overflow. */
582 if (procdata->itemcount < 6)
583 {
584 ++(procdata->itemcount);
585 }
586 }
587
588
589 return VISIT_CONTINUE;
590}
591
592static int
593visit_basename(struct process_data *procdata, void *context)
594{
595 (void) context;
596 procdata->munged_filename = base_name(procdata->original_filename);
597
598 return VISIT_CONTINUE;
599}
600
601
602static int
603visit_casefold(struct process_data *procdata, void *context)
604{
605 struct stringbuf *b = context;
606
607 if (*b->preqlen+1 > b->buffersize)
608 {
609 b->buffer = xrealloc(b->buffer, *b->preqlen+1); /* XXX: consider using extendbuf(). */
610 b->buffersize = *b->preqlen+1;
611 }
612 lc_strcpy(b->buffer, procdata->munged_filename);
613
614 return VISIT_CONTINUE;
615}
616
617/* visit_existing_follow implements -L -e */
618static int
619visit_existing_follow(struct process_data *procdata, void *context)
620{
621 struct stat st;
622 (void) context;
623
624 /* munged_filename has been converted in some way (to lower case,
625 * or is just the base name of the file), and original_filename has not.
626 * Hence only original_filename is still actually the name of the file
627 * whose existence we would need to check.
628 */
629 if (stat(procdata->original_filename, &st) != 0)
630 {
631 return VISIT_REJECTED;
632 }
633 else
634 {
635 return VISIT_CONTINUE;
636 }
637}
638
639/* visit_non_existing_follow implements -L -E */
640static int
641visit_non_existing_follow(struct process_data *procdata, void *context)
642{
643 struct stat st;
644 (void) context;
645
646 /* munged_filename has been converted in some way (to lower case,
647 * or is just the base name of the file), and original_filename has not.
648 * Hence only original_filename is still actually the name of the file
649 * whose existence we would need to check.
650 */
651 if (stat(procdata->original_filename, &st) == 0)
652 {
653 return VISIT_REJECTED;
654 }
655 else
656 {
657 return VISIT_CONTINUE;
658 }
659}
660
661/* visit_existing_nofollow implements -P -e */
662static int
663visit_existing_nofollow(struct process_data *procdata, void *context)
664{
665 struct stat st;
666 (void) context;
667
668 /* munged_filename has been converted in some way (to lower case,
669 * or is just the base name of the file), and original_filename has not.
670 * Hence only original_filename is still actually the name of the file
671 * whose existence we would need to check.
672 */
673 if (lstat(procdata->original_filename, &st) != 0)
674 {
675 return VISIT_REJECTED;
676 }
677 else
678 {
679 return VISIT_CONTINUE;
680 }
681}
682
683/* visit_non_existing_nofollow implements -P -E */
684static int
685visit_non_existing_nofollow(struct process_data *procdata, void *context)
686{
687 struct stat st;
688 (void) context;
689
690 /* munged_filename has been converted in some way (to lower case,
691 * or is just the base name of the file), and original_filename has not.
692 * Hence only original_filename is still actually the name of the file
693 * whose existence we would need to check.
694 */
695 if (lstat(procdata->original_filename, &st) == 0)
696 {
697 return VISIT_REJECTED;
698 }
699 else
700 {
701 return VISIT_CONTINUE;
702 }
703}
704
705static int
706visit_substring_match_nocasefold(struct process_data *procdata, void *context)
707{
708 const char *pattern = context;
709
710 if (NULL != strstr(procdata->munged_filename, pattern))
711 return VISIT_ACCEPTED;
712 else
713 return VISIT_REJECTED;
714}
715
716static int
717visit_substring_match_casefold(struct process_data *procdata, void *context)
718{
719 const struct casefolder * p = context;
720 const struct stringbuf * b = p->pbuf;
721 (void) procdata;
722
723 if (NULL != strstr(b->buffer, p->pattern))
724 return VISIT_ACCEPTED;
725 else
726 return VISIT_REJECTED;
727}
728
729
730static int
731visit_globmatch_nofold(struct process_data *procdata, void *context)
732{
733 const char *glob = context;
734 if (fnmatch(glob, procdata->munged_filename, 0) != 0)
735 return VISIT_REJECTED;
736 else
737 return VISIT_ACCEPTED;
738}
739
740
741static int
742visit_globmatch_casefold(struct process_data *procdata, void *context)
743{
744 const char *glob = context;
745 if (fnmatch(glob, procdata->munged_filename, FNM_CASEFOLD) != 0)
746 return VISIT_REJECTED;
747 else
748 return VISIT_ACCEPTED;
749}
750
751
752static int
753visit_regex(struct process_data *procdata, void *context)
754{
755 struct regular_expression *p = context;
756 const size_t len = strlen(procdata->munged_filename);
757
758 int rv = re_search (&p->regex, procdata->munged_filename,
759 len, 0, len,
760 (struct re_registers *) NULL);
761 if (rv < 0)
762 {
763 return VISIT_REJECTED; /* no match (-1), or internal error (-2) */
764 }
765 else
766 {
767 return VISIT_ACCEPTED; /* match */
768 }
769}
770
771
772static int
773visit_stats(struct process_data *procdata, void *context)
774{
775 struct locate_stats *p = context;
776 size_t len = strlen(procdata->original_filename);
777 const char *s;
778 int highbit, whitespace, newline;
779
780 ++(p->total_filename_count);
781 p->total_filename_length += len;
782
783 highbit = whitespace = newline = 0;
784 for (s=procdata->original_filename; *s; ++s)
785 {
786 if ( (int)(*s) & 128 )
787 highbit = 1;
788 if ('\n' == *s)
789 {
790 newline = whitespace = 1;
791 }
792 else if (isspace((unsigned char)*s))
793 {
794 whitespace = 1;
795 }
796 }
797
798 if (highbit)
799 ++(p->highbit_filename_count);
800 if (whitespace)
801 ++(p->whitespace_count);
802 if (newline)
803 ++(p->newline_count);
804
805 return VISIT_CONTINUE;
806}
807
808
809static int
810visit_limit(struct process_data *procdata, void *context)
811{
812 struct locate_limits *p = context;
813
814 (void) procdata;
815
816 if (++p->items_accepted >= p->limit)
817 return VISIT_ABORT;
818 else
819 return VISIT_CONTINUE;
820}
821
822static int
823visit_count(struct process_data *procdata, void *context)
824{
825 struct locate_limits *p = context;
826
827 (void) procdata;
828
829 ++p->items_accepted;
830 return VISIT_CONTINUE;
831}
832
833/* Emit the statistics.
834 */
835static void
836print_stats(int argc, size_t database_file_size)
837{
838 char hbuf[LONGEST_HUMAN_READABLE + 1];
839
840 printf(_("Locate database size: %s bytes\n"),
841 human_readable ((uintmax_t) database_file_size,
842 hbuf, human_ceiling, 1, 1));
843
844 printf( (results_were_filtered ?
845 _("Matching Filenames: %s ") :
846 _("All Filenames: %s ")),
847 human_readable (statistics.total_filename_count,
848 hbuf, human_ceiling, 1, 1));
849 printf(_("with a cumulative length of %s bytes"),
850 human_readable (statistics.total_filename_length,
851 hbuf, human_ceiling, 1, 1));
852
853 printf(_("\n\tof which %s contain whitespace, "),
854 human_readable (statistics.whitespace_count,
855 hbuf, human_ceiling, 1, 1));
856 printf(_("\n\t%s contain newline characters, "),
857 human_readable (statistics.newline_count,
858 hbuf, human_ceiling, 1, 1));
859 printf(_("\n\tand %s contain characters with the high bit set.\n"),
860 human_readable (statistics.highbit_filename_count,
861 hbuf, human_ceiling, 1, 1));
862
863 if (!argc)
864 {
865 if (results_were_filtered)
866 {
867 printf(_("Some filenames may have been filtered out, "
868 "so we cannot compute the compression ratio.\n"));
869 }
870 else
871 {
872 if (statistics.total_filename_length)
873 {
874 printf(_("Compression ratio %4.2f%%\n"),
875 100.0 * ((double)statistics.total_filename_length
876 - (double) database_file_size)
877 / (double) statistics.total_filename_length);
878 }
879 else
880 {
881 printf(_("Compression ratio is undefined\n"));
882 }
883 }
884 }
885 printf("\n");
886}
887
888/*
889 * Return nonzero if the data we read in indicates that we are
890 * looking at a LOCATE02 locate database.
891 */
892static int
893looking_at_gnu_locatedb (const char *data, size_t len)
894{
895 if (len < sizeof (LOCATEDB_MAGIC))
896 return 0;
897 else if (0 == memcmp (data, LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC)))
898 return 1; /* We saw the magic byte sequence */
899 else
900 return 0;
901}
902
903/*
904 * Return nonzero if the data we read in indicates that we are
905 * looking at an slocate database.
906 */
907static int
908looking_at_slocate_locatedb (const char *filename,
909 const char *data,
910 size_t len,
911 int *seclevel)
912{
913 char slocate_magic[] = "1";
914 size_t lenwanted = sizeof(slocate_magic);
915 assert(len <= 2);
916
917 if (len < 2)
918 {
919 return 0;
920 }
921 else
922 {
923 /* Check that the magic number is a one-byte string */
924 if (0 == data[1])
925 {
926 if (isdigit((unsigned char)data[0]))
927 {
928 /* looks promising. */
929 *seclevel = (data[0] - '0');
930
931 if (*seclevel > 1)
932 {
933 /* Hmm, well it's probably an slocate database
934 * of some awsomely huge security level, like 2.
935 * We don't know how to handle those.
936 */
937 error(0, 0,
938 _("locate database `%s' looks like an slocate "
939 "database but it seems to have security level %c, "
940 "which GNU findutils does not currently support"),
941 filename, data[1]);
942 return 1;
943 }
944 else
945 {
946 return 1;
947 }
948 }
949 }
950 else
951 {
952 /* Definitely not slocate. */
953 return 0;
954 }
955 }
956}
957
958/* Print or count the entries in DBFILE that match shell globbing patterns in
959 ARGV. Return the number of entries matched. */
960
961static unsigned long
962search_one_database (int argc,
963 char **argv,
964 const char *dbfile,
965 FILE *fp,
966 off_t filesize,
967 int ignore_case,
968 int enable_print,
969 int basename_only,
970 int use_limit,
971 struct locate_limits *plimit,
972 int stats,
973 int op_and,
974 int regex,
975 int regex_options)
976{
977 char *pathpart; /* A pattern to consider. */
978 int argn; /* Index to current pattern in argv. */
979 int need_fold; /* Set when folding and any pattern is non-glob. */
980 int nread; /* number of bytes read from an entry. */
981 struct process_data procdata; /* Storage for data shared with visitors. */
982 int slocate_seclevel;
983 struct visitor* pvis; /* temp for determining past_pat_inspector. */
984 const char *format_name;
985 enum ExistenceCheckType do_check_existence;
986
987
988 /* We may turn on existence checking for a given database.
989 * We ensure that we can return to the previous behaviour
990 * by using two variables, do_check_existence (which we act on)
991 * and check_existence (whcih indicates the default before we
992 * adjust it on the bassis of what kind of database we;re using
993 */
994 do_check_existence = check_existence;
995
996
997 if (ignore_case)
998 regex_options |= RE_ICASE;
999
1000 procdata.len = procdata.count = 0;
1001 procdata.slocatedb_format = 0;
1002 procdata.itemcount = 0;
1003
1004 procdata.dbfile = dbfile;
1005 procdata.fp = fp;
1006
1007 /* Set up the inspection regime */
1008 inspectors = NULL;
1009 lastinspector = NULL;
1010 past_pat_inspector = NULL;
1011 results_were_filtered = false;
1012
1013 procdata.pathsize = 1026; /* Increased as necessary by locate_read_str. */
1014 procdata.original_filename = xmalloc (procdata.pathsize);
1015
1016
1017 nread = fread (procdata.original_filename, 1, SLOCATE_DB_MAGIC_LEN,
1018 procdata.fp);
1019 if (looking_at_slocate_locatedb(procdata.dbfile,
1020 procdata.original_filename,
1021 nread,
1022 &slocate_seclevel))
1023 {
1024 error(0, 0,
1025 _("`%s' is an slocate database. "
1026 "Support for these is new, expect problems for now "
1027 "(you are, after all, using the CVS code)."),
1028 procdata.dbfile);
1029
1030 /* slocate also uses frcode, but with a different header.
1031 * We handle the header here and then work with the data
1032 * in the normal way.
1033 */
1034 if (slocate_seclevel > 1)
1035 {
1036 /* We don't know what those security levels mean,
1037 * so do nothing further
1038 */
1039 return 0;
1040 }
1041 else if (slocate_seclevel > 0)
1042 {
1043 /* Don't show the filenames to the user if they don't exist.
1044 * Showing stats is safe since filenames are only counted
1045 * after the existence check
1046 */
1047 if (ACCEPT_NON_EXISTING == check_existence)
1048 {
1049 /* Do not allow the user to see a list of filenames that they
1050 * cannot stat().
1051 */
1052 error(0, 0,
1053 _("You specified the -E option, but that option "
1054 "cannot be used with slocate-format databases "
1055 "with a non-zero security level. No results will be "
1056 "generated for this database.\n"));
1057 return 0;
1058 }
1059 if (ACCEPT_EXISTING != do_check_existence)
1060 {
1061 if (enable_print || stats)
1062 {
1063 error(0, 0,
1064 _("`%s' is an slocate database. "
1065 "Turning on the '-e' option."),
1066 procdata.dbfile);
1067 }
1068 do_check_existence = ACCEPT_EXISTING;
1069 }
1070 }
1071 add_visitor(visit_locate02_format, NULL);
1072 format_name = "slocate";
1073 procdata.slocatedb_format = 1;
1074 }
1075 else
1076 {
1077 int nread2;
1078
1079 procdata.slocatedb_format = 0;
1080 nread2 = fread (procdata.original_filename+nread, 1, sizeof (LOCATEDB_MAGIC)-nread,
1081 procdata.fp);
1082 if (looking_at_gnu_locatedb(procdata.original_filename, nread+nread2))
1083 {
1084 add_visitor(visit_locate02_format, NULL);
1085 format_name = "GNU LOCATE02";
1086 }
1087 else /* Use the old format */
1088 {
1089 int i;
1090
1091 nread += nread2;
1092 /* Read the list of the most common bigrams in the database. */
1093 if (nread < 256)
1094 {
1095 int more_read = fread (procdata.original_filename + nread, 1,
1096 256 - nread, procdata.fp);
1097 /* XXX: check more_read+nread! */
1098 }
1099
1100 for (i = 0; i < 128; i++)
1101 {
1102 procdata.bigram1[i] = procdata.original_filename[i << 1];
1103 procdata.bigram2[i] = procdata.original_filename[(i << 1) + 1];
1104 }
1105 format_name = "old";
1106 add_visitor(visit_old_format, NULL);
1107 }
1108 }
1109
1110 if (basename_only)
1111 add_visitor(visit_basename, NULL);
1112
1113 /* See if we need fold. */
1114 if (ignore_case && !regex)
1115 for ( argn = 0; argn < argc; argn++ )
1116 {
1117 pathpart = argv[argn];
1118 if (!contains_metacharacter(pathpart))
1119 {
1120 need_fold = 1;
1121 break;
1122 }
1123 }
1124
1125 if (need_fold)
1126 {
1127 add_visitor(visit_casefold, &casebuf);
1128 casebuf.preqlen = &procdata.pathsize;
1129 }
1130
1131 /* Add an inspector for each pattern we're looking for. */
1132 for ( argn = 0; argn < argc; argn++ )
1133 {
1134 results_were_filtered = true;
1135 pathpart = argv[argn];
1136 if (regex)
1137 {
1138 struct regular_expression *p = xmalloc(sizeof(*p));
1139 const char *error_message = NULL;
1140
1141 memset (&p->regex, 0, sizeof (p->regex));
1142
1143 re_set_syntax(regex_options);
1144 p->regex.allocated = 100;
1145 p->regex.buffer = (unsigned char *) xmalloc (p->regex.allocated);
1146 p->regex.fastmap = NULL;
1147 p->regex.syntax = regex_options;
1148 p->regex.translate = NULL;
1149
1150 error_message = re_compile_pattern (pathpart, strlen (pathpart),
1151 &p->regex);
1152 if (error_message)
1153 {
1154 error (1, 0, "%s", error_message);
1155 }
1156 else
1157 {
1158 add_visitor(visit_regex, p);
1159 }
1160 }
1161 else if (contains_metacharacter(pathpart))
1162 {
1163 if (ignore_case)
1164 add_visitor(visit_globmatch_casefold, pathpart);
1165 else
1166 add_visitor(visit_globmatch_nofold, pathpart);
1167 }
1168 else
1169 {
1170 /* No glob characters used. Hence we match on
1171 * _any part_ of the filename, not just the
1172 * basename. This seems odd to me, but it is the
1173 * traditional behaviour.
1174 * James Youngman <jay@gnu.org>
1175 */
1176 if (ignore_case)
1177 {
1178 struct casefolder * cf = xmalloc(sizeof(*cf));
1179 cf->pattern = pathpart;
1180 cf->pbuf = &casebuf;
1181 add_visitor(visit_substring_match_casefold, cf);
1182 /* If we ignore case, convert it to lower now so we don't have to
1183 * do it every time
1184 */
1185 lc_strcpy(pathpart, pathpart);
1186 }
1187 else
1188 {
1189 add_visitor(visit_substring_match_nocasefold, pathpart);
1190 }
1191 }
1192 }
1193
1194 pvis = lastinspector;
1195
1196 /* We add visit_existing_*() as late as possible to reduce the
1197 * number of stat() calls.
1198 */
1199 switch (do_check_existence)
1200 {
1201 case ACCEPT_EXISTING:
1202 results_were_filtered = true;
1203 if (follow_symlinks) /* -L, default */
1204 add_visitor(visit_existing_follow, NULL);
1205 else /* -P */
1206 add_visitor(visit_existing_nofollow, NULL);
1207 break;
1208
1209 case ACCEPT_NON_EXISTING:
1210 results_were_filtered = true;
1211 if (follow_symlinks) /* -L, default */
1212 add_visitor(visit_non_existing_follow, NULL);
1213 else /* -P */
1214 add_visitor(visit_non_existing_nofollow, NULL);
1215 break;
1216
1217 case ACCEPT_EITHER: /* Default, neither -E nor -e */
1218 /* do nothing; no extra processing. */
1219 break;
1220 }
1221
1222 /* Security issue: The stats visitor must be added immediately
1223 * before the print visitor, because otherwise the -S option would
1224 * leak information about files that the caller cannot see.
1225 */
1226 if (stats)
1227 add_visitor(visit_stats, &statistics);
1228
1229 if (enable_print)
1230 {
1231 if (print_quoted_filename)
1232 add_visitor(visit_justprint_quoted, NULL);
1233 else
1234 add_visitor(visit_justprint_unquoted, NULL);
1235 }
1236
1237
1238 if (use_limit)
1239 add_visitor(visit_limit, plimit);
1240 else
1241 add_visitor(visit_count, plimit);
1242
1243
1244 if (argc > 1)
1245 {
1246 past_pat_inspector = pvis->next;
1247 if (op_and)
1248 mainprocessor = process_and;
1249 else
1250 mainprocessor = process_or;
1251 }
1252 else
1253 mainprocessor = process_simple;
1254
1255 if (stats)
1256 {
1257 printf(_("Database %s is in the %s format.\n"),
1258 procdata.dbfile,
1259 format_name);
1260 }
1261
1262
1263 procdata.c = getc (procdata.fp);
1264 /* If we are searching for filename patterns, the inspector list
1265 * will contain an entry for each pattern for which we are searching.
1266 */
1267 while ( (procdata.c != EOF) &&
1268 (VISIT_ABORT != (mainprocessor)(&procdata)) )
1269 {
1270 /* Do nothing; all the work is done in the visitor functions. */
1271 }
1272
1273 if (stats)
1274 {
1275 if (filesize)
1276 print_stats(argc, filesize);
1277 }
1278
1279 if (ferror (procdata.fp))
1280 {
1281 error (0, errno, "%s", procdata.dbfile);
1282 return 0;
1283 }
1284 return plimit->items_accepted;
1285}
1286
1287
1288
1289
1290extern char *version_string;
1291
1292/* The name this program was run with. */
1293char *program_name;
1294
1295static void
1296usage (FILE *stream)
1297{
1298 fprintf (stream, _("\
1299Usage: %s [-d path | --database=path] [-e | -E | --[non-]existing]\n\
1300 [-i | --ignore-case] [-w | --wholename] [-b | --basename] \n\
1301 [--limit=N | -l N] [-S | --statistics] [-0 | --null] [-c | --count]\n\
1302 [-P | -H | --nofollow] [-L | --follow] [-m | --mmap ] [ -s | --stdio ]\n\
1303 [-A | --all] [-p | --print] [-r | --regex ] [--regextype=TYPE]\n\
1304 [-version] [--help]\n\
1305 pattern...\n"),
1306 program_name);
1307 fputs (_("\nReport bugs to <bug-findutils@gnu.org>.\n"), stream);
1308}
1309enum
1310 {
1311 REGEXTYPE_OPTION = CHAR_MAX + 1
1312 };
1313
1314
1315static struct option const longopts[] =
1316{
1317 {"database", required_argument, NULL, 'd'},
1318 {"existing", no_argument, NULL, 'e'},
1319 {"non-existing", no_argument, NULL, 'E'},
1320 {"ignore-case", no_argument, NULL, 'i'},
1321 {"all", no_argument, NULL, 'A'},
1322 {"help", no_argument, NULL, 'h'},
1323 {"version", no_argument, NULL, 'v'},
1324 {"null", no_argument, NULL, '0'},
1325 {"count", no_argument, NULL, 'c'},
1326 {"wholename", no_argument, NULL, 'w'},
1327 {"wholepath", no_argument, NULL, 'w'}, /* Synonym. */
1328 {"basename", no_argument, NULL, 'b'},
1329 {"print", no_argument, NULL, 'p'},
1330 {"stdio", no_argument, NULL, 's'},
1331 {"mmap", no_argument, NULL, 'm'},
1332 {"limit", required_argument, NULL, 'l'},
1333 {"regex", no_argument, NULL, 'r'},
1334 {"regextype", required_argument, NULL, REGEXTYPE_OPTION},
1335 {"statistics", no_argument, NULL, 'S'},
1336 {"follow", no_argument, NULL, 'L'},
1337 {"nofollow", no_argument, NULL, 'P'},
1338 {NULL, no_argument, NULL, 0}
1339};
1340
1341
1342static int
1343drop_privs(void)
1344{
1345 const char * what = "failed";
1346 uid_t orig_euid = geteuid();
1347
1348 /* Use of setgroups() is restrcted to root only. */
1349 if (0 == orig_euid)
1350 {
1351 gid_t groups[1];
1352 groups[1] = getgid();
1353 if (0 != setgroups(1, groups))
1354 {
1355 what = _("failed to drop group privileges");
1356 goto fail;
1357 }
1358 }
1359
1360 if (0 != setuid(getuid()))
1361 {
1362 what = _("failed to drop setuid privileges");
1363 goto fail;
1364 }
1365
1366 /* Defend against the case where the attacker runs us with the
1367 * capability to call setuid() turned off, which on some systems
1368 * will cause the above attempt to drop privileges fail (leaving us
1369 * privileged).
1370 */
1371 if (0 == setuid(0))
1372 {
1373#ifndef __EMX__
1374 what = _("Failed to drop privileges");
1375 goto fail;
1376#endif
1377 }
1378
1379 /* success. */
1380 return 0;
1381
1382 fail:
1383 error(1, errno, "%s", what);
1384 abort();
1385 kill(0, SIGKILL);
1386 _exit(1);
1387 /*NOTREACHED*/
1388 /* ... we hope. */
1389 for (;;)
1390 {
1391 /* deliberate infinite loop */
1392 }
1393}
1394
1395
1396static int
1397opendb(const char *name)
1398{
1399 int fd = open(name, O_RDONLY
1400#if defined(O_LARGEFILE)
1401 |O_LARGEFILE
1402#endif
1403 );
1404 if (fd >= 0)
1405 {
1406 /* Make sure it won't survive an exec */
1407 if (0 != fcntl(fd, F_SETFD, FD_CLOEXEC))
1408 {
1409 close(fd);
1410 fd = -1;
1411 }
1412 }
1413 return fd;
1414}
1415
1416
1417int
1418dolocate (int argc, char **argv, int secure_db_fd)
1419{
1420 char *dbpath;
1421 unsigned long int found = 0uL;
1422 int optc;
1423 int ignore_case = 0;
1424 int print = 0;
1425 int just_count = 0;
1426 int basename_only = 0;
1427 int use_limit = 0;
1428 int regex = 0;
1429 int regex_options = RE_SYNTAX_EMACS;
1430 int stats = 0;
1431 int op_and = 0;
1432 const char *e;
1433 FILE *fp;
1434 int they_chose_db = 0;
1435 bool did_stdin = false; /* Set to prevent rereading stdin. */
1436
1437 program_name = argv[0];
1438
1439#ifdef HAVE_SETLOCALE
1440 setlocale (LC_ALL, "");
1441#endif
1442 bindtextdomain (PACKAGE, LOCALEDIR);
1443 textdomain (PACKAGE);
1444 atexit (close_stdout);
1445
1446 limits.limit = 0;
1447 limits.items_accepted = 0;
1448
1449 quote_opts = clone_quoting_options (NULL);
1450 print_quoted_filename = true;
1451
1452 /* We cannot simultaneously trust $LOCATE_PATH and use the
1453 * setuid-access-controlled database,, since that could cause a leak
1454 * of private data.
1455 */
1456 dbpath = getenv ("LOCATE_PATH");
1457 if (dbpath)
1458 {
1459 they_chose_db = 1;
1460 }
1461
1462 check_existence = ACCEPT_EITHER;
1463
1464 while ((optc = getopt_long (argc, argv, "Abcd:eEil:prsm0SwHPL", longopts, (int *) 0)) != -1)
1465 switch (optc)
1466 {
1467 case '0':
1468 separator = 0;
1469 print_quoted_filename = false; /* print filename 'raw'. */
1470 break;
1471
1472 case 'A':
1473 op_and = 1;
1474 break;
1475
1476 case 'b':
1477 basename_only = 1;
1478 break;
1479
1480 case 'c':
1481 just_count = 1;
1482 break;
1483
1484 case 'd':
1485 dbpath = optarg;
1486 they_chose_db = 1;
1487 break;
1488
1489 case 'e':
1490 check_existence = ACCEPT_EXISTING;
1491 break;
1492
1493 case 'E':
1494 check_existence = ACCEPT_NON_EXISTING;
1495 break;
1496
1497 case 'i':
1498 ignore_case = 1;
1499 break;
1500
1501 case 'h':
1502 usage (stdout);
1503 return 0;
1504
1505 case 'p':
1506 print = 1;
1507 break;
1508
1509 case 'v':
1510 printf (_("GNU locate version %s\n"), version_string);
1511 return 0;
1512
1513 case 'w':
1514 basename_only = 0;
1515 break;
1516
1517 case 'r':
1518 regex = 1;
1519 break;
1520
1521 case REGEXTYPE_OPTION:
1522 regex_options = get_regex_type(optarg);
1523 break;
1524
1525 case 'S':
1526 stats = 1;
1527 break;
1528
1529 case 'L':
1530 follow_symlinks = 1;
1531 break;
1532
1533 /* In find, -P and -H differ in the way they handle paths
1534 * given on the command line. This is not relevant for
1535 * locate, but the -H option is supported because it is
1536 * probably more intuitive to do so.
1537 */
1538 case 'P':
1539 case 'H':
1540 follow_symlinks = 0;
1541 break;
1542
1543 case 'l':
1544 {
1545 char *end = optarg;
1546 strtol_error err = xstrtoumax(optarg, &end, 10, &limits.limit, NULL);
1547 if (LONGINT_OK != err)
1548 {
1549 STRTOL_FATAL_ERROR(optarg, _("argument to --limit"), err);
1550 }
1551 use_limit = 1;
1552 }
1553 break;
1554
1555 case 's': /* use stdio */
1556 case 'm': /* use mmap */
1557 /* These options are implemented simply for
1558 * compatibility with FreeBSD
1559 */
1560 break;
1561
1562 default:
1563 usage (stderr);
1564 return 1;
1565 }
1566
1567
1568 /* If the user gave the -d option or set LOCATE_PATH,
1569 * relinquish access to the secure database.
1570 */
1571 if (they_chose_db)
1572 {
1573 if (secure_db_fd >= 0)
1574 {
1575 close(secure_db_fd);
1576 secure_db_fd = 0;
1577 }
1578 }
1579
1580 if (!just_count && !stats)
1581 print = 1;
1582
1583 if (stats)
1584 {
1585 if (optind == argc)
1586 use_limit = 0;
1587 }
1588 else
1589 {
1590 if (!just_count && optind == argc)
1591 {
1592 usage (stderr);
1593 return 1;
1594 }
1595 }
1596
1597
1598 if (1 == isatty(STDOUT_FILENO))
1599 stdout_is_a_tty = true;
1600 else
1601 stdout_is_a_tty = false;
1602
1603 if (they_chose_db)
1604 next_element (dbpath, 0); /* Initialize. */
1605
1606 /* Bail out early if limit already reached. */
1607 while (!use_limit || limits.limit > limits.items_accepted)
1608 {
1609 struct stat st;
1610 int fd;
1611 off_t filesize;
1612
1613 statistics.compressed_bytes =
1614 statistics.total_filename_count =
1615 statistics.total_filename_length =
1616 statistics.whitespace_count =
1617 statistics.newline_count =
1618 statistics.highbit_filename_count = 0u;
1619
1620 if (they_chose_db)
1621 {
1622 /* Take the next element from the list of databases */
1623 e = next_element ((char *) NULL, 0);
1624 if (NULL == e)
1625 break;
1626
1627 if (0 == strcmp (e, "-"))
1628 {
1629 if (did_stdin)
1630 {
1631 error (0, 0,
1632 _("warning: the locate database can only be read from stdin once."));
1633 return 0;
1634 }
1635 else
1636 {
1637 e = "<stdin>";
1638 fd = 0;
1639 did_stdin = true;
1640 }
1641 }
1642 else
1643 {
1644 if (0 == strlen(e) || 0 == strcmp(e, "."))
1645 {
1646 e = LOCATE_DB;
1647 }
1648
1649 /* open the database */
1650 fd = opendb(e);
1651 if (fd < 0)
1652 {
1653 error (0, errno, "%s", e);
1654 return 0;
1655 }
1656 }
1657 }
1658 else
1659 {
1660 if (-1 == secure_db_fd)
1661 {
1662 /* Already searched the database, it's time to exit the loop */
1663 break;
1664 }
1665 else
1666 {
1667 e = selected_secure_db;
1668 fd = secure_db_fd;
1669 secure_db_fd = -1;
1670 }
1671 }
1672
1673 /* Check the database to see if it is old. */
1674 if (fstat(fd, &st))
1675 {
1676 error (0, errno, "%s", e);
1677 /* continue anyway */
1678 filesize = (off_t)0;
1679 }
1680 else
1681 {
1682 time_t now;
1683
1684 filesize = st.st_size;
1685
1686 if ((time_t)-1 == time(&now))
1687 {
1688 /* If we can't tell the time, we don't know how old the
1689 * database is. But since the message is just advisory,
1690 * we continue anyway.
1691 */
1692 error (0, errno, "time system call");
1693 }
1694 else
1695 {
1696 if (now - st.st_mtime > WARN_SECONDS)
1697 {
1698 /* For example:
1699 warning: database `fred' is more than 8 days old */
1700 error (0, 0,
1701 _("warning: database `%s' is more than %d %s old"),
1702 e, WARN_NUMBER_UNITS, _(warn_name_units));
1703 }
1704 }
1705 }
1706
1707 fp = fdopen(fd, "r");
1708 if (NULL == fp)
1709 {
1710 error (0, errno, "%s", e);
1711 return 0;
1712 }
1713
1714 /* Search this database for all patterns simultaneously */
1715 found = search_one_database (argc - optind, &argv[optind],
1716 e, fp, filesize,
1717 ignore_case, print, basename_only,
1718 use_limit, &limits, stats,
1719 op_and, regex, regex_options);
1720
1721 /* Close the databsase (even if it is stdin) */
1722 if (fclose (fp) == EOF)
1723 {
1724 error (0, errno, "%s", e);
1725 return 0;
1726 }
1727 }
1728
1729 if (just_count)
1730 {
1731 printf("%ld\n", found);
1732 }
1733
1734 if (found || (use_limit && (limits.limit==0)) || stats )
1735 return 0;
1736 else
1737 return 1;
1738}
1739
1740
1741#define ARRAYSIZE(a) (sizeof(a)/sizeof(a[0]))
1742static int
1743open_secure_db(void)
1744{
1745 int fd, i;
1746
1747 const char * secure_db_list[] =
1748 {
1749 LOCATE_DB,
1750 "/var/lib/slocate/slocate.db",
1751 NULL
1752 };
1753 for (i=0; secure_db_list[i]; ++i)
1754 {
1755 fd = opendb(secure_db_list[i]);
1756 if (fd >= 0)
1757 {
1758 selected_secure_db = secure_db_list[i];
1759 return fd;
1760 }
1761 }
1762 return -1;
1763}
1764
1765int
1766main (int argc, char **argv)
1767{
1768 int dbfd = open_secure_db();
1769 drop_privs();
1770
1771 return dolocate(argc, argv, dbfd);
1772}
1773
Note: See TracBrowser for help on using the repository browser.