builtin-am: auto-detect mbox patches
Since 15ced75
(git-am foreign patch support: autodetect some patch
formats, 2009-05-27), git-am.sh is able to autodetect mbox, stgit and
mercurial patches through heuristics.
Re-implement support for autodetecting mbox/maildir files in
builtin/am.c.
RFC 2822 requires that lines are terminated by "\r\n". To support this,
implement strbuf_getline_crlf(), which will remove both '\n' and "\r\n"
from the end of the line.
Helped-by: Junio C Hamano <gitster@pobox.com>
Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Helped-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Paul Tan <pyokagan@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
11c2177f2c
commit
c29807b27d
109
builtin/am.c
109
builtin/am.c
@ -10,6 +10,21 @@
|
||||
#include "dir.h"
|
||||
#include "run-command.h"
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
enum patch_format {
|
||||
PATCH_FORMAT_UNKNOWN = 0,
|
||||
PATCH_FORMAT_MBOX
|
||||
@ -127,6 +142,92 @@ static void am_destroy(const struct am_state *state)
|
||||
strbuf_release(&sb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the file looks like a piece of RFC2822 mail by grabbing all
|
||||
* non-indented lines and checking if they look like they begin with valid
|
||||
* header field names.
|
||||
*
|
||||
* Returns 1 if the file looks like a piece of mail, 0 otherwise.
|
||||
*/
|
||||
static int is_mail(FILE *fp)
|
||||
{
|
||||
const char *header_regex = "^[!-9;-~]+:";
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
regex_t regex;
|
||||
int ret = 1;
|
||||
|
||||
if (fseek(fp, 0L, SEEK_SET))
|
||||
die_errno(_("fseek failed"));
|
||||
|
||||
if (regcomp(®ex, header_regex, REG_NOSUB | REG_EXTENDED))
|
||||
die("invalid pattern: %s", header_regex);
|
||||
|
||||
while (!strbuf_getline_crlf(&sb, fp)) {
|
||||
if (!sb.len)
|
||||
break; /* End of header */
|
||||
|
||||
/* Ignore indented folded lines */
|
||||
if (*sb.buf == '\t' || *sb.buf == ' ')
|
||||
continue;
|
||||
|
||||
/* It's a header if it matches header_regex */
|
||||
if (regexec(®ex, sb.buf, 0, NULL, 0)) {
|
||||
ret = 0;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
regfree(®ex);
|
||||
strbuf_release(&sb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect the patch_format of the patches contained in `paths`,
|
||||
* returning the PATCH_FORMAT_* enum value. Returns PATCH_FORMAT_UNKNOWN if
|
||||
* detection fails.
|
||||
*/
|
||||
static int detect_patch_format(const char **paths)
|
||||
{
|
||||
enum patch_format ret = PATCH_FORMAT_UNKNOWN;
|
||||
struct strbuf l1 = STRBUF_INIT;
|
||||
FILE *fp;
|
||||
|
||||
/*
|
||||
* We default to mbox format if input is from stdin and for directories
|
||||
*/
|
||||
if (!*paths || !strcmp(*paths, "-") || is_directory(*paths))
|
||||
return PATCH_FORMAT_MBOX;
|
||||
|
||||
/*
|
||||
* Otherwise, check the first few lines of the first patch, starting
|
||||
* from the first non-blank line, to try to detect its format.
|
||||
*/
|
||||
|
||||
fp = xfopen(*paths, "r");
|
||||
|
||||
while (!strbuf_getline_crlf(&l1, fp)) {
|
||||
if (l1.len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (starts_with(l1.buf, "From ") || starts_with(l1.buf, "From: ")) {
|
||||
ret = PATCH_FORMAT_MBOX;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (l1.len && is_mail(fp)) {
|
||||
ret = PATCH_FORMAT_MBOX;
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
fclose(fp);
|
||||
strbuf_release(&l1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits out individual email patches from `paths`, where each path is either
|
||||
* a mbox file or a Maildir. Returns 0 on success, -1 on failure.
|
||||
@ -185,6 +286,14 @@ static int split_mail(struct am_state *state, enum patch_format patch_format,
|
||||
static void am_setup(struct am_state *state, enum patch_format patch_format,
|
||||
const char **paths)
|
||||
{
|
||||
if (!patch_format)
|
||||
patch_format = detect_patch_format(paths);
|
||||
|
||||
if (!patch_format) {
|
||||
fprintf_ln(stderr, _("Patch format detection failed."));
|
||||
exit(128);
|
||||
}
|
||||
|
||||
if (mkdir(state->dir, 0777) < 0 && errno != EEXIST)
|
||||
die_errno(_("failed to create directory '%s'"), state->dir);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user