+ constexpr int EXTENDED_REGEX = 1000;
+ constexpr int FLUSH_CACHE = 1001;
+ static const struct option long_options[] = {
+ { "help", no_argument, 0, 'h' },
+ { "count", no_argument, 0, 'c' },
+ { "basename", no_argument, 0, 'b' },
+ { "database", required_argument, 0, 'd' },
+ { "ignore-case", no_argument, 0, 'i' },
+ { "limit", required_argument, 0, 'l' },
+ { "null", no_argument, 0, '0' },
+ { "version", no_argument, 0, 'V' },
+ { "regexp", no_argument, 0, 'r' },
+ { "regex", no_argument, 0, EXTENDED_REGEX },
+ { "wholename", no_argument, 0, 'w' },
+ { "debug", no_argument, 0, 'D' }, // Not documented.
+ // Enable to test cold-cache behavior (except for access()). Not documented.
+ { "flush-cache", no_argument, 0, FLUSH_CACHE },
+ { 0, 0, 0, 0 }
+ };
+
+ setlocale(LC_ALL, "");
+ for (;;) {
+ int option_index = 0;
+ int c = getopt_long(argc, argv, "bcd:hil:n:0rwVD", long_options, &option_index);
+ if (c == -1) {
+ break;
+ }
+ switch (c) {
+ case 'b':
+ match_basename = true;
+ break;
+ case 'c':
+ only_count = true;
+ break;
+ case 'd':
+ dbpath = strdup(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ case 'i':
+ ignore_case = true;
+ break;
+ case 'l':
+ case 'n':
+ limit_matches = limit_left = atoll(optarg);
+ if (limit_matches <= 0) {
+ fprintf(stderr, "Error: limit must be a strictly positive number.\n");
+ exit(1);
+ }
+ break;
+ case '0':
+ print_nul = true;
+ break;
+ case 'r':
+ patterns_are_regex = true;
+ break;
+ case EXTENDED_REGEX:
+ patterns_are_regex = true;
+ use_extended_regex = true;
+ break;
+ case 'w':
+ match_basename = false; // No-op unless -b is given first.
+ break;
+ case 'D':
+ use_debug = true;
+ break;
+ case FLUSH_CACHE:
+ flush_cache = true;
+ break;
+ case 'V':
+ version();
+ break;
+ default:
+ exit(1);
+ }
+ }
+
+ if (use_debug || flush_cache) {
+ // Debug information would leak information about which files exist,
+ // so drop setgid before we open the file; one would either need to run
+ // as root, or use a locally-built file. Doing the same thing for
+ // flush_cache is mostly paranoia, in an attempt to prevent random users
+ // from making plocate slow for everyone else.
+ if (setgid(getgid()) != 0) {
+ perror("setgid");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!print_nul) {
+ stdout_is_tty = isatty(1);
+ }
+
+ vector<Needle> needles;
+ for (int i = optind; i < argc; ++i) {
+ Needle needle;
+ needle.str = argv[i];
+
+ // See if there are any wildcard characters, which indicates we should treat it
+ // as an (anchored) glob.
+ bool any_wildcard = false;
+ for (size_t i = 0; i < needle.str.size(); i += read_unigram(needle.str, i).second) {
+ if (read_unigram(needle.str, i).first == WILDCARD_UNIGRAM) {
+ any_wildcard = true;
+ break;
+ }
+ }
+
+ if (patterns_are_regex) {
+ needle.type = Needle::REGEX;
+ needle.re = compile_regex(needle.str);
+ } else if (any_wildcard) {
+ needle.type = Needle::GLOB;
+ } else if (ignore_case) {
+ // strcasestr() doesn't handle locales correctly (even though LSB
+ // claims it should), but somehow, fnmatch() does, and it's about
+ // the same speed as using a regex.
+ needle.type = Needle::GLOB;
+ needle.str = "*" + needle.str + "*";
+ } else {
+ needle.type = Needle::STRSTR;
+ needle.str = unescape_glob_to_plain_string(needle.str);
+ }
+ needles.push_back(move(needle));
+ }
+ if (needles.empty()) {
+ fprintf(stderr, "plocate: no pattern to search for specified\n");
+ exit(0);
+ }
+ do_search_file(needles, dbpath);