summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRocco Rutte <pdmef@gmx.net>2009-06-13 01:08:01 +0200
committerRocco Rutte <pdmef@gmx.net>2009-06-13 01:08:01 +0200
commite85a61d88857e2e00aabf2ea5aa1ae3eff651b78 (patch)
tree95a18dd96407506e9d723c2bb8f206779f10e535
parent7d83f6df09d352b0ba58efad5de722c69c81cbe5 (diff)
Rewrite header folding
We now distinct between sending and display case. For display, we always use tabs for folding for readability; for sending we now correctly fold using whitespace found in the header. Closes #2995. Closes #3080.
-rw-r--r--commands.c2
-rw-r--r--copy.c2
-rw-r--r--copy.h1
-rw-r--r--handler.c7
-rw-r--r--protos.h2
-rw-r--r--send.c4
-rw-r--r--sendlib.c364
7 files changed, 239 insertions, 143 deletions
diff --git a/commands.c b/commands.c
index b9febd1d..7d9c90d2 100644
--- a/commands.c
+++ b/commands.c
@@ -146,7 +146,7 @@ int mutt_display_message (HEADER *cur)
}
res = mutt_copy_message (fpout, Context, cur, cmflags,
- (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM);
+ (option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) | CH_DECODE | CH_FROM | CH_DISPLAY);
if ((safe_fclose (&fpout) != 0 && errno != EPIPE) || res < 0)
{
mutt_error (_("Could not copy message"));
diff --git a/copy.c b/copy.c
index 90a65dad..9ddf411b 100644
--- a/copy.c
+++ b/copy.c
@@ -287,7 +287,7 @@ mutt_copy_hdr (FILE *in, FILE *out, LOFF_T off_start, LOFF_T off_end, int flags,
if (flags & (CH_DECODE|CH_PREFIX))
{
if (mutt_write_one_header (out, 0, headers[x],
- flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap)) == -1)
+ flags & CH_PREFIX ? prefix : 0, mutt_term_width (Wrap), flags) == -1)
{
error = TRUE;
break;
diff --git a/copy.h b/copy.h
index 9aff9da6..5f12a3c1 100644
--- a/copy.h
+++ b/copy.h
@@ -52,6 +52,7 @@
#define CH_NOQFROM (1<<15) /* ignore ">From " line */
#define CH_UPDATE_IRT (1<<16) /* update In-Reply-To: */
#define CH_UPDATE_REFS (1<<17) /* update References: */
+#define CH_DISPLAY (1<<18) /* display result to user */
int mutt_copy_hdr (FILE *, FILE *, LOFF_T, LOFF_T, int, const char *);
diff --git a/handler.c b/handler.c
index 37810221..57dab0f4 100644
--- a/handler.c
+++ b/handler.c
@@ -1089,7 +1089,8 @@ static int message_handler (BODY *a, STATE *s)
{
mutt_copy_hdr (s->fpin, s->fpout, off_start, b->parts->offset,
(((s->flags & M_WEED) || ((s->flags & (M_DISPLAY|M_PRINTING)) && option (OPTWEED))) ? (CH_WEED | CH_REORDER) : 0) |
- (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM, s->prefix);
+ (s->prefix ? CH_PREFIX : 0) | CH_DECODE | CH_FROM |
+ (s->flags & M_DISPLAY) ? CH_DISPLAY : 0, s->prefix);
if (s->prefix)
state_puts (s->prefix, s);
@@ -1424,7 +1425,7 @@ static int external_body_handler (BODY *b, STATE *s)
mutt_copy_hdr(s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
(option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
- CH_DECODE, NULL);
+ CH_DECODE | CH_DISPLAY, NULL);
}
}
else
@@ -1441,7 +1442,7 @@ static int external_body_handler (BODY *b, STATE *s)
access_type);
mutt_copy_hdr (s->fpin, s->fpout, ftello (s->fpin), b->parts->offset,
(option (OPTWEED) ? (CH_WEED | CH_REORDER) : 0) |
- CH_DECODE , NULL);
+ CH_DECODE | CH_DISPLAY, NULL);
}
}
diff --git a/protos.h b/protos.h
index f35e6d9d..fb9f3047 100644
--- a/protos.h
+++ b/protos.h
@@ -371,7 +371,7 @@ int mutt_which_case (const char *);
int mutt_write_fcc (const char *path, HEADER *hdr, const char *msgid, int, char *);
int mutt_write_mime_body (BODY *, FILE *);
int mutt_write_mime_header (BODY *, FILE *);
-int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen);
+int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen, int flags);
int mutt_write_rfc822_header (FILE *, ENVELOPE *, BODY *, int, int);
void mutt_write_references (LIST *, FILE *, int);
int mutt_yesorno (const char *, int);
diff --git a/send.c b/send.c
index a04e0157..c509df17 100644
--- a/send.c
+++ b/send.c
@@ -388,6 +388,10 @@ static int include_forward (CONTEXT *ctx, HEADER *cur, FILE *out)
if (option (OPTFORWQUOTE))
cmflags |= M_CM_PREFIX;
+ /* wrapping headers for forwarding is considered a display
+ * rather than send action */
+ chflags |= CH_DISPLAY;
+
mutt_copy_message (out, ctx, cur, cmflags, chflags);
mutt_forward_trailer (out);
return 0;
diff --git a/sendlib.c b/sendlib.c
index 2bddbe7e..6a11d227 100644
--- a/sendlib.c
+++ b/sendlib.c
@@ -1548,180 +1548,270 @@ void mutt_write_references (LIST *r, FILE *f, int trim)
FREE (&ref);
}
+static const char *find_word (const char *src)
+{
+ const char *p = src;
+
+ while (p && *p && strchr (" \t\n", *p))
+ p++;
+ while (p && *p && !strchr (" \t\n", *p))
+ p++;
+ return p;
+}
-static void foldingstrfcpy (char *d, const char *s, int n)
+/* like wcwidth(), but gets const char* not wchar_t* */
+static int my_width (const char *str, int col, int flags)
{
- while (--n >= 0 && *s)
+ wchar_t wc;
+ int l, w = 0, nl = 0;
+ const char *p = str;
+
+ while (p && *p)
{
- *d = *s++;
- if (*d == '\t')
- *d = ' ';
- if (!(d[0] == '\n' && (*s == '\t' || *s == ' ')))
- d++;
+ if (mbtowc (&wc, p, MB_CUR_MAX) >= 0)
+ {
+ l = wcwidth (wc);
+ if (l < 0)
+ l = 1;
+ /* correctly calc tab stop, even for sending as the
+ * line should look pretty on the receiving end */
+ if (wc == L'\t' || (nl && wc == L' '))
+ {
+ nl = 0;
+ l = 8 - (col % 8);
+ }
+ /* track newlines for display-case: if we have a space
+ * after a newline, assume 8 spaces as for display we
+ * always tab-fold */
+ else if ((flags & CH_DISPLAY) && wc == '\n')
+ nl = 1;
+ }
+ else
+ l = 1;
+ w += l;
+ p++;
}
- *d = '\0';
+ return w;
}
-int mutt_write_one_header (FILE *fp, const char *tag, const char *value, const char *pfx, int wraplen)
+static int print_val (FILE *fp, const char *pfx, const char *value, int flags)
{
- int col = 0;
- int i, k, n;
- const char *cp;
- char buf [HUGE_STRING];
- wchar_t w = (wchar_t) -1;
- wchar_t last = (wchar_t) '\n';
- int l = 0;
- int first = 1;
- int wrapped = 0;
- int in_encoded_word = 0;
-
- if (wraplen <= 0)
- wraplen = 76;
-
- if (tag)
+ while (value && *value)
{
- if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
+ if (fputc (*value, fp) == EOF)
return -1;
-
- col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
- }
- else
- col = 0;
-
- *buf = '\0';
- cp = value;
-
- while (cp && *cp)
- {
- if (!col)
+ if (*value == '\n')
{
- if (fputs (NONULL (pfx), fp) == EOF)
+ if (*(value + 1) && pfx && *pfx && fputs (pfx, fp) == EOF)
return -1;
- col = mutt_strlen (pfx);
-
- /* Space padding, but only if necessary */
- if (!first && *cp != '\t' && *cp != ' ')
+ /* for display, turn folding spaces into folding tabs */
+ if ((flags & CH_DISPLAY) && (*(value + 1) == ' ' || *(value + 1) == '\t'))
{
+ value++;
+ while (*value && (*value == ' ' || *value == '\t'))
+ value++;
if (fputc ('\t', fp) == EOF)
return -1;
- col += 8 - (col % 8);
+ continue;
}
}
+ value++;
+ }
+ return 0;
+}
- if (first)
+static int fold_one_header (FILE *fp, const char *tag, const char *value,
+ const char *pfx, int wraplen, int flags)
+{
+ const char *p = value, *next, *sp;
+ char buf[HUGE_STRING] = "";
+ int first = 1, enc, col = 0, w, l = 0, fold;
+
+ dprint(4,(debugfile,"mwoh: pfx=[%s], tag=[%s], flags=%d value=[%s]\n",
+ pfx, tag, flags, value));
+
+ if (fprintf (fp, "%s%s: ", NONULL (pfx), tag) < 0)
+ return -1;
+ col = mutt_strlen (tag) + 2 + mutt_strlen (pfx);
+
+ while (p && *p)
+ {
+ fold = 0;
+
+ /* find the next word and place it in `buf'. it may start with
+ * whitespace we can fold before */
+ next = find_word (p);
+ l = MIN(sizeof (buf), next - p);
+ memcpy (buf, p, l);
+ buf[l] = 0;
+
+ /* determine width: character cells for display, bytes for sending
+ * (we get pure ascii only) */
+ w = my_width (buf, col, flags);
+ enc = mutt_strncmp (buf, "=?", 2) == 0;
+
+ dprint(5,(debugfile,"mwoh: word=[%s], col=%d, w=%d, next=[0x0%x]\n",
+ buf, col, w, *next));
+
+ /* insert a folding \n before the current word's lwsp except for
+ * header name, first word on a line (word longer than wrap width)
+ * and encoded words */
+ if (!first && !enc && col && col + w >= wraplen)
{
- last = '\n';
- wrapped = 0;
- first = 0;
+ col = mutt_strlen (pfx);
+ fold = 1;
+ if (fprintf (fp, "\n%s", NONULL(pfx)) <= 0)
+ return -1;
}
- /*
- * i is our running pointer, and always points to the *beginning* of an mb character.
- * k is the pointer to the beginning of the last white-space character we have seen.
- * n is the pointer to the beginning of the first character after white-space.
- *
- * yuck
- */
-
- for (i = 0, k = 0, l = 0, n = 0; i + MB_CUR_MAX < sizeof (buf)
- && cp[i] != '\0' && (col < wraplen || in_encoded_word);
- i += l, last = w)
+ /* print the actual word; for display, ignore leading ws for word
+ * and fold with tab for readability */
+ if ((flags & CH_DISPLAY) && fold)
{
-
- /* Brief look at the last character we had... */
- if (iswspace (last))
+ char *p = buf;
+ while (*p && (*p == ' ' || *p == '\t'))
{
- /* ... and if the next thing is an encoded word ... */
- if (strncmp (&cp[i], "=?", 2) == 0)
- in_encoded_word = 1;
- else
- in_encoded_word = 0;
+ p++;
+ col--;
}
-
- /* If there is a line break in the header, honor it. */
- if (cp[i] == '\n')
- {
- in_encoded_word = 0;
+ if (fputc ('\t', fp) == EOF)
+ return -1;
+ if (print_val (fp, pfx, p, flags) < 0)
+ return -1;
+ col += 8;
+ }
+ else if (print_val (fp, pfx, buf, flags) < 0)
+ return -1;
+ col += w;
- if (cp[i+1] != ' ' && cp[i+1] != '\t')
- first = 1;
-
- if (first || !wrapped)
- {
- k = i;
- n = k + 1;
- l = 1;
- w = (wchar_t) '\n';
- break;
- }
- }
+ /* if the current word ends in \n, ignore all its trailing spaces
+ * and reset column; this prevents us from putting only spaces (or
+ * even none) on a line if the trailing spaces are located at our
+ * current line width
+ * XXX this covers ASCII space only, for display we probably
+ * XXX want something like iswspace() here */
+ sp = next;
+ while (*sp && (*sp == ' ' || *sp == '\t'))
+ sp++;
+ if (*sp == '\n')
+ {
+ next = sp;
+ col = 0;
+ }
- /* Eat the current character; cannot be '\0' */
+ p = next;
+ first = 0;
+ }
- if ((l = mbtowc (&w, &cp[i], MB_CUR_MAX)) <= 0)
- {
- dprint (1, (debugfile, "mutt_write_one_header: encoutered bad multi-byte character at %d.\n", i));
- l = 1; /* if bad, move on by one character */
- w = (wchar_t) -1;
- }
- else
- {
- if (wcwidth (w) >= 0)
- col += wcwidth (w);
+ /* if we have printed something but didn't \n-terminate it, do it
+ * except the last word we printed ended in \n already */
+ if (col && buf[l - 1] != '\n')
+ if (putc ('\n', fp) == EOF)
+ return -1;
- if (iswspace (w) &&
- (!k || col <= wraplen))
- {
- if (!k || i != n)
- k = i;
- n = i + l;
- }
- }
+ return 0;
+}
- /*
- * As long as we haven't seen whitespace, we advance at least by one character.
- */
- if (!k)
- n = i + l;
- }
+static int write_one_header (FILE *fp, int pfxw, int max, int wraplen,
+ const char *pfx, const char *start, const char *end,
+ int flags)
+{
+ char *tagbuf, *valbuf, *t;
- /* If no whitespace was found, copy as much as we can */
- if (!k)
- k = n;
-
- /* If we're done, we're done. */
- if (!cp[i])
- k = n = i;
+ /* only pass through folding machinery if necessary for sending */
+ if (!(flags & CH_DISPLAY) && pfxw + max <= wraplen)
+ {
+ valbuf = mutt_substrdup (start, end);
+ dprint(4,(debugfile,"mwoh: buf[%s%s] short enough, "
+ "max width = %d <= %d\n",
+ NONULL(pfx), valbuf, max, wraplen));
+ if (pfx && *pfx)
+ if (fputs (pfx, fp) == EOF)
+ return -1;
+ if (print_val (fp, pfx, valbuf, flags) < 0)
+ {
+ FREE(&valbuf);
+ return -1;
+ }
+ FREE(&valbuf);
+ }
+ else
+ {
+ dprint(4,(debugfile,"mwoh: buf[%s%s] too long, "
+ "max width = %d > %dn",
+ NONULL(pfx), valbuf, max, wraplen));
+ t = strchr (start, ':');
+ tagbuf = mutt_substrdup (start, t);
+ valbuf = mutt_substrdup (t + 2, end);
+ if (fold_one_header (fp, tagbuf, valbuf, pfx, wraplen, flags) < 0)
+ return -1;
+ FREE (&tagbuf);
+ FREE (&valbuf);
+ }
+ return 0;
+}
- if (k < i) /* we had to go back to an earlier wrapping point */
- wrapped = 1;
-
- buf[0] = *cp;
- foldingstrfcpy (buf + 1, cp + 1, k - 1);
+/* split several headers into individual ones and call write_one_header
+ * for each one */
+int mutt_write_one_header (FILE *fp, const char *tag, const char *value,
+ const char *pfx, int wraplen, int flags)
+{
+ char *p = (char *)value, *last, *line;
+ int max = 0, w;
+ int pfxw = mutt_strwidth (pfx);
- if (fprintf (fp, "%s\n", buf) < 0)
- return -1;
- col = 0;
-
- cp = &cp[n];
+ /* when not displaying, use sane wrap value */
+ if (!(flags & CH_DISPLAY))
+ wraplen = 76;
+ else if (wraplen <= 0 || wraplen > COLS)
+ wraplen = COLS;
- while (*cp)
+ if (tag)
+ {
+ /* if header is short enough, simply print it */
+ if (!(flags & CH_DISPLAY) && mutt_strwidth (tag) + 2 + pfxw +
+ mutt_strwidth (value) <= wraplen)
{
- last = w;
- if ((l = mbtowc (&w, cp, MB_CUR_MAX)) > 0 && iswspace (w))
- cp += l;
- else
- break;
+ dprint(4,(debugfile,"mwoh: buf[%s%s: %s] is short enough\n",
+ NONULL(pfx), tag, value));
+ if (fprintf (fp, "%s%s: %s\n", NONULL(pfx), tag, value) <= 0)
+ return -1;
+ return 0;
}
+ else
+ return fold_one_header (fp, tag, value, pfx, wraplen, flags);
}
- if (col)
+ p = last = line = (char *)value;
+ while (p && *p)
{
- if (fputc ('\n', fp) == EOF)
- return -1;
- col = 0;
+ p = strchr (p, '\n');
+
+ /* find maximum line width in current header */
+ if (p)
+ *p = 0;
+ if ((w = my_width (line, 0, flags)) > max)
+ max = w;
+ if (p)
+ *p = '\n';
+
+ if (!p)
+ break;
+
+ line = ++p;
+ if (*p != ' ' && *p != '\t')
+ {
+ if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
+ return -1;
+ last = p;
+ max = 0;
+ }
}
+ if (last && *last)
+ if (write_one_header (fp, pfxw, max, wraplen, pfx, last, p, flags) < 0)
+ return -1;
+
return 0;
}
@@ -1800,7 +1890,7 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
fputs ("Bcc: \n", fp);
if (env->subject)
- mutt_write_one_header (fp, "Subject", env->subject, NULL, 0);
+ mutt_write_one_header (fp, "Subject", env->subject, NULL, 0, 0);
else if (mode == 1)
fputs ("Subject: \n", fp);
@@ -1870,7 +1960,7 @@ int mutt_write_rfc822_header (FILE *fp, ENVELOPE *env, BODY *attach,
}
}
- mutt_write_one_header (fp, tmp->data, p, NULL, 0);
+ mutt_write_one_header (fp, tmp->data, p, NULL, 0, 0);
*q = ':';
}
}