65
return strchrnul(msg, '\n') - msg;
66
}
67
68
+/**
69
+ * Returns true if `str` consists of only whitespace, false otherwise.
70
+ */
71
+static int str_isspace(const char *str)
72
+{
73
+ for (; *str; str++)
74
+ if (!isspace(*str))
75
+ return 0;
76
+
77
+ return 1;
78
+}
79
+
80
enum patch_format {
81
PATCH_FORMAT_UNKNOWN = 0,
70
- PATCH_FORMAT_MBOX
82
+ PATCH_FORMAT_MBOX,
83
+ PATCH_FORMAT_STGIT
84
};
85
86
enum keep_type {
623
{
624
enum patch_format ret = PATCH_FORMAT_UNKNOWN;
625
struct strbuf l1 = STRBUF_INIT;
626
+ struct strbuf l2 = STRBUF_INIT;
627
+ struct strbuf l3 = STRBUF_INIT;
628
FILE *fp;
629
630
/*
650
goto done;
651
}
652
653
+ strbuf_reset(&l2);
654
+ strbuf_getline_crlf(&l2, fp);
655
+ strbuf_reset(&l3);
656
+ strbuf_getline_crlf(&l3, fp);
657
+
658
+ /*
659
+ * If the second line is empty and the third is a From, Author or Date
660
+ * entry, this is likely an StGit patch.
661
+ */
662
+ if (l1.len && !l2.len &&
663
+ (starts_with(l3.buf, "From:") ||
664
+ starts_with(l3.buf, "Author:") ||
665
+ starts_with(l3.buf, "Date:"))) {
666
+ ret = PATCH_FORMAT_STGIT;
667
+ goto done;
668
+ }
669
+
670
if (l1.len && is_mail(fp)) {
671
ret = PATCH_FORMAT_MBOX;
672
goto done;
706
return 0;
707
}
708
709
+/**
710
+ * Callback signature for split_mail_conv(). The foreign patch should be
711
+ * read from `in`, and the converted patch (in RFC2822 mail format) should be
712
+ * written to `out`. Return 0 on success, or -1 on failure.
713
+ */
714
+typedef int (*mail_conv_fn)(FILE *out, FILE *in, int keep_cr);
715
+
716
+/**
717
+ * Calls `fn` for each file in `paths` to convert the foreign patch to the
718
+ * RFC2822 mail format suitable for parsing with git-mailinfo.
719
+ *
720
+ * Returns 0 on success, -1 on failure.
721
+ */
722
+static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
723
+ const char **paths, int keep_cr)
724
+{
725
+ static const char *stdin_only[] = {"-", NULL};
726
+ int i;
727
+
728
+ if (!*paths)
729
+ paths = stdin_only;
730
+
731
+ for (i = 0; *paths; paths++, i++) {
732
+ FILE *in, *out;
733
+ const char *mail;
734
+ int ret;
735
+
736
+ if (!strcmp(*paths, "-"))
737
+ in = stdin;
738
+ else
739
+ in = fopen(*paths, "r");
740
+
741
+ if (!in)
742
+ return error(_("could not open '%s' for reading: %s"),
743
+ *paths, strerror(errno));
744
+
745
+ mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
746
+
747
+ out = fopen(mail, "w");
748
+ if (!out)
749
+ return error(_("could not open '%s' for writing: %s"),
750
+ mail, strerror(errno));
751
+
752
+ ret = fn(out, in, keep_cr);
753
+
754
+ fclose(out);
755
+ fclose(in);
756
+
757
+ if (ret)
758
+ return error(_("could not parse patch '%s'"), *paths);
759
+ }
760
+
761
+ state->cur = 1;
762
+ state->last = i;
763
+ return 0;
764
+}
765
+
766
+/**
767
+ * A split_mail_conv() callback that converts an StGit patch to an RFC2822
768
+ * message suitable for parsing with git-mailinfo.
769
+ */
770
+static int stgit_patch_to_mail(FILE *out, FILE *in, int keep_cr)
771
+{
772
+ struct strbuf sb = STRBUF_INIT;
773
+ int subject_printed = 0;
774
+
775
+ while (!strbuf_getline(&sb, in, '\n')) {
776
+ const char *str;
777
+
778
+ if (str_isspace(sb.buf))
779
+ continue;
780
+ else if (skip_prefix(sb.buf, "Author:", &str))
781
+ fprintf(out, "From:%s\n", str);
782
+ else if (starts_with(sb.buf, "From") || starts_with(sb.buf, "Date"))
783
+ fprintf(out, "%s\n", sb.buf);
784
+ else if (!subject_printed) {
785
+ fprintf(out, "Subject: %s\n", sb.buf);
786
+ subject_printed = 1;
787
+ } else {
788
+ fprintf(out, "\n%s\n", sb.buf);
789
+ break;
790
+ }
791
+ }
792
+
793
+ strbuf_reset(&sb);
794
+ while (strbuf_fread(&sb, 8192, in) > 0) {
795
+ fwrite(sb.buf, 1, sb.len, out);
796
+ strbuf_reset(&sb);
797
+ }
798
+
799
+ strbuf_release(&sb);
800
+ return 0;
801
+}
802
+
803
/**
804
* Splits a list of files/directories into individual email patches. Each path
805
* in `paths` must be a file/directory that is formatted according to
828
switch (patch_format) {
829
case PATCH_FORMAT_MBOX:
830
return split_mail_mbox(state, paths, keep_cr);
831
+ case PATCH_FORMAT_STGIT:
832
+ return split_mail_conv(stgit_patch_to_mail, state, paths, keep_cr);
833
default:
834
die("BUG: invalid patch_format");
835
}
1878
1879
if (!strcmp(arg, "mbox"))
1880
*opt_value = PATCH_FORMAT_MBOX;
1881
+ else if (!strcmp(arg, "stgit"))
1882
+ *opt_value = PATCH_FORMAT_STGIT;
1883
else
1884
return error(_("Invalid value for --patch-format: %s"), arg);
1885
return 0;