]> granicus.if.org Git - neomutt/commitdiff
Rewrite header folding
authorRocco Rutte <pdmef@gmx.net>
Fri, 12 Jun 2009 23:08:01 +0000 (01:08 +0200)
committerRocco Rutte <pdmef@gmx.net>
Fri, 12 Jun 2009 23:08:01 +0000 (01:08 +0200)
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.

commands.c
copy.c
copy.h
handler.c
protos.h
send.c
sendlib.c

index b9febd1dc09e6435c07f1ab63c1177f2159e4a86..7d9c90d2850dbf32a3964b96280047e75422dc59 100644 (file)
@@ -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 90a65dad9762d96f522144c102ec7621c9828128..9ddf411b752dec7246f149cc2ad9cfd5728de434 100644 (file)
--- 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 9aff9da65787d12a90ea79e3a1b889c6100b39d2..5f12a3c1e3b9ce1eb39cc34f8b4ee228922367a8 100644 (file)
--- 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 *);
index 378102212eaa8c903aae4c0fccbc3b384a4c4d5f..57dab0f4196ea37528542bdc207c6b55fec10338 100644 (file)
--- 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);
     }
   }
   
index f35e6d9de298a9656255570a31bfb292a10ef8b3..fb9f3047430547a295ef51ade9ba7ac3dd12a7c1 100644 (file)
--- 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 a04e0157c93f557aabb0615a07c5652ed2e7c42e..c509df176b58755872ad605171c9f2751aac2b5e 100644 (file)
--- 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;
index 2bddbe7e3a698d4187a7b820e275b0e79e4d5ee3..6a11d22707f13e44fc8abc98a5f7ab31ad5a8910 100644 (file)
--- 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 = ':';
     }
   }