/* For loading git/hg ignore patterns */ void load_ignore_patterns(ignores *ig, const char *path) { FILE *fp = NULL; fp = fopen(path, "r"); if (fp == NULL) { log_debug("Skipping ignore file %s: not readable", path); return; } log_debug("Loading ignore file %s.", path); char *line = NULL; ssize_t line_len = 0; size_t line_cap = 0; while ((line_len = getline(&line, &line_cap, fp)) > 0) { if (line_len == 0 || line[0] == '\n' || line[0] == '#') { continue; } if (line[line_len - 1] == '\n') { line[line_len - 1] = '\0'; /* kill the \n */ } add_ignore_pattern(ig, line); } free(line); fclose(fp); }
/* For loading git/svn/hg ignore patterns */ void load_ignore_patterns(const char *ignore_filename) { FILE *fp = NULL; fp = fopen(ignore_filename, "r"); if (fp == NULL) { log_debug("Skipping ignore file %s", ignore_filename); return; } char *line = NULL; ssize_t line_len = 0; size_t line_cap = 0; while ((line_len = getline(&line, &line_cap, fp)) > 0) { if (line_len == 0 || line[0] == '\n') { continue; } if (line[line_len-1] == '\n') { line[line_len-1] = '\0'; /* kill the \n */ } add_ignore_pattern(line); } free(line); fclose(fp); }
void load_svn_ignore_patterns(ignores *ig, const char *path) { FILE *fp = NULL; char *dir_prop_base; ag_asprintf(&dir_prop_base, "%s/%s", path, SVN_DIR_PROP_BASE); fp = fopen(dir_prop_base, "r"); if (fp == NULL) { log_debug("Skipping svn ignore file %s", dir_prop_base); free(dir_prop_base); return; } char *entry = NULL; size_t entry_len = 0; char *key = ag_malloc(32); /* Sane start for max key length. */ size_t key_len = 0; size_t bytes_read = 0; char *entry_line; size_t line_len; int matches; while (fscanf(fp, "K %zu\n", &key_len) == 1) { key = ag_realloc(key, key_len + 1); bytes_read = fread(key, 1, key_len, fp); key[key_len] = '\0'; matches = fscanf(fp, "\nV %zu\n", &entry_len); if (matches != 1) { log_debug("Unable to parse svnignore file %s: fscanf() got %i matches, expected 1.", dir_prop_base, matches); goto cleanup; } if (strncmp(SVN_PROP_IGNORE, key, bytes_read) != 0) { log_debug("key is %s, not %s. skipping %u bytes", key, SVN_PROP_IGNORE, entry_len); /* Not the key we care about. fseek and repeat */ fseek(fp, entry_len + 1, SEEK_CUR); /* +1 to account for newline. yes I know this is hacky */ continue; } /* Aww yeah. Time to ignore stuff */ entry = ag_malloc(entry_len + 1); bytes_read = fread(entry, 1, entry_len, fp); entry[bytes_read] = '\0'; log_debug("entry: %s", entry); break; } if (entry == NULL) { goto cleanup; } char *patterns = entry; size_t patterns_len = strlen(patterns); while (*patterns != '\0' && patterns < (entry + bytes_read)) { for (line_len = 0; line_len < patterns_len; line_len++) { if (patterns[line_len] == '\n') { break; } } if (line_len > 0) { entry_line = ag_strndup(patterns, line_len); add_ignore_pattern(ig, entry_line); free(entry_line); } patterns += line_len + 1; patterns_len -= line_len + 1; } free(entry); cleanup: free(dir_prop_base); free(key); fclose(fp); }
void parse_options(int argc, char **argv, char **paths[]) { int ch; int i; int path_len = 0; int useless = 0; int group = 1; int help = 0; int version = 0; int opt_index = 0; const char *home_dir = getenv("HOME"); char *ignore_file_path = NULL; int needs_query = 1; init_options(); struct option longopts[] = { { "ackmate", no_argument, &(opts.ackmate), 1 }, { "ackmate-dir-filter", required_argument, NULL, 0 }, { "after", required_argument, NULL, 'A' }, { "all-text", no_argument, NULL, 't' }, { "all-types", no_argument, NULL, 'a' }, { "before", required_argument, NULL, 'B' }, { "break", no_argument, &(opts.print_break), 1 }, { "case-sensitive", no_argument, NULL, 's' }, { "color", no_argument, &(opts.color), 1 }, { "column", no_argument, &(opts.column), 1 }, { "context", optional_argument, NULL, 'C' }, { "debug", no_argument, NULL, 'D' }, { "depth", required_argument, NULL, 0 }, { "file-search-regex", required_argument, NULL, 'G' }, { "files-with-matches", no_argument, NULL, 'l' }, { "files-without-matches", no_argument, NULL, 'L' }, { "follow", no_argument, &(opts.follow_symlinks), 1 }, { "group", no_argument, &(group), 1 }, { "heading", no_argument, &(opts.print_heading), 1 }, { "help", no_argument, NULL, 'h' }, { "hidden", no_argument, &(opts.search_hidden_files), 1 }, { "ignore", required_argument, NULL, 0 }, { "ignore-case", no_argument, NULL, 'i' }, { "invert-match", no_argument, &(opts.invert_match), 1 }, { "literal", no_argument, NULL, 'Q' }, { "match", no_argument, &useless, 0 }, { "max-count", required_argument, NULL, 'm' }, { "no-recurse", no_argument, NULL, 'n' }, { "nobreak", no_argument, &(opts.print_break), 0 }, { "nocolor", no_argument, &(opts.color), 0 }, { "nofollow", no_argument, &(opts.follow_symlinks), 0 }, { "nogroup", no_argument, &(group), 0 }, { "noheading", no_argument, &(opts.print_heading), 0 }, { "parallel", no_argument, &(opts.parallel), 1}, { "path-to-agignore", required_argument, NULL, 'p'}, { "print-long-lines", no_argument, &(opts.print_long_lines), 1 }, { "recurse", no_argument, NULL, 'r' }, { "search-binary", no_argument, &(opts.search_binary_files), 1 }, { "search-files", no_argument, &(opts.search_stream), 0 }, { "skip-vcs-ignores", no_argument, NULL, 'U' }, { "smart-case", no_argument, NULL, 'S' }, { "stats", no_argument, &(opts.stats), 1 }, { "unrestricted", no_argument, NULL, 'u' }, { "version", no_argument, &version, 1 }, { "word-regexp", no_argument, NULL, 'w' }, { "workers", required_argument, NULL, 0 }, { NULL, 0, NULL, 0 } }; if (argc < 2) { usage(); exit(1); } /* stdin isn't a tty. something's probably being piped to ag */ if (!isatty(fileno(stdin))) { opts.search_stream = 1; } /* If we're not outputting to a terminal. change output to: * turn off colors * print filenames on every line */ if (!isatty(fileno(stdout))) { opts.color = 0; group = 0; } while ((ch = getopt_long(argc, argv, "A:aB:C:DG:g:fhiLlm:np:QRrSsvVtuUw", longopts, &opt_index)) != -1) { switch (ch) { case 'A': opts.after = atoi(optarg); break; case 'a': opts.search_all_files = 1; opts.search_binary_files = 1; break; case 'B': opts.before = atoi(optarg); break; case 'C': if (optarg) { opts.context = atoi(optarg); if (opts.context == 0 && errno == EINVAL) { /* This arg must be the search string instead of the context length */ optind--; opts.context = DEFAULT_CONTEXT_LEN; } } else { opts.context = DEFAULT_CONTEXT_LEN; } break; case 'D': set_log_level(LOG_LEVEL_DEBUG); break; case 'f': opts.follow_symlinks = 1; break; case 'g': needs_query = 0; opts.match_files = 1; /* Fall through and build regex */ case 'G': compile_study(&opts.file_search_regex, &opts.file_search_regex_extra, optarg, 0, 0); break; case 'h': help = 1; break; case 'i': opts.casing = CASE_INSENSITIVE; break; case 'L': opts.invert_match = 1; /* fall through */ case 'l': opts.print_filename_only = 1; break; case 'm': opts.max_matches_per_file = atoi(optarg); break; case 'n': opts.recurse_dirs = 0; break; case 'p': opts.path_to_agignore = optarg; break; case 'Q': opts.literal = 1; break; case 'R': case 'r': opts.recurse_dirs = 1; break; case 'S': opts.casing = CASE_SMART; break; case 's': opts.casing = CASE_SENSITIVE; break; case 't': opts.search_all_files = 1; break; case 'u': opts.search_binary_files = 1; opts.search_all_files = 1; opts.search_hidden_files = 1; break; case 'U': opts.skip_vcs_ignores = 1; break; case 'v': opts.invert_match = 1; break; case 'V': version = 1; break; case 'w': opts.word_regexp = 1; break; case 0: /* Long option */ if (strcmp(longopts[opt_index].name, "ackmate-dir-filter") == 0) { compile_study(&opts.ackmate_dir_filter, &opts.ackmate_dir_filter_extra, optarg, 0, 0); break; } else if (strcmp(longopts[opt_index].name, "depth") == 0) { opts.max_search_depth = atoi(optarg); break; } else if (strcmp(longopts[opt_index].name, "ignore") == 0) { add_ignore_pattern(root_ignores, optarg); break; } else if (strcmp(longopts[opt_index].name, "workers") == 0) { opts.workers = atoi(optarg); break; } /* Continue to usage if we don't recognize the option */ if (longopts[opt_index].flag != 0) { break; } log_err("option %s does not take a value", longopts[opt_index].name); default: usage(); exit(1); } } argc -= optind; argv += optind; if (help) { usage(); exit(0); } if (version) { print_version(); exit(0); } if (needs_query && argc == 0) { log_err("What do you want to search for?"); exit(1); } if (home_dir && !opts.search_all_files) { log_debug("Found user's home dir: %s", home_dir); asprintf(&ignore_file_path, "%s/%s", home_dir, ignore_pattern_files[0]); load_ignore_patterns(root_ignores, ignore_file_path); free(ignore_file_path); } if (opts.context > 0) { opts.before = opts.context; opts.after = opts.context; } if (opts.ackmate) { opts.color = 0; opts.print_break = 1; group = 1; opts.search_stream = 0; } if (opts.parallel) { opts.search_stream = 0; } if (opts.print_heading == 0 || opts.print_break == 0) { goto skip_group; } if (group) { opts.print_heading = 1; opts.print_break = 1; } else { opts.print_heading = 0; opts.print_break = 0; } skip_group:; if (opts.search_stream) { opts.print_break = 0; opts.print_heading = 0; opts.print_line_numbers = 0; } if (needs_query) { opts.query = strdup(argv[0]); argc--; argv++; } else { opts.query = strdup("."); } opts.query_len = strlen(opts.query); log_debug("Query is %s", opts.query); if (opts.query_len == 0) { log_err("Error: No query. What do you want to search for?"); exit(1); } if (!is_regex(opts.query)) { opts.literal = 1; } char *path = NULL; opts.paths_len = argc; if (argc > 0) { *paths = calloc(sizeof(char*), argc + 1); for (i = 0; i < argc; i++) { path = strdup(argv[i]); path_len = strlen(path); /* kill trailing slash */ if (path_len > 1 && path[path_len - 1] == '/') { path[path_len - 1] = '\0'; } (*paths)[i] = path; } (*paths)[i] = NULL; /* Make sure we search these paths instead of stdin. */ opts.search_stream = 0; } else { path = strdup("."); *paths = malloc(sizeof(char*) * 2); (*paths)[0] = path; (*paths)[1] = NULL; } }