* docs/MAIL.FILTERING: added note on removing procmail -f or
[mmh] / sbr / fmt_scan.c
index 4365e35..ca4020b 100644 (file)
 #  include <time.h>
 # endif
 #endif
+#ifdef MULTIBYTE_SUPPORT
+#  include <wctype.h>
+#  include <wchar.h>
+#endif
 
 #define        NFMTS MAXARGS
 
@@ -86,111 +90,159 @@ match (char *str, char *sub)
 }
 
 /*
- * macros to format data
+ * copy a number to the destination subject to a maximum width
  */
+static void
+cpnumber(char **dest, int num, unsigned int wid, char fill, size_t n) {
+    int i, c;
+    char *sp;
+    char *cp = *dest;
+    char *ep = cp + n;
+
+    if (cp + wid < ep) {
+       if ((i = (num)) < 0)
+           i = -(num);
+       if ((c = (wid)) < 0)
+           c = -c;
+       sp = cp + c;
+       do {
+           *--sp = (i % 10) + '0';
+           i /= 10;
+       } while (i > 0 && sp > cp);
+       if (i > 0)
+           *sp = '?';
+       else if ((num) < 0 && sp > cp)
+           *--sp = '-';
+       while (sp > cp)
+           *--sp = fill;
+       cp += c;
+    }
+    *dest = cp;
+}
 
