int main(int argc, char **argv) { char **paths = NULL; int i; int pcre_opts = PCRE_MULTILINE; int study_opts = 0; double time_diff; pthread_t *workers = NULL; int workers_len; set_log_level(LOG_LEVEL_WARN); work_queue = NULL; work_queue_tail = NULL; memset(&stats, 0, sizeof(stats)); root_ignores = init_ignore(NULL); out_fd = stdout; #ifdef USE_PCRE_JIT int has_jit = 0; pcre_config(PCRE_CONFIG_JIT, &has_jit); if (has_jit) { study_opts |= PCRE_STUDY_JIT_COMPILE; } #endif gettimeofday(&(stats.time_start), NULL); parse_options(argc, argv, &paths); log_debug("PCRE Version: %s", pcre_version()); if (opts.workers) { workers_len = opts.workers; } else { /* Experiments show that two worker threads appear to be optimal, both * on dual-core and quad-core systems. See * http://geoff.greer.fm/2012/09/07/the-silver-searcher-adding-pthreads/. * On single-core CPUs, more than one worker thread makes no sense. */ int ncpus = (int)sysconf(_SC_NPROCESSORS_ONLN); workers_len = (ncpus >= 2) ? 2 : 1; } log_debug("Using %i workers", workers_len); done_adding_files = FALSE; workers = ag_calloc(workers_len, sizeof(pthread_t)); if (pthread_cond_init(&files_ready, NULL)) { log_err("pthread_cond_init failed!"); exit(2); } if (pthread_mutex_init(&print_mtx, NULL)) { log_err("pthread_mutex_init failed!"); exit(2); } if (pthread_mutex_init(&stats_mtx, NULL)) { log_err("pthread_mutex_init failed!"); exit(2); } if (pthread_mutex_init(&work_queue_mtx, NULL)) { log_err("pthread_mutex_init failed!"); exit(2); } if (opts.casing == CASE_SMART) { opts.casing = contains_uppercase(opts.query) ? CASE_SENSITIVE : CASE_INSENSITIVE; } if (opts.literal) { if (opts.casing == CASE_INSENSITIVE) { /* Search routine needs the query to be lowercase */ char *c = opts.query; for (; *c != '\0'; ++c) { *c = (char) tolower(*c); } } generate_skip_lookup(opts.query, opts.query_len, skip_lookup, opts.casing == CASE_SENSITIVE); if (opts.word_regexp) { init_wordchar_table(); opts.literal_starts_wordchar = is_wordchar(opts.query[0]); opts.literal_ends_wordchar = is_wordchar(opts.query[opts.query_len - 1]); } } else { if (opts.casing == CASE_INSENSITIVE) { pcre_opts = pcre_opts | PCRE_CASELESS; } if (opts.word_regexp) { char *word_regexp_query; asprintf(&word_regexp_query, "\\b%s\\b", opts.query); free(opts.query); opts.query = word_regexp_query; opts.query_len = strlen(opts.query); } compile_study(&opts.re, &opts.re_extra, opts.query, pcre_opts, study_opts); } if (opts.search_stream) { search_stream(stdin, ""); } else { for (i = 0; i < workers_len; i++) { int ptc_rc = pthread_create(&(workers[i]), NULL, &search_file_worker, NULL); check_err(ptc_rc, "create worker thread"); } for (i = 0; paths[i] != NULL; i++) { log_debug("searching path %s for %s", paths[i], opts.query); search_dir(root_ignores, paths[i], 0); } done_adding_files = TRUE; pthread_cond_broadcast(&files_ready); for (i = 0; i < workers_len; i++) { if (pthread_join(workers[i], NULL)) { log_err("pthread_join failed!"); exit(2); } } } if (opts.stats) { gettimeofday(&(stats.time_end), NULL); time_diff = ((long)stats.time_end.tv_sec * 1000000 + stats.time_end.tv_usec) - ((long)stats.time_start.tv_sec * 1000000 + stats.time_start.tv_usec); time_diff /= 1000000; printf("%ld matches\n%ld files searched\n%ld bytes searched\n%f seconds\n", stats.total_matches, stats.total_files, stats.total_bytes, time_diff); } if (opts.pager) { pclose(out_fd); } pthread_cond_destroy(&files_ready); pthread_mutex_destroy(&work_queue_mtx); pthread_mutex_destroy(&stats_mtx); pthread_mutex_destroy(&print_mtx); cleanup_ignore(root_ignores); free(workers); free(paths); return 0; }
int main(int argc, char **argv) { char **base_paths = NULL; char **paths = NULL; int i; int pcre_opts = PCRE_MULTILINE; int study_opts = 0; double time_diff; worker_t *workers = NULL; int workers_len; int num_cores; #ifdef KJK_BUILD extern void setup_crash_handler(); /* in kjk_crash_handler.cpp */ setup_crash_handler(); #endif set_log_level(LOG_LEVEL_WARN); work_queue = NULL; work_queue_tail = NULL; memset(&stats, 0, sizeof(stats)); root_ignores = init_ignore(NULL, "", 0); out_fd = stdout; #ifdef USE_PCRE_JIT int has_jit = 0; pcre_config(PCRE_CONFIG_JIT, &has_jit); if (has_jit) { study_opts |= PCRE_STUDY_JIT_COMPILE; } #endif gettimeofday(&(stats.time_start), NULL); parse_options(argc, argv, &base_paths, &paths); log_debug("PCRE Version: %s", pcre_version()); #ifdef _WIN32 { SYSTEM_INFO si; GetSystemInfo(&si); num_cores = si.dwNumberOfProcessors; } #else num_cores = (int)sysconf(_SC_NPROCESSORS_ONLN); #endif workers_len = num_cores; if (opts.literal) { workers_len--; } if (opts.workers) { workers_len = opts.workers; } if (workers_len < 1) { workers_len = 1; } log_debug("Using %i workers", workers_len); done_adding_files = FALSE; workers = (worker_t *) ag_calloc(workers_len, sizeof(worker_t)); if (pthread_cond_init(&files_ready, NULL)) { die("pthread_cond_init failed!"); } if (pthread_mutex_init(&print_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (pthread_mutex_init(&stats_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (pthread_mutex_init(&work_queue_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (opts.casing == CASE_SMART) { opts.casing = is_lowercase(opts.query) ? CASE_INSENSITIVE : CASE_SENSITIVE; } if (opts.literal) { if (opts.casing == CASE_INSENSITIVE) { /* Search routine needs the query to be lowercase */ char *c = opts.query; for (; *c != '\0'; ++c) { *c = (char)tolower(*c); } } generate_alpha_skip(opts.query, opts.query_len, alpha_skip_lookup, opts.casing == CASE_SENSITIVE); find_skip_lookup = NULL; generate_find_skip(opts.query, opts.query_len, &find_skip_lookup, opts.casing == CASE_SENSITIVE); if (opts.word_regexp) { init_wordchar_table(); opts.literal_starts_wordchar = is_wordchar(opts.query[0]); opts.literal_ends_wordchar = is_wordchar(opts.query[opts.query_len - 1]); } } else { if (opts.casing == CASE_INSENSITIVE) { pcre_opts |= PCRE_CASELESS; } if (opts.word_regexp) { char *word_regexp_query; ag_asprintf(&word_regexp_query, "\\b%s\\b", opts.query); free(opts.query); opts.query = word_regexp_query; opts.query_len = strlen(opts.query); } compile_study(&opts.re, &opts.re_extra, opts.query, pcre_opts, study_opts); } if (opts.search_stream) { // search_stream(stdin, ""); } else { for (i = 0; i < workers_len; i++) { workers[i].id = i; int rv = pthread_create(&(workers[i].thread), NULL, &search_file_worker, &(workers[i].id)); if (rv != 0) { die("error in pthread_create(): %s", strerror(rv)); } #if defined(HAVE_PTHREAD_SETAFFINITY_NP) && defined(USE_CPU_SET) if (opts.use_thread_affinity) { cpu_set_t cpu_set; CPU_ZERO(&cpu_set); CPU_SET(i % num_cores, &cpu_set); rv = pthread_setaffinity_np(workers[i].thread, sizeof(cpu_set), &cpu_set); if (rv != 0) { die("error in pthread_setaffinity_np(): %s", strerror(rv)); } log_debug("Thread %i set to CPU %i", i, i); } else { log_debug("Thread affinity disabled."); } #else log_debug("No CPU affinity support."); #endif } for (i = 0; paths[i] != NULL; i++) { log_debug("searching path %s for %s", paths[i], opts.query); symhash = NULL; ignores *ig = init_ignore(root_ignores, "", 0); struct stat s; s.st_dev = 0; #ifndef _WIN32 /* The device is ignored if opts.one_dev is false, so it's fine * to leave it at the default 0 */ if (opts.one_dev && lstat(paths[i], &s) == -1) { log_err("Failed to get device information for path %s. Skipping...", paths[i]); } #endif search_dir(ig, base_paths[i], paths[i], 0, s.st_dev); cleanup_ignore(ig); } pthread_mutex_lock(&work_queue_mtx); done_adding_files = TRUE; pthread_cond_broadcast(&files_ready); pthread_mutex_unlock(&work_queue_mtx); for (i = 0; i < workers_len; i++) { if (pthread_join(workers[i].thread, NULL)) { die("pthread_join failed!"); } } } if (opts.stats) { gettimeofday(&(stats.time_end), NULL); time_diff = ((long)stats.time_end.tv_sec * 1000000 + stats.time_end.tv_usec) - ((long)stats.time_start.tv_sec * 1000000 + stats.time_start.tv_usec); time_diff /= 1000000; printf("%ld matches\n%ld files searched\n%ld bytes searched\n%f seconds\n", stats.total_matches, stats.total_files, stats.total_bytes, time_diff); } if (opts.pager) { pclose(out_fd); } cleanup_options(); pthread_cond_destroy(&files_ready); pthread_mutex_destroy(&work_queue_mtx); pthread_mutex_destroy(&stats_mtx); pthread_mutex_destroy(&print_mtx); cleanup_ignore(root_ignores); free(workers); for (i = 0; paths[i] != NULL; i++) { free(paths[i]); free(base_paths[i]); } free(base_paths); free(paths); if (find_skip_lookup) { free(find_skip_lookup); } return !opts.match_found; }
int main(int argc, char **argv) { char **base_paths = NULL; char **paths = NULL; int i; int pcre_opts = PCRE_MULTILINE; int study_opts = 0; double time_diff; pthread_t *workers = NULL; int workers_len; set_log_level(LOG_LEVEL_WARN); work_queue = NULL; work_queue_tail = NULL; memset(&stats, 0, sizeof(stats)); root_ignores = init_ignore(NULL, "", 0); out_fd = stdout; #ifdef USE_PCRE_JIT int has_jit = 0; pcre_config(PCRE_CONFIG_JIT, &has_jit); if (has_jit) { study_opts |= PCRE_STUDY_JIT_COMPILE; } #endif gettimeofday(&(stats.time_start), NULL); parse_options(argc, argv, &base_paths, &paths); log_debug("PCRE Version: %s", pcre_version()); #ifdef _WIN32 { SYSTEM_INFO si; GetSystemInfo(&si); workers_len = si.dwNumberOfProcessors; } #else workers_len = (int)sysconf(_SC_NPROCESSORS_ONLN); #endif if (opts.literal) { workers_len--; } if (opts.workers) { workers_len = opts.workers; } if (workers_len < 1) { workers_len = 1; } log_debug("Using %i workers", workers_len); done_adding_files = FALSE; workers = ag_calloc(workers_len, sizeof(pthread_t)); if (pthread_cond_init(&files_ready, NULL)) { die("pthread_cond_init failed!"); } if (pthread_mutex_init(&print_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (pthread_mutex_init(&stats_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (pthread_mutex_init(&work_queue_mtx, NULL)) { die("pthread_mutex_init failed!"); } if (opts.casing == CASE_SMART) { opts.casing = is_lowercase(opts.query) ? CASE_INSENSITIVE : CASE_SENSITIVE; } if (opts.literal) { if (opts.casing == CASE_INSENSITIVE) { /* Search routine needs the query to be lowercase */ char *c = opts.query; for (; *c != '\0'; ++c) { *c = (char)tolower(*c); } } generate_alpha_skip(opts.query, opts.query_len, alpha_skip_lookup, opts.casing == CASE_SENSITIVE); find_skip_lookup = NULL; generate_find_skip(opts.query, opts.query_len, &find_skip_lookup, opts.casing == CASE_SENSITIVE); if (opts.word_regexp) { init_wordchar_table(); opts.literal_starts_wordchar = is_wordchar(opts.query[0]); opts.literal_ends_wordchar = is_wordchar(opts.query[opts.query_len - 1]); } } else { if (opts.casing == CASE_INSENSITIVE) { pcre_opts |= PCRE_CASELESS; } if (opts.word_regexp) { char *word_regexp_query; ag_asprintf(&word_regexp_query, "\\b%s\\b", opts.query); free(opts.query); opts.query = word_regexp_query; opts.query_len = strlen(opts.query); } compile_study(&opts.re, &opts.re_extra, opts.query, pcre_opts, study_opts); } if (opts.search_stream) { search_stream(stdin, ""); } else { for (i = 0; i < workers_len; i++) { int rv = pthread_create(&(workers[i]), NULL, &search_file_worker, &i); if (rv != 0) { die("error in pthread_create(): %s", strerror(rv)); } } for (i = 0; paths[i] != NULL; i++) { log_debug("searching path %s for %s", paths[i], opts.query); symhash = NULL; ignores *ig = init_ignore(root_ignores, "", 0); search_dir(ig, base_paths[i], paths[i], 0); cleanup_ignore(ig); } pthread_mutex_lock(&work_queue_mtx); done_adding_files = TRUE; pthread_cond_broadcast(&files_ready); pthread_mutex_unlock(&work_queue_mtx); for (i = 0; i < workers_len; i++) { if (pthread_join(workers[i], NULL)) { die("pthread_join failed!"); } } } if (opts.stats) { gettimeofday(&(stats.time_end), NULL); time_diff = ((long)stats.time_end.tv_sec * 1000000 + stats.time_end.tv_usec) - ((long)stats.time_start.tv_sec * 1000000 + stats.time_start.tv_usec); time_diff /= 1000000; printf("%ld matches\n%ld files searched\n%ld bytes searched\n%f seconds\n", stats.total_matches, stats.total_files, stats.total_bytes, time_diff); } if (opts.pager) { pclose(out_fd); } cleanup_options(); pthread_cond_destroy(&files_ready); pthread_mutex_destroy(&work_queue_mtx); pthread_mutex_destroy(&stats_mtx); pthread_mutex_destroy(&print_mtx); cleanup_ignore(root_ignores); free(workers); for (i = 0; paths[i] != NULL; i++) { free(paths[i]); free(base_paths[i]); } free(base_paths); free(paths); if (find_skip_lookup) { free(find_skip_lookup); } return !opts.match_found; }
void print_file_matches(const char* path, const char* buf, const int buf_len, const match matches[], const int matches_len) { int line = 1; char **context_prev_lines = NULL; int prev_line = 0; int last_prev_line = 0; int prev_line_offset = 0; int cur_match = 0; /* TODO the line below contains a terrible hack */ int lines_since_last_match = 1000000; /* if I initialize this to INT_MAX it'll overflow */ int lines_to_print = 0; int last_printed_match = 0; char sep = '-'; int i, j; int in_a_match = FALSE; int printing_a_match = FALSE; if (opts.ackmate) { sep = ':'; } print_file_separator(); if (opts.print_heading == TRUE) { print_path(path, '\n'); } context_prev_lines = ag_calloc(sizeof(char*), (opts.before + 1)); for (i = 0; i <= buf_len && (cur_match < matches_len || lines_since_last_match <= opts.after); i++) { if (cur_match < matches_len && i == matches[cur_match].end) { /* We found the end of a match. */ cur_match++; in_a_match = FALSE; } if (cur_match < matches_len && i == matches[cur_match].start) { in_a_match = TRUE; /* We found the start of a match */ if (cur_match > 0 && opts.context && lines_since_last_match > (opts.before + opts.after + 1)) { fprintf(out_fd, "--\n"); } if (lines_since_last_match > 0 && opts.before > 0) { /* TODO: better, but still needs work */ /* print the previous line(s) */ lines_to_print = lines_since_last_match - (opts.after + 1); if (lines_to_print < 0) { lines_to_print = 0; } else if (lines_to_print > opts.before) { lines_to_print = opts.before; } for (j = (opts.before - lines_to_print); j < opts.before; j++) { prev_line = (last_prev_line + j) % opts.before; if (context_prev_lines[prev_line] != NULL) { if (opts.print_heading == 0) { print_path(path, ':'); } print_line_number(line - (opts.before - j), sep); fprintf(out_fd, "%s\n", context_prev_lines[prev_line]); } } } lines_since_last_match = 0; } /* We found the end of a line. */ if (buf[i] == '\n' && opts.before > 0) { if (context_prev_lines[last_prev_line] != NULL) { free(context_prev_lines[last_prev_line]); } /* We don't want to strcpy the \n */ context_prev_lines[last_prev_line] = ag_strndup(&buf[prev_line_offset], i - prev_line_offset); last_prev_line = (last_prev_line + 1) % opts.before; } if (buf[i] == '\n' || i == buf_len) { if (lines_since_last_match == 0) { if (opts.print_heading == 0 && !opts.search_stream) { print_path(path, ':'); } if (opts.ackmate) { /* print headers for ackmate to parse */ print_line_number(line, ';'); for (; last_printed_match < cur_match; last_printed_match++) { fprintf(out_fd, "%i %i", (matches[last_printed_match].start - prev_line_offset), (matches[last_printed_match].end - matches[last_printed_match].start) ); last_printed_match == cur_match - 1 ? fputc(':', out_fd) : fputc(',', out_fd); } j = prev_line_offset; /* print up to current char */ for (; j <= i; j++) { fputc(buf[j], out_fd); } } else { print_line_number(line, ':'); if (opts.column) { fprintf(out_fd, "%i:", (matches[last_printed_match].start - prev_line_offset) + 1); } if (printing_a_match && opts.color) { fprintf(out_fd, "%s", opts.color_match); } for (j = prev_line_offset; j <= i; j++) { if (j == matches[last_printed_match].end && last_printed_match < matches_len) { if (opts.color) { fprintf(out_fd, "%s", color_reset); } printing_a_match = FALSE; last_printed_match++; } if (j == matches[last_printed_match].start && last_printed_match < matches_len) { if (opts.color) { fprintf(out_fd, "%s", opts.color_match); } printing_a_match = TRUE; } /* Don't print the null terminator */ if (j < buf_len) { fputc(buf[j], out_fd); } } if (printing_a_match && opts.color) { fprintf(out_fd, "%s", color_reset); } } } else if (lines_since_last_match <= opts.after && i != buf_len) { /* print context after matching line */ if (opts.print_heading == 0) { print_path(path, ':'); } print_line_number(line, sep); for (j = prev_line_offset; j < i; j++) { fputc(buf[j], out_fd); } fputc('\n', out_fd); } prev_line_offset = i + 1; /* skip the newline */ line++; if (!in_a_match) { lines_since_last_match++; } /* File doesn't end with a newline. Print one so the output is pretty. */ if (i == buf_len && buf[i] != '\n') { fputc('\n', out_fd); } } } for (i = 0; i < opts.before; i++) { if (context_prev_lines[i] != NULL) { free(context_prev_lines[i]); } } free(context_prev_lines); }
void print_file_matches(const char *path, const char *buf, const size_t buf_len, const match_t matches[], const size_t matches_len) { size_t line = 1; char **context_prev_lines = NULL; size_t prev_line = 0; size_t last_prev_line = 0; size_t prev_line_offset = 0; size_t cur_match = 0; size_t lines_since_last_match = INT_MAX; ssize_t lines_to_print = 0; size_t last_printed_match = 0; char sep = '-'; size_t i, j; int in_a_match = FALSE; int printing_a_match = FALSE; if (opts.ackmate || opts.vimgrep) { sep = ':'; } print_file_separator(); if (opts.print_path == PATH_PRINT_DEFAULT) { opts.print_path = PATH_PRINT_TOP; } else if (opts.print_path == PATH_PRINT_DEFAULT_EACH_LINE) { opts.print_path = PATH_PRINT_EACH_LINE; } if (opts.print_path == PATH_PRINT_TOP) { if (opts.print_count) { print_path_count(path, opts.path_sep, matches_len); } else { print_path(path, opts.path_sep); } } context_prev_lines = ag_calloc(sizeof(char *), (opts.before + 1)); for (i = 0; i <= buf_len && (cur_match < matches_len || lines_since_last_match <= opts.after); i++) { if (cur_match < matches_len && i == matches[cur_match].start) { in_a_match = TRUE; /* We found the start of a match */ if (cur_match > 0 && opts.context && lines_since_last_match > (opts.before + opts.after + 1)) { fprintf(out_fd, "--\n"); } if (lines_since_last_match > 0 && opts.before > 0) { /* TODO: better, but still needs work */ /* print the previous line(s) */ lines_to_print = lines_since_last_match - (opts.after + 1); if (lines_to_print < 0) { lines_to_print = 0; } else if ((size_t)lines_to_print > opts.before) { lines_to_print = opts.before; } for (j = (opts.before - lines_to_print); j < opts.before; j++) { prev_line = (last_prev_line + j) % opts.before; if (context_prev_lines[prev_line] != NULL) { if (opts.print_path == PATH_PRINT_EACH_LINE) { print_path(path, ':'); } print_line_number(line - (opts.before - j), sep); fprintf(out_fd, "%s\n", context_prev_lines[prev_line]); } } } lines_since_last_match = 0; } if (cur_match < matches_len && i == matches[cur_match].end) { /* We found the end of a match. */ cur_match++; in_a_match = FALSE; } /* We found the end of a line. */ if (buf[i] == '\n' && opts.before > 0) { if (context_prev_lines[last_prev_line] != NULL) { free(context_prev_lines[last_prev_line]); } /* We don't want to strcpy the \n */ context_prev_lines[last_prev_line] = ag_strndup(&buf[prev_line_offset], i - prev_line_offset); last_prev_line = (last_prev_line + 1) % opts.before; } if (buf[i] == '\n' || i == buf_len) { if (lines_since_last_match == 0) { if (opts.print_path == PATH_PRINT_EACH_LINE && !opts.search_stream) { print_path(path, ':'); } if (opts.ackmate) { /* print headers for ackmate to parse */ print_line_number(line, ';'); for (; last_printed_match < cur_match; last_printed_match++) { /* Don't print negative offsets. This isn't quite right, but not many people use --ackmate */ long start = (long)(matches[last_printed_match].start - prev_line_offset); if (start < 0) { start = 0; } fprintf(out_fd, "%li %li", start, (long)(matches[last_printed_match].end - matches[last_printed_match].start)); last_printed_match == cur_match - 1 ? fputc(':', out_fd) : fputc(',', out_fd); } print_line(buf, i, prev_line_offset); } else if (opts.vimgrep) { for (; last_printed_match < cur_match; last_printed_match++) { print_path(path, sep); print_line_number(line, sep); print_column_number(matches, last_printed_match, prev_line_offset, sep); print_line(buf, i, prev_line_offset); } } else { print_line_number(line, ':'); int printed_match = FALSE; if (opts.column) { print_column_number(matches, last_printed_match, prev_line_offset, ':'); } if (printing_a_match && opts.color) { fprintf(out_fd, "%s", opts.color_match); } for (j = prev_line_offset; j <= i; j++) { if (last_printed_match < matches_len && j == matches[last_printed_match].end) { if (opts.color) { fprintf(out_fd, "%s", color_reset); } printing_a_match = FALSE; last_printed_match++; printed_match = TRUE; if (opts.only_matching) { fputc('\n', out_fd); } } if (last_printed_match < matches_len && j == matches[last_printed_match].start) { if (opts.only_matching && printed_match) { if (opts.print_path == PATH_PRINT_EACH_LINE) { print_path(path, ':'); } print_line_number(line, ':'); if (opts.column) { print_column_number(matches, last_printed_match, prev_line_offset, ':'); } } if (opts.color) { fprintf(out_fd, "%s", opts.color_match); } printing_a_match = TRUE; } /* Don't print the null terminator */ if (j < buf_len) { /* if only_matching is set, print only matches and newlines */ if (!opts.only_matching || printing_a_match) { fputc(buf[j], out_fd); } } } if (printing_a_match && opts.color) { fprintf(out_fd, "%s", color_reset); } } } else if (lines_since_last_match <= opts.after) { /* print context after matching line */ if (opts.print_path == PATH_PRINT_EACH_LINE) { print_path(path, ':'); } print_line_number(line, sep); for (j = prev_line_offset; j < i; j++) { fputc(buf[j], out_fd); } fputc('\n', out_fd); } prev_line_offset = i + 1; /* skip the newline */ line++; if (!in_a_match && lines_since_last_match < INT_MAX) { lines_since_last_match++; } /* File doesn't end with a newline. Print one so the output is pretty. */ if (i == buf_len && buf[i] != '\n' && !opts.search_stream) { fputc('\n', out_fd); } } } for (i = 0; i < opts.before; i++) { if (context_prev_lines[i] != NULL) { free(context_prev_lines[i]); } } free(context_prev_lines); }