int git_config_parse_parameter(const char *text) { struct config_item *ct; struct strbuf tmp = STRBUF_INIT; struct strbuf **pair; strbuf_addstr(&tmp, text); pair = strbuf_split(&tmp, '='); if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') strbuf_setlen(pair[0], pair[0]->len - 1); strbuf_trim(pair[0]); if (!pair[0]->len) { strbuf_list_free(pair); return -1; } ct = xcalloc(1, sizeof(struct config_item)); ct->name = strbuf_detach(pair[0], NULL); if (pair[1]) { strbuf_trim(pair[1]); ct->value = strbuf_detach(pair[1], NULL); } strbuf_list_free(pair); lowercase(ct->name); *config_parameters_tail = ct; config_parameters_tail = &ct->next; return 0; }
static void read_bisect_paths(struct argv_array *array) { struct strbuf str = STRBUF_INIT; const char *filename = git_path("BISECT_NAMES"); FILE *fp = fopen(filename, "r"); if (!fp) die_errno("Could not open file '%s'", filename); while (strbuf_getline(&str, fp, '\n') != EOF) { char *quoted; int res; strbuf_trim(&str); quoted = strbuf_detach(&str, NULL); res = sq_dequote_to_argv(quoted, &array->argv, &array->argv_nr, &array->argv_alloc); if (res) die("Badly quoted content in file '%s': %s", filename, quoted); } strbuf_release(&str); fclose(fp); }
/* * Turn * "pick d6a2f0303e897ec257dd0e0a39a5ccb709bc2047 some message" * into * "pick d6a2f03 some message" * * The function assumes that the line does not contain useless spaces * before or after the command. */ static void abbrev_sha1_in_line(struct strbuf *line) { struct strbuf **split; int i; if (starts_with(line->buf, "exec ") || starts_with(line->buf, "x ")) return; split = strbuf_split_max(line, ' ', 3); if (split[0] && split[1]) { unsigned char sha1[20]; const char *abbrev; /* * strbuf_split_max left a space. Trim it and re-add * it after abbreviation. */ strbuf_trim(split[1]); if (!get_sha1(split[1]->buf, sha1)) { abbrev = find_unique_abbrev(sha1, DEFAULT_ABBREV); strbuf_reset(split[1]); strbuf_addf(split[1], "%s ", abbrev); strbuf_reset(line); for (i = 0; split[i]; i++) strbuf_addf(line, "%s", split[i]->buf); } } for (i = 0; split[i]; i++) strbuf_release(split[i]); }
static void parse_bogus_from(const struct strbuf *line) { /* John Doe <johndoe> */ char *bra, *ket; /* This is fallback, so do not bother if we already have an * e-mail address. */ if (email.len) return; bra = strchr(line->buf, '<'); if (!bra) return; ket = strchr(bra, '>'); if (!ket) return; strbuf_reset(&email); strbuf_add(&email, bra + 1, ket - bra - 1); strbuf_reset(&name); strbuf_add(&name, line->buf, bra - line->buf); strbuf_trim(&name); get_sane_name(&name, &name, &email); }
static void cleanup_subject(struct strbuf *subject) { char *pos; size_t remove; while (subject->len) { switch (*subject->buf) { case 'r': case 'R': if (subject->len <= 3) break; if (!memcmp(subject->buf + 1, "e:", 2)) { strbuf_remove(subject, 0, 3); continue; } break; case ' ': case '\t': case ':': strbuf_remove(subject, 0, 1); continue; case '[': if ((pos = strchr(subject->buf, ']'))) { remove = pos - subject->buf; if (remove <= (subject->len - remove) * 2) { strbuf_remove(subject, 0, remove + 1); continue; } } else strbuf_remove(subject, 0, 1); break; } strbuf_trim(subject); return; } }
static int notes_cache_match_validity(struct repository *r, const char *ref, const char *validity) { struct object_id oid; struct commit *commit; struct pretty_print_context pretty_ctx; struct strbuf msg = STRBUF_INIT; int ret; if (read_ref(ref, &oid) < 0) return 0; commit = lookup_commit_reference_gently(r, &oid, 1); if (!commit) return 0; memset(&pretty_ctx, 0, sizeof(pretty_ctx)); format_commit_message(commit, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); ret = !strcmp(msg.buf, validity); strbuf_release(&msg); return ret; }
static int show_http_message(struct strbuf *type, struct strbuf *charset, struct strbuf *msg) { const char *p, *eol; /* * We only show text/plain parts, as other types are likely * to be ugly to look at on the user's terminal. */ if (strcmp(type->buf, "text/plain")) return -1; if (charset->len) strbuf_reencode(msg, charset->buf, get_log_output_encoding()); strbuf_trim(msg); if (!msg->len) return -1; p = msg->buf; do { eol = strchrnul(p, '\n'); fprintf(stderr, "remote: %.*s\n", (int)(eol - p), p); p = eol + 1; } while(*eol); return 0; }
// normalize reference: collapse internal whitespace to single space, // remove leading/trailing whitespace, case fold // Return NULL if the reference name is actually empty (i.e. composed // solely from whitespace) static unsigned char *normalize_reference(chunk *ref) { strbuf normalized = GH_BUF_INIT; unsigned char *result; if(ref == NULL) return NULL; if (ref->len == 0) return NULL; utf8proc_case_fold(&normalized, ref->data, ref->len); strbuf_trim(&normalized); strbuf_normalize_whitespace(&normalized); result = strbuf_detach(&normalized); assert(result); if (result[0] == '\0') { free(result); return NULL; } return result; }
static int show_http_message(struct strbuf *type, struct strbuf *msg) { const char *p, *eol; /* * We only show text/plain parts, as other types are likely * to be ugly to look at on the user's terminal. * * TODO should handle "; charset=XXX", and re-encode into * logoutputencoding */ if (strcasecmp(type->buf, "text/plain")) return -1; strbuf_trim(msg); if (!msg->len) return -1; p = msg->buf; do { eol = strchrnul(p, '\n'); fprintf(stderr, "remote: %.*s\n", (int)(eol - p), p); p = eol + 1; } while(*eol); return 0; }
const char *ident_default_name(void) { if (!git_default_name.len) { copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name); strbuf_trim(&git_default_name); } return git_default_name.buf; }
static void run_shell(void) { int done = 0; static const char *help_argv[] = { HELP_COMMAND, NULL }; /* Print help if enabled */ run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE); do { struct strbuf line = STRBUF_INIT; const char *prog; char *full_cmd; char *rawargs; char *split_args; const char **argv; int code; int count; fprintf(stderr, "git> "); if (strbuf_getline(&line, stdin, '\n') == EOF) { fprintf(stderr, "\n"); strbuf_release(&line); break; } strbuf_trim(&line); rawargs = strbuf_detach(&line, NULL); split_args = xstrdup(rawargs); count = split_cmdline(split_args, &argv); if (count < 0) { fprintf(stderr, "invalid command format '%s': %s\n", rawargs, split_cmdline_strerror(count)); free(split_args); free(rawargs); continue; } prog = argv[0]; if (!strcmp(prog, "")) { } else if (!strcmp(prog, "quit") || !strcmp(prog, "logout") || !strcmp(prog, "exit") || !strcmp(prog, "bye")) { done = 1; } else if (is_valid_cmd_name(prog)) { full_cmd = make_cmd(prog); argv[0] = full_cmd; code = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE); if (code == -1 && errno == ENOENT) { fprintf(stderr, "unrecognized command '%s'\n", prog); } free(full_cmd); } else { fprintf(stderr, "invalid command format '%s'\n", prog); } free(argv); free(rawargs); } while (!done); }
static int is_expected_rev(const char *expected_hex) { struct strbuf actual_hex = STRBUF_INIT; int res = 0; if (strbuf_read_file(&actual_hex, git_path_bisect_expected_rev(), 0) >= 40) { strbuf_trim(&actual_hex); res = !strcmp(actual_hex.buf, expected_hex); } strbuf_release(&actual_hex); return res; }
static int merge_commit(struct notes_merge_options *o) { struct strbuf msg = STRBUF_INIT; unsigned char sha1[20], parent_sha1[20]; struct notes_tree *t; struct commit *partial; struct pretty_print_context pretty_ctx; void *local_ref_to_free; int ret; /* * Read partial merge result from .git/NOTES_MERGE_PARTIAL, * and target notes ref from .git/NOTES_MERGE_REF. */ if (get_sha1("NOTES_MERGE_PARTIAL", sha1)) die("Failed to read ref NOTES_MERGE_PARTIAL"); else if (!(partial = lookup_commit_reference(sha1))) die("Could not find commit from NOTES_MERGE_PARTIAL."); else if (parse_commit(partial)) die("Could not parse commit from NOTES_MERGE_PARTIAL."); if (partial->parents) hashcpy(parent_sha1, partial->parents->item->object.sha1); else hashclr(parent_sha1); t = xcalloc(1, sizeof(struct notes_tree)); init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0); o->local_ref = local_ref_to_free = resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL); if (!o->local_ref) die("Failed to resolve NOTES_MERGE_REF"); if (notes_merge_commit(o, t, partial, sha1)) die("Failed to finalize notes merge"); /* Reuse existing commit message in reflog message */ memset(&pretty_ctx, 0, sizeof(pretty_ctx)); format_commit_message(partial, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); strbuf_insert(&msg, 0, "notes: ", 7); update_ref(msg.buf, o->local_ref, sha1, is_null_sha1(parent_sha1) ? NULL : parent_sha1, 0, UPDATE_REFS_DIE_ON_ERR); free_notes(t); strbuf_release(&msg); ret = merge_abort(o); free(local_ref_to_free); return ret; }
/** * xargs_read: read a record from xargs stream * * @param[in] xp xargs structure * @return result line */ char * xargs_read(XARGS *xp) { assert(xp != NULL); if (xp->end_of_arg) return NULL; if (xp->unread) { xp->unread = 0; return strbuf_value(xp->result); } if (xp->pipe && strbuf_fgets(xp->result, xp->pipe, STRBUF_NOCRLF) != NULL) { if (xp->trim_line) strbuf_trim(xp->result); return strbuf_value(xp->result); } if (xp->pipe) if (pclose(xp->pipe) != 0 && !xp->ignore_error) die("command failed in xargs_read()."); /* * Switch to the next segment. */ do { xp->pipe = execute_command(xp); if (xp->pipe && strbuf_fgets(xp->result, xp->pipe, STRBUF_NOCRLF) != NULL) { if (xp->trim_line) strbuf_trim(xp->result); return strbuf_value(xp->result); } if (xp->pipe) { if (pclose(xp->pipe) != 0 && !xp->ignore_error) die("command failed in xargs_read()."); } else { xp->end_of_arg = 1; } } while (!xp->end_of_arg); return NULL; }
static void cleanup_subject(struct mailinfo *mi, struct strbuf *subject) { size_t at = 0; while (at < subject->len) { char *pos; size_t remove; switch (subject->buf[at]) { case 'r': case 'R': if (subject->len <= at + 3) break; if ((subject->buf[at + 1] == 'e' || subject->buf[at + 1] == 'E') && subject->buf[at + 2] == ':') { strbuf_remove(subject, at, 3); continue; } at++; break; case ' ': case '\t': case ':': strbuf_remove(subject, at, 1); continue; case '[': pos = strchr(subject->buf + at, ']'); if (!pos) break; remove = pos - subject->buf + at + 1; if (!mi->keep_non_patch_brackets_in_subject || (7 <= remove && memmem(subject->buf + at, remove, "PATCH", 5))) strbuf_remove(subject, at, remove); else { at += remove; /* * If the input had a space after the ], keep * it. We don't bother with finding the end of * the space, since we later normalize it * anyway. */ if (isspace(subject->buf[at])) at += 1; } continue; } break; } strbuf_trim(subject); }
/* * NEEDSWORK: this function can go once the legacy-difftool Perl script is * retired. * * We intentionally avoid reading the config directly here, to avoid messing up * the GIT_* environment variables when we need to fall back to exec()ing the * Perl script. */ static int use_builtin_difftool(void) { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf out = STRBUF_INIT; int ret; argv_array_pushl(&cp.args, "config", "--bool", "difftool.usebuiltin", NULL); cp.git_cmd = 1; if (capture_command(&cp, &out, 6)) return 0; strbuf_trim(&out); ret = !strcmp("true", out.buf); strbuf_release(&out); return ret; }
static char *find_linked_symref(const char *symref, const char *branch, const char *id) { struct strbuf sb = STRBUF_INIT; struct strbuf path = STRBUF_INIT; struct strbuf gitdir = STRBUF_INIT; char *existing = NULL; /* * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside * $GIT_DIR so resolve_ref_unsafe() won't work (it uses * git_path). Parse the ref ourselves. */ if (id) strbuf_addf(&path, "%s/worktrees/%s/%s", get_git_common_dir(), id, symref); else strbuf_addf(&path, "%s/%s", get_git_common_dir(), symref); if (!strbuf_readlink(&sb, path.buf, 0)) { if (!starts_with(sb.buf, "refs/") || check_refname_format(sb.buf, 0)) goto done; } else if (strbuf_read_file(&sb, path.buf, 0) >= 0 && starts_with(sb.buf, "ref:")) { strbuf_remove(&sb, 0, strlen("ref:")); strbuf_trim(&sb); } else goto done; if (strcmp(sb.buf, branch)) goto done; if (id) { strbuf_reset(&path); strbuf_addf(&path, "%s/worktrees/%s/gitdir", get_git_common_dir(), id); if (strbuf_read_file(&gitdir, path.buf, 0) <= 0) goto done; strbuf_rtrim(&gitdir); } else strbuf_addstr(&gitdir, get_git_common_dir()); strbuf_strip_suffix(&gitdir, ".git"); existing = strbuf_detach(&gitdir, NULL); done: strbuf_release(&path); strbuf_release(&sb); strbuf_release(&gitdir); return existing; }
const char *ident_default_email(void) { if (!git_default_email.len) { const char *email = getenv("EMAIL"); if (email && email[0]) { strbuf_addstr(&git_default_email, email); committer_ident_explicitly_given |= IDENT_MAIL_GIVEN; author_ident_explicitly_given |= IDENT_MAIL_GIVEN; } else copy_email(xgetpwuid_self(&default_email_is_bogus), &git_default_email, &default_email_is_bogus); strbuf_trim(&git_default_email); } return git_default_email.buf; }
static void read_rebase_todolist(const char *fname, struct string_list *lines) { struct strbuf line = STRBUF_INIT; FILE *f = fopen(git_path("%s", fname), "r"); if (!f) die_errno("Could not open file %s for reading", git_path("%s", fname)); while (!strbuf_getline(&line, f, '\n')) { if (line.len && line.buf[0] == comment_line_char) continue; strbuf_trim(&line); if (!line.len) continue; abbrev_sha1_in_line(&line); string_list_append(lines, line.buf); } }
static void read_bisect_paths(struct argv_array *array) { struct strbuf str = STRBUF_INIT; const char *filename = git_path_bisect_names(); FILE *fp = fopen(filename, "r"); if (!fp) die_errno("Could not open file '%s'", filename); while (strbuf_getline(&str, fp, '\n') != EOF) { strbuf_trim(&str); if (sq_dequote_to_argv_array(str.buf, array)) die("Badly quoted content in file '%s': %s", filename, str.buf); } strbuf_release(&str); fclose(fp); }
int git_config_parse_parameter(const char *text, config_fn_t fn, void *data) { struct strbuf **pair; pair = strbuf_split_str(text, '=', 2); if (!pair[0]) return error("bogus config parameter: %s", text); if (pair[0]->len && pair[0]->buf[pair[0]->len - 1] == '=') strbuf_setlen(pair[0], pair[0]->len - 1); strbuf_trim(pair[0]); if (!pair[0]->len) { strbuf_list_free(pair); return error("bogus config parameter: %s", text); } lowercase(pair[0]->buf); if (fn(pair[0]->buf, pair[1] ? pair[1]->buf : NULL, data) < 0) { strbuf_list_free(pair); return -1; } strbuf_list_free(pair); return 0; }
const char *worktree_lock_reason(struct worktree *wt) { assert(!is_main_worktree(wt)); if (!wt->lock_reason_valid) { struct strbuf path = STRBUF_INIT; strbuf_addstr(&path, worktree_git_path(wt, "locked")); if (file_exists(path.buf)) { struct strbuf lock_reason = STRBUF_INIT; if (strbuf_read_file(&lock_reason, path.buf, 0) < 0) die_errno(_("failed to read '%s'"), path.buf); strbuf_trim(&lock_reason); wt->lock_reason = strbuf_detach(&lock_reason, NULL); } else wt->lock_reason = NULL; wt->lock_reason_valid = 1; strbuf_release(&path); } return wt->lock_reason; }
static int use_builtin_stash(void) { struct child_process cp = CHILD_PROCESS_INIT; struct strbuf out = STRBUF_INIT; int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1); if (env != -1) return env; argv_array_pushl(&cp.args, "config", "--bool", "stash.usebuiltin", NULL); cp.git_cmd = 1; if (capture_command(&cp, &out, 6)) { strbuf_release(&out); return 1; } strbuf_trim(&out); ret = !strcmp("true", out.buf); strbuf_release(&out); return ret; }
static int rollback_is_safe(void) { struct strbuf sb = STRBUF_INIT; struct object_id expected_head, actual_head; if (strbuf_read_file(&sb, git_path_abort_safety_file(), 0) >= 0) { strbuf_trim(&sb); if (get_oid_hex(sb.buf, &expected_head)) { strbuf_release(&sb); die(_("could not parse %s"), git_path_abort_safety_file()); } strbuf_release(&sb); } else if (errno == ENOENT) oidclr(&expected_head); else die_errno(_("could not read '%s'"), git_path_abort_safety_file()); if (get_oid("HEAD", &actual_head)) oidclr(&actual_head); return !oidcmp(&actual_head, &expected_head); }
/* * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL, * set is_detached to 1 (0) if the ref is detached (is not detached). * * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses * git_path). Parse the ref ourselves. * * return -1 if the ref is not a proper ref, 0 otherwise (success) */ static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached) { if (is_detached) *is_detached = 0; if (!strbuf_readlink(ref, path_to_ref, 0)) { /* HEAD is symbolic link */ if (!starts_with(ref->buf, "refs/") || check_refname_format(ref->buf, 0)) return -1; } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) { /* textual symref or detached */ if (!starts_with(ref->buf, "ref:")) { if (is_detached) *is_detached = 1; } else { strbuf_remove(ref, 0, strlen("ref:")); strbuf_trim(ref); if (check_refname_format(ref->buf, 0)) return -1; } } else return -1; return 0; }
static int notes_cache_match_validity(const char *ref, const char *validity) { unsigned char sha1[20]; struct commit *commit; struct pretty_print_context pretty_ctx; struct strbuf msg = STRBUF_INIT; int ret; if (read_ref(ref, sha1) < 0) return 0; commit = lookup_commit_reference_gently(sha1, 1); if (!commit) return 0; memset(&pretty_ctx, 0, sizeof(pretty_ctx)); format_commit_message(commit, "%s", &msg, &pretty_ctx); strbuf_trim(&msg); ret = !strcmp(msg.buf, validity); strbuf_release(&msg); return ret; }
static void handle_from(const struct strbuf *from) { char *at; size_t el; struct strbuf f; strbuf_init(&f, from->len); strbuf_addbuf(&f, from); at = strchr(f.buf, '@'); if (!at) { parse_bogus_from(from); return; } /* * If we already have one email, don't take any confusing lines */ if (email.len && strchr(at + 1, '@')) { strbuf_release(&f); return; } /* Pick up the string around '@', possibly delimited with <> * pair; that is the email part. */ while (at > f.buf) { char c = at[-1]; if (isspace(c)) break; if (c == '<') { at[-1] = ' '; break; } at--; } el = strcspn(at, " \n\t\r\v\f>"); strbuf_reset(&email); strbuf_add(&email, at, el); strbuf_remove(&f, at - f.buf, el + (at[el] ? 1 : 0)); /* The remainder is name. It could be * * - "John Doe <john.doe@xz>" (a), or * - "john.doe@xz (John Doe)" (b), or * - "John (zzz) Doe <john.doe@xz> (Comment)" (c) * * but we have removed the email part, so * * - remove extra spaces which could stay after email (case 'c'), and * - trim from both ends, possibly removing the () pair at the end * (cases 'a' and 'b'). */ cleanup_space(&f); strbuf_trim(&f); if (f.buf[0] == '(' && f.len && f.buf[f.len - 1] == ')') { strbuf_remove(&f, 0, 1); strbuf_setlen(&f, f.len - 1); } get_sane_name(&name, &f, &email); strbuf_release(&f); }
static void finalize(cmark_doc_parser *parser, cmark_node* b, int line_number) { int firstlinelen; int pos; cmark_node* item; cmark_node* subitem; if (!b->open) return; // don't do anything if the cmark_node is already closed b->open = false; if (line_number > b->start_line) { b->end_line = line_number - 1; } else { b->end_line = line_number; } switch (b->type) { case NODE_PARAGRAPH: pos = 0; while (strbuf_at(&b->string_content, 0) == '[' && (pos = parse_reference_inline(&b->string_content, parser->refmap))) { strbuf_drop(&b->string_content, pos); } if (is_blank(&b->string_content, 0)) { b->type = NODE_REFERENCE_DEF; } break; case NODE_INDENTED_CODE: remove_trailing_blank_lines(&b->string_content); strbuf_putc(&b->string_content, '\n'); break; case NODE_FENCED_CODE: // first line of contents becomes info firstlinelen = strbuf_strchr(&b->string_content, '\n', 0); strbuf_init(&b->as.code.info, 0); houdini_unescape_html_f( &b->as.code.info, b->string_content.ptr, firstlinelen ); strbuf_drop(&b->string_content, firstlinelen + 1); strbuf_trim(&b->as.code.info); strbuf_unescape(&b->as.code.info); break; case NODE_LIST: // determine tight/loose status b->as.list.tight = true; // tight by default item = b->first_child; while (item) { // check for non-final non-empty list item ending with blank line: if (item->last_line_blank && item->next) { b->as.list.tight = false; break; } // recurse into children of list item, to see if there are // spaces between them: subitem = item->first_child; while (subitem) { if (ends_with_blank_line(subitem) && (item->next || subitem->next)) { b->as.list.tight = false; break; } subitem = subitem->next; } if (!(b->as.list.tight)) { break; } item = item->next; } break; default: break; } }
void print_commit(struct commit *commit, struct rev_info *revs) { struct commitinfo *info; int cols = revs->graph ? 3 : 2; struct strbuf graphbuf = STRBUF_INIT; struct strbuf msgbuf = STRBUF_INIT; if (ctx.repo->enable_log_filecount) cols++; if (ctx.repo->enable_log_linecount) cols++; if (revs->graph) { /* Advance graph until current commit */ while (!graph_next_line(revs->graph, &graphbuf)) { /* Print graph segment in otherwise empty table row */ html("<tr class='nohover'><td class='commitgraph'>"); html(graphbuf.buf); htmlf("</td><td colspan='%d' /></tr>\n", cols); strbuf_setlen(&graphbuf, 0); } /* Current commit's graph segment is now ready in graphbuf */ } info = cgit_parse_commit(commit); htmlf("<tr%s>", ctx.qry.showmsg ? " class='logheader'" : ""); if (revs->graph) { /* Print graph segment for current commit */ html("<td class='commitgraph'>"); html(graphbuf.buf); html("</td>"); strbuf_setlen(&graphbuf, 0); } else { html("<td>"); cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); html("</td>"); } htmlf("<td%s>", ctx.qry.showmsg ? " class='logsubject'" : ""); if (ctx.qry.showmsg) { /* line-wrap long commit subjects instead of truncating them */ size_t subject_len = strlen(info->subject); if (subject_len > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) { /* symbol for signaling line-wrap (in PAGE_ENCODING) */ const char wrap_symbol[] = { ' ', 0xE2, 0x86, 0xB5, 0 }; int i = ctx.cfg.max_msg_len - strlen(wrap_symbol); /* Rewind i to preceding space character */ while (i > 0 && !isspace(info->subject[i])) --i; if (!i) /* Oops, zero spaces. Reset i */ i = ctx.cfg.max_msg_len - strlen(wrap_symbol); /* add remainder starting at i to msgbuf */ strbuf_add(&msgbuf, info->subject + i, subject_len - i); strbuf_trim(&msgbuf); strbuf_add(&msgbuf, "\n\n", 2); /* Place wrap_symbol at position i in info->subject */ strcpy(info->subject + i, wrap_symbol); } } cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head, sha1_to_hex(commit->object.sha1), ctx.qry.vpath, 0); show_commit_decorations(commit); html("</td><td>"); html_txt(info->author); if (revs->graph) { html("</td><td>"); cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE); } if (ctx.repo->enable_log_filecount || ctx.repo->enable_log_linecount) { files = 0; add_lines = 0; rem_lines = 0; cgit_diff_commit(commit, inspect_files, ctx.qry.vpath); } if (ctx.repo->enable_log_filecount) htmlf("</td><td>%d", files); if (ctx.repo->enable_log_linecount) htmlf("</td><td>-%d/+%d", rem_lines, add_lines); html("</td></tr>\n"); if (revs->graph || ctx.qry.showmsg) { /* Print a second table row */ html("<tr class='nohover'>"); if (ctx.qry.showmsg) { /* Concatenate commit message + notes in msgbuf */ if (info->msg && *(info->msg)) { strbuf_addstr(&msgbuf, info->msg); strbuf_addch(&msgbuf, '\n'); } format_note(NULL, commit->object.sha1, &msgbuf, PAGE_ENCODING, NOTES_SHOW_HEADER | NOTES_INDENT); strbuf_addch(&msgbuf, '\n'); strbuf_ltrim(&msgbuf); } if (revs->graph) { int lines = 0; /* Calculate graph padding */ if (ctx.qry.showmsg) { /* Count #lines in commit message + notes */ const char *p = msgbuf.buf; lines = 1; while ((p = strchr(p, '\n'))) { p++; lines++; } } /* Print graph padding */ html("<td class='commitgraph'>"); while (lines > 0 || !graph_is_commit_finished(revs->graph)) { if (graphbuf.len) html("\n"); strbuf_setlen(&graphbuf, 0); graph_next_line(revs->graph, &graphbuf); html(graphbuf.buf); lines--; } html("</td>\n"); } else html("<td/>"); /* Empty 'Age' column */ /* Print msgbuf into remainder of table row */ htmlf("<td colspan='%d'%s>\n", cols, ctx.qry.showmsg ? " class='logmsg'" : ""); html_txt(msgbuf.buf); html("</td></tr>\n"); } strbuf_release(&msgbuf); strbuf_release(&graphbuf); cgit_free_commitinfo(info); }
static int parse_commit(struct strbuf* input, struct string_list* files, struct strbuf* message) { int err; int pos; struct strbuf line; int lineno = 0; enum { BEGIN, FILES, MESSAGE } state = BEGIN; enum { OK, FAILED } result = OK; // if there is no data, just return if (input->len == 0) { return 0; } strbuf_init(&line, 0); pos = 0; while (result == OK) { err = get_strbuf_line(input, &pos, &line); lineno++; if (err != 0) { break; } if (is_comment(&line)) { continue; } switch (state) { case BEGIN: strbuf_trim(&line); if (line.len != 0) { if (0 == strcmp(line.buf, "Files:")) { state = FILES; } else { result = FAILED; } } break; case FILES: strbuf_trim(&line); if (line.len != 0) { if (0 == strcmp(line.buf, "Commit Message:")) { state = MESSAGE; } else if (0 == strip_file_info(&line)) { string_list_insert(line.buf, files); } else { result = FAILED; } } break; case MESSAGE: strbuf_rtrim(&line); strbuf_addbuf(message, &line); strbuf_addch(message, '\n'); break; } } strbuf_trim(message); return result == OK ? 0 : 1; }