-#ifdef LOCALE
-#define PUTSF(cp, str, wid, fill) {\
-               ljust = 0;\
-               if ((i = (wid)) < 0) {\
-                       i = -i;\
-                       ljust++;\
-               }\
-               if ((sp = (str))) {\
-                       if (ljust) {\
-                               c = strlen(sp);\
-                               if (c > i)\
-                                       sp += c - i;\
-                               else {\
-                                       while( --i >= c && cp < ep)\
-                                               *cp++ = fill;\
-                                       i++;\
-                               }\
-                       } else {\
-                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                               sp++;\
-                       }\
-                       while ((c = (unsigned char) *sp++) && --i >= 0 && cp < ep)\
-                               if (isgraph(c)) \
-                                   *cp++ = c;\
-                               else {\
-                                       while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                                               sp++;\
-                                           *cp++ = ' ';\
-                               }\
-               }\
-               if (!ljust)\
-               while( --i >= 0 && cp < ep)\
-                   *cp++ = fill;\
-       }
+/*
+ * copy string from str to dest padding with the fill character to a size
+ * of wid characters. if wid is negative, the string is right aligned
+ * no more than n bytes are copied
+ */
+static void
+cptrimmed(char **dest, char *str, unsigned int wid, char fill, size_t n) {
+    int remaining;     /* remaining output width available */
+    int c, ljust, w;
+    int end;           /* number of input bytes remaining in str */
+#ifdef MULTIBYTE_SUPPORT
+    int char_len;      /* bytes in current character */
+    wchar_t wide_char;
+#endif
+    char *sp;          /* current position in source string */
+    char *cp = *dest;  /* current position in destination string */
+    char *ep = cp + n; /* end of destination buffer */
+    int prevCtrl = 1;
+
+    /* get alignment */
+    ljust = 0;
+    if ((remaining = (wid)) < 0) {
+       remaining = -remaining;
+       ljust++;
+    }
+    if ((sp = (str))) {
+       mbtowc(NULL, NULL, 0); /* reset shift state */
+       end = strlen(str);
+       while (*sp && remaining > 0 && end > 0) {
+#ifdef MULTIBYTE_SUPPORT
+           char_len = mbtowc(&wide_char, sp, end);
+           if (char_len <= 0 || (cp + char_len > ep))
+               break;
 
-#define PUTS(cp, str) {\
-               if ((sp = (str))) {\
-                   while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                       sp++;\
-                   while((c = (unsigned char) *sp++) && cp < ep)\
-                       if (isgraph(c)) \
-                           *cp++ = c;\
-                       else {\
-                           while ((c = (unsigned char) *sp) && (iscntrl(c) || isspace(c)))\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
-       }
+           end -= char_len;
 
-#else /* LOCALE */
-#define PUTSF(cp, str, wid, fill) {\
-               ljust = 0;\
-               if ((i = (wid)) < 0) {\
-                       i = -i;\
-                       ljust++;\
-               }\
-               if (sp = (str)) {\
-                       if (ljust) {\
-                               c = strlen(sp);\
-                               if (c > i)\
-                                       sp += c - i;\
-                               else {\
-                                       while( --i >= c && cp < ep)\
-                                               *cp++ = fill;\
-                                       i++;\
-                               }\
-                       } else {\
-                   while ((c = *sp) && c <= 32)\
-                       sp++;\
-                       }\
-                       while ((c = *sp++) && --i >= 0 && cp < ep)\
-                               if (c > 32) \
-                           *cp++ = c;\
-                       else {\
-                                       while ((c = *sp) && c <= 32)\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
-               if (!ljust)\
-               while( --i >= 0 && cp < ep)\
-                   *cp++ = fill;\
+           if (iswcntrl(wide_char) || iswspace(wide_char)) {
+               sp += char_len;
+#else
+           end--;
+           if (iscntrl(*sp) || isspace(*sp)) {
+               sp++;
+#endif
+               if (!prevCtrl) {
+                   *cp++ = ' ';
+                   remaining--;
                }
 
-#define PUTS(cp, str) {\
-               if (sp = (str)) {\
-                   while ((c = *sp) && c <= 32)\
-                       sp++;\
-                   while( (c = *sp++) && cp < ep)\
-                       if ( c > 32 ) \
-                           *cp++ = c;\
-                       else {\
-                           while ( (c = *sp) && c <= 32 )\
-                               sp++;\
-                           *cp++ = ' ';\
-                       }\
-               }\
-               }
+               prevCtrl = 1;
+               continue;
+           }
+           prevCtrl = 0;
+
+#ifdef MULTIBYTE_SUPPORT
+           w = wcwidth(wide_char);
+           if (w >= 0 && remaining >= w) {
+               strncpy(cp, sp, char_len);
+               cp += char_len;
+               remaining -= w;
+           }
+           sp += char_len;
+#else
+           *cp++ = *sp++;
+           remaining--;
+#endif
+       }
+    }
+
+    if (ljust) {
+       if (cp + remaining > ep)
+           remaining = ep - cp;
+       ep = cp + remaining;
+       if (remaining > 0) {
+           /* copy string to the right */
+           while (--cp >= *dest)
+               *(cp + remaining) = *cp;
+           /* add padding at the beginning */
+           cp += remaining;
+           for (c=remaining; c>0; c--)
+               *cp-- = fill;
+       }
+       *dest = ep;
+    } else {
+       /* pad remaining space */
+       while (remaining-- > 0 && cp < ep)
+               *cp++ = fill;
+       *dest = cp;
+    }
+}
+
+static void
+cpstripped (char **start, char *end, char *str)
+{
+    int c;
+    char *s = str;
 
-#endif /* LOCALE */
+    if (!s)
+       return;
 
+    /* skip any initial control characters or spaces */
+    while ((c = (unsigned char) *s) &&
+#ifdef LOCALE
+           (iscntrl(c) || isspace(c)))
+#else
+           (c <= 32))
+#endif
+       s++;
+
+    /* compact repeated control characters and spaces into a single space */
+    while((c = (unsigned char) *s++) && *start < end)
+       if (!iscntrl(c) && !isspace(c))
+           *(*start)++ = c;
+       else {
+           while ((c = (unsigned char) *s) &&
+#ifdef LOCALE
+                   (iscntrl(c) || isspace(c)))
+#else
+                   (c <= 32))
+#endif
+               s++;
+           *(*start)++ = ' ';
+       }
+}
 
 static char *lmonth[] = { "January",  "February","March",   "April",
                          "May",      "June",    "July",    "August",
@@ -236,15 +288,17 @@ get_x400_comp (char *mbox, char *key, char *buffer, int buffer_len)
            || !(cp = strchr(mbox += idx + strlen (key), '/')))
        return 0;
 
-    snprintf (buffer, buffer_len, "%*.*s", cp - mbox, cp - mbox, mbox);
+    snprintf (buffer, buffer_len, "%*.*s", (int)(cp - mbox), (int)(cp - mbox), mbox);
     return 1;
 }
 
 struct format *
 fmt_scan (struct format *format, char *scanl, int width, int *dat)
 {
-    char *cp, *ep, *sp;
-    char *savestr, *str = NULL;
+    char *cp, *ep;
+    unsigned char *sp;
+    char *savestr;
+    unsigned char *str = NULL;
     char buffer[BUFSIZ], buffer2[BUFSIZ];
     int i, c, ljust, n;
     int value = 0;
@@ -271,10 +325,10 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
        switch (fmt->f_type) {
 
        case FT_COMP:
-           PUTS (cp, fmt->f_comp->c_text);
+           cpstripped (&cp, ep, fmt->f_comp->c_text);
            break;
        case FT_COMPF:
-           PUTSF (cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill);
+           cptrimmed (&cp, fmt->f_comp->c_text, fmt->f_width, fmt->f_fill, ep - cp);
            break;
 
        case FT_LIT:
@@ -297,25 +351,25 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_STR:
-           PUTS (cp, str);
+           cpstripped (&cp, ep, str);
            break;
        case FT_STRF:
-           PUTSF (cp, str, fmt->f_width, fmt->f_fill);
+           cptrimmed (&cp, str, fmt->f_width, fmt->f_fill, ep - cp);
            break;
        case FT_STRFW:
            adios (NULL, "internal error (FT_STRFW)");
 
        case FT_NUM:
-           n = snprintf(cp, ep - cp, "%d", value);
-           if (n >= 0) cp += n;
+           n = snprintf(cp, ep - cp + 1, "%d", value);
+           if (n >= 0) {
+               if (n >= ep - cp) {
+                   cp = ep;
+               } else
+                   cp += n;
+           }
            break;
        case FT_NUMF:
-           n = snprintf(cp, ep - cp, "%d", value);
-           if (n >= 0) {
-               cp += n;
-           } else n = 0;
-           c = abs(fmt->f_width) - n;
-           for (; (c > 0) && (cp < ep); c--) *cp++ = fmt->f_fill;
+           cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
            break;
 
        case FT_CHAR:
@@ -428,22 +482,23 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            break;
 
        case FT_LS_DECODECOMP:
-           if (decode_rfc2047(fmt->f_comp->c_text, buffer2))
+           if (decode_rfc2047(fmt->f_comp->c_text, buffer2, sizeof(buffer2)))
                str = buffer2;
            else
                str = fmt->f_comp->c_text;
            break;
 
        case FT_LS_DECODE:
-           if (str && decode_rfc2047(str, buffer2))
+           if (str && decode_rfc2047(str, buffer2, sizeof(buffer2)))
                str = buffer2;
            break;
 
        case FT_LS_TRIM:
            if (str) {
-                   char *xp;
+                   unsigned char *xp;
 
                    strncpy(buffer, str, sizeof(buffer));
+                   buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
                    while (isspace(*str))
                            str++;
@@ -632,6 +687,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            if ((str = mn->m_pers) == NULL) {
                if ((str = mn->m_note)) {
                    strncpy (buffer, str, sizeof(buffer));
+                   buffer[sizeof(buffer)-1] = '\0';
                    str = buffer;
                    if (*str == '(')
                        str++;
@@ -670,6 +726,40 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
            }
            break;
 
+
+               /* UNQUOTEs RFC-2822 quoted-string and quoted-pair */
+       case FT_LS_UNQUOTE:
+           if (str) {          
+               int m;
+               strncpy(buffer, str, sizeof(buffer));
+               /* strncpy doesn't NUL-terminate if it fills the buffer */
+               buffer[sizeof(buffer)-1] = '\0';
+               str = buffer;
+       
+               /* we will parse from buffer to buffer2 */
+               n = 0; /* n is the input position in str */
+               m = 0; /* m is the ouput position in buffer2 */
+
+               while ( str[n] != '\0') {
+                   switch ( str[n] ) {
+                       case '\\':
+                           n++;
+                           if ( str[n] != '\0')
+                               buffer2[m++] = str[n++];
+                           break;
+                       case '"':
+                           n++;
+                           break;
+                       default:
+                           buffer2[m++] = str[n++];
+                           break;
+                       }
+               }
+               buffer2[m] = '\0';
+               str = buffer2;
+            }
+           break;
+
        case FT_LOCALDATE:
            comp = fmt->f_comp;
            if ((t = comp->c_tws->tw_clock) == 0)
@@ -713,7 +803,8 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
             * (e.g., "To: ")
             */
            {
-           char *lp, *lastb;
+           unsigned char *lp;
+           char *lastb;
            int indent, wid, len;
 
            lp = str;
@@ -754,7 +845,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                        *cp++ = ' ';
                }
            }
-           PUTS (cp, lp);
+           cpstripped (&cp, ep, lp);
            }
            break;
 
@@ -776,7 +867,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
                comp->c_mn = &fmt_mnull;
            }
            break;
-           
+
        case FT_MYMBOX:
            /*
             * if there's no component, we say true.  Otherwise we
@@ -834,7 +925,7 @@ fmt_scan (struct format *format, char *scanl, int width, int *dat)
     while (fmt->f_type != FT_DONE)
        fmt++;
 
-    finished:;    
+    finished:;
     *cp = '\0';
     return (fmt->f_value ? ++fmt : (struct format *) 0);