int cgit_diff_files(const unsigned char *old_sha1, const unsigned char *new_sha1, unsigned long *old_size, unsigned long *new_size, int *binary, linediff_fn fn) { mmfile_t file1, file2; xpparam_t diff_params; xdemitconf_t emit_params; xdemitcb_t emit_cb; if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1)) return 1; *old_size = file1.size; *new_size = file2.size; if ((file1.ptr && buffer_is_binary(file1.ptr, file1.size)) || (file2.ptr && buffer_is_binary(file2.ptr, file2.size))) { *binary = 1; return 0; } memset(&diff_params, 0, sizeof(diff_params)); memset(&emit_params, 0, sizeof(emit_params)); memset(&emit_cb, 0, sizeof(emit_cb)); diff_params.flags = XDF_NEED_MINIMAL; emit_params.ctxlen = 3; emit_params.flags = XDL_EMIT_FUNCNAMES; emit_cb.outf = filediff_cb; emit_cb.priv = fn; xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb); return 0; }
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, mmbuffer_t *result, const char *path_unused, mmfile_t *orig, mmfile_t *src1, const char *name1, mmfile_t *src2, const char *name2, int virtual_ancestor) { xpparam_t xpp; if (buffer_is_binary(orig->ptr, orig->size) || buffer_is_binary(src1->ptr, src1->size) || buffer_is_binary(src2->ptr, src2->size)) { warning("Cannot merge binary files: %s vs. %s\n", name1, name2); return ll_binary_merge(drv_unused, result, path_unused, orig, src1, name1, src2, name2, virtual_ancestor); } memset(&xpp, 0, sizeof(xpp)); return xdl_merge(orig, src1, name1, src2, name2, &xpp, XDL_MERGE_ZEALOUS, result); }
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, mmbuffer_t *result, const char *path, mmfile_t *orig, const char *orig_name, mmfile_t *src1, const char *name1, mmfile_t *src2, const char *name2, int flag, int marker_size) { xmparam_t xmp; if (buffer_is_binary(orig->ptr, orig->size) || buffer_is_binary(src1->ptr, src1->size) || buffer_is_binary(src2->ptr, src2->size)) { warning("Cannot merge binary files: %s (%s vs. %s)\n", path, name1, name2); return ll_binary_merge(drv_unused, result, path, orig, orig_name, src1, name1, src2, name2, flag, marker_size); } memset(&xmp, 0, sizeof(xmp)); xmp.level = XDL_MERGE_ZEALOUS; xmp.favor = ll_opt_favor(flag); if (git_xmerge_style >= 0) xmp.style = git_xmerge_style; if (marker_size > 0) xmp.marker_size = marker_size; xmp.ancestor = orig_name; xmp.file1 = name1; xmp.file2 = name2; return xdl_merge(orig, src1, src2, &xmp, result); }
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, mmbuffer_t *result, const char *path, mmfile_t *orig, mmfile_t *src1, const char *name1, mmfile_t *src2, const char *name2, int flag, int marker_size) { xmparam_t xmp; int style = 0; int favor = (flag >> 1) & 03; if (buffer_is_binary(orig->ptr, orig->size) || buffer_is_binary(src1->ptr, src1->size) || buffer_is_binary(src2->ptr, src2->size)) { warning("Cannot merge binary files: %s (%s vs. %s)\n", path, name1, name2); return ll_binary_merge(drv_unused, result, path, orig, src1, name1, src2, name2, flag, marker_size); } memset(&xmp, 0, sizeof(xmp)); if (git_xmerge_style >= 0) style = git_xmerge_style; if (marker_size > 0) xmp.marker_size = marker_size; return xdl_merge(orig, src1, name1, src2, name2, &xmp, XDL_MERGE_FLAGS(XDL_MERGE_ZEALOUS, style, favor), result); }
static int grep_source_is_binary(struct grep_source *gs) { grep_source_load_driver(gs); if (gs->driver->binary != -1) return gs->driver->binary; if (!grep_source_load(gs)) return buffer_is_binary(gs->buf, gs->size); return 0; }
static int ll_xdl_merge(const struct ll_merge_driver *drv_unused, mmbuffer_t *result, const char *path, mmfile_t *orig, const char *orig_name, mmfile_t *src1, const char *name1, mmfile_t *src2, const char *name2, const struct ll_merge_options *opts, int marker_size) { xmparam_t xmp; assert(opts); if (orig->size > MAX_XDIFF_SIZE || src1->size > MAX_XDIFF_SIZE || src2->size > MAX_XDIFF_SIZE || buffer_is_binary(orig->ptr, orig->size) || buffer_is_binary(src1->ptr, src1->size) || buffer_is_binary(src2->ptr, src2->size)) { return ll_binary_merge(drv_unused, result, path, orig, orig_name, src1, name1, src2, name2, opts, marker_size); } memset(&xmp, 0, sizeof(xmp)); xmp.level = XDL_MERGE_ZEALOUS; xmp.favor = opts->variant; xmp.mark_all = opts->mark_all_changes; xmp.xpp.flags = opts->xdl_opts; if (git_xmerge_style >= 0) xmp.style = git_xmerge_style; if (marker_size > 0) xmp.marker_size = marker_size; xmp.ancestor = orig_name; xmp.file1 = name1; xmp.file2 = name2; return xdl_merge(orig, src1, src2, &xmp, result); }
static void print_object(const unsigned char *sha1, const char *path) { enum object_type type; char *buf, *ext; unsigned long size; struct string_list_item *mime; type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { html_status(404, "Not found", 0); return; } buf = read_sha1_file(sha1, &type, &size); if (!buf) { html_status(404, "Not found", 0); return; } ctx.page.mimetype = NULL; ext = strrchr(path, '.'); if (ext && *(++ext)) { mime = string_list_lookup(&ctx.cfg.mimetypes, ext); if (mime) ctx.page.mimetype = (char *)mime->util; } if (!ctx.page.mimetype) { if (buffer_is_binary(buf, size)) ctx.page.mimetype = "application/octet-stream"; else ctx.page.mimetype = "text/plain"; } ctx.page.filename = fmt("%s", path); ctx.page.size = size; ctx.page.etag = sha1_to_hex(sha1); cgit_print_http_headers(&ctx); html_raw(buf, size); match = 1; }
static int print_object(const unsigned char *sha1, const char *path) { enum object_type type; char *buf, *mimetype; unsigned long size; type = sha1_object_info(sha1, &size); if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Not found"); return 0; } buf = read_sha1_file(sha1, &type, &size); if (!buf) { cgit_print_error_page(404, "Not found", "Not found"); return 0; } mimetype = get_mimetype_for_filename(path); ctx.page.mimetype = mimetype; if (!ctx.page.mimetype) { if (buffer_is_binary(buf, size)) { ctx.page.mimetype = "application/octet-stream"; ctx.page.charset = NULL; } else { ctx.page.mimetype = "text/plain"; } } ctx.page.filename = path; ctx.page.size = size; ctx.page.etag = sha1_to_hex(sha1); cgit_print_http_headers(); html_raw(buf, size); free(mimetype); free(buf); return 1; }
static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { char *bol = buf; unsigned long left = size; unsigned lno = 1; unsigned last_hit = 0; int binary_match_only = 0; unsigned count = 0; int try_lookahead = 0; enum grep_context ctx = GREP_CONTEXT_HEAD; xdemitconf_t xecfg; opt->last_shown = 0; if (!opt->output) opt->output = std_output; if (buffer_is_binary(buf, size)) { switch (opt->binary) { case GREP_BINARY_DEFAULT: binary_match_only = 1; break; case GREP_BINARY_NOMATCH: return 0; /* Assume unmatch */ break; default: break; } } memset(&xecfg, 0, sizeof(xecfg)); if (opt->funcname && !opt->unmatch_name_only && !opt->status_only && !opt->name_only && !binary_match_only && !collect_hits) { struct userdiff_driver *drv = userdiff_find_by_path(name); if (drv && drv->funcname.pattern) { const struct userdiff_funcname *pe = &drv->funcname; xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); opt->priv = &xecfg; } } try_lookahead = should_lookahead(opt); while (left) { char *eol, ch; int hit; /* * look_ahead() skips quicly to the line that possibly * has the next hit; don't call it if we need to do * something more than just skipping the current line * in response to an unmatch for the current line. E.g. * inside a post-context window, we will show the current * line as a context around the previous hit when it * doesn't hit. */ if (try_lookahead && !(last_hit && lno <= last_hit + opt->post_context) && look_ahead(opt, &left, &lno, &bol)) break; eol = end_of_line(bol, &left); ch = *eol; *eol = 0; if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; hit = match_line(opt, bol, eol, ctx, collect_hits); *eol = ch; if (collect_hits) goto next_line; /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. */ if (opt->invert) hit = !hit; if (opt->unmatch_name_only) { if (hit) return 0; goto next_line; } if (hit) { count++; if (opt->status_only) return 1; if (binary_match_only) { opt->output(opt, "Binary file ", 12); output_color(opt, name, strlen(name), opt->color_filename); opt->output(opt, " matches\n", 9); return 1; } if (opt->name_only) { show_name(opt, name); return 1; } /* Hit at this line. If we haven't shown the * pre-context lines, we would need to show them. * When asked to do "count", this still show * the context which is nonsense, but the user * deserves to get that ;-). */ if (opt->pre_context) show_pre_context(opt, name, buf, bol, lno); else if (opt->funcname) show_funcname_line(opt, name, buf, bol, lno); if (!opt->count) show_line(opt, bol, eol, name, lno, ':'); last_hit = lno; } else if (last_hit && lno <= last_hit + opt->post_context) { /* If the last hit is within the post context, * we need to show this line. */ show_line(opt, bol, eol, name, lno, '-'); } next_line: bol = eol + 1; if (!left) break; left--; lno++; } if (collect_hits) return 0; if (opt->status_only) return 0; if (opt->unmatch_name_only) { /* We did not see any hit, so we want to show this */ show_name(opt, name); return 1; } xdiff_clear_find_func(&xecfg); opt->priv = NULL; /* NEEDSWORK: * The real "grep -c foo *.c" gives many "bar.c:0" lines, * which feels mostly useless but sometimes useful. Maybe * make it another option? For now suppress them. */ if (opt->count && count) { char buf[32]; output_color(opt, name, strlen(name), opt->color_filename); output_sep(opt, ':'); snprintf(buf, sizeof(buf), "%u\n", count); opt->output(opt, buf, strlen(buf)); } return !!last_hit; }
static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { char *bol = buf; unsigned long left = size; unsigned lno = 1; unsigned last_hit = 0; int binary_match_only = 0; unsigned count = 0; enum grep_context ctx = GREP_CONTEXT_HEAD; xdemitconf_t xecfg; opt->last_shown = 0; if (buffer_is_binary(buf, size)) { switch (opt->binary) { case GREP_BINARY_DEFAULT: binary_match_only = 1; break; case GREP_BINARY_NOMATCH: return 0; /* Assume unmatch */ break; default: break; } } memset(&xecfg, 0, sizeof(xecfg)); if (opt->funcname && !opt->unmatch_name_only && !opt->status_only && !opt->name_only && !binary_match_only && !collect_hits) { struct userdiff_driver *drv = userdiff_find_by_path(name); if (drv && drv->funcname.pattern) { const struct userdiff_funcname *pe = &drv->funcname; xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags); opt->priv = &xecfg; } } while (left) { char *eol, ch; int hit; eol = end_of_line(bol, &left); ch = *eol; *eol = 0; if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; hit = match_line(opt, bol, eol, ctx, collect_hits); *eol = ch; if (collect_hits) goto next_line; /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. */ if (opt->invert) hit = !hit; if (opt->unmatch_name_only) { if (hit) return 0; goto next_line; } if (hit) { count++; if (opt->status_only) return 1; if (binary_match_only) { printf("Binary file %s matches\n", name); return 1; } if (opt->name_only) { show_name(opt, name); return 1; } /* Hit at this line. If we haven't shown the * pre-context lines, we would need to show them. * When asked to do "count", this still show * the context which is nonsense, but the user * deserves to get that ;-). */ if (opt->pre_context) show_pre_context(opt, name, buf, bol, lno); else if (opt->funcname) show_funcname_line(opt, name, buf, bol, lno); if (!opt->count) show_line(opt, bol, eol, name, lno, ':'); last_hit = lno; } else if (last_hit && lno <= last_hit + opt->post_context) { /* If the last hit is within the post context, * we need to show this line. */ show_line(opt, bol, eol, name, lno, '-'); } next_line: bol = eol + 1; if (!left) break; left--; lno++; } if (collect_hits) return 0; if (opt->status_only) return 0; if (opt->unmatch_name_only) { /* We did not see any hit, so we want to show this */ show_name(opt, name); return 1; } xdiff_clear_find_func(&xecfg); opt->priv = NULL; /* NEEDSWORK: * The real "grep -c foo *.c" gives many "bar.c:0" lines, * which feels mostly useless but sometimes useful. Maybe * make it another option? For now suppress them. */ if (opt->count && count) printf("%s%c%u\n", name, opt->null_following_name ? '\0' : ':', count); return !!last_hit; }
int cgit_ref_path_exists(const char *path, const char *ref, int file_only) { unsigned char sha1[20]; unsigned long size; struct pathspec_item path_items = { .match = path, .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_sha1 = sha1, .found_path = 0, .file_only = file_only }; if (get_sha1(ref, sha1)) return 0; if (sha1_object_info(sha1, &size) != OBJ_COMMIT) return 0; read_tree_recursive(lookup_commit_reference(sha1)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); return walk_tree_ctx.found_path; } int cgit_print_file(char *path, const char *head, int file_only) { unsigned char sha1[20]; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_sha1 = sha1, .found_path = 0, .file_only = file_only }; if (get_sha1(head, sha1)) return -1; type = sha1_object_info(sha1, &size); if (type == OBJ_COMMIT && path) { commit = lookup_commit_reference(sha1); read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.found_path) return -1; type = sha1_object_info(sha1, &size); } if (type == OBJ_BAD) return -1; buf = read_sha1_file(sha1, &type, &size); if (!buf) return -1; buf[size] = '\0'; html_raw(buf, size); return 0; } void cgit_print_blob(const char *hex, char *path, const char *head, int file_only) { unsigned char sha1[20]; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = path ? strlen(path) : 0 }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_sha1 = sha1, .found_path = 0, .file_only = file_only }; if (hex) { if (get_sha1_hex(hex, sha1)) { cgit_print_error("Bad hex value: %s", hex); return; } } else { if (get_sha1(head, sha1)) { cgit_print_error("Bad ref: %s", head); return; } } type = sha1_object_info(sha1, &size); if ((!hex) && type == OBJ_COMMIT && path) { commit = lookup_commit_reference(sha1); read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); type = sha1_object_info(sha1,&size); } if (type == OBJ_BAD) { cgit_print_error("Bad object name: %s", hex); return; } buf = read_sha1_file(sha1, &type, &size); if (!buf) { cgit_print_error("Error reading object %s", hex); return; } buf[size] = '\0'; ctx.page.mimetype = ctx.qry.mimetype; if (!ctx.page.mimetype) { if (buffer_is_binary(buf, size)) ctx.page.mimetype = "application/octet-stream"; else ctx.page.mimetype = "text/plain"; } ctx.page.filename = path; cgit_print_http_headers(&ctx); html_raw(buf, size); }
int cmd_merge_file(int argc, const char **argv, const char *prefix) { const char *names[3] = { NULL, NULL, NULL }; mmfile_t mmfs[3]; mmbuffer_t result = {NULL, 0}; xmparam_t xmp = {{0}}; int ret = 0, i = 0, to_stdout = 0; int quiet = 0; int prefixlen = 0; struct option options[] = { OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"), OPT_SET_INT(0, "diff3", &xmp.style, "use a diff3 based merge", XDL_MERGE_DIFF3), OPT_SET_INT(0, "ours", &xmp.favor, "for conflicts, use our version", XDL_MERGE_FAVOR_OURS), OPT_SET_INT(0, "theirs", &xmp.favor, "for conflicts, use their version", XDL_MERGE_FAVOR_THEIRS), OPT_SET_INT(0, "union", &xmp.favor, "for conflicts, use a union version", XDL_MERGE_FAVOR_UNION), OPT_INTEGER(0, "marker-size", &xmp.marker_size, "for conflicts, use this marker size"), OPT__QUIET(&quiet, "do not warn about conflicts"), OPT_CALLBACK('L', NULL, names, "name", "set labels for file1/orig_file/file2", &label_cb), OPT_END(), }; xmp.level = XDL_MERGE_ZEALOUS_ALNUM; xmp.style = 0; xmp.favor = 0; if (startup_info->have_repository) { /* Read the configuration file */ git_config(git_xmerge_config, NULL); if (0 <= git_xmerge_style) xmp.style = git_xmerge_style; } argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0); if (argc != 3) usage_with_options(merge_file_usage, options); if (quiet) { if (!freopen("/dev/null", "w", stderr)) return error("failed to redirect stderr to /dev/null: " "%s", strerror(errno)); } if (prefix) prefixlen = strlen(prefix); for (i = 0; i < 3; i++) { const char *fname = prefix_filename(prefix, prefixlen, argv[i]); if (!names[i]) names[i] = argv[i]; if (read_mmfile(mmfs + i, fname)) return -1; if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size)) return error("Cannot merge binary files: %s", argv[i]); } xmp.ancestor = names[1]; xmp.file1 = names[0]; xmp.file2 = names[2]; ret = xdl_merge(mmfs + 1, mmfs + 0, mmfs + 2, &xmp, &result); for (i = 0; i < 3; i++) free(mmfs[i].ptr); if (ret >= 0) { const char *filename = argv[0]; FILE *f = to_stdout ? stdout : fopen(filename, "wb"); if (!f) ret = error("Could not open %s for writing", filename); else if (result.size && fwrite(result.ptr, result.size, 1, f) != 1) ret = error("Could not write to %s", filename); else if (fclose(f)) ret = error("Could not close %s", filename); free(result.ptr); } return ret; }
int cgit_ref_path_exists(const char *path, const char *ref, int file_only) { struct object_id oid; unsigned long size; struct pathspec_item path_items = { .match = xstrdup(path), .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (get_oid(ref, &oid)) goto done; if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT) goto done; read_tree_recursive(lookup_commit_reference(the_repository, &oid)->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); done: free(path_items.match); return walk_tree_ctx.found_path; } int cgit_print_file(char *path, const char *head, int file_only) { struct object_id oid; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = strlen(path) }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (get_oid(head, &oid)) return -1; type = oid_object_info(the_repository, &oid, &size); if (type == OBJ_COMMIT) { commit = lookup_commit_reference(the_repository, &oid); read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); if (!walk_tree_ctx.found_path) return -1; type = oid_object_info(the_repository, &oid, &size); } if (type == OBJ_BAD) return -1; buf = read_object_file(&oid, &type, &size); if (!buf) return -1; buf[size] = '\0'; html_raw(buf, size); free(buf); return 0; } void cgit_print_blob(const char *hex, char *path, const char *head, int file_only) { struct object_id oid; enum object_type type; char *buf; unsigned long size; struct commit *commit; struct pathspec_item path_items = { .match = path, .len = path ? strlen(path) : 0 }; struct pathspec paths = { .nr = 1, .items = &path_items }; struct walk_tree_context walk_tree_ctx = { .match_path = path, .matched_oid = &oid, .found_path = 0, .file_only = file_only }; if (hex) { if (get_oid_hex(hex, &oid)) { cgit_print_error_page(400, "Bad request", "Bad hex value: %s", hex); return; } } else { if (get_oid(head, &oid)) { cgit_print_error_page(404, "Not found", "Bad ref: %s", head); return; } } type = oid_object_info(the_repository, &oid, &size); if ((!hex) && type == OBJ_COMMIT && path) { commit = lookup_commit_reference(the_repository, &oid); read_tree_recursive(commit->maybe_tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx); type = oid_object_info(the_repository, &oid, &size); } if (type == OBJ_BAD) { cgit_print_error_page(404, "Not found", "Bad object name: %s", hex); return; } buf = read_object_file(&oid, &type, &size); if (!buf) { cgit_print_error_page(500, "Internal server error", "Error reading object %s", hex); return; } buf[size] = '\0'; if (buffer_is_binary(buf, size)) ctx.page.mimetype = "application/octet-stream"; else ctx.page.mimetype = "text/plain"; ctx.page.filename = path; html("X-Content-Type-Options: nosniff\n"); html("Content-Security-Policy: default-src 'none'\n"); cgit_print_http_headers(); html_raw(buf, size); free(buf); }
static int grep_buffer_1(struct grep_opt *opt, const char *name, char *buf, unsigned long size, int collect_hits) { char *bol = buf; unsigned long left = size; unsigned lno = 1; struct pre_context_line { char *bol; char *eol; } *prev = NULL, *pcl; unsigned last_hit = 0; unsigned last_shown = 0; int binary_match_only = 0; const char *hunk_mark = ""; unsigned count = 0; enum grep_context ctx = GREP_CONTEXT_HEAD; if (buffer_is_binary(buf, size)) { switch (opt->binary) { case GREP_BINARY_DEFAULT: binary_match_only = 1; break; case GREP_BINARY_NOMATCH: return 0; /* Assume unmatch */ break; default: break; } } if (opt->pre_context) prev = xcalloc(opt->pre_context, sizeof(*prev)); if (opt->pre_context || opt->post_context) hunk_mark = "--\n"; while (left) { char *eol, ch; int hit; eol = end_of_line(bol, &left); ch = *eol; *eol = 0; if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol)) ctx = GREP_CONTEXT_BODY; hit = match_line(opt, bol, eol, ctx, collect_hits); *eol = ch; if (collect_hits) goto next_line; /* "grep -v -e foo -e bla" should list lines * that do not have either, so inversion should * be done outside. */ if (opt->invert) hit = !hit; if (opt->unmatch_name_only) { if (hit) return 0; goto next_line; } if (hit) { count++; if (opt->status_only) return 1; if (binary_match_only) { printf("Binary file %s matches\n", name); return 1; } if (opt->name_only) { show_name(opt, name); return 1; } /* Hit at this line. If we haven't shown the * pre-context lines, we would need to show them. * When asked to do "count", this still show * the context which is nonsense, but the user * deserves to get that ;-). */ if (opt->pre_context) { unsigned from; if (opt->pre_context < lno) from = lno - opt->pre_context; else from = 1; if (from <= last_shown) from = last_shown + 1; if (last_shown && from != last_shown + 1) printf(hunk_mark); while (from < lno) { pcl = &prev[lno-from-1]; show_line(opt, pcl->bol, pcl->eol, name, from, '-'); from++; } last_shown = lno-1; } if (last_shown && lno != last_shown + 1) printf(hunk_mark); if (!opt->count) show_line(opt, bol, eol, name, lno, ':'); last_shown = last_hit = lno; } else if (last_hit && lno <= last_hit + opt->post_context) { /* If the last hit is within the post context, * we need to show this line. */ if (last_shown && lno != last_shown + 1) printf(hunk_mark); show_line(opt, bol, eol, name, lno, '-'); last_shown = lno; } if (opt->pre_context) { memmove(prev+1, prev, (opt->pre_context-1) * sizeof(*prev)); prev->bol = bol; prev->eol = eol; } next_line: bol = eol + 1; if (!left) break; left--; lno++; } free(prev); if (collect_hits) return 0; if (opt->status_only) return 0; if (opt->unmatch_name_only) { /* We did not see any hit, so we want to show this */ show_name(opt, name); return 1; } /* NEEDSWORK: * The real "grep -c foo *.c" gives many "bar.c:0" lines, * which feels mostly useless but sometimes useful. Maybe * make it another option? For now suppress them. */ if (opt->count && count) printf("%s%c%u\n", name, opt->null_following_name ? '\0' : ':', count); return !!last_hit; }
int cmd_merge_file(int argc, const char **argv, const char *prefix) { const char *names[3] = { NULL, NULL, NULL }; mmfile_t mmfs[3]; mmbuffer_t result = {NULL, 0}; xmparam_t xmp = {{XDF_NEED_MINIMAL}}; int ret = 0, i = 0, to_stdout = 0; int level = XDL_MERGE_ZEALOUS_ALNUM; int style = 0, quiet = 0; int favor = 0; int nongit; struct option options[] = { OPT_BOOLEAN('p', "stdout", &to_stdout, "send results to standard output"), OPT_SET_INT(0, "diff3", &style, "use a diff3 based merge", XDL_MERGE_DIFF3), OPT_SET_INT(0, "ours", &favor, "for conflicts, use our version", XDL_MERGE_FAVOR_OURS), OPT_SET_INT(0, "theirs", &favor, "for conflicts, use their version", XDL_MERGE_FAVOR_THEIRS), OPT__QUIET(&quiet), OPT_CALLBACK('L', NULL, names, "name", "set labels for file1/orig_file/file2", &label_cb), OPT_END(), }; prefix = setup_git_directory_gently(&nongit); if (!nongit) { /* Read the configuration file */ git_config(git_xmerge_config, NULL); if (0 <= git_xmerge_style) style = git_xmerge_style; } argc = parse_options(argc, argv, prefix, options, merge_file_usage, 0); if (argc != 3) usage_with_options(merge_file_usage, options); if (quiet) { if (!freopen("/dev/null", "w", stderr)) return error("failed to redirect stderr to /dev/null: " "%s\n", strerror(errno)); } for (i = 0; i < 3; i++) { if (!names[i]) names[i] = argv[i]; if (read_mmfile(mmfs + i, argv[i])) return -1; if (buffer_is_binary(mmfs[i].ptr, mmfs[i].size)) return error("Cannot merge binary files: %s\n", argv[i]); } ret = xdl_merge(mmfs + 1, mmfs + 0, names[0], mmfs + 2, names[2], &xmp, XDL_MERGE_FLAGS(level, style, favor), &result); for (i = 0; i < 3; i++) free(mmfs[i].ptr); if (ret >= 0) { const char *filename = argv[0]; FILE *f = to_stdout ? stdout : fopen(filename, "wb"); if (!f) ret = error("Could not open %s for writing", filename); else if (result.size && fwrite(result.ptr, result.size, 1, f) != 1) ret = error("Could not write to %s", filename); else if (fclose(f)) ret = error("Could not close %s", filename); free(result.ptr); } return ret; }