-
-/*
- * fmt_scan.c -- format string interpretation
+/* fmt_scan.c -- format string interpretation
*
* This code is Copyright (c) 2002, by the authors of nmh. See the
* COPYRIGHT file in the root directory of the nmh distribution for
* fmt_compile (found in fmt_compile.c).
*/
-#include <h/mh.h>
-#include <h/addrsbr.h>
-#include <h/fmt_scan.h>
-#include <h/tws.h>
-#include <h/fmt_compile.h>
-#include <h/utils.h>
+#include "h/mh.h"
+#include "fmt_addr.h"
+#include "dtime.h"
+#include "strindex.h"
+#include "fmt_rfc2047.h"
+#include "uprf.h"
+#include "context_find.h"
+#include "error.h"
+#include "h/addrsbr.h"
+#include "h/fmt_scan.h"
+#include "h/tws.h"
+#include "h/fmt_compile.h"
+#include "h/utils.h"
+#include "unquote.h"
#ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
/*
* static prototypes
*/
-static int match (char *, char *);
+static int match (char *, char *) PURE;
static char *get_x400_friendly (char *, char *, int);
static int get_x400_comp (char *, char *, char *, int);
return 1;
}
-/*
- * copy a number to the destination subject to a maximum width
- */
+/* cpnumber formats num as a signed decimal,
+ * appending it to dest if the result doesn't exceed max.
+ * The absolute value of width is the minimum width to produce.
+ * A smaller string is padded;
+ * on the left if width is positive, else the right.
+ * Left-padding uses fill. It is either ' ' or '0'.
+ * Right-padding is always with space. */
void
-cpnumber(charstring_t dest, int num, unsigned int wid, char fill, size_t max) {
- if (wid < (num >= 0 ? max : max-1)) {
- /* Build up the string representation of num in reverse. */
- charstring_t rev = charstring_create (0);
- int i = num >= 0 ? num : -num;
-
- do {
- charstring_push_back (rev, i % 10 + '0');
- i /= 10;
- } while (--wid > 0 && i > 0);
- if (i > 0) {
- /* Overflowed the field (wid). */
- charstring_push_back (rev, '?');
- } else if (num < 0 && wid > 0) {
- /* Shouldn't need the wid > 0 check, that's why the condition
- at the top checks wid < max-1 when num < 0. */
- --wid;
- if (fill == ' ') {
- charstring_push_back (rev, '-');
- }
- }
- while (wid-- > 0 && fill != 0) {
- charstring_push_back (rev, fill);
- }
- if (num < 0 && fill == '0') {
- charstring_push_back (rev, '-');
- }
+cpnumber(charstring_t dest, int num, int width, char fill, size_t max)
+{
+ if (width == 0 || width == INT_MIN) {
+ return;
+ }
- {
- /* Output the string in reverse. */
- size_t b = charstring_bytes (rev);
- const char *cp = b ? &charstring_buffer (rev)[b] : NULL;
+ bool padright = width < 0;
+ if (padright) {
+ width = -width; /* Can't overflow after above check. */
+ }
+ size_t w = width;
+ if (w > max) {
+ return; /* The padded result can't fit. */
+ }
+ if (num < 0 && w == 1) {
+ return; /* No room for `-' and a digit or `?'. */
+ }
- for (; b > 0; --b) {
- charstring_push_back (dest, *--cp);
- }
- }
+ char *s = m_str(num);
+ size_t len = strlen(s);
+ if (len == w) {
+ charstring_append_cstring(dest, s);
+ return;
+ }
+
+ bool neg = *s == '-';
+ if (len < w) {
+ if (padright) {
+ charstring_append_cstring(dest, s);
+ while (len++ < w) {
+ charstring_push_back(dest, ' ');
+ }
+ return;
+ }
- charstring_free (rev);
+ if (neg && fill == '0') {
+ charstring_push_back(dest, *s++);
+ }
+ while (len++ < w) {
+ charstring_push_back(dest, fill);
+ }
+ charstring_append_cstring(dest, s);
+ return;
}
+
+ /* Transform 1234567 -> 1234?67 and -1234567 -> 1234-?7. */
+ char *news = s + len - w;
+ if (neg) {
+ *news = '-';
+ }
+ news[neg] = '?';
+ charstring_append_cstring(dest, news);
}
/*
* aligned no more than max characters are copied
*/
void
-cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max) {
+cptrimmed(charstring_t dest, char *str, int wid, char fill, size_t max)
+{
int remaining; /* remaining output width available */
- int rjust;
+ bool rjust;
+ struct charstring *trimmed;
size_t end; /* number of input bytes remaining in str */
#ifdef MULTIBYTE_SUPPORT
int char_len; /* bytes in current character */
char *altstr = NULL;
#endif
char *sp; /* current position in source string */
- int prevCtrl = 1;
/* get alignment */
- rjust = 0;
+ rjust = false;
if ((remaining = wid) < 0) {
remaining = -remaining;
- rjust++;
+ rjust = true;
}
if (remaining > (int) max) { remaining = max; }
+ trimmed = rjust ? charstring_create(remaining) : dest;
+
+ bool prevCtrl = true;
if ((sp = str)) {
#ifdef MULTIBYTE_SUPPORT
if (mbtowc(NULL, NULL, 0)) {} /* reset shift state */
sp++;
#endif
if (!prevCtrl) {
- charstring_push_back (dest, ' ');
+ charstring_push_back (trimmed, ' ');
remaining--;
}
- prevCtrl = 1;
+ prevCtrl = true;
continue;
}
- prevCtrl = 0;
+ prevCtrl = false;
#ifdef MULTIBYTE_SUPPORT
if (w >= 0 && remaining >= w) {
- charstring_push_back_chars (dest, altstr ? altstr : sp,
+ charstring_push_back_chars (trimmed, altstr ? altstr : sp,
char_len, w);
remaining -= w;
altstr = NULL;
}
sp += char_len;
#else
- charstring_push_back (dest, *sp++);
+ charstring_push_back (trimmed, *sp++);
remaining--;
#endif
}
}
- if (rjust) {
- if (remaining > 0) {
- /* copy string to the right */
- charstring_t copy = charstring_copy (dest);
-
- /* add padding at the beginning */
- charstring_clear (dest);
- for (; remaining > 0; --remaining) {
- charstring_push_back (dest, fill);
- }
-
- charstring_append (dest, copy);
+ while (remaining-- > 0) {
+ charstring_push_back(dest, fill);
+ }
- charstring_free (copy);
- }
- } else {
- /* pad remaining space */
- while (remaining-- > 0) {
- charstring_push_back (dest, fill);
- }
+ if (rjust) {
+ charstring_append(dest, trimmed);
+ charstring_free(trimmed);
}
}
+#ifdef MULTIBYTE_SUPPORT
static void
cpstripped (charstring_t dest, size_t max, char *str)
{
- int prevCtrl = 1; /* This is 1 so we strip out leading spaces */
- int len;
-#ifdef MULTIBYTE_SUPPORT
- int char_len, w;
- wchar_t wide_char;
- char *altstr = NULL;
-#endif /* MULTIBYTE_SUPPORT */
-
- if (!str) {
- return;
- }
-
- len = strlen(str);
-
-#ifdef MULTIBYTE_SUPPORT
- if (mbtowc(NULL, NULL, 0)) {} /* Reset shift state */
-#endif /* MULTIBYTE_SUPPORT */
+ static bool deja_vu;
+ static char oddchar[MB_LEN_MAX * 2];
+ static size_t oddlen;
+ static char spacechar[MB_LEN_MAX * 2];
+ static size_t spacelen;
+ char *end;
+ bool squash;
+ char *src;
+ int srclen;
+ wchar_t rune;
+ int w;
- /*
- * Process each character at a time; if we have multibyte support
- * then deal with that here.
- */
+ if (!deja_vu) {
+ deja_vu = true;
- while (*str != '\0' && len > 0 && max > 0) {
-#ifdef MULTIBYTE_SUPPORT
- char_len = mbtowc(&wide_char, str, len);
- w = wcwidth(wide_char);
+ oddlen = wcstombs(oddchar, L"?", sizeof oddchar);
+ assert(oddlen > 0);
+ assert(wcwidth(L' ') == 1); /* Need to pad in ones. */
+ spacelen = wcstombs(spacechar, L" ", sizeof spacechar);
+ assert(spacelen > 0);
+ }
- /*
- * If mbrtowc() failed, then we have a character that isn't valid
- * in the current encoding. Replace it with a '?'. We do that by
- * setting the alstr variable to the value of the replacement string;
- * altstr is used below when the bytes are copied into the output
- * buffer.
- */
+ if (!str)
+ return; /* It's unclear why no padding in this case. */
+ end = str + strlen(str);
+
+ if (mbtowc(NULL, NULL, 0))
+ {} /* Reset shift state. */
+
+ squash = true; /* Trim `space' or `cntrl' from the start. */
+ while (max) {
+ if (!*str)
+ return; /* It's unclear why no padding in this case. */
+
+ srclen = mbtowc(&rune, str, end - str);
+ if (srclen == -1) {
+ /* Invalid rune, or not enough bytes to finish it. */
+ rune = L'?';
+ src = oddchar;
+ srclen = oddlen;
+ str++; /* Skip one byte. */
+ } else {
+ src = str;
+ str += srclen;
+ }
- if (char_len < 0) {
- altstr = "?";
- char_len = mbtowc(&wide_char, altstr, 1);
- }
+ if (iswspace(rune) || iswcntrl(rune)) {
+ if (squash)
+ continue; /* Amidst a run of these. */
+ rune = L' ';
+ src = spacechar;
+ srclen = spacelen;
+ squash = true;
+ } else
+ squash = false;
+
+ w = wcwidth(rune);
+ if (w == -1) {
+ rune = L'?';
+ w = wcwidth(rune);
+ assert(w != -1);
+ src = oddchar;
+ srclen = oddlen;
+ }
- if (char_len <= 0) {
- break;
- }
+ if ((size_t)w > max) {
+ /* No room for rune; pad. */
+ while (max--)
+ charstring_push_back_chars(dest, spacechar, spacelen, 1);
+ return;
+ }
- len -= char_len;
-
- if (iswcntrl(wide_char) || iswspace(wide_char)) {
- str += char_len;
-#else /* MULTIBYTE_SUPPORT */
- int c = (unsigned char) *str;
- len--;
- if (iscntrl(c) || isspace(c)) {
- str++;
-#endif /* MULTIBYTE_SUPPORT */
- if (! prevCtrl) {
- charstring_push_back (dest, ' ');
- --max;
- }
+ charstring_push_back_chars(dest, src, srclen, w);
+ max -= w;
+ }
+}
+#endif
- prevCtrl = 1;
- continue;
- }
+#ifndef MULTIBYTE_SUPPORT
+static void
+cpstripped (charstring_t dest, size_t max, char *str)
+{
+ bool squash;
+ int c;
- prevCtrl = 0;
+ if (!str)
+ return;
-#ifdef MULTIBYTE_SUPPORT
- assert(w >= 0);
- if (max >= (size_t) w) {
- charstring_push_back_chars (dest, altstr ? altstr : str, char_len, w);
- max -= w;
- str += char_len;
- altstr = NULL;
- } else {
- /* Not enough width available for the last character. Output
- space(s) to fill. */
- while (max-- > 0) {
- charstring_push_back (dest, ' ');
- }
- break;
- }
-#else /* MULTIBYE_SUPPORT */
- charstring_push_back (dest, *str++);
- --max;
-#endif /* MULTIBYTE_SUPPORT */
+ squash = true; /* Strip leading cases. */
+ while (max--) {
+ c = (unsigned char)*str++;
+ if (!c)
+ return;
+
+ if (isspace(c) || iscntrl(c)) {
+ if (squash)
+ continue;
+ c = ' ';
+ squash = true;
+ } else
+ squash = false;
+
+ charstring_push_back(dest, (char)c);
}
}
+#endif
static char *lmonth[] = { "January", "February","March", "April",
"May", "June", "July", "August",
struct fmt_callbacks *callbacks)
{
char *sp;
- char *savestr, *str;
+ /* If str points to part of buffer[] or buffer2[] then it must only
+ * ever point at their first element as otherwise undefined
+ * behaviour from overlapping strncpy(3)s can result. */
+ char *str, *savestr;
char buffer[NMH_BUFSIZ], buffer2[NMH_BUFSIZ];
- int i, c, rjust;
+ int i, c;
+ bool rjust;
int value;
time_t t;
size_t max;
break;
case FT_LITF:
sp = fmt->f_text;
- rjust = 0;
+ rjust = false;
i = fmt->f_width;
if (i < 0) {
i = -i;
- rjust++; /* XXX should do something with this */
+ rjust = true; /* XXX should do something with this */
}
while ((c = *sp++) && --i >= 0 && charstring_chars (scanlp) < max) {
charstring_push_back (scanlp, c);
if (str) charstring_push_back_chars (scanlp, str, strlen (str), 0);
break;
case FT_STRFW:
- adios (NULL, "internal error (FT_STRFW)");
+ die("internal error (FT_STRFW)");
case FT_NUM: {
int num = value;
unsigned int wid;
- for (wid = num <= 0 ? 1 : 0; num; ++wid, num /= 10) {}
+ for (wid = num <= 0; num; ++wid, num /= 10) {}
cpnumber (scanlp, value, wid, ' ',
max - charstring_chars (scanlp));
break;
str = buffer;
while (isspace((unsigned char) *str))
str++;
- rjust = 0;
+ rjust = false;
if ((i = fmt->f_width) < 0) {
i = -i;
- rjust++;
+ rjust = true;
}
if (!rjust && i > 0 && (int) strlen(str) > i)
*xp-- = '\0';
if (rjust && i > 0 && (int) strlen(str) > i)
str += strlen(str) - i;
+ str = memmove(buffer, str, strlen(str) + 1);
}
break;
value *= fmt->f_value;
break;
case FT_LV_DIVIDE_L:
- if (fmt->f_value)
- value = value / fmt->f_value;
- else
+ if (fmt->f_value == 0 || (fmt->f_value == -1 && value == INT_MIN)) {
+ // FIXME: Tell the user, and probably stop.
value = 0;
+ } else {
+ value /= fmt->f_value;
+ }
break;
case FT_LV_MODULO_L:
if (fmt->f_value)
- value = value % fmt->f_value;
+ value %= fmt->f_value;
else
+ // FIXME: Tell the user, and probably stop.
value = 0;
break;
case FT_SAVESTR:
case FT_LV_RCLOCK:
if ((value = fmt->f_comp->c_tws->tw_clock) == 0)
value = dmktime(fmt->f_comp->c_tws);
- value = time((time_t *) 0) - value;
+ value = time(NULL) - value;
break;
case FT_LV_DAYF:
if (!(((tws = fmt->f_comp->c_tws)->tw_flags) & (TW_SEXP|TW_SIMP)))
}
break;
case FT_LV_ZONEF:
- if ((fmt->f_comp->c_tws->tw_flags & TW_SZONE) == TW_SZEXP)
+ if (fmt->f_comp->c_tws->tw_flags & TW_SZEXP)
value = 1;
else
value = -1;
else
break;
}
+ str = memmove(buffer, str, strlen(str) + 1);
} else if (!(str = get_x400_friendly (mn->m_mbox,
buffer, sizeof(buffer)))) {
unfriendly:
*comp->c_tws = *tws;
comp->c_flags &= ~CF_TRUE;
} else if ((comp->c_flags & CF_DATEFAB) == 0) {
- memset (comp->c_tws, 0, sizeof *comp->c_tws);
+ ZERO(comp->c_tws);
comp->c_flags = CF_TRUE;
}
comp->c_flags |= CF_PARSED;
indent = strlen (sp);
wid -= indent;
if (wid <= 0) {
- adios(NULL, "putaddr -- num register (%d) must be greater "
+ die("putaddr -- num register (%d) must be greater "
"than label width (%d)", value, indent);
}
while ((c = *sp++) && charstring_chars (scanlp) < max) {
comp->c_mn will be run through
FT_LS_ADDR, which will strip off any
pers name. */
- free (comp->c_text);
+ /* NB: We remove the call to free() here
+ because it interferes with the buffer
+ management in scansbr.c. Revisit this
+ when we clean up memory handling */
+ /* free (comp->c_text); */
comp->c_text = str = strdup (mn->m_text);
comp->c_mn = mn;
}
}
}
- return (NULL);
+ return NULL;
}