From: Junio C Hamano Date: Fri, 29 Jan 2016 00:10:14 +0000 (-0800) Subject: Merge branch 'jc/strbuf-getline' X-Git-Tag: v2.8.0-rc0~91 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/b62624b51a54325e6884c197352dc0dcef700325?hp=-c Merge branch 'jc/strbuf-getline' The preliminary clean-up for jc/peace-with-crlf topic. * jc/strbuf-getline: strbuf: give strbuf_getline() to the "most text friendly" variant checkout-index: there are only two possible line terminations update-index: there are only two possible line terminations check-ignore: there are only two possible line terminations check-attr: there are only two possible line terminations mktree: there are only two possible line terminations strbuf: introduce strbuf_getline_{lf,nul}() strbuf: make strbuf_getline_crlf() global strbuf: miniscule style fix --- b62624b51a54325e6884c197352dc0dcef700325 diff --combined builtin/am.c index de235cf11a,7b8351dede..b55f903f05 --- a/builtin/am.c +++ b/builtin/am.c @@@ -45,21 -45,6 +45,6 @@@ static int is_empty_file(const char *fi return !st.st_size; } - /** - * Like strbuf_getline(), but treats both '\n' and "\r\n" as line terminators. - */ - static int strbuf_getline_crlf(struct strbuf *sb, FILE *fp) - { - if (strbuf_getwholeline(sb, fp, '\n')) - return EOF; - if (sb->buf[sb->len - 1] == '\n') { - strbuf_setlen(sb, sb->len - 1); - if (sb->len > 0 && sb->buf[sb->len - 1] == '\r') - strbuf_setlen(sb, sb->len - 1); - } - return 0; - } - /** * Returns the length of the first line of msg. */ @@@ -284,7 -269,7 +269,7 @@@ static char *read_shell_var(FILE *fp, c struct strbuf sb = STRBUF_INIT; const char *str; - if (strbuf_getline(&sb, fp, '\n')) + if (strbuf_getline_lf(&sb, fp)) goto fail; if (!skip_prefix(sb.buf, key, &str)) @@@ -573,7 -558,7 +558,7 @@@ static int copy_notes_for_rebase(const fp = xfopen(am_path(state, "rewritten"), "r"); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { unsigned char from_obj[GIT_SHA1_RAWSZ], to_obj[GIT_SHA1_RAWSZ]; if (sb.len != GIT_SHA1_HEXSZ * 2 + 1) { @@@ -628,7 -613,7 +613,7 @@@ static int is_mail(FILE *fp if (regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED)) die("invalid pattern: %s", header_regex); - while (!strbuf_getline_crlf(&sb, fp)) { + while (!strbuf_getline(&sb, fp)) { if (!sb.len) break; /* End of header */ @@@ -675,7 -660,7 +660,7 @@@ static int detect_patch_format(const ch fp = xfopen(*paths, "r"); - while (!strbuf_getline_crlf(&l1, fp)) { + while (!strbuf_getline(&l1, fp)) { if (l1.len) break; } @@@ -696,9 -681,9 +681,9 @@@ } strbuf_reset(&l2); - strbuf_getline_crlf(&l2, fp); + strbuf_getline(&l2, fp); strbuf_reset(&l3); - strbuf_getline_crlf(&l3, fp); + strbuf_getline(&l3, fp); /* * If the second line is empty and the third is a From, Author or Date @@@ -817,7 -802,7 +802,7 @@@ static int stgit_patch_to_mail(FILE *ou struct strbuf sb = STRBUF_INIT; int subject_printed = 0; - while (!strbuf_getline(&sb, in, '\n')) { + while (!strbuf_getline_lf(&sb, in)) { const char *str; if (str_isspace(sb.buf)) @@@ -875,7 -860,7 +860,7 @@@ static int split_mail_stgit_series(stru return error(_("could not open '%s' for reading: %s"), *paths, strerror(errno)); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { if (*sb.buf == '#') continue; /* skip comment lines */ @@@ -900,7 -885,7 +885,7 @@@ static int hg_patch_to_mail(FILE *out, { struct strbuf sb = STRBUF_INIT; - while (!strbuf_getline(&sb, in, '\n')) { + while (!strbuf_getline_lf(&sb, in)) { const char *str; if (skip_prefix(sb.buf, "# User ", &str)) @@@ -1317,7 -1302,7 +1302,7 @@@ static int parse_mail(struct am_state * /* Extract message and author information */ fp = xfopen(am_path(state, "info"), "r"); - while (!strbuf_getline(&sb, fp, '\n')) { + while (!strbuf_getline_lf(&sb, fp)) { const char *x; if (skip_prefix(sb.buf, "Subject: ", &x)) { @@@ -1383,7 -1368,7 +1368,7 @@@ static int get_mail_commit_sha1(unsigne FILE *fp = xfopen(mail, "r"); const char *x; - if (strbuf_getline(&sb, fp, '\n')) + if (strbuf_getline_lf(&sb, fp)) return -1; if (!skip_prefix(sb.buf, "From ", &x)) @@@ -1939,7 -1924,6 +1924,7 @@@ next */ if (!state->rebasing) { am_destroy(state); + close_all_packs(); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); } } diff --combined builtin/clone.c index a7c8def8cb,29741f4446..81e238f738 --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -339,7 -339,7 +339,7 @@@ static void copy_alternates(struct strb FILE *in = fopen(src->buf, "r"); struct strbuf line = STRBUF_INIT; - while (strbuf_getline(&line, in, '\n') != EOF) { + while (strbuf_getline_lf(&line, in) != EOF) { char *abs_path; if (!line.len || line.buf[0] == '#') continue; @@@ -636,11 -636,9 +636,11 @@@ static void update_remote_refs(const st struct strbuf head_ref = STRBUF_INIT; strbuf_addstr(&head_ref, branch_top); strbuf_addstr(&head_ref, "HEAD"); - create_symref(head_ref.buf, - remote_head_points_at->peer_ref->name, - msg); + if (create_symref(head_ref.buf, + remote_head_points_at->peer_ref->name, + msg) < 0) + die("unable to update %s", head_ref.buf); + strbuf_release(&head_ref); } } @@@ -650,8 -648,7 +650,8 @@@ static void update_head(const struct re const char *head; if (our && skip_prefix(our->name, "refs/heads/", &head)) { /* Local default branch link */ - create_symref("HEAD", our->name, NULL); + if (create_symref("HEAD", our->name, NULL) < 0) + die("unable to update HEAD"); if (!option_bare) { update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0, UPDATE_REFS_DIE_ON_ERR); diff --combined builtin/commit.c index 89bf6ad38a,d9db59e1d2..b3bd2d4181 --- a/builtin/commit.c +++ b/builtin/commit.c @@@ -761,7 -761,7 +761,7 @@@ static int prepare_to_commit(const cha hook_arg2 = ""; } - s->fp = fopen(git_path(commit_editmsg), "w"); + s->fp = fopen_for_writing(git_path(commit_editmsg)); if (s->fp == NULL) die_errno(_("could not open '%s'"), git_path(commit_editmsg)); @@@ -1690,7 -1690,7 +1690,7 @@@ int cmd_commit(int argc, const char **a if (fp == NULL) die_errno(_("could not open '%s' for reading"), git_path_merge_head()); - while (strbuf_getline(&m, fp, '\n') != EOF) { + while (strbuf_getline_lf(&m, fp) != EOF) { struct commit *parent; parent = get_merge_parent(m.buf); diff --combined builtin/grep.c index 5526fd7056,5a5beb8109..6c030dad6b --- a/builtin/grep.c +++ b/builtin/grep.c @@@ -24,11 -24,11 +24,11 @@@ static char const * const grep_usage[] NULL }; -static int use_threads = 1; +#define GREP_NUM_THREADS_DEFAULT 8 +static int num_threads; #ifndef NO_PTHREADS -#define THREADS 8 -static pthread_t threads[THREADS]; +static pthread_t *threads; /* We use one producer thread and THREADS consumer * threads. The producer adds struct work_items to 'todo' and the @@@ -63,13 -63,13 +63,13 @@@ static pthread_mutex_t grep_mutex static inline void grep_lock(void) { - if (use_threads) + if (num_threads) pthread_mutex_lock(&grep_mutex); } static inline void grep_unlock(void) { - if (use_threads) + if (num_threads) pthread_mutex_unlock(&grep_mutex); } @@@ -206,8 -206,7 +206,8 @@@ static void start_threads(struct grep_o strbuf_init(&todo[i].out, 0); } - for (i = 0; i < ARRAY_SIZE(threads); i++) { + threads = xcalloc(num_threads, sizeof(*threads)); + for (i = 0; i < num_threads; i++) { int err; struct grep_opt *o = grep_opt_dup(opt); o->output = strbuf_out; @@@ -239,14 -238,12 +239,14 @@@ static int wait_all(void pthread_cond_broadcast(&cond_add); grep_unlock(); - for (i = 0; i < ARRAY_SIZE(threads); i++) { + for (i = 0; i < num_threads; i++) { void *h; pthread_join(threads[i], &h); hit |= (int) (intptr_t) h; } + free(threads); + pthread_mutex_destroy(&grep_mutex); pthread_mutex_destroy(&grep_read_mutex); pthread_mutex_destroy(&grep_attr_mutex); @@@ -270,14 -267,6 +270,14 @@@ static int grep_cmd_config(const char * int st = grep_config(var, value, cb); if (git_color_default_config(var, value, cb) < 0) st = -1; + + if (!strcmp(var, "grep.threads")) { + num_threads = git_config_int(var, value); + if (num_threads < 0) + die(_("invalid number of threads specified (%d) for %s"), + num_threads, var); + } + return st; } @@@ -305,7 -294,7 +305,7 @@@ static int grep_sha1(struct grep_opt *o } #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1); strbuf_release(&pathbuf); return 0; @@@ -334,7 -323,7 +334,7 @@@ static int grep_file(struct grep_opt *o strbuf_addstr(&buf, filename); #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { add_work(opt, GREP_SOURCE_FILE, buf.buf, filename, filename); strbuf_release(&buf); return 0; @@@ -386,7 -375,7 +386,7 @@@ static int grep_cache(struct grep_opt * for (nr = 0; nr < active_nr; nr++) { const struct cache_entry *ce = active_cache[nr]; - if (!S_ISREG(ce->ce_mode)) + if (!S_ISREG(ce->ce_mode) || ce_intent_to_add(ce)) continue; if (!ce_path_match(ce, pathspec, NULL)) continue; @@@ -573,7 -562,7 +573,7 @@@ static int file_callback(const struct o patterns = from_stdin ? stdin : fopen(arg, "r"); if (!patterns) die_errno(_("cannot open '%s'"), arg); - while (strbuf_getline(&sb, patterns, '\n') == 0) { + while (strbuf_getline_lf(&sb, patterns) == 0) { /* ignore empty line like grep does */ if (sb.len == 0) continue; @@@ -708,8 -697,6 +708,8 @@@ int cmd_grep(int argc, const char **arg N_("show context lines before matches")), OPT_INTEGER('A', "after-context", &opt.post_context, N_("show context lines after matches")), + OPT_INTEGER(0, "threads", &num_threads, + N_("use worker threads")), OPT_NUMBER_CALLBACK(&opt, N_("shortcut for -C NUM"), context_callback), OPT_BOOL('p', "show-function", &opt.funcname, @@@ -768,15 -755,9 +768,15 @@@ PARSE_OPT_STOP_AT_NON_OPTION); grep_commit_pattern_type(pattern_type_arg, &opt); - if (use_index && !startup_info->have_repository) - /* die the same way as if we did it at the beginning */ - setup_git_directory(); + if (use_index && !startup_info->have_repository) { + int fallback = 0; + git_config_get_bool("grep.fallbacktonoindex", &fallback); + if (fallback) + use_index = 0; + else + /* die the same way as if we did it at the beginning */ + setup_git_directory(); + } /* * skip a -- separator; we know it cannot be @@@ -805,6 -786,7 +805,6 @@@ opt.output_priv = &path_list; opt.output = append_path; string_list_append(&path_list, show_in_pager); - use_threads = 0; } if (!opt.pattern_list) @@@ -835,18 -817,14 +835,18 @@@ } #ifndef NO_PTHREADS - if (list.nr || cached || online_cpus() == 1) - use_threads = 0; + if (list.nr || cached || show_in_pager) + num_threads = 0; + else if (num_threads == 0) + num_threads = GREP_NUM_THREADS_DEFAULT; + else if (num_threads < 0) + die(_("invalid number of threads specified (%d)"), num_threads); #else - use_threads = 0; + num_threads = 0; #endif #ifndef NO_PTHREADS - if (use_threads) { + if (num_threads) { if (!(opt.name_only || opt.unmatch_name_only || opt.count) && (opt.pre_context || opt.post_context || opt.file_break || opt.funcbody)) @@@ -916,7 -894,7 +916,7 @@@ hit = grep_objects(&opt, &pathspec, &list); } - if (use_threads) + if (num_threads) hit |= wait_all(); if (hit && show_in_pager) run_pager(&opt, prefix); diff --combined builtin/notes.c index e1556896f9,3775e38963..9d54934d6c --- a/builtin/notes.c +++ b/builtin/notes.c @@@ -286,11 -286,11 +286,11 @@@ static int notes_copy_from_stdin(int fo if (!c) return 0; } else { - init_notes(NULL, NULL, NULL, 0); + init_notes(NULL, NULL, NULL, NOTES_INIT_WRITABLE); t = &default_notes_tree; } - while (strbuf_getline(&buf, stdin, '\n') != EOF) { + while (strbuf_getline_lf(&buf, stdin) != EOF) { unsigned char from_obj[20], to_obj[20]; struct strbuf **split; int err; @@@ -329,18 -329,15 +329,18 @@@ return ret; } -static struct notes_tree *init_notes_check(const char *subcommand) +static struct notes_tree *init_notes_check(const char *subcommand, + int flags) { struct notes_tree *t; - init_notes(NULL, NULL, NULL, 0); + const char *ref; + init_notes(NULL, NULL, NULL, flags); t = &default_notes_tree; - if (!starts_with(t->ref, "refs/notes/")) + ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref; + if (!starts_with(ref, "refs/notes/")) die("Refusing to %s notes in %s (outside of refs/notes/)", - subcommand, t->ref); + subcommand, ref); return t; } @@@ -363,7 -360,7 +363,7 @@@ static int list(int argc, const char ** usage_with_options(git_notes_list_usage, options); } - t = init_notes_check("list"); + t = init_notes_check("list", 0); if (argc) { if (get_sha1(argv[0], object)) die(_("Failed to resolve '%s' as a valid ref."), argv[0]); @@@ -423,7 -420,7 +423,7 @@@ static int add(int argc, const char **a if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("add"); + t = init_notes_check("add", NOTES_INIT_WRITABLE); note = get_note(t, object); if (note) { @@@ -514,7 -511,7 +514,7 @@@ static int copy(int argc, const char ** if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("copy"); + t = init_notes_check("copy", NOTES_INIT_WRITABLE); note = get_note(t, object); if (note) { @@@ -592,7 -589,7 +592,7 @@@ static int append_edit(int argc, const if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check(argv[0]); + t = init_notes_check(argv[0], NOTES_INIT_WRITABLE); note = get_note(t, object); prepare_note_data(object, &d, edit ? note : NULL); @@@ -655,7 -652,7 +655,7 @@@ static int show(int argc, const char ** if (get_sha1(object_ref, object)) die(_("Failed to resolve '%s' as a valid ref."), object_ref); - t = init_notes_check("show"); + t = init_notes_check("show", 0); note = get_note(t, object); if (!note) @@@ -812,7 -809,7 +812,7 @@@ static int merge(int argc, const char * expand_notes_ref(&remote_ref); o.remote_ref = remote_ref.buf; - t = init_notes_check("merge"); + t = init_notes_check("merge", NOTES_INIT_WRITABLE); if (strategy) { if (parse_notes_merge_strategy(strategy, &o.strategy)) { @@@ -904,7 -901,7 +904,7 @@@ static int remove_cmd(int argc, const c argc = parse_options(argc, argv, prefix, options, git_notes_remove_usage, 0); - t = init_notes_check("remove"); + t = init_notes_check("remove", NOTES_INIT_WRITABLE); if (!argc && !from_stdin) { retval = remove_one_note(t, "HEAD", flag); @@@ -946,7 -943,7 +946,7 @@@ static int prune(int argc, const char * usage_with_options(git_notes_prune_usage, options); } - t = init_notes_check("prune"); + t = init_notes_check("prune", NOTES_INIT_WRITABLE); prune_notes(t, (verbose ? NOTES_PRUNE_VERBOSE : 0) | (show_only ? NOTES_PRUNE_VERBOSE|NOTES_PRUNE_DRYRUN : 0) ); diff --combined builtin/pull.c index c713fe065b,52606a84ce..10eff03967 --- a/builtin/pull.c +++ b/builtin/pull.c @@@ -22,8 -22,7 +22,8 @@@ enum rebase_type REBASE_INVALID = -1, REBASE_FALSE = 0, REBASE_TRUE, - REBASE_PRESERVE + REBASE_PRESERVE, + REBASE_INTERACTIVE }; /** @@@ -43,8 -42,6 +43,8 @@@ static enum rebase_type parse_config_re return REBASE_TRUE; else if (!strcmp(value, "preserve")) return REBASE_PRESERVE; + else if (!strcmp(value, "interactive")) + return REBASE_INTERACTIVE; if (fatal) die(_("Invalid value for %s: %s"), key, value); @@@ -98,7 -95,6 +98,7 @@@ static int opt_force static char *opt_tags; static char *opt_prune; static char *opt_recurse_submodules; +static char *max_children; static int opt_dry_run; static char *opt_keep; static char *opt_depth; @@@ -116,7 -112,7 +116,7 @@@ static struct option pull_options[] = /* Options passed to git-merge or git-rebase */ OPT_GROUP(N_("Options related to merging")), { OPTION_CALLBACK, 'r', "rebase", &opt_rebase, - "false|true|preserve", + "false|true|preserve|interactive", N_("incorporate changes by rebasing rather than merging"), PARSE_OPT_OPTARG, parse_opt_rebase }, OPT_PASSTHRU('n', NULL, &opt_diffstat, NULL, @@@ -182,9 -178,6 +182,9 @@@ N_("on-demand"), N_("control recursive fetching of submodules"), PARSE_OPT_OPTARG), + OPT_PASSTHRU('j', "jobs", &max_children, N_("n"), + N_("number of submodules pulled in parallel"), + PARSE_OPT_OPTARG), OPT_BOOL(0, "dry-run", &opt_dry_run, N_("dry run")), OPT_PASSTHRU('k', "keep", &opt_keep, NULL, @@@ -385,7 -378,7 +385,7 @@@ static void get_merge_heads(struct sha1 if (!(fp = fopen(filename, "r"))) die_errno(_("could not open '%s' for reading"), filename); - while (strbuf_getline(&sb, fp, '\n') != EOF) { + while (strbuf_getline_lf(&sb, fp) != EOF) { if (get_sha1_hex(sb.buf, sha1)) continue; /* invalid line: does not start with SHA1 */ if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t")) @@@ -532,8 -525,6 +532,8 @@@ static int run_fetch(const char *repo, argv_array_push(&args, opt_prune); if (opt_recurse_submodules) argv_array_push(&args, opt_recurse_submodules); + if (max_children) + argv_array_push(&args, max_children); if (opt_dry_run) argv_array_push(&args, "--dry-run"); if (opt_keep) @@@ -781,8 -772,6 +781,8 @@@ static int run_rebase(const unsigned ch /* Options passed to git-rebase */ if (opt_rebase == REBASE_PRESERVE) argv_array_push(&args, "--preserve-merges"); + else if (opt_rebase == REBASE_INTERACTIVE) + argv_array_push(&args, "--interactive"); if (opt_diffstat) argv_array_push(&args, opt_diffstat); argv_array_pushv(&args, opt_strategies.argv); diff --combined strbuf.c index 38686ffb65,47ac0457f7..bab316dda8 --- a/strbuf.c +++ b/strbuf.c @@@ -384,17 -384,6 +384,17 @@@ ssize_t strbuf_read(struct strbuf *sb, return sb->len - oldlen; } +ssize_t strbuf_read_once(struct strbuf *sb, int fd, size_t hint) +{ + ssize_t cnt; + + strbuf_grow(sb, hint ? hint : 8192); + cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1); + if (cnt > 0) + strbuf_setlen(sb, sb->len + cnt); + return cnt; +} + #define STRBUF_MAXLINK (2*PATH_MAX) int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint) @@@ -512,15 -501,37 +512,37 @@@ int strbuf_getwholeline(struct strbuf * } #endif - int strbuf_getline(struct strbuf *sb, FILE *fp, int term) + static int strbuf_getdelim(struct strbuf *sb, FILE *fp, int term) { if (strbuf_getwholeline(sb, fp, term)) return EOF; - if (sb->buf[sb->len-1] == term) - strbuf_setlen(sb, sb->len-1); + if (sb->buf[sb->len - 1] == term) + strbuf_setlen(sb, sb->len - 1); return 0; } + int strbuf_getline(struct strbuf *sb, FILE *fp) + { + if (strbuf_getwholeline(sb, fp, '\n')) + return EOF; + if (sb->buf[sb->len - 1] == '\n') { + strbuf_setlen(sb, sb->len - 1); + if (sb->len && sb->buf[sb->len - 1] == '\r') + strbuf_setlen(sb, sb->len - 1); + } + return 0; + } + + int strbuf_getline_lf(struct strbuf *sb, FILE *fp) + { + return strbuf_getdelim(sb, fp, '\n'); + } + + int strbuf_getline_nul(struct strbuf *sb, FILE *fp) + { + return strbuf_getdelim(sb, fp, '\0'); + } + int strbuf_getwholeline_fd(struct strbuf *sb, int fd, int term) { strbuf_reset(sb); diff --combined strbuf.h index 2bf90e70fc,970c24ab43..f72fd14c2e --- a/strbuf.h +++ b/strbuf.h @@@ -354,8 -354,8 +354,8 @@@ extern void strbuf_addftime(struct strb * * NOTE: The buffer is rewound if the read fails. If -1 is returned, * `errno` must be consulted, like you would do for `read(3)`. - * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline()` has the - * same behaviour as well. + * `strbuf_read()`, `strbuf_read_file()` and `strbuf_getline_*()` + * family of functions have the same behaviour as well. */ extern size_t strbuf_fread(struct strbuf *, size_t, FILE *); @@@ -366,14 -366,6 +366,14 @@@ */ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint); +/** + * Read the contents of a given file descriptor partially by using only one + * attempt of xread. The third argument can be used to give a hint about the + * file size, to avoid reallocs. Returns the number of new bytes appended to + * the sb. + */ +extern ssize_t strbuf_read_once(struct strbuf *, int fd, size_t hint); + /** * Read the contents of a file, specified by its path. The third argument * can be used to give a hint about the file size, to avoid reallocs. @@@ -387,14 -379,31 +387,31 @@@ extern ssize_t strbuf_read_file(struct extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint); /** - * Read a line from a FILE *, overwriting the existing contents - * of the strbuf. The second argument specifies the line - * terminator character, typically `'\n'`. + * Read a line from a FILE *, overwriting the existing contents of + * the strbuf. The strbuf_getline*() family of functions share + * this signature, but have different line termination conventions. + * * Reading stops after the terminator or at EOF. The terminator * is removed from the buffer before returning. Returns 0 unless * there was nothing left before EOF, in which case it returns `EOF`. */ - extern int strbuf_getline(struct strbuf *, FILE *, int); + typedef int (*strbuf_getline_fn)(struct strbuf *, FILE *); + + /* Uses LF as the line terminator */ + extern int strbuf_getline_lf(struct strbuf *sb, FILE *fp); + + /* Uses NUL as the line terminator */ + extern int strbuf_getline_nul(struct strbuf *sb, FILE *fp); + + /* + * Similar to strbuf_getline_lf(), but additionally treats a CR that + * comes immediately before the LF as part of the terminator. + * This is the most friendly version to be used to read "text" files + * that can come from platforms whose native text format is CRLF + * terminated. + */ + extern int strbuf_getline(struct strbuf *, FILE *); + /** * Like `strbuf_getline`, but keeps the trailing terminator (if