| 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
|
|---|
| 102 | extern 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 */
|
|---|
| 160 | static 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 |
|
|---|
| 165 | enum 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 |
|
|---|
| 173 | enum 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? */
|
|---|
| 181 | enum ExistenceCheckType check_existence = ACCEPT_EITHER;
|
|---|
| 182 |
|
|---|
| 183 | static int follow_symlinks = 1;
|
|---|
| 184 |
|
|---|
| 185 | /* What to separate the results with. */
|
|---|
| 186 | static int separator = '\n';
|
|---|
| 187 |
|
|---|
| 188 | static struct quoting_options * quote_opts = NULL;
|
|---|
| 189 | static bool stdout_is_a_tty;
|
|---|
| 190 | static bool print_quoted_filename;
|
|---|
| 191 | static bool results_were_filtered;
|
|---|
| 192 |
|
|---|
| 193 | static char* slocate_db_pathname = "/var/lib/slocate/slocate.db";
|
|---|
| 194 |
|
|---|
| 195 | static const char *selected_secure_db = NULL;
|
|---|
| 196 |
|
|---|
| 197 |
|
|---|
| 198 | /* Read in a 16-bit int, high byte first (network byte order). */
|
|---|
| 199 |
|
|---|
| 200 | static short
|
|---|
| 201 | get_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 |
|
|---|
| 211 | const char * const metacharacters = "*?[]\\";
|
|---|
| 212 |
|
|---|
| 213 | /* Return nonzero if S contains any shell glob characters.
|
|---|
| 214 | */
|
|---|
| 215 | static int
|
|---|
| 216 | contains_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 | */
|
|---|
| 238 | static int
|
|---|
| 239 | locate_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 |
|
|---|
| 272 | static void
|
|---|
| 273 | lc_strcpy(char *dest, const char *src)
|
|---|
| 274 | {
|
|---|
| 275 | while (*src)
|
|---|
| 276 | {
|
|---|
| 277 | *dest++ = TOLOWER(*src);
|
|---|
| 278 | ++src;
|
|---|
| 279 | }
|
|---|
| 280 | *dest = 0;
|
|---|
| 281 | }
|
|---|
| 282 |
|
|---|
| 283 | struct locate_limits
|
|---|
| 284 | {
|
|---|
| 285 | uintmax_t limit;
|
|---|
| 286 | uintmax_t items_accepted;
|
|---|
| 287 | };
|
|---|
| 288 | static struct locate_limits limits;
|
|---|
| 289 |
|
|---|
| 290 |
|
|---|
| 291 | struct 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 | };
|
|---|
| 300 | static struct locate_stats statistics;
|
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 | struct stringbuf
|
|---|
| 304 | {
|
|---|
| 305 | char *buffer;
|
|---|
| 306 | size_t buffersize;
|
|---|
| 307 | size_t *preqlen;
|
|---|
| 308 | };
|
|---|
| 309 | static struct stringbuf casebuf;
|
|---|
| 310 |
|
|---|
| 311 |
|
|---|
| 312 | struct casefolder
|
|---|
| 313 | {
|
|---|
| 314 | const char *pattern;
|
|---|
| 315 | struct stringbuf *pbuf;
|
|---|
| 316 | };
|
|---|
| 317 |
|
|---|
| 318 | struct regular_expression
|
|---|
| 319 | {
|
|---|
| 320 | struct re_pattern_buffer regex; /* for --regex */
|
|---|
| 321 | };
|
|---|
| 322 |
|
|---|
| 323 |
|
|---|
| 324 | struct 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 |
|
|---|
| 343 | typedef int (*visitfunc)(struct process_data *procdata,
|
|---|
| 344 | void *context);
|
|---|
| 345 |
|
|---|
| 346 | struct visitor
|
|---|
| 347 | {
|
|---|
| 348 | visitfunc inspector;
|
|---|
| 349 | void * context;
|
|---|
| 350 | struct visitor *next;
|
|---|
| 351 | };
|
|---|
| 352 |
|
|---|
| 353 |
|
|---|
| 354 | static struct visitor *inspectors = NULL;
|
|---|
| 355 | static struct visitor *lastinspector = NULL;
|
|---|
| 356 | static struct visitor *past_pat_inspector = NULL;
|
|---|
| 357 |
|
|---|
| 358 | /* 0 or 1 pattern(s) */
|
|---|
| 359 | static int
|
|---|
| 360 | process_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. */
|
|---|
| 375 | static int
|
|---|
| 376 | process_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. */
|
|---|
| 408 | static int
|
|---|
| 409 | process_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 |
|
|---|
| 440 | typedef int (*processfunc)(struct process_data *procdata);
|
|---|
| 441 |
|
|---|
| 442 | static processfunc mainprocessor = NULL;
|
|---|
| 443 |
|
|---|
| 444 | static void
|
|---|
| 445 | add_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 |
|
|---|
| 465 | static int
|
|---|
| 466 | visit_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 |
|
|---|
| 476 | static int
|
|---|
| 477 | visit_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 |
|
|---|
| 485 | static int
|
|---|
| 486 | visit_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 |
|
|---|
| 517 | static int
|
|---|
| 518 | visit_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 |
|
|---|
| 592 | static int
|
|---|
| 593 | visit_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 |
|
|---|
| 602 | static int
|
|---|
| 603 | visit_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 */
|
|---|
| 618 | static int
|
|---|
| 619 | visit_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 */
|
|---|
| 640 | static int
|
|---|
| 641 | visit_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 */
|
|---|
| 662 | static int
|
|---|
| 663 | visit_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 */
|
|---|
| 684 | static int
|
|---|
| 685 | visit_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 |
|
|---|
| 705 | static int
|
|---|
| 706 | visit_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 |
|
|---|
| 716 | static int
|
|---|
| 717 | visit_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 |
|
|---|
| 730 | static int
|
|---|
| 731 | visit_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 |
|
|---|
| 741 | static int
|
|---|
| 742 | visit_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 |
|
|---|
| 752 | static int
|
|---|
| 753 | visit_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 |
|
|---|
| 772 | static int
|
|---|
| 773 | visit_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 |
|
|---|
| 809 | static int
|
|---|
| 810 | visit_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 |
|
|---|
| 822 | static int
|
|---|
| 823 | visit_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 | */
|
|---|
| 835 | static void
|
|---|
| 836 | print_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 | */
|
|---|
| 892 | static int
|
|---|
| 893 | looking_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 | */
|
|---|
| 907 | static int
|
|---|
| 908 | looking_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 |
|
|---|
| 961 | static unsigned long
|
|---|
| 962 | search_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 |
|
|---|
| 1290 | extern char *version_string;
|
|---|
| 1291 |
|
|---|
| 1292 | /* The name this program was run with. */
|
|---|
| 1293 | char *program_name;
|
|---|
| 1294 |
|
|---|
| 1295 | static void
|
|---|
| 1296 | usage (FILE *stream)
|
|---|
| 1297 | {
|
|---|
| 1298 | fprintf (stream, _("\
|
|---|
| 1299 | Usage: %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 | }
|
|---|
| 1309 | enum
|
|---|
| 1310 | {
|
|---|
| 1311 | REGEXTYPE_OPTION = CHAR_MAX + 1
|
|---|
| 1312 | };
|
|---|
| 1313 |
|
|---|
| 1314 |
|
|---|
| 1315 | static 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 |
|
|---|
| 1342 | static int
|
|---|
| 1343 | drop_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 |
|
|---|
| 1396 | static int
|
|---|
| 1397 | opendb(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 |
|
|---|
| 1417 | int
|
|---|
| 1418 | dolocate (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]))
|
|---|
| 1742 | static int
|
|---|
| 1743 | open_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 |
|
|---|
| 1765 | int
|
|---|
| 1766 | main (int argc, char **argv)
|
|---|
| 1767 | {
|
|---|
| 1768 | int dbfd = open_secure_db();
|
|---|
| 1769 | drop_privs();
|
|---|
| 1770 |
|
|---|
| 1771 | return dolocate(argc, argv, dbfd);
|
|---|
| 1772 | }
|
|---|
| 1773 |
|
|---|