diff options
-rw-r--r-- | builtin/am.c | 103 | ||||
-rw-r--r-- | contrib/completion/git-completion.bash | 7 | ||||
-rw-r--r-- | daemon.c | 25 | ||||
-rw-r--r-- | hex.c | 3 | ||||
-rw-r--r-- | path.c | 3 | ||||
-rw-r--r-- | perl/Git.pm | 15 | ||||
-rwxr-xr-x | t/t9000/test.pl | 8 | ||||
-rwxr-xr-x | t/t9001-send-email.sh | 29 |
8 files changed, 116 insertions, 77 deletions
diff --git a/builtin/am.c b/builtin/am.c index 9e2ae5cba4..9daeb27225 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -28,6 +28,7 @@ #include "rerere.h" #include "prompt.h" #include "mailinfo.h" +#include "string-list.h" /** * Returns 1 if the file is empty or does not exist, 0 otherwise. @@ -258,38 +259,29 @@ static int read_state_file(struct strbuf *sb, const struct am_state *state, } /** - * Reads a KEY=VALUE shell variable assignment from `fp`, returning the VALUE - * as a newly-allocated string. VALUE must be a quoted string, and the KEY must - * match `key`. Returns NULL on failure. - * - * This is used by read_author_script() to read the GIT_AUTHOR_* variables from - * the author-script. + * Take a series of KEY='VALUE' lines where VALUE part is + * sq-quoted, and append <KEY, VALUE> at the end of the string list */ -static char *read_shell_var(FILE *fp, const char *key) +static int parse_key_value_squoted(char *buf, struct string_list *list) { - struct strbuf sb = STRBUF_INIT; - const char *str; - - if (strbuf_getline_lf(&sb, fp)) - goto fail; - - if (!skip_prefix(sb.buf, key, &str)) - goto fail; - - if (!skip_prefix(str, "=", &str)) - goto fail; - - strbuf_remove(&sb, 0, str - sb.buf); - - str = sq_dequote(sb.buf); - if (!str) - goto fail; - - return strbuf_detach(&sb, NULL); - -fail: - strbuf_release(&sb); - return NULL; + while (*buf) { + struct string_list_item *item; + char *np; + char *cp = strchr(buf, '='); + if (!cp) + return -1; + np = strchrnul(cp, '\n'); + *cp++ = '\0'; + item = string_list_append(list, buf); + + buf = np + (*np == '\n'); + *np = '\0'; + cp = sq_dequote(cp); + if (!cp) + return -1; + item->util = xstrdup(cp); + } + return 0; } /** @@ -311,44 +303,39 @@ fail: static int read_author_script(struct am_state *state) { const char *filename = am_path(state, "author-script"); - FILE *fp; + struct strbuf buf = STRBUF_INIT; + struct string_list kv = STRING_LIST_INIT_DUP; + int retval = -1; /* assume failure */ + int fd; assert(!state->author_name); assert(!state->author_email); assert(!state->author_date); - fp = fopen(filename, "r"); - if (!fp) { + fd = open(filename, O_RDONLY); + if (fd < 0) { if (errno == ENOENT) return 0; die_errno(_("could not open '%s' for reading"), filename); } + strbuf_read(&buf, fd, 0); + close(fd); + if (parse_key_value_squoted(buf.buf, &kv)) + goto finish; - state->author_name = read_shell_var(fp, "GIT_AUTHOR_NAME"); - if (!state->author_name) { - fclose(fp); - return -1; - } - - state->author_email = read_shell_var(fp, "GIT_AUTHOR_EMAIL"); - if (!state->author_email) { - fclose(fp); - return -1; - } - - state->author_date = read_shell_var(fp, "GIT_AUTHOR_DATE"); - if (!state->author_date) { - fclose(fp); - return -1; - } - - if (fgetc(fp) != EOF) { - fclose(fp); - return -1; - } - - fclose(fp); - return 0; + if (kv.nr != 3 || + strcmp(kv.items[0].string, "GIT_AUTHOR_NAME") || + strcmp(kv.items[1].string, "GIT_AUTHOR_EMAIL") || + strcmp(kv.items[2].string, "GIT_AUTHOR_DATE")) + goto finish; + state->author_name = kv.items[0].util; + state->author_email = kv.items[1].util; + state->author_date = kv.items[2].util; + retval = 0; +finish: + string_list_clear(&kv, !!retval); + strbuf_release(&buf); + return retval; } /** diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash index 9c8f7380d0..21016bf8df 100644 --- a/contrib/completion/git-completion.bash +++ b/contrib/completion/git-completion.bash @@ -338,7 +338,7 @@ __git_tags () __git_refs () { local i hash dir="$(__gitdir "${1-}")" track="${2-}" - local format refs + local format refs pfx if [ -d "$dir" ]; then case "$cur" in refs|refs/*) @@ -347,14 +347,15 @@ __git_refs () track="" ;; *) + [[ "$cur" == ^* ]] && pfx="^" for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do - if [ -e "$dir/$i" ]; then echo $i; fi + if [ -e "$dir/$i" ]; then echo $pfx$i; fi done format="refname:short" refs="refs/tags refs/heads refs/remotes" ;; esac - git --git-dir="$dir" for-each-ref --format="%($format)" \ + git --git-dir="$dir" for-each-ref --format="$pfx%($format)" \ $refs if [ -n "$track" ]; then # employ the heuristic used by git checkout @@ -160,6 +160,7 @@ static const char *path_ok(const char *directory, struct hostinfo *hi) { static char rpath[PATH_MAX]; static char interp_path[PATH_MAX]; + size_t rlen; const char *path; const char *dir; @@ -187,8 +188,12 @@ static const char *path_ok(const char *directory, struct hostinfo *hi) namlen = slash - dir; restlen -= namlen; loginfo("userpath <%s>, request <%s>, namlen %d, restlen %d, slash <%s>", user_path, dir, namlen, restlen, slash); - snprintf(rpath, PATH_MAX, "%.*s/%s%.*s", - namlen, dir, user_path, restlen, slash); + rlen = snprintf(rpath, sizeof(rpath), "%.*s/%s%.*s", + namlen, dir, user_path, restlen, slash); + if (rlen >= sizeof(rpath)) { + logerror("user-path too large: %s", rpath); + return NULL; + } dir = rpath; } } @@ -207,7 +212,15 @@ static const char *path_ok(const char *directory, struct hostinfo *hi) strbuf_expand(&expanded_path, interpolated_path, expand_path, &context); - strlcpy(interp_path, expanded_path.buf, PATH_MAX); + + rlen = strlcpy(interp_path, expanded_path.buf, + sizeof(interp_path)); + if (rlen >= sizeof(interp_path)) { + logerror("interpolated path too large: %s", + interp_path); + return NULL; + } + strbuf_release(&expanded_path); loginfo("Interpolated dir '%s'", interp_path); @@ -219,7 +232,11 @@ static const char *path_ok(const char *directory, struct hostinfo *hi) logerror("'%s': Non-absolute path denied (base-path active)", dir); return NULL; } - snprintf(rpath, PATH_MAX, "%s%s", base_path, dir); + rlen = snprintf(rpath, sizeof(rpath), "%s%s", base_path, dir); + if (rlen >= sizeof(rpath)) { + logerror("base-path too large: %s", rpath); + return NULL; + } dir = rpath; } @@ -78,7 +78,8 @@ char *sha1_to_hex(const unsigned char *sha1) { static int bufno; static char hexbuffer[4][GIT_SHA1_HEXSZ + 1]; - return sha1_to_hex_r(hexbuffer[3 & ++bufno], sha1); + bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer); + return sha1_to_hex_r(hexbuffer[bufno], sha1); } char *oid_to_hex(const struct object_id *oid) @@ -24,7 +24,8 @@ static struct strbuf *get_pathname(void) STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; static int index; - struct strbuf *sb = &pathname_array[3 & ++index]; + struct strbuf *sb = &pathname_array[index]; + index = (index + 1) % ARRAY_SIZE(pathname_array); strbuf_reset(sb); return sb; } diff --git a/perl/Git.pm b/perl/Git.pm index ce7e4e8da3..864123fe8e 100644 --- a/perl/Git.pm +++ b/perl/Git.pm @@ -871,6 +871,8 @@ Return an array of mailboxes extracted from a string. =cut +# Very close to Mail::Address's parser, but we still have minor +# differences in some cases (see t9000 for examples). sub parse_mailboxes { my $re_comment = qr/\((?:[^)]*)\)/; my $re_quote = qr/"(?:[^\"\\]|\\.)*"/; @@ -879,6 +881,7 @@ sub parse_mailboxes { # divide the string in tokens of the above form my $re_token = qr/(?:$re_quote|$re_word|$re_comment|\S)/; my @tokens = map { $_ =~ /\s*($re_token)\s*/g } @_; + my $end_of_addr_seen = 0; # add a delimiter to simplify treatment for the last mailbox push @tokens, ","; @@ -888,10 +891,10 @@ sub parse_mailboxes { if ($token =~ /^[,;]$/) { # if buffer still contains undeterminated strings # append it at the end of @address or @phrase - if (@address) { - push @address, @buffer; - } else { + if ($end_of_addr_seen) { push @phrase, @buffer; + } else { + push @address, @buffer; } my $str_phrase = join ' ', @phrase; @@ -915,16 +918,16 @@ sub parse_mailboxes { push @addr_list, $str_mailbox if ($str_mailbox); @phrase = @address = @comment = @buffer = (); + $end_of_addr_seen = 0; } elsif ($token =~ /^\(/) { push @comment, $token; } elsif ($token eq "<") { push @phrase, (splice @address), (splice @buffer); } elsif ($token eq ">") { + $end_of_addr_seen = 1; push @address, (splice @buffer); - } elsif ($token eq "@") { + } elsif ($token eq "@" && !$end_of_addr_seen) { push @address, (splice @buffer), "@"; - } elsif ($token eq ".") { - push @address, (splice @buffer), "."; } else { push @buffer, $token; } diff --git a/t/t9000/test.pl b/t/t9000/test.pl index 2d05d3eeab..dfeaa9c655 100755 --- a/t/t9000/test.pl +++ b/t/t9000/test.pl @@ -32,15 +32,15 @@ my @success_list = (q[Jane], q["Jane\" Doe" <jdoe@example.com>], q[Doe, jane <jdoe@example.com>], q["Jane Doe <jdoe@example.com>], - q['Jane 'Doe' <jdoe@example.com>]); + q['Jane 'Doe' <jdoe@example.com>], + q[Jane@:;\.,()<>Doe <jdoe@example.com>], + q[Jane <jdoe@example.com> Doe], + q[<jdoe@example.com> Jane Doe]); my @known_failure_list = (q[Jane\ Doe <jdoe@example.com>], q["Doe, Ja"ne <jdoe@example.com>], q["Doe, Katarina" Jane <jdoe@example.com>], - q[Jane@:;\.,()<>Doe <jdoe@example.com>], q[Jane jdoe@example.com], - q[<jdoe@example.com> Jane Doe], - q[Jane <jdoe@example.com> Doe], q["Jane "Kat"a" ri"na" ",Doe" <jdoe@example.com>], q[Jane Doe], q[Jane "Doe <jdoe@example.com>"], diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh index b3355d2c70..3dc4a3454d 100755 --- a/t/t9001-send-email.sh +++ b/t/t9001-send-email.sh @@ -140,6 +140,35 @@ test_expect_success $PREREQ 'Verify commandline' ' test_cmp expected commandline1 ' +test_expect_success $PREREQ 'setup expect for cc trailer' " +cat >expected-cc <<\EOF +!recipient@example.com! +!author@example.com! +!one@example.com! +!two@example.com! +!three@example.com! +!four@example.com! +!five@example.com! +EOF +" + +test_expect_success $PREREQ 'cc trailer with various syntax' ' + test_commit cc-trailer && + test_when_finished "git reset --hard HEAD^" && + git commit --amend -F - <<-EOF && + Test Cc: trailers. + + Cc: one@example.com + Cc: <two@example.com> # this is part of the name + Cc: <three@example.com>, <four@example.com> # not.five@example.com + Cc: "Some # Body" <five@example.com> [part.of.name.too] + EOF + clean_fake_sendmail && + git send-email -1 --to=recipient@example.com \ + --smtp-server="$(pwd)/fake.sendmail" && + test_cmp expected-cc commandline1 +' + test_expect_success $PREREQ 'setup expect' " cat >expected-show-all-headers <<\EOF 0001-Second.patch |