/* Case-independent string comparison HTString.c
**
** Original version came with listserv implementation.
** Version TBL Oct 91 replaces one which modified the strings.
** 02-Dec-91 (JFG) Added stralloccopy and stralloccat
** 23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike
** 6 Oct 92 (TBL) Moved WWW_TraceFlag in here to be in library
** 15 Nov 98 (TD) Added HTSprintf.
*/
#include <HTUtils.h>
#include <LYLeaks.h>
#include <LYStrings.h>
#ifndef NO_LYNX_TRACE
BOOLEAN WWW_TraceFlag = 0; /* Global trace flag for ALL W3 code */
int WWW_TraceMask = 0; /* Global trace flag for ALL W3 code */
#endif
#ifndef VC
#define VC "unknown"
#endif /* !VC */
#ifdef _WINDOWS
const char * HTLibraryVersion = "2.14FM"; /* String for help screen etc */
#else
const char * HTLibraryVersion = VC; /* String for help screen etc */
#endif
/*
** strcasecomp8 is a variant of strcasecomp (below)
** ------------ -----------
** but uses 8bit upper/lower case information
** from the current display charset.
** It returns 0 if exact match.
*/
int strcasecomp8 (
const char* a,
const char * b)
{
const char *p = a;
const char *q = b;
for ( ; *p && *q; p++, q++) {
int diff = UPPER8(*p, *q);
if (diff) return diff;
}
if (*p)
return 1; /* p was longer than q */
if (*q)
return -1; /* p was shorter than q */
return 0; /* Exact match */
}
/*
** strncasecomp8 is a variant of strncasecomp (below)
** ------------- ------------
** but uses 8bit upper/lower case information
** from the current display charset.
** It returns 0 if exact match.
*/
int strncasecomp8 (
const char* a,
const char * b,
int n)
{
const char *p = a;
const char *q = b;
for ( ; ; p++, q++) {
int diff;
if (p == (a+n))
return 0; /* Match up to n characters */
if (!(*p && *q))
return (*p - *q);
diff = UPPER8(*p, *q);
if (diff)
return diff;
}
/*NOTREACHED*/
}
#ifndef VM /* VM has these already it seems */
#ifdef SH_EX /* 1997/12/23 (Tue) 16:40:31 */
/*
* This array is designed for mapping upper and lower case letter
* together for a case independent comparison. The mappings are
* based upon ascii character sequences.
*/
static unsigned char charmap[] = {
'\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007',
'\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017',
'\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027',
'\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037',
'\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047',
'\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057',
'\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067',
'\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077',
'\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
'\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137',
'\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147',
'\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157',
'\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167',
'\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177',
'\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207',
'\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217',
'\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227',
'\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237',
'\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247',
'\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257',
'\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267',
'\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277',
'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\327',
'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\337',
'\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347',
'\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357',
'\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367',
'\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377',
};
int strcasecomp (
const char* s1,
const char* s2)
{
register unsigned char *cm = charmap;
register unsigned char *us1 = (unsigned char *)s1;
register unsigned char *us2 = (unsigned char *)s2;
while (cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
return(0);
return (cm[*us1] - cm[*--us2]);
}
int strncasecomp (
const char* a,
const char* b,
int n)
{
register unsigned char *cm = charmap;
register unsigned char *us1 = (unsigned char *)a;
register unsigned char *us2 = (unsigned char *)b;
while ((long)(--n) >= 0 && cm[*us1] == cm[*us2++])
if (*us1++ == '\0')
return(0);
return ((long)n < 0 ? 0 : cm[*us1] - cm[*--us2]);
}
#else /* SH_EX */
/* Strings of any length
** ---------------------
*/
int strcasecomp (
const char* a,
const char * b)
{
const char *p = a;
const char *q = b;
for ( ; *p && *q; p++, q++) {
int diff = TOLOWER(*p) - TOLOWER(*q);
if (diff) return diff;
}
if (*p)
return 1; /* p was longer than q */
if (*q)
return -1; /* p was shorter than q */
return 0; /* Exact match */
}
/* With count limit
** ----------------
*/
int strncasecomp (
const char* a,
const char * b,
int n)
{
const char *p = a;
const char *q = b;
for ( ; ; p++, q++) {
int diff;
if (p == (a+n))
return 0; /* Match up to n characters */
if (!(*p && *q))
return (*p - *q);
diff = TOLOWER(*p) - TOLOWER(*q);
if (diff)
return diff;
}
/*NOTREACHED*/
}
#endif /* SH_EX */
#endif /* VM */
#ifdef NOT_ASCII
/* Case-insensitive with ASCII collating sequence
** ----------------
*/
int AS_casecomp (
const char* p,
const char* q)
{
int diff;
for ( ; ; p++, q++) {
if (!(*p && *q))
return (UCH(*p) - UCH(*q));
diff = TOASCII(TOLOWER(*p))
- TOASCII(TOLOWER(*q));
if (diff)
return diff;
}
/*NOTREACHED*/
}
/* With count limit and ASCII collating sequence
** ----------------
** AS_cmp uses n == -1 to compare indefinite length.
*/
int AS_ncmp (
const char * p,
const char * q,
unsigned int n)
{
const char *a = p;
int diff;
for ( ; (p-a) < n; p++, q++) {
if (!(*p && *q))
return (UCH(*p) - UCH(*q));
diff = TOASCII(*p)
- TOASCII(*q);
if (diff)
return diff;
}
return 0; /* Match up to n characters */
}
/* With ASCII collating sequence
** ----------------
*/
int AS_cmp (
const char * p,
const char * q)
{
return( AS_ncmp( p, q, -1 ) );
}
#endif /* NOT_ASCII */
/* Allocate a new copy of a string, and returns it
*/
char * HTSACopy (
char ** dest,
const char * src)
{
if (src != 0) {
if (src != *dest) {
size_t size = strlen(src) + 1;
FREE(*dest);
*dest = (char *) malloc(size);
if (*dest == NULL)
outofmem(__FILE__, "HTSACopy");
memcpy(*dest, src, size);
}
} else {
FREE(*dest);
}
return *dest;
}
/* String Allocate and Concatenate
*/
char * HTSACat (
char ** dest,
const char * src)
{
if (src && *src && (src != *dest)) {
if (*dest) {
size_t length = strlen(*dest);
*dest = (char *)realloc(*dest, length + strlen(src) + 1);
if (*dest == NULL)
outofmem(__FILE__, "HTSACat");
strcpy (*dest + length, src);
} else {
*dest = (char *)malloc(strlen(src) + 1);
if (*dest == NULL)
outofmem(__FILE__, "HTSACat");
strcpy (*dest, src);
}
}
return *dest;
}
/* optimized for heavily realloc'd strings, store length inside */
#define EXTRA_TYPE size_t /* type we use for length */
#define EXTRA_SIZE sizeof(void *) /* alignment >= sizeof(EXTRA_TYPE) */
void HTSAFree_extra (
char * s)
{
free(s - EXTRA_SIZE);
}
/* never shrink */
char * HTSACopy_extra (
char ** dest,
const char * src)
{
if (src != 0) {
size_t srcsize = strlen(src) + 1;
EXTRA_TYPE size = 0;
if (*dest != 0) {
size = *(EXTRA_TYPE *)((*dest) - EXTRA_SIZE);
}
if (size < srcsize) {
FREE_extra(*dest);
size = srcsize * 2; /* x2 step */
*dest = (char *) malloc(size + EXTRA_SIZE);
if (*dest == NULL)
outofmem(__FILE__, "HTSACopy_extra");
*(EXTRA_TYPE *)(*dest) = size;
*dest += EXTRA_SIZE;
}
memcpy(*dest, src, srcsize);
} else {
Clear_extra(*dest);
}
return *dest;
}
/* Find next Field
** ---------------
**
** On entry,
** *pstr points to a string containig white space separated
** field, optionlly quoted.
**
** On exit,
** *pstr has been moved to the first delimiter past the
** field
** THE STRING HAS BEEN MUTILATED by a 0 terminator
**
** returns a pointer to the first field
*/
char * HTNextField (
char ** pstr)
{
char * p = *pstr;
char * start; /* start of field */
while (*p && WHITE(*p))
p++; /* Strip white space */
if (!*p) {
*pstr = p;
return NULL; /* No first field */
}
if (*p == '"') { /* quoted field */
p++;
start = p;
for (; *p && *p!='"'; p++) {
if (*p == '\\' && p[1])
p++; /* Skip escaped chars */
}
} else {
start = p;
while (*p && !WHITE(*p))
p++; /* Skip first field */
}
if (*p)
*p++ = '\0';
*pstr = p;
return start;
}
/* Find next Token
** ---------------
** Finds the next token in a string
** On entry,
** *pstr points to a string to be parsed.
** delims lists characters to be recognized as delimiters.
** If NULL, default is white space "," ";" or "=".
** The word can optionally be quoted or enclosed with
** chars from bracks.
** Comments surrrounded by '(' ')' are filtered out
** unless they are specifically reqested by including
** ' ' or '(' in delims or bracks.
** bracks lists bracketing chars. Some are recognized as
** special, for those give the opening char.
** If NULL, defaults to <"> and "<" ">".
** found points to location to fill with the ending delimiter
** found, or is NULL.
**
** On exit,
** *pstr has been moved to the first delimiter past the
** field
** THE STRING HAS BEEN MUTILATED by a 0 terminator
** found points to the delimiter found unless it was NULL.
** Returns a pointer to the first word or NULL on error
*/
char * HTNextTok (
char ** pstr,
const char * delims,
const char * bracks,
char * found)
{
char * p = *pstr;
char * start = NULL;
BOOL get_blanks, skip_comments;
BOOL get_comments;
BOOL get_closing_char_too = FALSE;
char closer;
if (isEmpty(pstr)) return NULL;
if (!delims) delims = " ;,=" ;
if (!bracks) bracks = "<\"" ;
get_blanks = (BOOL) (!strchr(delims,' ') && !strchr(bracks,' '));
get_comments = (BOOL) (strchr(bracks,'(') != NULL);
skip_comments = (BOOL) (!get_comments && !strchr(delims,'(') && !get_blanks);
#define skipWHITE(c) (!get_blanks && WHITE(c))
while (*p && skipWHITE(*p))
p++; /* Strip white space */
if (!*p) {
*pstr = p;
if (found) *found = '\0';
return NULL; /* No first field */
}
while (1) {
/* Strip white space and other delimiters */
while (*p && (skipWHITE(*p) || strchr(delims,*p))) p++;
if (!*p) {
*pstr = p;
if (found) *found = *(p-1);
return NULL; /* No field */
}
if (*p == '(' && (skip_comments || get_comments)) { /* Comment */
int comment_level = 0;
if (get_comments && !start) start = p+1;
for(;*p && (*p!=')' || --comment_level>0); p++) {
if (*p == '(') comment_level++;
else if (*p == '"') { /* quoted field within Comment */
for(p++; *p && *p!='"'; p++)
if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
if (!*p) break; /* (invalid) end of string found, leave */
}
if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
}
if (get_comments)
break;
if (*p) p++;
if (get_closing_char_too) {
if (!*p || (!strchr(bracks,*p) && strchr(delims,*p))) {
break;
} else
get_closing_char_too = (BOOL) (strchr(bracks,*p) != NULL);
}
} else if (strchr(bracks,*p)) { /* quoted or bracketed field */
switch (*p) {
case '<': closer = '>'; break;
case '[': closer = ']'; break;
case '{': closer = '}'; break;
case ':': closer = ';'; break;
default: closer = *p;
}
if (!start) start = ++p;
for(;*p && *p!=closer; p++)
if (*p == '\\' && *(p+1)) p++; /* Skip escaped chars */
if (get_closing_char_too) {
p++;
if (!*p || (!strchr(bracks,*p) && strchr(delims,*p))) {
break;
} else
get_closing_char_too = (BOOL) (strchr(bracks,*p) != NULL);
} else
break; /* kr95-10-9: needs to stop here */
} else { /* Spool field */
if (!start) start = p;
while(*p && !skipWHITE(*p) && !strchr(bracks,*p) &&
!strchr(delims,*p))
p++;
if (*p && strchr(bracks,*p)) {
get_closing_char_too = TRUE;
} else {
if (*p=='(' && skip_comments) {
*pstr = p;
HTNextTok(pstr, NULL, "(", found); /* Advance pstr */
*p = '\0';
if (*pstr && **pstr) (*pstr)++;
return start;
}
break; /* Got it */
}
}
}
if (found) *found = *p;
if (*p) *p++ = '\0';
*pstr = p;
return start;
}
static char *HTAlloc (char * ptr, size_t length)
{
if (ptr != 0)
ptr = (char *)realloc(ptr, length);
else
ptr = (char *)malloc(length);
if (ptr == 0)
outofmem(__FILE__, "HTAlloc");
return ptr;
}
/*
* If SAVE_TIME_NOT_SPACE is defined, StrAllocVsprintf will hang on to
* its temporary string buffers instead of allocating and freeing them
* in each invocation. They only grow and never shrink, and won't be
* cleaned up on exit. - kw
*/
#if defined(_REENTRANT) || defined(_THREAD_SAFE) || defined(LY_FIND_LEAKS)
#undef SAVE_TIME_NOT_SPACE
#endif
/*
* Replacement for sprintf, allocates buffer on the fly according to what's
* needed for its arguments. Unlike sprintf, this always concatenates to the
* destination buffer, so we do not have to provide both flavors.
*/
typedef enum { Flags, Width, Prec, Type, Format } PRINTF;
#define VA_INTGR(type) ival = va_arg((*ap), type)
#define VA_FLOAT(type) fval = va_arg((*ap), type)
#define VA_POINT(type) pval = (void *)va_arg((*ap), type)
#define NUM_WIDTH 10 /* allow for width substituted for "*" in "%*s" */
/* also number of chars assumed to be needed in addition
to a given precision in floating point formats */
#define GROW_EXPR(n) (((n) * 3) / 2)
#define GROW_SIZE 256
PUBLIC_IF_FIND_LEAKS char * StrAllocVsprintf (
char ** pstr,
size_t dst_len,
const char * fmt,
va_list * ap)
{
#ifdef SAVE_TIME_NOT_SPACE
static size_t tmp_len = 0;
static size_t fmt_len = 0;
static char *tmp_ptr = NULL;
static char *fmt_ptr = NULL;
#else
size_t tmp_len = GROW_SIZE;
char *tmp_ptr = 0;
char *fmt_ptr;
#endif /* SAVE_TIME_NOT_SPACE */
size_t have, need;
char *dst_ptr = *pstr;
const char *format = fmt;
if (fmt == 0 || *fmt == '\0')
return 0;
#ifdef USE_VASPRINTF
if (pstr && !dst_len) {
if (*pstr)
FREE(*pstr);
if (vasprintf(pstr, fmt, *ap) >= 0) {
mark_malloced(*pstr, strlen(*pstr)+1);
return(*pstr);
}
}
#endif /* USE_VASPRINTF */
need = strlen(fmt) + 1;
#ifdef SAVE_TIME_NOT_SPACE
if (!fmt_ptr || fmt_len < need*NUM_WIDTH) {
fmt_ptr = HTAlloc(fmt_ptr, fmt_len = need*NUM_WIDTH);
}
if (!tmp_ptr || tmp_len < GROW_SIZE) {
tmp_ptr = HTAlloc(tmp_ptr, tmp_len = GROW_SIZE);
}
#else
if ((fmt_ptr = malloc(need*NUM_WIDTH)) == 0
|| (tmp_ptr = malloc(tmp_len)) == 0) {
outofmem(__FILE__, "StrAllocVsprintf");
}
#endif /* SAVE_TIME_NOT_SPACE */
if (dst_ptr == 0) {
dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
} else {
have = strlen(dst_ptr) + 1;
need += dst_len;
if (have < need)
dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
}
while (*fmt != '\0') {
if (*fmt == '%') {
static char dummy[] = "";
PRINTF state = Flags;
char *pval = dummy; /* avoid const-cast */
double fval = 0.0;
int done = FALSE;
int ival = 0;
int prec = -1;
int type = 0;
int used = 0;
int width = -1;
size_t f = 0;
fmt_ptr[f++] = *fmt;
while (*++fmt != '\0' && !done) {
fmt_ptr[f++] = *fmt;
if (isdigit(UCH(*fmt))) {
int num = *fmt - '0';
if (state == Flags && num != 0)
state = Width;
if (state == Width) {
if (width < 0)
width = 0;
width = (width * 10) + num;
} else if (state == Prec) {
if (prec < 0)
prec = 0;
prec = (prec * 10) + num;
}
} else if (*fmt == '*') {
VA_INTGR(int);
if (state == Flags)
state = Width;
if (state == Width) {
width = ival;
} else if (state == Prec) {
prec = ival;
}
sprintf(&fmt_ptr[--f], "%d", ival);
f = strlen(fmt_ptr);
} else if (isalpha(UCH(*fmt))) {
done = TRUE;
switch (*fmt) {
case 'Z': /* FALLTHRU */
case 'h': /* FALLTHRU */
case 'l': /* FALLTHRU */
case 'L': /* FALLTHRU */
done = FALSE;
type = *fmt;
break;
case 'o': /* FALLTHRU */
case 'i': /* FALLTHRU */
case 'd': /* FALLTHRU */
case 'u': /* FALLTHRU */
case 'x': /* FALLTHRU */
case 'X': /* FALLTHRU */
if (type == 'l')
VA_INTGR(long);
else if (type == 'Z')
VA_INTGR(size_t);
else
VA_INTGR(int);
used = 'i';
break;
case 'f': /* FALLTHRU */
case 'e': /* FALLTHRU */
case 'E': /* FALLTHRU */
case 'g': /* FALLTHRU */
case 'G': /* FALLTHRU */
VA_FLOAT(double);
used = 'f';
break;
case 'c':
VA_INTGR(int);
used = 'c';
break;
case 's':
VA_POINT(char *);
if (prec < 0)
prec = strlen(pval);
used = 's';
break;
case 'p':
VA_POINT(void *);
used = 'p';
break;
case 'n':
VA_POINT(int *);
used = 0;
break;
default:
CTRACE((tfp, "unknown format character '%c' in %s\n",
*fmt, format));
break;
}
} else if (*fmt == '.') {
state = Prec;
} else if (*fmt == '%') {
done = TRUE;
used = '%';
}
}
fmt_ptr[f] = '\0';
if (prec > 0) {
switch (used) {
case 'f':
if (width < prec + NUM_WIDTH)
width = prec + NUM_WIDTH;
/* FALLTHRU */
case 'i':
/* FALLTHRU */
case 'p':
if (width < prec + 2)
width = prec + 2; /* leading sign/space/zero, "0x" */
break;
case 'c':
break;
case '%':
break;
default:
if (width < prec)
width = prec;
break;
}
}
if (width >= (int)tmp_len) {
tmp_len = GROW_EXPR(tmp_len + width);
tmp_ptr = HTAlloc(tmp_ptr, tmp_len);
}
switch (used) {
case 'i':
case 'c':
sprintf(tmp_ptr, fmt_ptr, ival);
break;
case 'f':
sprintf(tmp_ptr, fmt_ptr, fval);
break;
default:
sprintf(tmp_ptr, fmt_ptr, pval);
break;
}
need = dst_len + strlen(tmp_ptr) + 1;
if (need >= have) {
dst_ptr = HTAlloc(dst_ptr, have = GROW_EXPR(need));
}
strcpy(dst_ptr + dst_len, tmp_ptr);
dst_len += strlen(tmp_ptr);
} else {
if ((dst_len + 2) >= have) {
dst_ptr = HTAlloc(dst_ptr, (have += GROW_SIZE));
}
dst_ptr[dst_len++] = *fmt++;
}
}
#ifndef SAVE_TIME_NOT_SPACE
FREE(tmp_ptr);
FREE(fmt_ptr);
#endif
dst_ptr[dst_len] = '\0';
if (pstr)
*pstr = dst_ptr;
return (dst_ptr);
}
#undef SAVE_TIME_NOT_SPACE
/*
* Replacement for sprintf, allocates buffer on the fly according to what's
* needed for its arguments. Unlike sprintf, this always concatenates to the
* destination buffer.
*/
/* Note: if making changes, also check the memory tracking version
* LYLeakHTSprintf in LYLeaks.c. - kw */
#ifdef HTSprintf /* if hidden by LYLeaks stuff */
#undef HTSprintf
#endif
#if ANSI_VARARGS
char * HTSprintf (char ** pstr, const char * fmt, ...)
#else
char * HTSprintf (va_alist)
va_dcl
#endif
{
char *result = 0;
size_t inuse = 0;
va_list ap;
LYva_start(ap,fmt);
{
#if !ANSI_VARARGS
char ** pstr = va_arg(ap, char **);
const char * fmt = va_arg(ap, const char *);
#endif
if (pstr != 0 && *pstr != 0)
inuse = strlen(*pstr);
result = StrAllocVsprintf(pstr, inuse, fmt, &ap);
}
va_end(ap);
return (result);
}
/*
* Replacement for sprintf, allocates buffer on the fly according to what's
* needed for its arguments. Like sprintf, this always resets the destination
* buffer.
*/
/* Note: if making changes, also check the memory tracking version
* LYLeakHTSprintf0 in LYLeaks.c. - kw */
#ifdef HTSprintf0 /* if hidden by LYLeaks stuff */
#undef HTSprintf0
#endif
#if ANSI_VARARGS
char * HTSprintf0 (char ** pstr, const char * fmt, ...)
#else
char * HTSprintf0 (va_alist)
va_dcl
#endif
{
char *result = 0;
va_list ap;
LYva_start(ap,fmt);
{
#if !ANSI_VARARGS
char ** pstr = va_arg(ap, char **);
const char * fmt = va_arg(ap, const char *);
#endif
#ifdef USE_VASPRINTF
if (pstr) {
if (*pstr)
FREE(*pstr);
if (vasprintf(pstr, fmt, ap) >= 0) /* else call outofmem?? */
mark_malloced(*pstr, strlen(*pstr)+1);
result = *pstr;
} else
#endif /* USE_VASPRINTF */
result = StrAllocVsprintf(pstr, 0, fmt, &ap);
}
va_end(ap);
return (result);
}
/*
* Returns a quoted or escaped form of the given parameter, suitable for use in
* a command string.
*/
#if USE_QUOTED_PARAMETER
#define S_QUOTE '\''
#define D_QUOTE '"'
char *HTQuoteParameter (
const char * parameter)
{
size_t i;
size_t last;
size_t n = 0;
size_t quoted = 0;
char * result;
if (parameter == 0)
parameter = "";
last = strlen(parameter);
for (i=0; i < last; ++i)
if (strchr("\\&#$^*?(){}<>\"';`|", parameter[i]) != 0
|| isspace(UCH(parameter[i])))
++quoted;
result = (char *)malloc(last + 5*quoted + 3);
if (result == NULL)
outofmem(__FILE__, "HTQuoteParameter");
n = 0;
if (quoted)
result[n++] = S_QUOTE;
for (i = 0; i < last; i++) {
if (parameter[i] == S_QUOTE) {
result[n++] = S_QUOTE;
result[n++] = D_QUOTE;
result[n++] = parameter[i];
result[n++] = D_QUOTE;
result[n++] = S_QUOTE;
} else {
/* Note: No special handling of other characters, including
backslash, since we are constructing a single-quoted string!
Backslash has no special escape meaning within those for sh
and compatible shells, so trying to escape a backslash by
doubling it is unnecessary and would be interpreted by the
shell as an additional data character. - kw 2000-05-02
*/
result[n++] = parameter[i];
}
}
if (quoted)
result[n++] = S_QUOTE;
result[n] = '\0';
return result;
}
#endif
#define HTIsParam(string) ((string[0] == '%' && string[1] == 's'))
/*
* Returns the number of "%s" tokens in a system command-template.
*/
int HTCountCommandArgs (
const char * command)
{
int number = 0;
while (command[0] != 0) {
if (HTIsParam(command))
number++;
command++;
}
return number;
}
/*
* Returns a pointer into the given string after the given parameter number
*/
static const char *HTAfterCommandArg (
const char * command,
int number)
{
while (number > 0) {
if (command[0] != 0) {
if (HTIsParam(command)) {
number--;
command++;
}
command++;
} else {
break;
}
}
return command;
}
/*
* Like HTAddParam, but the parameter may be an environment variable, which we
* will expand and append. Do this only for things like the command-verb,
* where we obtain the parameter from the user's configuration. Any quoting
* required for the environment variable has to be done within its value, e.g.,
*
* setenv EDITOR 'xvile -name "No such class"'
*
* This is useful only when we quote parameters, of course.
*/
#if USE_QUOTED_PARAMETER
void HTAddXpand (
char ** result,
const char * command,
int number,
const char * parameter)
{
if (number > 0) {
const char *last = HTAfterCommandArg(command, number - 1);
const char *next = last;
if (number <= 1) {
FREE(*result);
}
while (next[0] != 0) {
if (HTIsParam(next)) {
if (next != last) {
size_t len = (next - last)
+ ((*result != 0) ? strlen(*result) : 0);
HTSACat(result, last);
(*result)[len] = 0;
}
HTSACat(result, parameter);
CTRACE((tfp, "PARAM-EXP:%s\n", *result));
return;
}
next++;
}
}
}
#endif /* USE_QUOTED_PARAMETER */
/*
* Append string to a system command that we are constructing, without quoting.
* We're given the index of the newest parameter we're processing. Zero
* indicates none, so a value of '1' indicates that we copy from the beginning
* of the command string up to the first parameter, substitute the quoted
* parameter and return the result.
*
* Parameters are substituted at "%s" tokens, like printf. Other printf-style
* tokens are not substituted; they are passed through without change.
*/
void HTAddToCmd (
char ** result,
const char * command,
int number,
const char * string)
{
if (number > 0) {
const char *last = HTAfterCommandArg(command, number - 1);
const char *next = last;
if (number <= 1) {
FREE(*result);
}
if (string == 0)
string = "";
while (next[0] != 0) {
if (HTIsParam(next)) {
if (next != last) {
size_t len = (next - last)
+ ((*result != 0) ? strlen(*result) : 0);
HTSACat(result, last);
(*result)[len] = 0;
}
HTSACat(result, string);
CTRACE((tfp, "PARAM-ADD:%s\n", *result));
return;
}
next++;
}
}
}
/*
* Append string-parameter to a system command that we are constructing. The
* string is a complete parameter (which is a necessary assumption so we can
* quote it properly).
*/
void HTAddParam (
char ** result,
const char * command,
int number,
const char * parameter)
{
if (number > 0) {
#if USE_QUOTED_PARAMETER
char *quoted = HTQuoteParameter(parameter);
HTAddToCmd(result, command, number, quoted);
FREE(quoted);
#else
HTAddToCmd(result, command, number, parameter);
#endif
}
}
/*
* Append the remaining command-string to a system command (compare with
* HTAddParam). Any remaining "%s" tokens are copied as empty strings.
*/
void HTEndParam (
char ** result,
const char * command,
int number)
{
const char *last;
int count;
count = HTCountCommandArgs (command);
if (count < number)
number = count;
last = HTAfterCommandArg(command, number);
if (last[0] != 0) {
HTSACat(result, last);
}
CTRACE((tfp, "PARAM-END:%s\n", *result));
}
/* Binary-strings (may have embedded nulls).
* Some modules (HTGopher) assume there is a null on the end, anyway.
*/
/* Allocate a new bstring, and return it.
*/
void HTSABCopy (
bstring** dest,
const char * src,
int len)
{
bstring *t;
unsigned need = len + 1;
CTRACE2(TRACE_BSTRING, (tfp, "HTSABCopy(%p, %p, %d)\n", dest, src, len));
HTSABFree(dest);
if (src) {
if (TRACE_BSTRING) {
CTRACE((tfp, "=== %4d:", len));
trace_bstring2(src, len);
CTRACE((tfp, "\n"));
}
if ((t = (bstring*) malloc(sizeof(bstring))) == NULL)
outofmem(__FILE__, "HTSABCopy");
if ((t->str = (char *) malloc (need)) == NULL)
outofmem(__FILE__, "HTSABCopy");
memcpy (t->str, src, len);
t->len = len;
t->str[t->len] = '\0';
*dest = t;
}
if (TRACE_BSTRING) {
CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
trace_bstring(*dest);
CTRACE((tfp, "\n"));
}
}
/*
* Initialize with a null-terminated string (discards the null).
*/
void HTSABCopy0 (
bstring** dest,
const char * src)
{
HTSABCopy(dest, src, strlen(src));
}
/*
* Append a block of memory to a bstring.
*/
void HTSABCat (
bstring ** dest,
const char * src,
int len)
{
bstring *t = *dest;
CTRACE2(TRACE_BSTRING, (tfp, "HTSABCat(%p, %p, %d)\n", dest, src, len));
if (src) {
unsigned need = len + 1;
if (TRACE_BSTRING) {
CTRACE((tfp, "=== %4d:", len));
trace_bstring2(src, len);
CTRACE((tfp, "\n"));
}
if (t) {
unsigned length = t->len + need;
if ((t->str = (char *)realloc(t->str, length)) == NULL)
outofmem(__FILE__, "HTSACat");
} else {
if ((t = typecalloc(bstring)) == NULL)
outofmem(__FILE__, "HTSACat");
t->str = (char *)malloc(need);
}
if (t->str == NULL)
outofmem(__FILE__, "HTSACat");
memcpy (t->str + t->len, src, len);
t->len += len;
t->str[t->len] = '\0';
*dest = t;
}
if (TRACE_BSTRING) {
CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
trace_bstring(*dest);
CTRACE((tfp, "\n"));
}
}
/*
* Append a null-terminated string (discards the null).
*/
void HTSABCat0 (
bstring** dest,
const char * src)
{
HTSABCat(dest, src, strlen(src));
}
/*
* Compare two bstring's for equality
*/
BOOL HTSABEql (
bstring * a,
bstring * b)
{
unsigned len_a = (a != 0) ? a->len : 0;
unsigned len_b = (b != 0) ? b->len : 0;
if (len_a == len_b) {
if (len_a == 0
|| memcmp(a->str, b->str, a->len) == 0)
return TRUE;
}
return FALSE;
}
/*
* Deallocate a bstring.
*/
void HTSABFree (
bstring ** ptr)
{
if (*ptr != NULL) {
FREE((*ptr)->str);
FREE(*ptr);
*ptr = NULL;
}
}
/*
* Use this function to perform formatted sprintf's onto the end of a bstring.
* The bstring may contain embedded nulls; the formatted portions must not.
*/
#ifdef ANSI_VARARGS
bstring * HTBprintf (bstring ** pstr, const char * fmt, ...)
#else
bstring * HTBprintf (va_alist)
va_dcl
#endif
{
bstring *result = 0;
char *temp = 0;
va_list ap;
LYva_start(ap,fmt);
{
#if !ANSI_VARARGS
bstring ** pstr = va_arg(ap, char **);
const char * fmt = va_arg(ap, const char *);
#endif
temp = StrAllocVsprintf(&temp, 0, fmt, &ap);
if (!isEmpty(temp)) {
HTSABCat (pstr, temp, strlen(temp));
}
FREE(temp);
result = *pstr;
}
va_end(ap);
return (result);
}
/*
* Write binary-data to the logfile, making it safe for most editors to view.
* That is most, since we do not restrict line-length. Nulls and other
* non-printing characters are addressed.
*/
void trace_bstring2 (
const char * text,
int size)
{
int n;
if (text != 0) {
for (n = 0; n < size; ++n) {
int ch = UCH(text[n]);
switch (ch) {
case '\\':
fputs("\\\\", tfp);
break;
case '\r':
fputs("\\r", tfp);
break;
case '\t':
fputs("\\t", tfp);
break;
case '\f':
fputs("\\f", tfp);
break;
default:
if (isprint(ch) || isspace(ch)) {
fputc(ch, tfp);
} else {
fprintf(tfp, "\\%03o", ch);
}
break;
}
}
}
}
void trace_bstring (
bstring * data)
{
trace_bstring2(BStrData(data), BStrLen(data));
}