* fmt_scan.c -- format string interpretation
*
* $Id$
+ *
+ * This code is Copyright (c) 2002, by the authors of nmh. See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
*/
#include <h/mh.h>
#include <h/addrsbr.h>
#include <h/fmt_scan.h>
-#include <zotnet/tws/tws.h>
+#include <h/tws.h>
#include <h/fmt_compile.h>
+#ifdef TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifdef TM_IN_SYS_TIME
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+#ifdef MULTIBYTE_SUPPORT
+# include <wctype.h>
+# include <wchar.h>
+#endif
+
#define NFMTS MAXARGS
extern char *formataddr (); /* hook for custom address formatting */
}
/*
- * 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;
+}
-#define PUTDF(cp, num, wid, fill)\
- 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;\
- }
+/*
+ * 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 PUTD(cp, num)\
- if (cp < ep) {\
- if ((i = (num)) == 0)\
- *cp++ = '0';\
- else {\
- if ((i = (num)) < 0) {\
- *cp++ = '-';\
- i = -(num);\
- }\
- c = 10;\
- while (c <= i) \
- c *= 10;\
- while (cp < ep && c > 1) {\
- c /= 10;\
- *cp++ = (i / c) + '0';\
- i %= c;\
- }\
- }\
- }
+ end -= char_len;
-#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;\
- }
+ 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 = (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++ = ' ';\
- }\
- }\
+ 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
}
+ }
-#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 (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;
+ }
+}
-#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++ = ' ';\
- }\
- }\
- }
+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",
return NULL;
if (get_x400_comp (mbox, "/PN=", buffer, buffer_len)) {
- for (mbox = buffer; mbox = strchr(mbox, '.'); )
+ for (mbox = buffer; (mbox = strchr(mbox, '.')); )
*mbox++ = ' ';
return buffer;
|| !(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;
+ int i, c, ljust, n;
int value = 0;
time_t t;
struct format *fmt;
cp = scanl;
ep = scanl + width - 1;
+
+ for (fmt = format; fmt->f_type != FT_DONE; fmt++)
+ switch (fmt->f_type) {
+ case FT_PARSEADDR:
+ case FT_PARSEDATE:
+ fmt->f_comp->c_flags &= ~CF_PARSED;
+ break;
+ }
+
fmt = format;
while (cp < ep) {
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:
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:
- PUTD (cp, value);
+ n = snprintf(cp, ep - cp + 1, "%d", value);
+ if (n >= 0) {
+ if (n >= ep - cp) {
+ cp = ep;
+ } else
+ cp += n;
+ }
break;
case FT_NUMF:
- PUTDF (cp, value, fmt->f_width, fmt->f_fill);
+ cpnumber (&cp, value, fmt->f_width, fmt->f_fill, ep - cp);
break;
case FT_CHAR:
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++;
break;
case FT_LV_COMPFLAG:
- value = fmt->f_comp->c_flags;
+ value = (fmt->f_comp->c_flags & CF_TRUE) != 0;
break;
case FT_LV_COMP:
value = (comp = fmt->f_comp)->c_text ? atoi(comp->c_text) : 0;
value = dat[fmt->f_value];
break;
case FT_LV_STRLEN:
- value = strlen(str);
+ if (str != NULL)
+ value = strlen(str);
+ else
+ value = 0;
break;
case FT_LV_CHAR_LEFT:
value = width - (cp - scanl);
}
if (fmt->f_type == FT_LS_ADDR)
goto unfriendly;
- if ((str = mn->m_pers) == NULL)
+ 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++;
break;
}
}
+ }
+ 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:
case FT_PARSEDATE:
comp = fmt->f_comp;
+ if (comp->c_flags & CF_PARSED)
+ break;
if ((sp = comp->c_text) && (tws = dparsetime(sp))) {
*comp->c_tws = *tws;
- comp->c_flags = 0;
- } else if (comp->c_flags >= 0) {
+ comp->c_flags &= ~CF_TRUE;
+ } else if ((comp->c_flags & CF_DATEFAB) == 0) {
memset ((char *) comp->c_tws, 0, sizeof *comp->c_tws);
- comp->c_flags = 1;
+ comp->c_flags = CF_TRUE;
}
+ comp->c_flags |= CF_PARSED;
break;
case FT_FORMATADDR:
* (e.g., "To: ")
*/
{
- char *lp, *lastb;
+ unsigned char *lp;
+ char *lastb;
int indent, wid, len;
lp = str;
len -= sp - lp + 1;
while (cp < ep && lp <= sp)
*cp++ = *lp++;
- *cp++ = '\n';
- for (i=indent; cp < ep && i > 0; i--)
- *cp++ = ' ';
while (isspace(*lp))
lp++, len--;
if (*lp) {
*cp++ = ' ';
}
}
- PUTS (cp, lp);
+ cpstripped (&cp, ep, lp);
}
break;
case FT_PARSEADDR:
comp = fmt->f_comp;
+ if (comp->c_flags & CF_PARSED)
+ break;
if (comp->c_mn != &fmt_mnull)
mnfree (comp->c_mn);
if ((sp = comp->c_text) && (sp = getname(sp)) &&
comp->c_mn = mn;
while (getname(""))
;
+ comp->c_flags |= CF_PARSED;
} else {
while (getname("")) /* XXX */
;
comp->c_mn = &fmt_mnull;
}
break;
-
+
case FT_MYMBOX:
/*
* if there's no component, we say true. Otherwise we
if ((sp = comp->c_text) && (sp = getname(sp)) &&
(mn = getm (sp, NULL, 0, AD_NAME, NULL))) {
comp->c_mn = mn;
- comp->c_flags = ismymbox(mn);
+ if (ismymbox(mn))
+ comp->c_flags |= CF_TRUE;
+ else
+ comp->c_flags &= ~CF_TRUE;
while ((sp = getname(sp)))
- if (comp->c_flags == 0 &&
+ if ((comp->c_flags & CF_TRUE) == 0 &&
(mn = getm (sp, NULL, 0, AD_NAME, NULL)))
- comp->c_flags |= ismymbox(mn);
+ if (ismymbox(mn))
+ comp->c_flags |= CF_TRUE;
} else {
while (getname("")) /* XXX */
;
- comp->c_flags = (comp->c_text == 0);
+ if (comp->c_text == 0)
+ comp->c_flags |= CF_TRUE;
+ else
+ comp->c_flags &= ~CF_TRUE;
comp->c_mn = &fmt_mnull;
}
break;
while (fmt->f_type != FT_DONE)
fmt++;
- finished:;
+ finished:;
*cp = '\0';
return (fmt->f_value ? ++fmt : (struct format *) 0);