From 80767bc9732bf6716697198e53ff2cb8d4ae96be Mon Sep 17 00:00:00 2001 From: Bryce Kwon Date: Wed, 12 Aug 2026 18:23:17 -1000 Subject: Restyle the sources and fix the audit's findings --- source/ui-patch.c | 124 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 46 deletions(-) (limited to 'source/ui-patch.c') diff --git a/source/ui-patch.c b/source/ui-patch.c index 29432ac..6f75b6a 100644 --- a/source/ui-patch.c +++ b/source/ui-patch.c @@ -1,83 +1,107 @@ -/* ui-patch.c: generate patch view - * - * Copyright (C) 2006-2014 cgit Development Team - * - * Licensed under GNU General Public License v2 - * (see LICENSE.txt for full license text) +/* + * The patch page, which serves a commit or a range of commits as plain text + * in the mail format git format-patch writes, so that a change read in cgit + * can be fed straight to git am. It is one of the repository commands in + * cmd.c, taking the newer revision from id, the older one from id2, and an + * optional path that narrows the diff. A merge carries no single patch and so + * drops out of a range, and max-patch-count bounds how many commits one + * request may emit. */ #define USE_THE_REPOSITORY_VARIABLE #include "cgit.h" -#include "ui-patch.h" #include "html.h" +#include "ui-diff.h" +#include "ui-patch.h" #include "ui-shared.h" -/* two commit hashes with two dots in between and termination */ -#define REV_RANGE_LEN 2 * GIT_MAX_HEXSZ + 3 +// Room for two hex object ids, the two dots between them, and the null byte. +#define REV_RANGE_LEN (2 * GIT_MAX_HEXSZ + 3) -void cgit_print_patch(const char *new_rev, const char *old_rev, - const char *prefix) +// Where the format argument sits in the walk arguments below. +#define FORMAT_ARG 2 + +/* + * A root commit has no parent, so the old end is left null and the caller asks + * for that one commit instead. + */ +static int resolve_range(const char *new_rev, const char *old_rev, + struct object_id *new_oid, struct object_id *old_oid) { - struct rev_info rev; struct commit *commit; - struct object_id new_rev_oid, old_rev_oid; - char rev_range[REV_RANGE_LEN]; - const char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range, "--", prefix, NULL }; - int rev_argc = ARRAY_SIZE(rev_argv) - 1; - char *patchname; - if (!prefix) - rev_argc--; - - if (!new_rev) - new_rev = ctx.qry.head; - - if (repo_get_oid(the_repository, new_rev, &new_rev_oid)) { + if (repo_get_oid(the_repository, new_rev, new_oid)) { cgit_print_error_page(404, "Not found", "Bad object id: %s", new_rev); - return; + return -1; } - commit = lookup_commit_reference(the_repository, &new_rev_oid); + commit = lookup_commit_reference(the_repository, new_oid); if (!commit) { cgit_print_error_page(404, "Not found", "Bad commit reference: %s", new_rev); - return; + return -1; } if (old_rev) { - if (repo_get_oid(the_repository, old_rev, &old_rev_oid)) { + if (repo_get_oid(the_repository, old_rev, old_oid)) { cgit_print_error_page(404, "Not found", "Bad object id: %s", old_rev); - return; + return -1; } - if (!lookup_commit_reference(the_repository, &old_rev_oid)) { + if (!lookup_commit_reference(the_repository, old_oid)) { cgit_print_error_page(404, "Not found", "Bad commit reference: %s", old_rev); - return; + return -1; } } else if (commit->parents && commit->parents->item) { - oidcpy(&old_rev_oid, &commit->parents->item->object.oid); + oidcpy(old_oid, &commit->parents->item->object.oid); } else { - oidclr(&old_rev_oid, the_repository->hash_algo); + oidclr(old_oid, the_repository->hash_algo); } + return 0; +} + +void cgit_print_patch(const char *new_rev, const char *old_rev, + const char *prefix) +{ + struct rev_info rev; + struct commit *commit; + struct object_id new_oid, old_oid; + char rev_range[REV_RANGE_LEN]; + // setup_revisions reads these the way git reads a command line, so the + // first entry stands in for the program name and is skipped, and the + // array has to stay null terminated because the path after the double + // dash is picked up past the count. + const char *rev_argv[] = { NULL, "--reverse", "--format=email", + rev_range, "--", prefix, NULL }; + int rev_argc = ARRAY_SIZE(rev_argv) - 1; + + if (!prefix) + rev_argc--; + + if (!new_rev) + new_rev = ctx.qry.head; + + if (resolve_range(new_rev, old_rev, &new_oid, &old_oid)) + return; - if (is_null_oid(&old_rev_oid)) { - memcpy(rev_range, oid_to_hex(&new_rev_oid), the_hash_algo->hexsz + 1); + if (is_null_oid(&old_oid)) { + memcpy(rev_range, oid_to_hex(&new_oid), the_hash_algo->hexsz + 1); } else { - xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", oid_to_hex(&old_rev_oid), - oid_to_hex(&new_rev_oid)); + xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", + oid_to_hex(&old_oid), oid_to_hex(&new_oid)); } - patchname = cgit_fmt("%s.patch", rev_range); ctx.page.mimetype = "text/plain"; - ctx.page.filename = patchname; + ctx.page.filename = cgit_fmt("%s.patch", rev_range); cgit_print_http_headers(); if (ctx.cfg.noplainemail) { - rev_argv[2] = "--format=format:From %H Mon Sep 17 00:00:00 " - "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: " - "%s%n%n%w(0)%b"; + rev_argv[FORMAT_ARG] = + "--format=format:From %H Mon Sep 17 00:00:00 " + "2001%nFrom: %an%nDate: %aD%n%w(78,0,1)Subject: " + "%s%n%n%w(0)%b"; } repo_init_revisions(the_repository, &rev, NULL); @@ -89,17 +113,25 @@ void cgit_print_patch(const char *new_rev, const char *old_rev, rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT | DIFF_FORMAT_PATCH | DIFF_FORMAT_SUMMARY; if (prefix) - rev.diffopt.stat_sep = cgit_fmt("(limited to '%s')\n\n", prefix); + // Allocated rather than formatted into cgit_fmt's fixed + // buffer, because the path comes from the request and a long + // one would abort the process here, with the headers for a + // successful response already on the wire. + rev.diffopt.stat_sep = cgit_fmtalloc("(limited to '%s')\n\n", + prefix); setup_revisions(rev_argc, rev_argv, &rev, NULL); - // A single commit resolves to a parent..commit range, so this only - // bounds an explicit id/id2 range and keeps one request from - // emitting a patch for the entire history. + // A single commit resolves to a range starting at its parent, so this + // only ever cuts an explicit range short and keeps one request from + // emitting a patch for the whole history. if (ctx.cfg.max_patch_count > 0) rev.max_count = ctx.cfg.max_patch_count; prepare_revision_walk(&rev); while ((commit = get_revision(&rev)) != NULL) { log_tree_commit(&rev, commit); + // Two dashes and a space is the mail signature separator, so + // git am and mail readers drop the version note below rather + // than carrying it into the commit message. printf("-- \ncgit %s\n\n", cgit_version); } } -- cgit v2.8.0