~ubuntu-branches/ubuntu/gutsy/findutils/gutsy-proposed

« back to all changes in this revision

Viewing changes to locate/locate.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Metzler
  • Date: 2005-07-04 11:37:37 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20050704113737-ll89ui8be35r0pir
Tags: 4.2.22-2
* Remove locatedb on purge. (Closes: #315343)
* revert regex-syntax back to emacs-re. (Closes: #315136) Future versions
  will allow to select this by commandline parameter.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/* locate -- search databases for filenames that match patterns
2
 
   Copyright (C) 1994, 96, 98, 99, 2000, 2003 Free Software Foundation, Inc.
 
2
   Copyright (C) 1994, 1996, 1998, 1999, 2000, 2003,
 
3
                 2004, 2005 Free Software Foundation, Inc.
3
4
 
4
5
   This program is free software; you can redistribute it and/or modify
5
6
   it under the terms of the GNU General Public License as published by
13
14
 
14
15
   You should have received a copy of the GNU General Public License
15
16
   along with this program; if not, write to the Free Software
16
 
   Foundation, Inc., 9 Temple Place - Suite 330, Boston, MA 02111-1307,
 
17
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
17
18
   USA.
18
19
*/
19
20
 
32
33
   128-255      bigram codes (the 128 most common, as determined by `updatedb')
33
34
   32-127       single character (printable) ASCII remainder
34
35
 
35
 
   Uses a novel two-tiered string search technique:
36
 
 
37
 
   First, match a metacharacter-free subpattern and a partial pathname
38
 
   BACKWARDS to avoid full expansion of the pathname list.
39
 
   The time savings is 40-50% over forward matching, which cannot efficiently
40
 
   handle overlapped search patterns and compressed path remainders.
41
 
 
42
 
   Then, match the actual shell glob-style regular expression (if in this form)
43
 
   against the candidate pathnames using the slower shell filename
44
 
   matching routines.
45
 
 
46
 
   Described more fully in Usenix ;login:, Vol 8, No 1,
47
 
   February/March, 1983, p. 8.
 
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
 
48
57
 
49
58
   Written by James A. Woods <jwoods@adobe.com>.
50
 
   Modified by David MacKenzie <djm@gnu.ai.mit.edu>.  */
 
59
   Modified by David MacKenzie <djm@gnu.org>.  
 
60
   Additional work by James Youngman and Bas van Gompel.
 
61
*/
51
62
 
52
 
#define _GNU_SOURCE
53
 
#include <gnulib/config.h>
54
 
#undef VERSION
55
 
#undef PACKAGE_VERSION
56
 
#undef PACKAGE_TARNAME
57
 
#undef PACKAGE_STRING
58
 
#undef PACKAGE_NAME
59
 
#undef PACKAGE
60
63
#include <config.h>
61
64
#include <stdio.h>
 
65
#include <ctype.h>
62
66
#include <sys/types.h>
63
67
#include <sys/stat.h>
64
68
#include <time.h>
65
69
#include <fnmatch.h>
66
70
#include <getopt.h>
 
71
#include <xstrtol.h>
 
72
 
 
73
#ifdef HAVE_UNISTD_H
 
74
/* We need <unistd.h> for isatty(). */
 
75
#include <unistd.h>
 
76
#endif
 
77
 
67
78
 
68
79
#define NDEBUG
69
80
#include <assert.h>
100
111
#ifdef gettext_noop
101
112
# define N_(String) gettext_noop (String)
102
113
#else
103
 
# define N_(String) (String)
 
114
/* We used to use (String) instead of just String, but apparentl;y ISO C
 
115
 * doesn't allow this (at least, that's what HP said when someone reported
 
116
 * this as a compiler bug).  This is HP case number 1205608192.  See
 
117
 * also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11250 (which references 
 
118
 * ANSI 3.5.7p14-15).  The Intel icc compiler also rejects constructs
 
119
 * like: static const char buf[] = ("string");
 
120
 */
 
121
# define N_(String) String
104
122
#endif
105
123
 
106
124
#include "locatedb.h"
107
125
#include <getline.h>
 
126
#include "../gnulib/lib/xalloc.h"
 
127
#include "../gnulib/lib/error.h"
 
128
#include "../gnulib/lib/human.h"
 
129
#include "dirname.h"
 
130
#include "closeout.h"
 
131
#include "nextelem.h"
 
132
#include "regex.h"
 
133
#include "quote.h"
 
134
#include "quotearg.h"
 
135
#include "printquoted.h"
 
136
 
108
137
 
109
138
/* Note that this evaluates C many times.  */
110
139
#ifdef _LIBC
115
144
# define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch))
116
145
#endif
117
146
 
118
 
typedef enum {false, true} boolean;
 
147
/* typedef enum {false, true} boolean; */
119
148
 
120
149
/* Warn if a database is older than this.  8 days allows for a weekly
121
150
   update that takes up to a day to perform.  */
126
155
 
127
156
#define WARN_SECONDS ((SECONDS_PER_UNIT) * (WARN_NUMBER_UNITS))
128
157
 
 
158
enum visit_result
 
159
  {
 
160
    VISIT_CONTINUE = 1,  /* please call the next visitor */
 
161
    VISIT_ACCEPTED = 2,  /* accepted, call no futher callbacks for this file */
 
162
    VISIT_REJECTED = 4,  /* rejected, process next file. */
 
163
    VISIT_ABORT    = 8   /* rejected, process no more files. */
 
164
  };
 
165
 
 
166
enum ExistenceCheckType 
 
167
  {
 
168
    ACCEPT_EITHER,              /* Corresponds to lack of -E/-e option */
 
169
    ACCEPT_EXISTING,            /* Corresponds to option -e */
 
170
    ACCEPT_NON_EXISTING         /* Corresponds to option -E */
 
171
  };
 
172
 
129
173
/* Check for existence of files before printing them out? */
130
 
int check_existence = 0;
131
 
 
132
 
char *next_element ();
133
 
char *xmalloc ();
134
 
char *xrealloc ();
135
 
void error ();
 
174
enum ExistenceCheckType check_existence = ACCEPT_EITHER;
 
175
 
 
176
static int follow_symlinks = 1;
 
177
 
 
178
/* What to separate the results with. */
 
179
static int separator = '\n';
 
180
 
 
181
static struct quoting_options * quote_opts = NULL;
 
182
static bool stdout_is_a_tty;
 
183
static bool print_quoted_filename;
136
184
 
137
185
/* Read in a 16-bit int, high byte first (network byte order).  */
138
186
 
139
 
static int
140
 
get_short (fp)
141
 
     FILE *fp;
 
187
static short
 
188
get_short (FILE *fp)
142
189
{
143
190
 
144
191
  register short x;
145
192
 
146
 
  x = fgetc (fp) << 8;
 
193
  x = (signed char) fgetc (fp) << 8;
147
194
  x |= (fgetc (fp) & 0xff);
148
195
  return x;
149
196
}
150
197
 
151
 
/* Return a pointer to the last character in a static copy of the last
152
 
   glob-free subpattern in NAME,
153
 
   with '\0' prepended for a fast backwards pre-match.  */
 
198
const char * const metacharacters = "*?[]\\";
154
199
 
155
 
static char *
156
 
last_literal_end (name)
157
 
     char *name;
 
200
/* Return nonzero if S contains any shell glob characters.
 
201
 */
 
202
static int 
 
203
contains_metacharacter(const char *s)
158
204
{
159
 
  static char *globfree = NULL; /* A copy of the subpattern in NAME.  */
160
 
  static size_t gfalloc = 0;    /* Bytes allocated for `globfree'.  */
161
 
  register char *subp;          /* Return value.  */
162
 
  register char *p;             /* Search location in NAME.  */
163
 
 
164
 
  /* Find the end of the subpattern.
165
 
     Skip trailing metacharacters and [] ranges. */
166
 
  for (p = name + strlen (name) - 1; p >= name && strchr ("*?]", *p) != NULL;
167
 
       p--)
168
 
    {
169
 
      if (*p == ']')
170
 
        while (p >= name && *p != '[')
171
 
          p--;
172
 
    }
173
 
  if (p < name)
174
 
    p = name;
175
 
 
176
 
  if (p - name + 3 > gfalloc)
177
 
    {
178
 
      gfalloc = p - name + 3 + 64; /* Room to grow.  */
179
 
      globfree = xrealloc (globfree, gfalloc);
180
 
    }
181
 
  subp = globfree;
182
 
  *subp++ = '\0';
183
 
 
184
 
  /* If the pattern has only metacharacters, make every path match the
185
 
     subpattern, so it gets checked the slow way.  */
186
 
  if (p == name && strchr ("?*[]", *p) != NULL)
187
 
    *subp++ = '/';
 
205
  if (NULL == strpbrk(s, metacharacters))
 
206
    return 0;
188
207
  else
189
 
    {
190
 
      char *endmark;
191
 
      /* Find the start of the metacharacter-free subpattern.  */
192
 
      for (endmark = p; p >= name && strchr ("]*?", *p) == NULL; p--)
193
 
        ;
194
 
      /* Copy the subpattern into globfree.  */
195
 
      for (++p; p <= endmark; )
196
 
        *subp++ = *p++;
197
 
    }
198
 
  *subp-- = '\0';               /* Null terminate, though it's not needed.  */
199
 
 
200
 
  return subp;
 
208
    return 1;
201
209
}
202
210
 
203
 
/* getstr()
 
211
/* locate_read_str()
204
212
 *
205
213
 * Read bytes from FP into the buffer at offset OFFSET in (*BUF),
206
214
 * until we reach DELIMITER or end-of-file.   We reallocate the buffer 
214
222
 * We call the function locate_read_str() to avoid a name clash with the curses
215
223
 * function getstr().
216
224
 */
217
 
static int locate_read_str(char **buf, size_t *siz, FILE *fp, int delimiter, int offs)
 
225
static int
 
226
locate_read_str(char **buf, size_t *siz, FILE *fp, int delimiter, int offs)
218
227
{
219
228
  char * p = NULL;
220
229
  size_t sz = 0;
225
234
    {
226
235
      assert(p != NULL);
227
236
      
228
 
      needed = offs + nread;
 
237
      needed = offs + nread + 1;
229
238
      if (needed > (*siz))
230
239
        {
231
240
          char *pnew = realloc(*buf, needed);
246
255
}
247
256
 
248
257
 
249
 
/* Print the entries in DBFILE that match shell globbing pattern PATHPART.
 
258
static void
 
259
lc_strcpy(char *dest, const char *src)
 
260
{
 
261
  while (*src)
 
262
    {
 
263
      *dest++ = TOLOWER(*src);
 
264
      ++src;
 
265
    }
 
266
  *dest = 0;
 
267
}
 
268
 
 
269
struct locate_limits
 
270
{
 
271
  uintmax_t limit;
 
272
  uintmax_t items_accepted;
 
273
};
 
274
static struct locate_limits limits;
 
275
 
 
276
 
 
277
struct locate_stats
 
278
{
 
279
  uintmax_t compressed_bytes;
 
280
  uintmax_t total_filename_count;
 
281
  uintmax_t total_filename_length;
 
282
  uintmax_t whitespace_count;
 
283
  uintmax_t newline_count;
 
284
  uintmax_t highbit_filename_count;
 
285
};
 
286
static struct locate_stats statistics;
 
287
 
 
288
 
 
289
struct stringbuf
 
290
{
 
291
  char *buffer;
 
292
  size_t buffersize;
 
293
  size_t *soffs;
 
294
  size_t *preqlen;
 
295
};
 
296
static struct stringbuf casebuf;
 
297
 
 
298
 
 
299
struct casefolder
 
300
{
 
301
  const char *pattern;
 
302
  struct stringbuf *pbuf;
 
303
};
 
304
 
 
305
struct regular_expression
 
306
{
 
307
  regex_t re;
 
308
};
 
309
 
 
310
 
 
311
struct process_data
 
312
{
 
313
  int c;                        /* An input byte.  */
 
314
  int count; /* The length of the prefix shared with the previous database entry.  */
 
315
  int len;
 
316
  char *original_filename;      /* The current input database entry. */
 
317
  size_t pathsize;              /* Amount allocated for it.  */
 
318
  char *munged_filename;        /* path or base_name(path) */
 
319
  FILE *fp;                     /* The pathname database.  */
 
320
  char *dbfile;                 /* Its name, or "<stdin>" */
 
321
  /* for the old database format,
 
322
     the first and second characters of the most common bigrams.  */
 
323
  char bigram1[128];
 
324
  char bigram2[128];
 
325
};
 
326
 
 
327
 
 
328
typedef int (*visitfunc)(struct process_data *procdata,
 
329
                         void *context);
 
330
 
 
331
struct visitor
 
332
{
 
333
  visitfunc      inspector;
 
334
  void *         context;
 
335
  struct visitor *next;
 
336
};
 
337
 
 
338
 
 
339
static struct visitor *inspectors = NULL;
 
340
static struct visitor *lastinspector = NULL;
 
341
static struct visitor *past_pat_inspector = NULL;
 
342
 
 
343
/* 0 or 1 pattern(s) */
 
344
static int
 
345
process_simple(struct process_data *procdata)
 
346
{
 
347
  int result = VISIT_CONTINUE;
 
348
  const struct visitor *p = inspectors;
 
349
  
 
350
  while ( ((VISIT_CONTINUE | VISIT_ACCEPTED) & result) && (NULL != p) )
 
351
    {
 
352
      result = (p->inspector)(procdata, p->context);
 
353
      p = p->next;
 
354
    }
 
355
 
 
356
    return result;
 
357
}
 
358
 
 
359
/* Accept if any pattern matches. */
 
360
static int
 
361
process_or (struct process_data *procdata)
 
362
{
 
363
  int result = VISIT_CONTINUE;
 
364
  const struct visitor *p = inspectors;
 
365
  
 
366
  while ( ((VISIT_CONTINUE | VISIT_REJECTED) & result) && (past_pat_inspector != p) )
 
367
    {
 
368
      result = (p->inspector)(procdata, p->context);
 
369
      p = p->next;
 
370
    }
 
371
 
 
372
  if (result == VISIT_CONTINUE)
 
373
    result = VISIT_REJECTED;
 
374
  if (result & (VISIT_ABORT | VISIT_REJECTED))
 
375
    return result;
 
376
 
 
377
  p = past_pat_inspector;
 
378
  result = VISIT_CONTINUE;
 
379
 
 
380
  while ( (VISIT_CONTINUE == result) && (NULL != p) )
 
381
    {
 
382
      result = (p->inspector)(procdata, p->context);
 
383
      p = p->next;
 
384
    }
 
385
  
 
386
  if (VISIT_CONTINUE == result)
 
387
    return VISIT_ACCEPTED;
 
388
  else
 
389
    return result;
 
390
}
 
391
 
 
392
/* Accept if all pattern match. */
 
393
static int
 
394
process_and (struct process_data *procdata)
 
395
{
 
396
  int result = VISIT_CONTINUE;
 
397
  const struct visitor *p = inspectors;
 
398
  
 
399
  while ( ((VISIT_CONTINUE | VISIT_ACCEPTED) & result) && (past_pat_inspector != p) )
 
400
    {
 
401
      result = (p->inspector)(procdata, p->context);
 
402
      p = p->next;
 
403
    }
 
404
 
 
405
  if (result == VISIT_CONTINUE)
 
406
    result = VISIT_REJECTED;
 
407
  if (result & (VISIT_ABORT | VISIT_REJECTED))
 
408
    return result;
 
409
 
 
410
  p = past_pat_inspector;
 
411
  result = VISIT_CONTINUE;
 
412
 
 
413
  while ( (VISIT_CONTINUE == result) && (NULL != p) )
 
414
    {
 
415
      result = (p->inspector)(procdata, p->context);
 
416
      p = p->next;
 
417
    }
 
418
  
 
419
  if (VISIT_CONTINUE == result)
 
420
    return VISIT_ACCEPTED;
 
421
  else
 
422
    return result;
 
423
}
 
424
 
 
425
typedef int (*processfunc)(struct process_data *procdata);
 
426
 
 
427
static processfunc mainprocessor = NULL;
 
428
 
 
429
static void
 
430
add_visitor(visitfunc fn, void *context)
 
431
{
 
432
  struct visitor *p = xmalloc(sizeof(struct visitor));
 
433
  p->inspector = fn;
 
434
  p->context   = context;
 
435
  p->next = NULL;
 
436
  
 
437
  if (NULL == lastinspector)
 
438
    {
 
439
      lastinspector = inspectors = p;
 
440
    }
 
441
  else
 
442
    {
 
443
      lastinspector->next = p;
 
444
      lastinspector = p;
 
445
    }
 
446
}
 
447
 
 
448
 
 
449
 
 
450
static int
 
451
visit_justprint_quoted(struct process_data *procdata, void *context)
 
452
{
 
453
  (void) context;
 
454
  print_quoted (stdout, quote_opts, stdout_is_a_tty,
 
455
                "%s", 
 
456
                procdata->original_filename);
 
457
  putchar(separator);
 
458
  return VISIT_CONTINUE;
 
459
}
 
460
 
 
461
static int
 
462
visit_justprint_unquoted(struct process_data *procdata, void *context)
 
463
{
 
464
  (void) context;
 
465
  fputs(procdata->original_filename, stdout);
 
466
  putchar(separator);
 
467
  return VISIT_CONTINUE;
 
468
}
 
469
 
 
470
static int
 
471
visit_old_format(struct process_data *procdata, void *context)
 
472
{
 
473
  register char *s;
 
474
  (void) context;
 
475
 
 
476
  /* Get the offset in the path where this path info starts.  */
 
477
  if (procdata->c == LOCATEDB_OLD_ESCAPE)
 
478
    procdata->count += getw (procdata->fp) - LOCATEDB_OLD_OFFSET;
 
479
  else
 
480
    procdata->count += procdata->c - LOCATEDB_OLD_OFFSET;
 
481
 
 
482
  /* Overlay the old path with the remainder of the new.  */
 
483
  for (s = procdata->original_filename + procdata->count;
 
484
       (procdata->c = getc (procdata->fp)) > LOCATEDB_OLD_ESCAPE;)
 
485
    if (procdata->c < 0200)
 
486
      *s++ = procdata->c;               /* An ordinary character.  */
 
487
    else
 
488
      {
 
489
        /* Bigram markers have the high bit set. */
 
490
        procdata->c &= 0177;
 
491
        *s++ = procdata->bigram1[procdata->c];
 
492
        *s++ = procdata->bigram2[procdata->c];
 
493
      }
 
494
  *s-- = '\0';
 
495
 
 
496
  procdata->munged_filename = procdata->original_filename;
 
497
  
 
498
  return VISIT_CONTINUE;
 
499
}
 
500
 
 
501
 
 
502
static int
 
503
visit_locate02_format(struct process_data *procdata, void *context)
 
504
{
 
505
  register char *s;
 
506
  int nread;
 
507
  (void) context;
 
508
 
 
509
  if (procdata->c == LOCATEDB_ESCAPE)
 
510
    procdata->count += (short)get_short (procdata->fp);
 
511
  else if (procdata->c > 127)
 
512
    procdata->count += procdata->c - 256;
 
513
  else
 
514
    procdata->count += procdata->c;
 
515
 
 
516
  if (procdata->count > procdata->len || procdata->count < 0)
 
517
    {
 
518
      /* This should not happen generally , but since we're
 
519
       * reading in data which is outside our control, we
 
520
       * cannot prevent it.
 
521
       */
 
522
      error(1, 0, _("locate database `%s' is corrupt or invalid"), procdata->dbfile);
 
523
    }
 
524
 
 
525
  /* Overlay the old path with the remainder of the new.  */
 
526
  nread = locate_read_str (&procdata->original_filename, &procdata->pathsize,
 
527
                           procdata->fp, 0, procdata->count);
 
528
  if (nread < 0)
 
529
    return VISIT_ABORT;
 
530
  procdata->c = getc (procdata->fp);
 
531
  procdata->len = procdata->count + nread;
 
532
  s = procdata->original_filename + procdata->len - 1; /* Move to the last char in path.  */
 
533
  assert (s[0] != '\0');
 
534
  assert (s[1] == '\0'); /* Our terminator.  */
 
535
  assert (s[2] == '\0'); /* Added by locate_read_str.  */
 
536
 
 
537
  procdata->munged_filename = procdata->original_filename;
 
538
  
 
539
  return VISIT_CONTINUE;
 
540
}
 
541
 
 
542
static int
 
543
visit_basename(struct process_data *procdata, void *context)
 
544
{
 
545
  (void) context;
 
546
  procdata->munged_filename = base_name(procdata->original_filename);
 
547
 
 
548
  return VISIT_CONTINUE;
 
549
}
 
550
 
 
551
 
 
552
static int
 
553
visit_casefold(struct process_data *procdata, void *context)
 
554
{
 
555
  struct stringbuf *b = context;
 
556
 
 
557
  if (*b->preqlen+1 > b->buffersize)
 
558
    {
 
559
      b->buffer = xrealloc(b->buffer, *b->preqlen+1); /* XXX: consider using extendbuf(). */
 
560
      b->buffersize = *b->preqlen+1;
 
561
    }
 
562
  lc_strcpy(b->buffer, procdata->munged_filename);
 
563
 
 
564
  return VISIT_CONTINUE;
 
565
}
 
566
  
 
567
/* visit_existing_follow implements -L -e */
 
568
static int
 
569
visit_existing_follow(struct process_data *procdata, void *context)
 
570
{
 
571
  struct stat st;
 
572
  (void) context;
 
573
 
 
574
  /* munged_filename has been converted in some way (to lower case,
 
575
   * or is just the base name of the file), and original_filename has not.  
 
576
   * Hence only original_filename is still actually the name of the file 
 
577
   * whose existence we would need to check.
 
578
   */
 
579
  if (stat(procdata->original_filename, &st) != 0)
 
580
    {
 
581
      return VISIT_REJECTED;
 
582
    }
 
583
  else
 
584
    {
 
585
      return VISIT_CONTINUE;
 
586
    }
 
587
}
 
588
 
 
589
/* visit_non_existing_follow implements -L -E */
 
590
static int
 
591
visit_non_existing_follow(struct process_data *procdata, void *context)
 
592
{
 
593
  struct stat st;
 
594
  (void) context;
 
595
 
 
596
  /* munged_filename has been converted in some way (to lower case,
 
597
   * or is just the base name of the file), and original_filename has not.  
 
598
   * Hence only original_filename is still actually the name of the file 
 
599
   * whose existence we would need to check.
 
600
   */
 
601
  if (stat(procdata->original_filename, &st) == 0)
 
602
    {
 
603
      return VISIT_REJECTED;
 
604
    }
 
605
  else
 
606
    {
 
607
      return VISIT_CONTINUE;
 
608
    }
 
609
}
 
610
 
 
611
/* visit_existing_nofollow implements -P -e */
 
612
static int
 
613
visit_existing_nofollow(struct process_data *procdata, void *context)
 
614
{
 
615
  struct stat st;
 
616
  (void) context;
 
617
 
 
618
  /* munged_filename has been converted in some way (to lower case,
 
619
   * or is just the base name of the file), and original_filename has not.  
 
620
   * Hence only original_filename is still actually the name of the file 
 
621
   * whose existence we would need to check.
 
622
   */
 
623
  if (lstat(procdata->original_filename, &st) != 0)
 
624
    {
 
625
      return VISIT_REJECTED;
 
626
    }
 
627
  else
 
628
    {
 
629
      return VISIT_CONTINUE;
 
630
    }
 
631
}
 
632
 
 
633
/* visit_non_existing_nofollow implements -P -E */
 
634
static int
 
635
visit_non_existing_nofollow(struct process_data *procdata, void *context)
 
636
{
 
637
  struct stat st;
 
638
  (void) context;
 
639
 
 
640
  /* munged_filename has been converted in some way (to lower case,
 
641
   * or is just the base name of the file), and original_filename has not.  
 
642
   * Hence only original_filename is still actually the name of the file 
 
643
   * whose existence we would need to check.
 
644
   */
 
645
  if (lstat(procdata->original_filename, &st) == 0)
 
646
    {
 
647
      return VISIT_REJECTED;
 
648
    }
 
649
  else
 
650
    {
 
651
      return VISIT_CONTINUE;
 
652
    }
 
653
}
 
654
 
 
655
static int
 
656
visit_substring_match_nocasefold(struct process_data *procdata, void *context)
 
657
{
 
658
  const char *pattern = context;
 
659
 
 
660
  if (NULL != strstr(procdata->munged_filename, pattern))
 
661
    return VISIT_ACCEPTED;
 
662
  else
 
663
    return VISIT_REJECTED;
 
664
}
 
665
 
 
666
static int
 
667
visit_substring_match_casefold(struct process_data *procdata, void *context)
 
668
{
 
669
  const struct casefolder * p = context;
 
670
  const struct stringbuf * b = p->pbuf;
 
671
  (void) procdata;
 
672
 
 
673
  if (NULL != strstr(b->buffer, p->pattern))
 
674
    return VISIT_ACCEPTED;
 
675
  else
 
676
    return VISIT_REJECTED;
 
677
}
 
678
 
 
679
 
 
680
static int
 
681
visit_globmatch_nofold(struct process_data *procdata, void *context)
 
682
{
 
683
  const char *glob = context;
 
684
  if (fnmatch(glob, procdata->munged_filename, 0) != 0)
 
685
    return VISIT_REJECTED;
 
686
  else
 
687
    return VISIT_ACCEPTED;
 
688
}
 
689
 
 
690
 
 
691
static int
 
692
visit_globmatch_casefold(struct process_data *procdata, void *context)
 
693
{
 
694
  const char *glob = context;
 
695
  if (fnmatch(glob, procdata->munged_filename, FNM_CASEFOLD) != 0)
 
696
    return VISIT_REJECTED;
 
697
  else
 
698
    return VISIT_ACCEPTED;
 
699
}
 
700
 
 
701
 
 
702
static int
 
703
visit_regex(struct process_data *procdata, void *context)
 
704
{
 
705
  struct regular_expression *p = context;
 
706
  
 
707
  if (0 == regexec(&p->re, procdata->munged_filename, 0u, NULL, 0))
 
708
    return VISIT_ACCEPTED;      /* match */
 
709
  else
 
710
    return VISIT_REJECTED;      /* no match */
 
711
}
 
712
 
 
713
 
 
714
static int
 
715
visit_stats(struct process_data *procdata, void *context)
 
716
{
 
717
  struct locate_stats *p = context;
 
718
  size_t len = strlen(procdata->original_filename);
 
719
  const char *s;
 
720
  int highbit, whitespace, newline;
 
721
  
 
722
  ++(p->total_filename_count);
 
723
  p->total_filename_length += len;
 
724
  
 
725
  highbit = whitespace = newline = 0;
 
726
  for (s=procdata->original_filename; *s; ++s)
 
727
    {
 
728
      if ( (int)(*s) & 128 )
 
729
        highbit = 1;
 
730
      if ('\n' == *s)
 
731
        {
 
732
          newline = whitespace = 1;
 
733
        }
 
734
      else if (isspace((unsigned char)*s))
 
735
        {
 
736
          whitespace = 1;
 
737
        }
 
738
    }
 
739
 
 
740
  if (highbit)
 
741
    ++(p->highbit_filename_count);
 
742
  if (whitespace)
 
743
    ++(p->whitespace_count);
 
744
  if (newline)
 
745
    ++(p->newline_count);
 
746
 
 
747
  return VISIT_CONTINUE;
 
748
}
 
749
 
 
750
 
 
751
/* Emit the statistics.
 
752
 */
 
753
static void
 
754
print_stats(int argc, size_t database_file_size)
 
755
{
 
756
  char hbuf[LONGEST_HUMAN_READABLE + 1];
 
757
  
 
758
  printf(_("Locate database size: %s bytes\n"),
 
759
         human_readable ((uintmax_t) database_file_size,
 
760
                         hbuf, human_ceiling, 1, 1));
 
761
  
 
762
  printf(_("Filenames: %s "),
 
763
         human_readable (statistics.total_filename_count,
 
764
                         hbuf, human_ceiling, 1, 1));
 
765
  printf(_("with a cumulative length of %s bytes"),
 
766
         human_readable (statistics.total_filename_length,
 
767
                         hbuf, human_ceiling, 1, 1));
 
768
  
 
769
  printf(_("\n\tof which %s contain whitespace, "),
 
770
         human_readable (statistics.whitespace_count,
 
771
                         hbuf, human_ceiling, 1, 1));
 
772
  printf(_("\n\t%s contain newline characters, "),
 
773
         human_readable (statistics.newline_count,
 
774
                         hbuf, human_ceiling, 1, 1));
 
775
  printf(_("\n\tand %s contain characters with the high bit set.\n"),
 
776
         human_readable (statistics.highbit_filename_count,
 
777
                         hbuf, human_ceiling, 1, 1));
 
778
  
 
779
  if (!argc)
 
780
    printf(_("Compression ratio %4.2f%%\n"),
 
781
           100.0 * ((double)statistics.total_filename_length
 
782
                    - (double) database_file_size)
 
783
           / (double) statistics.total_filename_length);
 
784
  printf("\n");
 
785
}
 
786
 
 
787
 
 
788
/* Print the entries in DBFILE that match shell globbing patterns in ARGV.
250
789
   Return the number of entries printed.  */
251
790
 
252
 
static int
253
 
locate (pathpart, dbfile, ignore_case)
254
 
     char *pathpart, *dbfile;
255
 
     int ignore_case;
 
791
static unsigned long
 
792
locate (int argc,
 
793
        char **argv,
 
794
        char *dbfile,
 
795
        int ignore_case,
 
796
        int enable_print,
 
797
        int basename_only,
 
798
        int use_limit,
 
799
        struct locate_limits *plimit,
 
800
        int stats,
 
801
        int op_and,
 
802
        int regex)
256
803
{
257
 
  /* The pathname database.  */
258
 
  FILE *fp;
259
 
  /* An input byte.  */
260
 
  int c;
261
 
  /* Number of bytes read from an entry.  */
262
 
  int nread;
263
 
 
264
 
  /* true if PATHPART contains globbing metacharacters.  */
265
 
  boolean globflag;
266
 
  /* The end of the last glob-free subpattern in PATHPART.  */
267
 
  char *patend;
268
 
 
269
 
  /* The current input database entry.  */
270
 
  char *path;
271
 
  /* Amount allocated for it.  */
272
 
  size_t pathsize;
273
 
 
274
 
  /* The length of the prefix shared with the previous database entry.  */
275
 
  int count = 0;
276
 
  /* Where in `path' to stop the backward search for the last character
277
 
     in the subpattern.  Set according to `count'.  */
278
 
  char *cutoff;
279
 
 
280
 
  /* true if we found a fast match (of patend) on the previous path.  */
281
 
  boolean prev_fast_match = false;
282
 
  /* The return value.  */
283
 
  int printed = 0;
284
 
 
285
 
  /* true if reading a bigram-encoded database.  */
286
 
  boolean old_format = false;
287
 
  /* For the old database format,
288
 
     the first and second characters of the most common bigrams.  */
289
 
  char bigram1[128], bigram2[128];
290
 
 
 
804
  char *pathpart;               /* A pattern to consider. */
 
805
  int argn;                     /* Index to current pattern in argv. */
 
806
  int need_fold;        /* Set when folding and any pattern is non-glob. */
 
807
  int nread;                 /* number of bytes read from an entry. */
 
808
  struct process_data procdata; /* Storage for data shared with visitors. */
 
809
  
 
810
  int old_format = 0; /* true if reading a bigram-encoded database.  */
 
811
  static bool did_stdin = false; /* Set to prevent rereading stdin. */
 
812
  struct visitor* pvis; /* temp for determining past_pat_inspector. */
 
813
  
291
814
  /* To check the age of the database.  */
292
815
  struct stat st;
293
816
  time_t now;
294
817
 
295
 
  if (stat (dbfile, &st) || (fp = fopen (dbfile, "r")) == NULL)
296
 
    {
297
 
      error (0, errno, "%s", dbfile);
298
 
      return 0;
299
 
    }
300
 
  time(&now);
301
 
  if (now - st.st_mtime > WARN_SECONDS)
302
 
    {
303
 
      /* For example:
304
 
         warning: database `fred' is more than 8 days old */
305
 
      error (0, 0, _("warning: database `%s' is more than %d %s old"),
306
 
             dbfile, WARN_NUMBER_UNITS, _(warn_name_units));
307
 
    }
308
 
 
309
 
  pathsize = 1026;              /* Increased as necessary by locate_read_str.  */
310
 
  path = xmalloc (pathsize);
311
 
 
312
 
  nread = fread (path, 1, sizeof (LOCATEDB_MAGIC), fp);
 
818
 
 
819
  procdata.len = procdata.count = 0;
 
820
  if (!strcmp (dbfile, "-"))
 
821
    {
 
822
      if (did_stdin)
 
823
        {
 
824
          error (0, 0, _("warning: the locate database can only be read from stdin once."));
 
825
          return 0;
 
826
        }
 
827
      
 
828
            
 
829
      procdata.dbfile = "<stdin>";
 
830
      procdata.fp = stdin;
 
831
      did_stdin = true;
 
832
    }
 
833
  else
 
834
    {
 
835
      if (stat (dbfile, &st) || (procdata.fp = fopen (dbfile, "r")) == NULL)
 
836
        {
 
837
          error (0, errno, "%s", dbfile);
 
838
          return 0;
 
839
        }
 
840
      time(&now);
 
841
      if (now - st.st_mtime > WARN_SECONDS)
 
842
        {
 
843
          /* For example:
 
844
             warning: database `fred' is more than 8 days old */
 
845
          error (0, 0, _("warning: database `%s' is more than %d %s old"),
 
846
                 dbfile, WARN_NUMBER_UNITS, _(warn_name_units));
 
847
        }
 
848
      procdata.dbfile = dbfile;
 
849
    }
 
850
 
 
851
  procdata.pathsize = 1026;     /* Increased as necessary by locate_read_str.  */
 
852
  procdata.original_filename = xmalloc (procdata.pathsize);
 
853
 
 
854
  nread = fread (procdata.original_filename, 1, sizeof (LOCATEDB_MAGIC),
 
855
                 procdata.fp);
313
856
  if (nread != sizeof (LOCATEDB_MAGIC)
314
 
      || memcmp (path, LOCATEDB_MAGIC, sizeof (LOCATEDB_MAGIC)))
 
857
      || memcmp (procdata.original_filename, LOCATEDB_MAGIC,
 
858
                 sizeof (LOCATEDB_MAGIC)))
315
859
    {
316
860
      int i;
317
861
      /* Read the list of the most common bigrams in the database.  */
318
 
      fseek (fp, 0, 0);
 
862
      nread = fread (procdata.original_filename + sizeof (LOCATEDB_MAGIC), 1,
 
863
          256 - sizeof (LOCATEDB_MAGIC), procdata.fp);
319
864
      for (i = 0; i < 128; i++)
320
865
        {
321
 
          bigram1[i] = getc (fp);
322
 
          bigram2[i] = getc (fp);
323
 
        }
324
 
      old_format = true;
325
 
    }
326
 
 
327
 
  /* If we ignore case,
328
 
     convert it to lower first so we don't have to do it every time */
329
 
  if (ignore_case){
330
 
    for (patend=pathpart;*patend;++patend){
331
 
     *patend=TOLOWER(*patend);
332
 
    }
333
 
  }
334
 
  
335
 
  
336
 
  globflag = strchr (pathpart, '*') || strchr (pathpart, '?')
337
 
    || strchr (pathpart, '[');
338
 
 
339
 
  patend = last_literal_end (pathpart);
340
 
 
341
 
  c = getc (fp);
342
 
  while (c != EOF)
343
 
    {
344
 
      register char *s;         /* Scan the path we read in.  */
345
 
 
346
 
      if (old_format)
347
 
        {
348
 
          /* Get the offset in the path where this path info starts.  */
349
 
          if (c == LOCATEDB_OLD_ESCAPE)
350
 
            count += getw (fp) - LOCATEDB_OLD_OFFSET;
351
 
          else
352
 
            count += c - LOCATEDB_OLD_OFFSET;
353
 
 
354
 
          /* Overlay the old path with the remainder of the new.  */
355
 
          for (s = path + count; (c = getc (fp)) > LOCATEDB_OLD_ESCAPE;)
356
 
            if (c < 0200)
357
 
              *s++ = c;         /* An ordinary character.  */
358
 
            else
359
 
              {
360
 
                /* Bigram markers have the high bit set. */
361
 
                c &= 0177;
362
 
                *s++ = bigram1[c];
363
 
                *s++ = bigram2[c];
364
 
              }
365
 
          *s-- = '\0';
366
 
        }
367
 
      else
368
 
        {
369
 
          if (c == LOCATEDB_ESCAPE)
370
 
            count += get_short (fp);
371
 
          else if (c > 127)
372
 
            count += c - 256;
373
 
          else
374
 
            count += c;
375
 
 
376
 
          /* Overlay the old path with the remainder of the new.  */
377
 
          nread = locate_read_str (&path, &pathsize, fp, 0, count); 
378
 
          if (nread < 0)
 
866
          procdata.bigram1[i] = procdata.original_filename[i << 1];
 
867
          procdata.bigram2[i] = procdata.original_filename[(i << 1) + 1];
 
868
        }
 
869
      old_format = 1;
 
870
    }
 
871
 
 
872
  /* Set up the inspection regime */
 
873
  inspectors = NULL;
 
874
  lastinspector = NULL;
 
875
  past_pat_inspector = NULL;
 
876
 
 
877
  if (old_format)
 
878
    add_visitor(visit_old_format, NULL);
 
879
  else
 
880
    add_visitor(visit_locate02_format, NULL);
 
881
 
 
882
  if (basename_only)
 
883
    add_visitor(visit_basename, NULL);
 
884
  
 
885
  /* See if we need fold. */
 
886
  if (ignore_case && !regex)
 
887
    for ( argn = 0; argn < argc; argn++ )
 
888
      {
 
889
        pathpart = argv[argn];
 
890
        if (!contains_metacharacter(pathpart))
 
891
          {
 
892
            need_fold = 1;
379
893
            break;
380
 
          c = getc (fp);
381
 
          s = path + count + nread - 2; /* Move to the last char in path.  */
382
 
          assert (s[0] != '\0');
383
 
          assert (s[1] == '\0'); /* Our terminator.  */
384
 
          assert (s[2] == '\0'); /* Added by locate_read_str.  */
385
 
        }
386
 
 
387
 
      /* If the previous path matched, scan the whole path for the last
388
 
         char in the subpattern.  If not, the shared prefix doesn't match
389
 
         the pattern, so don't scan it for the last char.  */
390
 
      cutoff = prev_fast_match ? path : path + count;
391
 
 
392
 
      /* Search backward starting at the end of the path we just read in,
393
 
         for the character at the end of the last glob-free subpattern
394
 
         in PATHPART.  */
395
 
      if (ignore_case)
396
 
        {
397
 
          for (prev_fast_match = false; s >= cutoff; s--)
398
 
            /* Fast first char check. */
399
 
            if (TOLOWER(*s) == *patend)
400
 
              {
401
 
                char *s2;               /* Scan the path we read in. */
402
 
                register char *p2;      /* Scan `patend'.  */
403
 
 
404
 
                for (s2 = s - 1, p2 = patend - 1; *p2 != '\0' && TOLOWER(*s2) == *p2;
405
 
                     s2--, p2--)
406
 
                  ;
407
 
                if (*p2 == '\0')
408
 
                  {
409
 
                    /* Success on the fast match.  Compare the whole pattern
410
 
                       if it contains globbing characters.  */
411
 
                    prev_fast_match = true;
412
 
                    if (globflag == false || fnmatch (pathpart, path, FNM_CASEFOLD) == 0)
413
 
                      {
414
 
                        if (!check_existence || stat(path, &st) == 0)
415
 
                          {
416
 
                            puts (path);
417
 
                            ++printed;
418
 
                          }
419
 
                      }
420
 
                    break;
421
 
                  }
422
 
              }
423
 
        }
424
 
      else {
425
 
        
426
 
      for (prev_fast_match = false; s >= cutoff; s--)
427
 
        /* Fast first char check. */
428
 
        if (*s == *patend)
429
 
          {
430
 
            char *s2;           /* Scan the path we read in. */
431
 
            register char *p2;  /* Scan `patend'.  */
432
 
 
433
 
            for (s2 = s - 1, p2 = patend - 1; *p2 != '\0' && *s2 == *p2;
434
 
                                               s2--, p2--)
435
 
              ;
436
 
            if (*p2 == '\0')
437
 
              {
438
 
                /* Success on the fast match.  Compare the whole pattern
439
 
                   if it contains globbing characters.  */
440
 
                prev_fast_match = true;
441
 
                if (globflag == false || fnmatch (pathpart, path,
442
 
                                                  0) == 0)
443
 
                  {
444
 
                    if (!check_existence || stat(path, &st) == 0)
445
 
                      {
446
 
                        puts (path);
447
 
                        ++printed;
448
 
                      }
449
 
                  }
450
 
                break;
451
 
              }
452
894
          }
453
895
      }
454
 
      
455
 
    }
456
 
  
457
 
  if (ferror (fp))
458
 
    {
459
 
      error (0, errno, "%s", dbfile);
460
 
      return 0;
461
 
    }
462
 
  if (fclose (fp) == EOF)
463
 
    {
464
 
      error (0, errno, "%s", dbfile);
465
 
      return 0;
466
 
    }
467
 
 
468
 
  return printed;
 
896
 
 
897
  if (need_fold)
 
898
    {
 
899
      add_visitor(visit_casefold, &casebuf);
 
900
      casebuf.preqlen = &procdata.pathsize;
 
901
      casebuf.soffs = &procdata.count;
 
902
    }
 
903
  
 
904
  /* Add an inspector for each pattern we're looking for. */
 
905
  for ( argn = 0; argn < argc; argn++ )
 
906
    {
 
907
      pathpart = argv[argn];
 
908
      if (regex)
 
909
        {
 
910
          struct regular_expression *p = xmalloc(sizeof(*p));
 
911
          int cflags = REG_EXTENDED | REG_NOSUB 
 
912
            | (ignore_case ? REG_ICASE : 0);
 
913
          errno = 0;
 
914
          if (0 == regcomp(&p->re, pathpart, cflags))
 
915
            {
 
916
              add_visitor(visit_regex, p);
 
917
            }
 
918
          else 
 
919
            {
 
920
              error (1, errno, "Invalid regular expression; %s", pathpart);
 
921
            }
 
922
        }
 
923
      else if (contains_metacharacter(pathpart))
 
924
        {
 
925
          if (ignore_case)
 
926
            add_visitor(visit_globmatch_casefold, pathpart);
 
927
          else
 
928
            add_visitor(visit_globmatch_nofold, pathpart);
 
929
        }
 
930
      else
 
931
        {
 
932
          /* No glob characters used.  Hence we match on 
 
933
           * _any part_ of the filename, not just the 
 
934
           * basename.  This seems odd to me, but it is the 
 
935
           * traditional behaviour.
 
936
           * James Youngman <jay@gnu.org> 
 
937
           */
 
938
          if (ignore_case)
 
939
            {
 
940
              struct casefolder * cf = xmalloc(sizeof(*cf));
 
941
              cf->pattern = pathpart;
 
942
              cf->pbuf = &casebuf;
 
943
              add_visitor(visit_substring_match_casefold, cf);
 
944
              /* If we ignore case, convert it to lower now so we don't have to
 
945
               * do it every time
 
946
               */
 
947
              lc_strcpy(pathpart, pathpart);
 
948
            }
 
949
          else
 
950
            {
 
951
              add_visitor(visit_substring_match_nocasefold, pathpart);
 
952
            }
 
953
        }
 
954
    }
 
955
 
 
956
  pvis = lastinspector;
 
957
 
 
958
  /* We add visit_existing_*() as late as possible to reduce the
 
959
   * number of stat() calls.
 
960
   */
 
961
  switch (check_existence)
 
962
    {
 
963
      case ACCEPT_EXISTING:
 
964
        if (follow_symlinks)    /* -L, default */
 
965
          add_visitor(visit_existing_follow, NULL);
 
966
        else                    /* -P */
 
967
          add_visitor(visit_existing_nofollow, NULL);
 
968
        break;
 
969
          
 
970
      case ACCEPT_NON_EXISTING:
 
971
        if (follow_symlinks)    /* -L, default */
 
972
          add_visitor(visit_non_existing_follow, NULL);
 
973
        else                    /* -P */
 
974
          add_visitor(visit_non_existing_nofollow, NULL);
 
975
        break;
 
976
 
 
977
      case ACCEPT_EITHER:       /* Default, neither -E nor -e */
 
978
        /* do nothing; no extra processing. */
 
979
        break;
 
980
    }
 
981
      
 
982
  if (stats)
 
983
    add_visitor(visit_stats, &statistics);
 
984
 
 
985
  if (enable_print)
 
986
    {
 
987
      if (print_quoted_filename)
 
988
        add_visitor(visit_justprint_quoted,   NULL);
 
989
      else
 
990
        add_visitor(visit_justprint_unquoted, NULL);
 
991
    }
 
992
  
 
993
 
 
994
  if (argc > 1)
 
995
    {
 
996
      past_pat_inspector = pvis->next;
 
997
      if (op_and)
 
998
        mainprocessor = process_and;
 
999
      else
 
1000
        mainprocessor = process_or;
 
1001
    }
 
1002
  else
 
1003
    mainprocessor = process_simple;
 
1004
 
 
1005
  if (stats)
 
1006
    {
 
1007
        printf(_("Database %s is in the %s format.\n"),
 
1008
               procdata.dbfile,
 
1009
               old_format ? _("old") : "LOCATE02");
 
1010
    }
 
1011
  
 
1012
  procdata.c = getc (procdata.fp);
 
1013
  while ( (procdata.c != EOF) && (!use_limit || (plimit->limit > 0)) )
 
1014
    {
 
1015
 
 
1016
      /* If we are searching for filename patterns, the inspector list 
 
1017
       * will contain an entry for each pattern for which we are searching.
 
1018
       */
 
1019
      if ((VISIT_ACCEPTED | VISIT_CONTINUE) & (mainprocessor)(&procdata))
 
1020
        {
 
1021
          if ((++plimit->items_accepted >= plimit->limit) && use_limit)
 
1022
            {
 
1023
              break;
 
1024
            }
 
1025
        }
 
1026
    }
 
1027
 
 
1028
      
 
1029
  if (stats)
 
1030
    {
 
1031
      print_stats(argc, st.st_size);
 
1032
    }
 
1033
  
 
1034
  if (ferror (procdata.fp))
 
1035
    {
 
1036
      error (0, errno, "%s", procdata.dbfile);
 
1037
      return 0;
 
1038
    }
 
1039
  if (procdata.fp != stdin && fclose (procdata.fp) == EOF)
 
1040
    {
 
1041
      error (0, errno, "%s", dbfile);
 
1042
      return 0;
 
1043
    }
 
1044
 
 
1045
  return plimit->items_accepted;
469
1046
}
470
1047
 
 
1048
 
 
1049
 
 
1050
 
471
1051
extern char *version_string;
472
1052
 
473
1053
/* The name this program was run with. */
474
1054
char *program_name;
475
1055
 
476
1056
static void
477
 
usage (stream, status)
478
 
     FILE *stream;
479
 
     int status;
 
1057
usage (FILE *stream)
480
1058
{
481
1059
  fprintf (stream, _("\
482
 
Usage: %s [-d path | --database=path] [-e | --existing]\n\
483
 
      [-i | --ignore-case] [--version] [--help] pattern...\n"),
 
1060
Usage: %s [-d path | --database=path] [-e | -E | --[non-]existing]\n\
 
1061
      [-i | --ignore-case] [-w | --wholename] [-b | --basename] \n\
 
1062
      [--limit=N | -l N] [-S | --statistics] [-0 | --null] [-c | --count]\n\
 
1063
      [-P | -H | --nofollow] [-L | --follow] [-m | --mmap ] [ -s | --stdio ]\n\
 
1064
      [-A | --all] [-p | --print] [-r | --regex ] [--version] [--help]\n\
 
1065
      pattern...\n"),
484
1066
           program_name);
485
 
  fputs (_("\nReport bugs to <bug-findutils@gnu.org>."), stream);
486
 
  exit (status);
 
1067
  fputs (_("\nReport bugs to <bug-findutils@gnu.org>.\n"), stream);
487
1068
}
488
1069
 
489
1070
static struct option const longopts[] =
490
1071
{
491
1072
  {"database", required_argument, NULL, 'd'},
492
1073
  {"existing", no_argument, NULL, 'e'},
 
1074
  {"non-existing", no_argument, NULL, 'E'},
493
1075
  {"ignore-case", no_argument, NULL, 'i'},
 
1076
  {"all", no_argument, NULL, 'A'},
494
1077
  {"help", no_argument, NULL, 'h'},
495
1078
  {"version", no_argument, NULL, 'v'},
 
1079
  {"null", no_argument, NULL, '0'},
 
1080
  {"count", no_argument, NULL, 'c'},
 
1081
  {"wholename", no_argument, NULL, 'w'},
 
1082
  {"wholepath", no_argument, NULL, 'w'}, /* Synonym. */
 
1083
  {"basename", no_argument, NULL, 'b'},
 
1084
  {"print", no_argument, NULL, 'p'},
 
1085
  {"stdio", no_argument, NULL, 's'},
 
1086
  {"mmap",  no_argument, NULL, 'm'},
 
1087
  {"limit",  required_argument, NULL, 'l'},
 
1088
  {"regex",  no_argument, NULL, 'r'},
 
1089
  {"statistics",  no_argument, NULL, 'S'},
 
1090
  {"follow",      no_argument, NULL, 'L'},
 
1091
  {"nofollow",    no_argument, NULL, 'P'},
496
1092
  {NULL, no_argument, NULL, 0}
497
1093
};
498
1094
 
499
1095
int
500
 
main (argc, argv)
501
 
     int argc;
502
 
     char **argv;
 
1096
main (int argc, char **argv)
503
1097
{
504
1098
  char *dbpath;
505
 
  int fnmatch_flags = 0;
506
 
  int found = 0, optc;
 
1099
  unsigned long int found = 0uL;
 
1100
  int optc;
507
1101
  int ignore_case = 0;
508
 
 
 
1102
  int print = 0;
 
1103
  int just_count = 0;
 
1104
  int basename_only = 0;
 
1105
  int use_limit = 0;
 
1106
  int regex = 0;
 
1107
  int stats = 0;
 
1108
  int op_and = 0;
 
1109
  char *e;
 
1110
  
509
1111
  program_name = argv[0];
510
1112
 
511
1113
#ifdef HAVE_SETLOCALE
513
1115
#endif
514
1116
  bindtextdomain (PACKAGE, LOCALEDIR);
515
1117
  textdomain (PACKAGE);
516
 
 
 
1118
  atexit (close_stdout);
 
1119
 
 
1120
  limits.limit = 0;
 
1121
  limits.items_accepted = 0;
 
1122
 
 
1123
  quote_opts = clone_quoting_options (NULL);
 
1124
  print_quoted_filename = true;
 
1125
  
517
1126
  dbpath = getenv ("LOCATE_PATH");
518
1127
  if (dbpath == NULL)
519
1128
    dbpath = LOCATE_DB;
520
1129
 
521
 
  check_existence = 0;
 
1130
  check_existence = ACCEPT_EITHER;
522
1131
 
523
 
  while ((optc = getopt_long (argc, argv, "d:ei", longopts, (int *) 0)) != -1)
 
1132
  while ((optc = getopt_long (argc, argv, "Abcd:eEil:prsm0SwHPL", longopts, (int *) 0)) != -1)
524
1133
    switch (optc)
525
1134
      {
 
1135
      case '0':
 
1136
        separator = 0;
 
1137
        print_quoted_filename = false; /* print filename 'raw'. */
 
1138
        break;
 
1139
 
 
1140
      case 'A':
 
1141
        op_and = 1;
 
1142
        break;
 
1143
 
 
1144
      case 'b':
 
1145
        basename_only = 1;
 
1146
        break;
 
1147
 
 
1148
      case 'c':
 
1149
        just_count = 1;
 
1150
        break;
 
1151
 
526
1152
      case 'd':
527
1153
        dbpath = optarg;
528
1154
        break;
529
1155
 
530
1156
      case 'e':
531
 
        check_existence = 1;
 
1157
        check_existence = ACCEPT_EXISTING;
 
1158
        break;
 
1159
 
 
1160
      case 'E':
 
1161
        check_existence = ACCEPT_NON_EXISTING;
532
1162
        break;
533
1163
 
534
1164
      case 'i':
535
1165
        ignore_case = 1;
536
 
        fnmatch_flags |= FNM_CASEFOLD;
537
1166
        break;
538
1167
        
539
1168
      case 'h':
540
 
        usage (stdout, 0);
 
1169
        usage (stdout);
 
1170
        return 0;
 
1171
 
 
1172
      case 'p':
 
1173
        print = 1;
 
1174
        break;
541
1175
 
542
1176
      case 'v':
543
1177
        printf (_("GNU locate version %s\n"), version_string);
544
 
        exit (0);
 
1178
        return 0;
 
1179
 
 
1180
      case 'w':
 
1181
        basename_only = 0;
 
1182
        break;
 
1183
 
 
1184
      case 'r':
 
1185
        regex = 1;
 
1186
        break;
 
1187
 
 
1188
      case 'S':
 
1189
        stats = 1;
 
1190
        break;
 
1191
 
 
1192
      case 'L':
 
1193
        follow_symlinks = 1;
 
1194
        break;
 
1195
 
 
1196
        /* In find, -P and -H differ in the way they handle paths
 
1197
         * given on the command line.  This is not relevant for
 
1198
         * locate, but the -H option is supported because it is
 
1199
         * probably more intuitive to do so.
 
1200
         */
 
1201
      case 'P':
 
1202
      case 'H':
 
1203
        follow_symlinks = 0;
 
1204
        break;
 
1205
 
 
1206
      case 'l':
 
1207
        {
 
1208
          char *end = optarg;
 
1209
          strtol_error err = xstrtoumax(optarg, &end, 10, &limits.limit, NULL);
 
1210
          if (LONGINT_OK != err)
 
1211
            {
 
1212
              STRTOL_FATAL_ERROR(optarg, _("argument to --limit"), err);
 
1213
            }
 
1214
          use_limit = 1;
 
1215
        }
 
1216
        break;
 
1217
 
 
1218
      case 's':                 /* use stdio */
 
1219
      case 'm':                 /* use mmap  */
 
1220
        /* These options are implemented simply for
 
1221
         * compatibility with FreeBSD
 
1222
         */ 
 
1223
        break;
545
1224
 
546
1225
      default:
547
 
        usage (stderr, 1);
 
1226
        usage (stderr);
 
1227
        return 1;
548
1228
      }
549
1229
 
550
 
  if (optind == argc)
551
 
    usage (stderr, 1);
552
 
 
553
 
  for (; optind < argc; optind++)
554
 
    {
555
 
      char *e;
556
 
      next_element (dbpath);    /* Initialize.  */
557
 
      while ((e = next_element ((char *) NULL)) != NULL)
558
 
        found |= locate (argv[optind], e, ignore_case);
559
 
    }
560
 
 
561
 
  exit (!found);
 
1230
  if (!just_count && !stats)
 
1231
    print = 1;
 
1232
 
 
1233
  if (stats)
 
1234
    {
 
1235
      if (optind == argc)
 
1236
          use_limit = 0;
 
1237
    }
 
1238
  else
 
1239
    {
 
1240
      if (!just_count && optind == argc)
 
1241
        {
 
1242
          usage (stderr);
 
1243
          return 1;
 
1244
        }
 
1245
    }
 
1246
  
 
1247
 
 
1248
  if (1 == isatty(STDOUT_FILENO))
 
1249
    stdout_is_a_tty = true;
 
1250
  else
 
1251
    stdout_is_a_tty = false;
 
1252
 
 
1253
  next_element (dbpath, 0);     /* Initialize.  */
 
1254
  while ((e = next_element ((char *) NULL, 0)) != NULL)
 
1255
    {
 
1256
      statistics.compressed_bytes = 
 
1257
      statistics.total_filename_count = 
 
1258
      statistics.total_filename_length = 
 
1259
      statistics.whitespace_count = 
 
1260
      statistics.newline_count = 
 
1261
      statistics.highbit_filename_count = 0u;
 
1262
 
 
1263
      if (0 == strlen(e) || 0 == strcmp(e, "."))
 
1264
        {
 
1265
          /* Use the default database name instead (note: we
 
1266
           * don't use 'dbpath' since that might itself contain a 
 
1267
           * colon-separated list.
 
1268
           */
 
1269
          e = LOCATE_DB;
 
1270
        }
 
1271
          
 
1272
      found = locate (argc - optind, &argv[optind], e, ignore_case, print, basename_only, use_limit, &limits, stats, op_and, regex);
 
1273
    }
 
1274
  
 
1275
  if (just_count)
 
1276
    {
 
1277
      printf("%ld\n", found);
 
1278
    }
 
1279
  
 
1280
  if (found || (use_limit && (limits.limit==0)) || stats )
 
1281
    return 0;
 
1282
  else
 
1283
    return 1;
562
1284
}