-/* $Cambridge: exim/src/src/string.c,v 1.9 2006/02/13 11:13:37 ph10 Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2006 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
/* Miscellaneous string-handling functions. Some are not required for
+/*************************************************
+* Copy string if long, inserting newlines *
+*************************************************/
+
+/* If the given string is longer than 75 characters, it is copied, and within
+the copy, certain space characters are converted into newlines.
+
+Argument: pointer to the string
+Returns: pointer to the possibly altered string
+*/
+
+uschar *
+string_split_message(uschar *msg)
+{
+uschar *s, *ss;
+
+if (msg == NULL || Ustrlen(msg) <= 75) return msg;
+s = ss = msg = string_copy(msg);
+
+for (;;)
+ {
+ int i = 0;
+ while (i < 75 && *ss != 0 && *ss != '\n') ss++, i++;
+ if (*ss == 0) break;
+ if (*ss == '\n')
+ s = ++ss;
+ else
+ {
+ uschar *t = ss + 1;
+ uschar *tt = NULL;
+ while (--t > s + 35)
+ {
+ if (*t == ' ')
+ {
+ if (t[-1] == ':') { tt = t; break; }
+ if (tt == NULL) tt = t;
+ }
+ }
+
+ if (tt == NULL) /* Can't split behind - try ahead */
+ {
+ t = ss + 1;
+ while (*t != 0)
+ {
+ if (*t == ' ' || *t == '\n')
+ { tt = t; break; }
+ t++;
+ }
+ }
+
+ if (tt == NULL) break; /* Can't find anywhere to split */
+ *tt = '\n';
+ s = ss = tt+1;
+ }
+ }
+
+return msg;
+}
+
+
+
/*************************************************
* Copy returned DNS domain name, de-escaping *
*************************************************/
*/
uschar *
-string_sprintf(char *format, ...)
+string_sprintf(const char *format, ...)
{
va_list ap;
uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
*/
int
-strncmpic(uschar *s, uschar *t, int n)
+strncmpic(const uschar *s, const uschar *t, int n)
{
while (n--)
{
*/
int
-strcmpic(uschar *s, uschar *t)
+strcmpic(const uschar *s, const uschar *t)
{
while (*s != 0)
{
/* Leading and trailing space is removed from each item. The separator in the
list is controlled by the int pointed to by the separator argument as follows:
- If its value is > 0 it is used as the delimiter.
- (If its value is actually > UCHAR_MAX there is only one item in the list.
+ If the value is > 0 it is used as the separator. This is typically used for
+ sublists such as slash-separated options. The value is always a printing
+ character.
+
+ (If the value is actually > UCHAR_MAX there is only one item in the list.
This is used for some cases when called via functions that sometimes
plough through lists, and sometimes are given single items.)
- If its value is <= 0, the string is inspected for a leading <x, where
- x is an ispunct() value. If found, it is used as the delimiter. If not
- found: (a) if separator == 0, ':' is used
- (b) if separator <0, then -separator is used
- In all cases the value of the separator that is used is written back to
- the int so that it is used on subsequent calls as we progress through
- the list.
-The separator can always be represented in the string by doubling.
+ If the value is <= 0, the string is inspected for a leading <x, where x is an
+ ispunct() or an iscntrl() character. If found, x is used as the separator. If
+ not found:
+
+ (a) if separator == 0, ':' is used
+ (b) if separator <0, -separator is used
+
+ In all cases the value of the separator that is used is written back to the
+ int so that it is used on subsequent calls as we progress through the list.
+
+A literal ispunct() separator can be represented in an item by doubling, but
+there is no way to include an iscntrl() separator as part of the data.
Arguments:
listptr points to a pointer to the current start of the list; the
uschar *
string_nextinlist(uschar **listptr, int *separator, uschar *buffer, int buflen)
{
-register int p = 0;
register int sep = *separator;
register uschar *s = *listptr;
+BOOL sep_is_special;
if (s == NULL) return NULL;
-while (isspace(*s)) s++;
+
+/* This allows for a fixed specified separator to be an iscntrl() character,
+but at the time of implementation, this is never the case. However, it's best
+to be conservative. */
+
+while (isspace(*s) && *s != sep) s++;
+
+/* A change of separator is permitted, so look for a leading '<' followed by an
+allowed character. */
if (sep <= 0)
{
- if (*s == '<' && ispunct(s[1]))
+ if (*s == '<' && (ispunct(s[1]) || iscntrl(s[1])))
{
sep = s[1];
s += 2;
- while (isspace(*s)) s++;
+ while (isspace(*s) && *s != sep) s++;
}
else
{
*separator = sep;
}
+/* An empty string has no list elements */
+
if (*s == 0) return NULL;
+/* Note whether whether or not the separator is an iscntrl() character. */
+
+sep_is_special = iscntrl(sep);
+
/* Handle the case when a buffer is provided. */
if (buffer != NULL)
{
+ register int p = 0;
for (; *s != 0; s++)
{
- if (*s == sep && *(++s) != sep) break;
+ if (*s == sep && (*(++s) != sep || sep_is_special)) break;
if (p < buflen - 1) buffer[p++] = *s;
}
while (p > 0 && isspace(buffer[p-1])) p--;
else
{
+ int size = 0;
+ int ptr = 0;
+ uschar *ss;
+
/* We know that *s != 0 at this point. However, it might be pointing to a
- separator, which could indicate an empty string, or could be doubled to
- indicate a separator character as data at the start of a string. */
+ separator, which could indicate an empty string, or (if an ispunct()
+ character) could be doubled to indicate a separator character as data at the
+ start of a string. Avoid getting working memory for an empty item. */
if (*s == sep)
{
s++;
- if (*s != sep) buffer = string_copy(US"");
+ if (*s != sep || sep_is_special)
+ {
+ *listptr = s;
+ return string_copy(US"");
+ }
}
- if (buffer == NULL)
+ /* Not an empty string; the first character is guaranteed to be a data
+ character. */
+
+ for (;;)
{
- int size = 0;
- int ptr = 0;
- uschar *ss;
- for (;;)
- {
- for (ss = s + 1; *ss != 0 && *ss != sep; ss++);
- buffer = string_cat(buffer, &size, &ptr, s, ss-s);
- s = ss;
- if (*s == 0 || *(++s) != sep) break;
- }
- while (ptr > 0 && isspace(buffer[ptr-1])) ptr--;
- buffer[ptr] = 0;
+ for (ss = s + 1; *ss != 0 && *ss != sep; ss++);
+ buffer = string_cat(buffer, &size, &ptr, s, ss-s);
+ s = ss;
+ if (*s == 0 || *(++s) != sep || sep_is_special) break;
}
+ while (ptr > 0 && isspace(buffer[ptr-1])) ptr--;
+ buffer[ptr] = 0;
}
/* Update the current pointer and return the new string */
The formats are the usual printf() ones, with some omissions (never used) and
two additions for strings: %S forces lower case, and %#s or %#S prints nothing
for a NULL string. Without the # "NULL" is printed (useful in debugging). There
-is also the addition of %D, which inserts the date in the form used for
+is also the addition of %D and %M, which insert the date in the form used for
datestamped log files.
Arguments:
*/
BOOL
-string_format(uschar *buffer, int buflen, char *format, ...)
+string_format(uschar *buffer, int buflen, const char *format, ...)
{
BOOL yield;
va_list ap;
BOOL
-string_vformat(uschar *buffer, int buflen, char *format, va_list ap)
+string_vformat(uschar *buffer, int buflen, const char *format, va_list ap)
{
enum { L_NORMAL, L_SHORT, L_LONG, L_LONGLONG, L_LONGDOUBLE };
BOOL yield = TRUE;
int width, precision;
-char *fp = format; /* Deliberately not unsigned */
+const char *fp = format; /* Deliberately not unsigned */
uschar *p = buffer;
uschar *last = buffer + buflen - 1;
string_datestamp_offset = -1; /* Datestamp not inserted */
+string_datestamp_length = 0; /* Datestamp not inserted */
+string_datestamp_type = 0; /* Datestamp not inserted */
/* Scan the format and handle the insertions */
int length = L_NORMAL;
int *nptr;
int slen;
- char *null = "NULL"; /* ) These variables */
- char *item_start, *s; /* ) are deliberately */
+ const char *null = "NULL"; /* ) These variables */
+ const char *item_start, *s; /* ) are deliberately */
char newformat[16]; /* ) not unsigned */
/* Non-% characters just get copied verbatim */
*p++ = va_arg(ap, int);
break;
- case 'D': /* Insert datestamp for log file names */
- s = CS tod_stamp(tod_log_datestamp);
+ case 'D': /* Insert daily datestamp for log file names */
+ s = CS tod_stamp(tod_log_datestamp_daily);
+ string_datestamp_offset = p - buffer; /* Passed back via global */
+ string_datestamp_length = Ustrlen(s); /* Passed back via global */
+ string_datestamp_type = tod_log_datestamp_daily;
+ slen = string_datestamp_length;
+ goto INSERT_STRING;
+
+ case 'M': /* Insert monthly datestamp for log file names */
+ s = CS tod_stamp(tod_log_datestamp_monthly);
string_datestamp_offset = p - buffer; /* Passed back via global */
+ string_datestamp_length = Ustrlen(s); /* Passed back via global */
+ string_datestamp_type = tod_log_datestamp_monthly;
+ slen = string_datestamp_length;
goto INSERT_STRING;
case 's':
case 'S': /* Forces *lower* case */
s = va_arg(ap, char *);
- INSERT_STRING: /* Come to from %D above */
if (s == NULL) s = null;
slen = Ustrlen(s);
+ INSERT_STRING: /* Come to from %D or %M above */
+
/* If the width is specified, check that there is a precision
set; if not, set it to the width to prevent overruns of long
strings. */
not OK, add part of the string (debugging uses this to show as
much as possible). */
+ if (p == last)
+ {
+ yield = FALSE;
+ goto END_FORMAT;
+ }
if (p >= last - width)
{
yield = FALSE;
width = precision = last - p - 1;
+ if (width < 0) width = 0;
+ if (precision < 0) precision = 0;
}
sprintf(CS p, "%*.*s", width, precision, s);
if (fp[-1] == 'S')
*/
uschar *
-string_open_failed(int eno, char *format, ...)
+string_open_failed(int eno, const char *format, ...)
{
va_list ap;
uschar buffer[1024];
while (fgets(CS buffer, sizeof(buffer), stdin) != NULL)
{
void *args[3];
+ long long llargs[3];
double dargs[3];
int dflag = 0;
+ int llflag = 0;
int n = 0;
int count;
int countset = 0;
dflag = 1;
dargs[n++] = Ustrtod(outbuf, NULL);
}
+ else if (Ustrstr(outbuf, "ll") != NULL)
+ {
+ llflag = 1;
+ llargs[n++] = strtoull(CS outbuf, NULL, 10);
+ }
else
{
args[n++] = (void *)Uatoi(outbuf);
if (*s == ',') s++;
}
- if (!dflag) printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
- args[0], args[1], args[2])? "True" : "False");
+ if (!dflag && !llflag)
+ printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
+ args[0], args[1], args[2])? "True" : "False");
+
+ else if (dflag)
+ printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
+ dargs[0], dargs[1], dargs[2])? "True" : "False");
else printf("%s\n", string_format(outbuf, sizeof(outbuf), CS format,
- dargs[0], dargs[1], dargs[2])? "True" : "False");
+ llargs[0], llargs[1], llargs[2])? "True" : "False");
printf("%s\n", CS outbuf);
if (countset) printf("count=%d\n", count);