am: support --patch-format=mboxrd
[gitweb.git] / pretty.c
index 92b2870a7eabb6a99ffbbcfc222d2ff1c1186fa0..6abd8a12151635206d587f078015ad8b88b80b6f 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -16,6 +16,7 @@ static struct cmt_fmt_map {
        const char *name;
        enum cmit_fmt format;
        int is_tformat;
+       int expand_tabs_in_log;
        int is_alias;
        const char *user_format;
 } *commit_formats;
@@ -87,13 +88,14 @@ static int git_pretty_formats_config(const char *var, const char *value, void *c
 static void setup_commit_formats(void)
 {
        struct cmt_fmt_map builtin_formats[] = {
-               { "raw",        CMIT_FMT_RAW,           0 },
-               { "medium",     CMIT_FMT_MEDIUM,        0 },
-               { "short",      CMIT_FMT_SHORT,         0 },
-               { "email",      CMIT_FMT_EMAIL,         0 },
-               { "fuller",     CMIT_FMT_FULLER,        0 },
-               { "full",       CMIT_FMT_FULL,          0 },
-               { "oneline",    CMIT_FMT_ONELINE,       1 }
+               { "raw",        CMIT_FMT_RAW,           0,      0 },
+               { "medium",     CMIT_FMT_MEDIUM,        0,      8 },
+               { "short",      CMIT_FMT_SHORT,         0,      0 },
+               { "email",      CMIT_FMT_EMAIL,         0,      0 },
+               { "mboxrd",     CMIT_FMT_MBOXRD,        0,      0 },
+               { "fuller",     CMIT_FMT_FULLER,        0,      8 },
+               { "full",       CMIT_FMT_FULL,          0,      8 },
+               { "oneline",    CMIT_FMT_ONELINE,       1,      0 }
        };
        commit_formats_len = ARRAY_SIZE(builtin_formats);
        builtin_formats_len = commit_formats_len;
@@ -172,6 +174,7 @@ void get_commit_format(const char *arg, struct rev_info *rev)
 
        rev->commit_format = commit_format->format;
        rev->use_terminator = commit_format->is_tformat;
+       rev->expand_tabs_in_log_default = commit_format->expand_tabs_in_log;
        if (commit_format->format == CMIT_FMT_USERFORMAT) {
                save_user_format(rev, commit_format->user_format,
                                 commit_format->is_tformat);
@@ -442,7 +445,7 @@ void pp_user_info(struct pretty_print_context *pp,
        if (pp->mailmap)
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) {
                        struct strbuf buf = STRBUF_INIT;
 
@@ -492,6 +495,7 @@ void pp_user_info(struct pretty_print_context *pp,
                            show_ident_date(&ident, &pp->date_mode));
                break;
        case CMIT_FMT_EMAIL:
+       case CMIT_FMT_MBOXRD:
                strbuf_addf(sb, "Date: %s\n",
                            show_ident_date(&ident, DATE_MODE(RFC2822)));
                break;
@@ -533,7 +537,7 @@ static void add_merge_info(const struct pretty_print_context *pp,
 {
        struct commit_list *parent = commit->parents;
 
-       if ((pp->fmt == CMIT_FMT_ONELINE) || (pp->fmt == CMIT_FMT_EMAIL) ||
+       if ((pp->fmt == CMIT_FMT_ONELINE) || (cmit_fmt_is_mail(pp->fmt)) ||
            !parent || !parent->next)
                return;
 
@@ -1612,7 +1616,7 @@ void pp_title_line(struct pretty_print_context *pp,
        if (pp->after_subject) {
                strbuf_addstr(sb, pp->after_subject);
        }
-       if (pp->fmt == CMIT_FMT_EMAIL) {
+       if (cmit_fmt_is_mail(pp->fmt)) {
                strbuf_addch(sb, '\n');
        }
 
@@ -1629,6 +1633,82 @@ void pp_title_line(struct pretty_print_context *pp,
        strbuf_release(&title);
 }
 
+static int pp_utf8_width(const char *start, const char *end)
+{
+       int width = 0;
+       size_t remain = end - start;
+
+       while (remain) {
+               int n = utf8_width(&start, &remain);
+               if (n < 0 || !start)
+                       return -1;
+               width += n;
+       }
+       return width;
+}
+
+static void strbuf_add_tabexpand(struct strbuf *sb, int tabwidth,
+                                const char *line, int linelen)
+{
+       const char *tab;
+
+       while ((tab = memchr(line, '\t', linelen)) != NULL) {
+               int width = pp_utf8_width(line, tab);
+
+               /*
+                * If it wasn't well-formed utf8, or it
+                * had characters with badly defined
+                * width (control characters etc), just
+                * give up on trying to align things.
+                */
+               if (width < 0)
+                       break;
+
+               /* Output the data .. */
+               strbuf_add(sb, line, tab - line);
+
+               /* .. and the de-tabified tab */
+               strbuf_addchars(sb, ' ', tabwidth - (width % tabwidth));
+
+               /* Skip over the printed part .. */
+               linelen -= tab + 1 - line;
+               line = tab + 1;
+       }
+
+       /*
+        * Print out everything after the last tab without
+        * worrying about width - there's nothing more to
+        * align.
+        */
+       strbuf_add(sb, line, linelen);
+}
+
+/*
+ * pp_handle_indent() prints out the intendation, and
+ * the whole line (without the final newline), after
+ * de-tabifying.
+ */
+static void pp_handle_indent(struct pretty_print_context *pp,
+                            struct strbuf *sb, int indent,
+                            const char *line, int linelen)
+{
+       strbuf_addchars(sb, ' ', indent);
+       if (pp->expand_tabs_in_log)
+               strbuf_add_tabexpand(sb, pp->expand_tabs_in_log, line, linelen);
+       else
+               strbuf_add(sb, line, linelen);
+}
+
+static int is_mboxrd_from(const char *line, int len)
+{
+       /*
+        * a line matching /^From $/ here would only have len == 4
+        * at this point because is_empty_line would've trimmed all
+        * trailing space
+        */
+       return len > 4 && starts_with(line + strspn(line, ">"), "From ");
+}
+
 void pp_remainder(struct pretty_print_context *pp,
                  const char **msg_p,
                  struct strbuf *sb,
@@ -1653,8 +1733,17 @@ void pp_remainder(struct pretty_print_context *pp,
 
                strbuf_grow(sb, linelen + indent + 20);
                if (indent)
-                       strbuf_addchars(sb, ' ', indent);
-               strbuf_add(sb, line, linelen);
+                       pp_handle_indent(pp, sb, indent, line, linelen);
+               else if (pp->expand_tabs_in_log)
+                       strbuf_add_tabexpand(sb, pp->expand_tabs_in_log,
+                                            line, linelen);
+               else {
+                       if (pp->fmt == CMIT_FMT_MBOXRD &&
+                                       is_mboxrd_from(line, linelen))
+                               strbuf_addch(sb, '>');
+
+                       strbuf_add(sb, line, linelen);
+               }
                strbuf_addch(sb, '\n');
        }
 }
@@ -1678,14 +1767,14 @@ void pretty_print_commit(struct pretty_print_context *pp,
        encoding = get_log_output_encoding();
        msg = reencoded = logmsg_reencode(commit, NULL, encoding);
 
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                indent = 0;
 
        /*
         * We need to check and emit Content-type: to mark it
         * as 8-bit if we haven't done so.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && need_8bit_cte == 0) {
+       if (cmit_fmt_is_mail(pp->fmt) && need_8bit_cte == 0) {
                int i, ch, in_body;
 
                for (in_body = i = 0; (ch = msg[i]); i++) {
@@ -1713,7 +1802,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        msg = skip_empty_lines(msg);
 
        /* These formats treat the title line specially. */
-       if (pp->fmt == CMIT_FMT_ONELINE || pp->fmt == CMIT_FMT_EMAIL)
+       if (pp->fmt == CMIT_FMT_ONELINE || cmit_fmt_is_mail(pp->fmt))
                pp_title_line(pp, &msg, sb, encoding, need_8bit_cte);
 
        beginning_of_body = sb->len;
@@ -1730,7 +1819,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
         * format.  Make sure we did not strip the blank line
         * between the header and the body.
         */
-       if (pp->fmt == CMIT_FMT_EMAIL && sb->len <= beginning_of_body)
+       if (cmit_fmt_is_mail(pp->fmt) && sb->len <= beginning_of_body)
                strbuf_addch(sb, '\n');
 
        unuse_commit_buffer(commit, reencoded);