add read_author_script() to libgit

Add read_author_script() to sequencer.c based on the implementation in builtin/am.c and update read_am_author_script() to use read_author_script(). The sequencer code that reads the author script will be updated in the next commit. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Phillip Wood committed Oct 31, 2018 at 10:15 UTC bcd33ec25f28514776f3b17af6a5a80b1f329f81
3 files changed +110 -84
builtin/am.c
+2 -84
@@ -260,36 +260,6 @@ static int read_state_file(struct strbuf *sb, const struct am_state *state,
260 die_errno(_("could not read '%s'"), am_path(state, file));
261 }
262
263 -/**
264 - * Take a series of KEY='VALUE' lines where VALUE part is
265 - * sq-quoted, and append <KEY, VALUE> at the end of the string list
266 - */
267 -static int parse_key_value_squoted(char *buf, struct string_list *list)
268 -{
269 - while (*buf) {
270 - struct string_list_item *item;
271 - char *np;
272 - char *cp = strchr(buf, '=');
273 - if (!cp) {
274 - np = strchrnul(buf, '\n');
275 - return error(_("unable to parse '%.*s'"),
276 - (int) (np - buf), buf);
277 - }
278 - np = strchrnul(cp, '\n');
279 - *cp++ = '\0';
280 - item = string_list_append(list, buf);
281 -
282 - buf = np + (*np == '\n');
283 - *np = '\0';
284 - cp = sq_dequote(cp);
285 - if (!cp)
286 - return error(_("unable to dequote value of '%s'"),
287 - item->string);
288 - item->util = xstrdup(cp);
289 - }
290 - return 0;
291 -}
292 -
263 /**
264 * Reads and parses the state directory's "author-script" file, and sets
265 * state->author_name, state->author_email and state->author_date accordingly.
@@ -309,65 +279,13 @@ static int parse_key_value_squoted(char *buf, struct string_list *list)
279 static int read_am_author_script(struct am_state *state)
280 {
281 const char *filename = am_path(state, "author-script");
312 - struct strbuf buf = STRBUF_INIT;
313 - struct string_list kv = STRING_LIST_INIT_DUP;
314 - int retval = -1; /* assume failure */
315 - int i, name_i = -2, email_i = -2, date_i = -2, err = 0;
316 - int fd;
282
283 assert(!state->author_name);
284 assert(!state->author_email);
285 assert(!state->author_date);
286
322 - fd = open(filename, O_RDONLY);
323 - if (fd < 0) {
324 - if (errno == ENOENT)
325 - return 0;
326 - return error_errno(_("could not open '%s' for reading"),
327 - filename);
328 - }
329 - strbuf_read(&buf, fd, 0);
330 - close(fd);
331 - if (parse_key_value_squoted(buf.buf, &kv))
332 - goto finish;
333 -
334 - for (i = 0; i < kv.nr; i++) {
335 - if (!strcmp(kv.items[i].string, "GIT_AUTHOR_NAME")) {
336 - if (name_i != -2)
337 - name_i = error(_("'GIT_AUTHOR_NAME' already given"));
338 - else
339 - name_i = i;
340 - } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_EMAIL")) {
341 - if (email_i != -2)
342 - email_i = error(_("'GIT_AUTHOR_EMAIL' already given"));
343 - else
344 - email_i = i;
345 - } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_DATE")) {
346 - if (date_i != -2)
347 - date_i = error(_("'GIT_AUTHOR_DATE' already given"));
348 - else
349 - date_i = i;
350 - } else {
351 - err = error(_("unknown variable '%s'"),
352 - kv.items[i].string);
353 - }
354 - }
355 - if (name_i == -2)
356 - error(_("missing 'GIT_AUTHOR_NAME'"));
357 - if (email_i == -2)
358 - error(_("missing 'GIT_AUTHOR_EMAIL'"));
359 - if (date_i == -2)
360 - error(_("missing 'GIT_AUTHOR_DATE'"));
361 - if (date_i < 0 || email_i < 0 || date_i < 0 || err)
362 - goto finish;
363 - state->author_name = kv.items[name_i].util;
364 - state->author_email = kv.items[email_i].util;
365 - state->author_date = kv.items[date_i].util;
366 - retval = 0;
367 -finish:
368 - string_list_clear(&kv, !!retval);
369 - strbuf_release(&buf);
370 - return retval;
287 + return read_author_script(filename, &state->author_name,
288 + &state->author_email, &state->author_date, 1);
289 }
290
291 /**
sequencer.c
+105
@@ -664,6 +664,111 @@ missing_author:
664 return res;
665 }
666
667 +/**
668 + * Take a series of KEY='VALUE' lines where VALUE part is
669 + * sq-quoted, and append <KEY, VALUE> at the end of the string list
670 + */
671 +static int parse_key_value_squoted(char *buf, struct string_list *list)
672 +{
673 + while (*buf) {
674 + struct string_list_item *item;
675 + char *np;
676 + char *cp = strchr(buf, '=');
677 + if (!cp) {
678 + np = strchrnul(buf, '\n');
679 + return error(_("no key present in '%.*s'"),
680 + (int) (np - buf), buf);
681 + }
682 + np = strchrnul(cp, '\n');
683 + *cp++ = '\0';
684 + item = string_list_append(list, buf);
685 +
686 + buf = np + (*np == '\n');
687 + *np = '\0';
688 + cp = sq_dequote(cp);
689 + if (!cp)
690 + return error(_("unable to dequote value of '%s'"),
691 + item->string);
692 + item->util = xstrdup(cp);
693 + }
694 + return 0;
695 +}
696 +
697 +/**
698 + * Reads and parses the state directory's "author-script" file, and sets name,
699 + * email and date accordingly.
700 + * Returns 0 on success, -1 if the file could not be parsed.
701 + *
702 + * The author script is of the format:
703 + *
704 + * GIT_AUTHOR_NAME='$author_name'
705 + * GIT_AUTHOR_EMAIL='$author_email'
706 + * GIT_AUTHOR_DATE='$author_date'
707 + *
708 + * where $author_name, $author_email and $author_date are quoted. We are strict
709 + * with our parsing, as the file was meant to be eval'd in the old
710 + * git-am.sh/git-rebase--interactive.sh scripts, and thus if the file differs
711 + * from what this function expects, it is better to bail out than to do
712 + * something that the user does not expect.
713 + */
714 +int read_author_script(const char *path, char **name, char **email, char **date,
715 + int allow_missing)
716 +{
717 + struct strbuf buf = STRBUF_INIT;
718 + struct string_list kv = STRING_LIST_INIT_DUP;
719 + int retval = -1; /* assume failure */
720 + int i, name_i = -2, email_i = -2, date_i = -2, err = 0;
721 +
722 + if (strbuf_read_file(&buf, path, 256) <= 0) {
723 + strbuf_release(&buf);
724 + if (errno == ENOENT && allow_missing)
725 + return 0;
726 + else
727 + return error_errno(_("could not open '%s' for reading"),
728 + path);
729 + }
730 +
731 + if (parse_key_value_squoted(buf.buf, &kv))
732 + goto finish;
733 +
734 + for (i = 0; i < kv.nr; i++) {
735 + if (!strcmp(kv.items[i].string, "GIT_AUTHOR_NAME")) {
736 + if (name_i != -2)
737 + name_i = error(_("'GIT_AUTHOR_NAME' already given"));
738 + else
739 + name_i = i;
740 + } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_EMAIL")) {
741 + if (email_i != -2)
742 + email_i = error(_("'GIT_AUTHOR_EMAIL' already given"));
743 + else
744 + email_i = i;
745 + } else if (!strcmp(kv.items[i].string, "GIT_AUTHOR_DATE")) {
746 + if (date_i != -2)
747 + date_i = error(_("'GIT_AUTHOR_DATE' already given"));
748 + else
749 + date_i = i;
750 + } else {
751 + err = error(_("unknown variable '%s'"),
752 + kv.items[i].string);
753 + }
754 + }
755 + if (name_i == -2)
756 + error(_("missing 'GIT_AUTHOR_NAME'"));
757 + if (email_i == -2)
758 + error(_("missing 'GIT_AUTHOR_EMAIL'"));
759 + if (date_i == -2)
760 + error(_("missing 'GIT_AUTHOR_DATE'"));
761 + if (date_i < 0 || email_i < 0 || date_i < 0 || err)
762 + goto finish;
763 + *name = kv.items[name_i].util;
764 + *email = kv.items[email_i].util;
765 + *date = kv.items[date_i].util;
766 + retval = 0;
767 +finish:
768 + string_list_clear(&kv, !!retval);
769 + strbuf_release(&buf);
770 + return retval;
771 +}
772
773 /*
774 * write_author_script() used to fail to terminate the last line with a "'" and
sequencer.h
+3
@@ -114,4 +114,7 @@ void commit_post_rewrite(const struct commit *current_head,
114 #define SUMMARY_SHOW_AUTHOR_DATE (1 << 1)
115 void print_commit_summary(const char *prefix, const struct object_id *oid,
116 unsigned int flags);
117 +
118 +int read_author_script(const char *path, char **name, char **email, char **date,
119 + int allow_missing);
120 #endif