1 /*
2  * $LynxId: HTString.c,v 1.71 2013/05/04 12:01:30 tom Exp $
3  *
4  *	Case-independent string comparison		HTString.c
5  *
6  *	Original version came with listserv implementation.
7  *	Version TBL Oct 91 replaces one which modified the strings.
8  *	02-Dec-91 (JFG) Added stralloccopy and stralloccat
9  *	23 Jan 92 (TBL) Changed strallocc* to 8 char HTSAC* for VM and suchlike
10  *	 6 Oct 92 (TBL) Moved WWW_TraceFlag in here to be in library
11  *	15 Nov 98 (TD)  Added HTSprintf.
12  */
13 
14 #include <HTUtils.h>
15 
16 #include <LYLeaks.h>
17 #include <LYUtils.h>
18 #include <LYStrings.h>
19 
20 #ifdef USE_IGNORE_RC
21 int ignore_unused;
22 #endif
23 
24 #ifndef NO_LYNX_TRACE
25 BOOLEAN WWW_TraceFlag = 0;	/* Global trace flag for ALL W3 code */
26 int WWW_TraceMask = 0;		/* Global trace flag for ALL W3 code */
27 #endif
28 
29 #ifdef _WINDOWS
30 #undef VC
31 #define VC "2.14FM"
32 #endif
33 
34 #ifndef VC
35 #define VC "2.14"
36 #endif /* !VC */
37 
38 const char *HTLibraryVersion = VC;	/* String for help screen etc */
39 
40 /*
41  *     strcasecomp8 is a variant of strcasecomp (below)
42  *     ------------		    -----------
43  *     but uses 8bit upper/lower case information
44  *     from the current display charset.
45  *     It returns 0 if exact match.
46  */
strcasecomp8(const char * a,const char * b)47 int strcasecomp8(const char *a,
48 		 const char *b)
49 {
50     const char *p = a;
51     const char *q = b;
52 
53     for (; *p && *q; p++, q++) {
54 	int diff = UPPER8(*p, *q);
55 
56 	if (diff)
57 	    return diff;
58     }
59     if (*p)
60 	return 1;		/* p was longer than q */
61     if (*q)
62 	return -1;		/* p was shorter than q */
63     return 0;			/* Exact match */
64 }
65 
66 /*
67  *     strncasecomp8 is a variant of strncasecomp (below)
68  *     -------------		     ------------
69  *     but uses 8bit upper/lower case information
70  *     from the current display charset.
71  *     It returns 0 if exact match.
72  */
strncasecomp8(const char * a,const char * b,int n)73 int strncasecomp8(const char *a,
74 		  const char *b,
75 		  int n)
76 {
77     const char *p = a;
78     const char *q = b;
79 
80     for (;; p++, q++) {
81 	int diff;
82 
83 	if (p == (a + n))
84 	    return 0;		/*   Match up to n characters */
85 	if (!(*p && *q))
86 	    return (*p - *q);
87 	diff = UPPER8(*p, *q);
88 	if (diff)
89 	    return diff;
90     }
91     /*NOTREACHED */
92 }
93 
94 #ifndef VM			/* VM has these already it seems */
95 /*	Strings of any length
96  *	---------------------
97  */
strcasecomp(const char * a,const char * b)98 int strcasecomp(const char *a,
99 		const char *b)
100 {
101     const char *p = a;
102     const char *q = b;
103 
104     for (; *p && *q; p++, q++) {
105 	int diff = TOLOWER(*p) - TOLOWER(*q);
106 
107 	if (diff)
108 	    return diff;
109     }
110     if (*p)
111 	return 1;		/* p was longer than q */
112     if (*q)
113 	return -1;		/* p was shorter than q */
114     return 0;			/* Exact match */
115 }
116 
117 /*	With count limit
118  *	----------------
119  */
strncasecomp(const char * a,const char * b,int n)120 int strncasecomp(const char *a,
121 		 const char *b,
122 		 int n)
123 {
124     const char *p = a;
125     const char *q = b;
126 
127     for (;; p++, q++) {
128 	int diff;
129 
130 	if (p == (a + n))
131 	    return 0;		/*   Match up to n characters */
132 	if (!(*p && *q))
133 	    return (*p - *q);
134 	diff = TOLOWER(*p) - TOLOWER(*q);
135 	if (diff)
136 	    return diff;
137     }
138     /*NOTREACHED */
139 }
140 #endif /* VM */
141 
142 #define end_component(p) (*(p) == '.' || *(p) == '\0')
143 
144 #ifdef DEBUG_ASTERISK
145 #define SHOW_ASTERISK CTRACE
146 #else
147 #define SHOW_ASTERISK(p)	/* nothing */
148 #endif
149 
150 #define SHOW_ASTERISK_NUM(a,b,c)  \
151 	SHOW_ASTERISK((tfp, "test @%d, '%s' vs '%s' (%d)\n", __LINE__, a,b,c))
152 
153 #define SHOW_ASTERISK_TXT(a,b,c)  \
154 	SHOW_ASTERISK((tfp, "test @%d, '%s' vs '%s' %s\n", __LINE__, a,b,c))
155 
156 /*
157  * Compare names as described in RFC 2818: ignore case, allow wildcards.
158  * Return zero on a match, nonzero on mismatch -TD
159  *
160  * From RFC 2818:
161  * Names may contain the wildcard character * which is considered to match any
162  * single domain name component or component fragment.  E.g., *.a.com matches
163  * foo.a.com but not bar.foo.a.com.  f*.com matches foo.com but not bar.com.
164  */
strcasecomp_asterisk(const char * a,const char * b)165 int strcasecomp_asterisk(const char *a, const char *b)
166 {
167     const char *p;
168     int result = 0;
169     int done = FALSE;
170 
171     while (!result && !done) {
172 	SHOW_ASTERISK_TXT(a, b, "main");
173 	if (*a == '*') {
174 	    p = b;
175 	    for (;;) {
176 		SHOW_ASTERISK_TXT(a, p, "loop");
177 		if (end_component(p)) {
178 		    if (end_component(a + 1)) {
179 			b = p - 1;
180 			result = 0;
181 		    } else {
182 			result = 1;
183 		    }
184 		    break;
185 		} else if (strcasecomp_asterisk(a + 1, p)) {
186 		    ++p;
187 		} else {
188 		    b = p - 1;
189 		    result = 0;	/* found a match starting at 'p' */
190 		    done = TRUE;
191 		    break;
192 		}
193 	    }
194 	    SHOW_ASTERISK_NUM(a, b, result);
195 	} else if (*b == '*') {
196 	    result = strcasecomp_asterisk(b, a);
197 	    SHOW_ASTERISK_NUM(a, b, result);
198 	    done = (result == 0);
199 	} else if (*a == '\0' || *b == '\0') {
200 	    result = (*a != *b);
201 	    SHOW_ASTERISK_NUM(a, b, result);
202 	    break;
203 	} else if (TOLOWER(UCH(*a)) != TOLOWER(UCH(*b))) {
204 	    result = 1;
205 	    SHOW_ASTERISK_NUM(a, b, result);
206 	    break;
207 	}
208 	++a;
209 	++b;
210     }
211     return result;
212 }
213 
214 #ifdef DEBUG_ASTERISK
mismatch_asterisk(void)215 void mismatch_asterisk(void)
216 {
217     /* *INDENT-OFF* */
218     static struct {
219 	const char *a;
220 	const char *b;
221 	int	    code;
222     } table[] = {
223 	{ "foo.bar",	 "*.*",	      0 },
224 	{ "foo.bar",	 "*.b*",      0 },
225 	{ "foo.bar",	 "*.ba*",     0 },
226 	{ "foo.bar",	 "*.bar*",    0 },
227 	{ "foo.bar",	 "*.*bar*",   0 },
228 	{ "foo.bar",	 "*.*.",      1 },
229 	{ "foo.bar",	 "fo*.b*",    0 },
230 	{ "*oo.bar",	 "fo*.b*",    0 },
231 	{ "*oo.bar.com", "fo*.b*",    1 },
232 	{ "*oo.bar.com", "fo*.b*m",   1 },
233 	{ "*oo.bar.com", "fo*.b*.c*", 0 },
234     };
235     /* *INDENT-ON* */
236 
237     unsigned n;
238     int code;
239 
240     CTRACE((tfp, "mismatch_asterisk testing\n"));
241     for (n = 0; n < TABLESIZE(table); ++n) {
242 	CTRACE((tfp, "-------%d\n", n));
243 	code = strcasecomp_asterisk(table[n].a, table[n].b);
244 	if (code != table[n].code) {
245 	    CTRACE((tfp, "mismatch_asterisk '%s' '%s' got %d, want %d\n",
246 		    table[n].a, table[n].b, code, table[n].code));
247 	}
248     }
249 }
250 #endif
251 
252 #ifdef NOT_ASCII
253 
254 /*	Case-insensitive with ASCII collating sequence
255  *	----------------
256  */
AS_casecomp(const char * p,const char * q)257 int AS_casecomp(const char *p,
258 		const char *q)
259 {
260     int diff;
261 
262     for (;; p++, q++) {
263 	if (!(*p && *q))
264 	    return (UCH(*p) - UCH(*q));
265 	diff = TOASCII(TOLOWER(*p))
266 	    - TOASCII(TOLOWER(*q));
267 	if (diff)
268 	    return diff;
269     }
270     /*NOTREACHED */
271 }
272 
273 /*	With count limit and ASCII collating sequence
274  *	----------------
275  *	AS_cmp uses n == -1 to compare indefinite length.
276  */
AS_ncmp(const char * p,const char * q,unsigned int n)277 int AS_ncmp(const char *p,
278 	    const char *q,
279 	    unsigned int n)
280 {
281     const char *a = p;
282     int diff;
283 
284     for (; (unsigned) (p - a) < n; p++, q++) {
285 	if (!(*p && *q))
286 	    return (UCH(*p) - UCH(*q));
287 	diff = TOASCII(*p)
288 	    - TOASCII(*q);
289 	if (diff)
290 	    return diff;
291     }
292     return 0;			/*   Match up to n characters */
293 }
294 #endif /* NOT_ASCII */
295 
296 /*	Allocate a new copy of a string, and returns it
297 */
HTSACopy(char ** dest,const char * src)298 char *HTSACopy(char **dest,
299 	       const char *src)
300 {
301     if (src != 0) {
302 	if (src != *dest) {
303 	    size_t size = strlen(src) + 1;
304 
305 	    FREE(*dest);
306 	    *dest = (char *) malloc(size);
307 	    if (*dest == NULL)
308 		outofmem(__FILE__, "HTSACopy");
309 	    assert(*dest != NULL);
310 	    MemCpy(*dest, src, size);
311 	}
312     } else {
313 	FREE(*dest);
314     }
315     return *dest;
316 }
317 
318 /*	String Allocate and Concatenate
319 */
HTSACat(char ** dest,const char * src)320 char *HTSACat(char **dest,
321 	      const char *src)
322 {
323     if (src && *src && (src != *dest)) {
324 	if (*dest) {
325 	    size_t length = strlen(*dest);
326 
327 	    *dest = (char *) realloc(*dest, length + strlen(src) + 1);
328 	    if (*dest == NULL)
329 		outofmem(__FILE__, "HTSACat");
330 	    assert(*dest != NULL);
331 	    strcpy(*dest + length, src);
332 	} else {
333 	    *dest = (char *) malloc(strlen(src) + 1);
334 	    if (*dest == NULL)
335 		outofmem(__FILE__, "HTSACat");
336 	    assert(*dest != NULL);
337 	    strcpy(*dest, src);
338 	}
339     }
340     return *dest;
341 }
342 
343 /* optimized for heavily realloc'd strings, store length inside */
344 
345 #define EXTRA_TYPE size_t	/* type we use for length */
346 #define EXTRA_SIZE sizeof(void *)	/* alignment >= sizeof(EXTRA_TYPE) */
347 
HTSAFree_extra(char * s)348 void HTSAFree_extra(char *s)
349 {
350     free(s - EXTRA_SIZE);
351 }
352 
353 /* never shrink */
HTSACopy_extra(char ** dest,const char * src)354 char *HTSACopy_extra(char **dest,
355 		     const char *src)
356 {
357     if (src != 0) {
358 	size_t srcsize = strlen(src) + 1;
359 	EXTRA_TYPE size = 0;
360 
361 	if (*dest != 0) {
362 	    size = *(EXTRA_TYPE *) (void *) ((*dest) - EXTRA_SIZE);
363 	}
364 	if ((*dest == 0) || (size < srcsize)) {
365 	    FREE_extra(*dest);
366 	    size = srcsize * 2;	/* x2 step */
367 	    *dest = (char *) malloc(size + EXTRA_SIZE);
368 	    if (*dest == NULL)
369 		outofmem(__FILE__, "HTSACopy_extra");
370 	    assert(*dest != NULL);
371 	    *(EXTRA_TYPE *) (void *) (*dest) = size;
372 	    *dest += EXTRA_SIZE;
373 	}
374 	MemCpy(*dest, src, srcsize);
375     } else {
376 	Clear_extra(*dest);
377     }
378     return *dest;
379 }
380 
381 /*	Find next Field
382  *	---------------
383  *
384  * On entry,
385  *	*pstr	points to a string containig white space separated
386  *		field, optionlly quoted.
387  *
388  * On exit,
389  *	*pstr	has been moved to the first delimiter past the
390  *		field
391  *		THE STRING HAS BEEN MUTILATED by a 0 terminator
392  *
393  *	returns a pointer to the first field
394  */
HTNextField(char ** pstr)395 char *HTNextField(char **pstr)
396 {
397     char *p = *pstr;
398     char *start = NULL;		/* start of field */
399 
400     if (p != NULL) {
401 	while (*p && WHITE(*p))
402 	    p++;		/* Strip white space */
403 	if (!*p) {
404 	    *pstr = p;
405 	} else {
406 	    if (*p == '"') {	/* quoted field */
407 		p++;
408 		start = p;
409 		for (; *p && *p != '"'; p++) {
410 		    if (*p == '\\' && p[1])
411 			p++;	/* Skip escaped chars */
412 		}
413 	    } else {
414 		start = p;
415 		while (*p && !WHITE(*p))
416 		    p++;	/* Skip first field */
417 	    }
418 	    if (*p)
419 		*p++ = '\0';
420 	    *pstr = p;
421 	}
422     }
423     return start;
424 }
425 
426 /*	Find next Token
427  *	---------------
428  *	Finds the next token in a string
429  *	On entry,
430  *	*pstr	points to a string to be parsed.
431  *	delims	lists characters to be recognized as delimiters.
432  *		If NULL, default is white space "," ";" or "=".
433  *		The word can optionally be quoted or enclosed with
434  *		chars from bracks.
435  *		Comments surrrounded by '(' ')' are filtered out
436  *		unless they are specifically reqested by including
437  *		' ' or '(' in delims or bracks.
438  *	bracks	lists bracketing chars.  Some are recognized as
439  *		special, for those give the opening char.
440  *		If NULL, defaults to <"> and "<" ">".
441  *	found	points to location to fill with the ending delimiter
442  *		found, or is NULL.
443  *
444  *	On exit,
445  *	*pstr	has been moved to the first delimiter past the
446  *		field
447  *		THE STRING HAS BEEN MUTILATED by a 0 terminator
448  *	found	points to the delimiter found unless it was NULL.
449  *	Returns a pointer to the first word or NULL on error
450  */
HTNextTok(char ** pstr,const char * delims,const char * bracks,char * found)451 char *HTNextTok(char **pstr,
452 		const char *delims,
453 		const char *bracks,
454 		char *found)
455 {
456     char *p = *pstr;
457     char *start = NULL;
458     BOOL get_blanks, skip_comments;
459     BOOL get_comments;
460     BOOL get_closing_char_too = FALSE;
461     char closer;
462 
463     if (isEmpty(pstr))
464 	return NULL;
465     if (!delims)
466 	delims = " ;,=";
467     if (!bracks)
468 	bracks = "<\"";
469 
470     get_blanks = (BOOL) (!strchr(delims, ' ') && !strchr(bracks, ' '));
471     get_comments = (BOOL) (strchr(bracks, '(') != NULL);
472     skip_comments = (BOOL) (!get_comments && !strchr(delims, '(') && !get_blanks);
473 #define skipWHITE(c) (!get_blanks && WHITE(c))
474 
475     while (*p && skipWHITE(*p))
476 	p++;			/* Strip white space */
477     if (!*p) {
478 	*pstr = p;
479 	if (found)
480 	    *found = '\0';
481 	return NULL;		/* No first field */
482     }
483     while (1) {
484 	/* Strip white space and other delimiters */
485 	while (*p && (skipWHITE(*p) || strchr(delims, *p)))
486 	    p++;
487 	if (!*p) {
488 	    *pstr = p;
489 	    if (found)
490 		*found = *(p - 1);
491 	    return NULL;	/* No field */
492 	}
493 
494 	if (*p == '(' && (skip_comments || get_comments)) {	/* Comment */
495 	    int comment_level = 0;
496 
497 	    if (get_comments && !start)
498 		start = p + 1;
499 	    for (; *p && (*p != ')' || --comment_level > 0); p++) {
500 		if (*p == '(')
501 		    comment_level++;
502 		else if (*p == '"') {	/* quoted field within Comment */
503 		    for (p++; *p && *p != '"'; p++)
504 			if (*p == '\\' && *(p + 1))
505 			    p++;	/* Skip escaped chars */
506 		    if (!*p)
507 			break;	/* (invalid) end of string found, leave */
508 		}
509 		if (*p == '\\' && *(p + 1))
510 		    p++;	/* Skip escaped chars */
511 	    }
512 	    if (get_comments)
513 		break;
514 	    if (*p)
515 		p++;
516 	    if (get_closing_char_too) {
517 		if (!*p || (!strchr(bracks, *p) && strchr(delims, *p))) {
518 		    break;
519 		} else
520 		    get_closing_char_too = (BOOL) (strchr(bracks, *p) != NULL);
521 	    }
522 	} else if (strchr(bracks, *p)) {	/* quoted or bracketed field */
523 	    switch (*p) {
524 	    case '<':
525 		closer = '>';
526 		break;
527 	    case '[':
528 		closer = ']';
529 		break;
530 	    case '{':
531 		closer = '}';
532 		break;
533 	    case ':':
534 		closer = ';';
535 		break;
536 	    default:
537 		closer = *p;
538 	    }
539 	    if (!start)
540 		start = ++p;
541 	    for (; *p && *p != closer; p++)
542 		if (*p == '\\' && *(p + 1))
543 		    p++;	/* Skip escaped chars */
544 	    if (get_closing_char_too) {
545 		p++;
546 		if (!*p || (!strchr(bracks, *p) && strchr(delims, *p))) {
547 		    break;
548 		} else
549 		    get_closing_char_too = (BOOL) (strchr(bracks, *p) != NULL);
550 	    } else
551 		break;		/* kr95-10-9: needs to stop here */
552 	} else {		/* Spool field */
553 	    if (!start)
554 		start = p;
555 	    while (*p && !skipWHITE(*p) && !strchr(bracks, *p) &&
556 		   !strchr(delims, *p))
557 		p++;
558 	    if (*p && strchr(bracks, *p)) {
559 		get_closing_char_too = TRUE;
560 	    } else {
561 		if (*p == '(' && skip_comments) {
562 		    *pstr = p;
563 		    HTNextTok(pstr, NULL, "(", found);	/*      Advance pstr */
564 		    *p = '\0';
565 		    if (*pstr && **pstr)
566 			(*pstr)++;
567 		    return start;
568 		}
569 		break;		/* Got it */
570 	    }
571 	}
572     }
573     if (found)
574 	*found = *p;
575 
576     if (*p)
577 	*p++ = '\0';
578     *pstr = p;
579     return start;
580 }
581 
HTAlloc(char * ptr,size_t length)582 static char *HTAlloc(char *ptr, size_t length)
583 {
584     if (ptr != 0)
585 	ptr = (char *) realloc(ptr, length);
586     else
587 	ptr = (char *) malloc(length);
588     if (ptr == 0)
589 	outofmem(__FILE__, "HTAlloc");
590     assert(ptr != NULL);
591     return ptr;
592 }
593 
594 /*
595  * If SAVE_TIME_NOT_SPACE is defined, StrAllocVsprintf will hang on to
596  * its temporary string buffers instead of allocating and freeing them
597  * in each invocation.  They only grow and never shrink, and won't be
598  * cleaned up on exit. - kw
599  */
600 #if defined(_REENTRANT) || defined(_THREAD_SAFE) || defined(LY_FIND_LEAKS)
601 #undef SAVE_TIME_NOT_SPACE
602 #endif
603 
604 /*
605  * Replacement for sprintf, allocates buffer on the fly according to what's
606  * needed for its arguments.  Unlike sprintf, this always concatenates to the
607  * destination buffer, so we do not have to provide both flavors.
608  */
609 typedef enum {
610     Flags,
611     Width,
612     Prec,
613     Type,
614     Format
615 } PRINTF;
616 
617 #define VA_INTGR(type) ival = (int)    va_arg((*ap), type)
618 #define VA_FLOAT(type) fval = (double) va_arg((*ap), type)
619 #define VA_POINT(type) pval = (char *) va_arg((*ap), type)
620 
621 #define NUM_WIDTH 10		/* allow for width substituted for "*" in "%*s" */
622 		/* also number of chars assumed to be needed in addition
623 		   to a given precision in floating point formats */
624 
625 #define GROW_EXPR(n) (((n) * 3) / 2)
626 #define GROW_SIZE 256
627 
StrAllocVsprintf(char ** pstr,size_t dst_len,const char * fmt,va_list * ap)628 PUBLIC_IF_FIND_LEAKS char *StrAllocVsprintf(char **pstr,
629 					    size_t dst_len,
630 					    const char *fmt,
631 					    va_list * ap)
632 {
633 #ifdef HAVE_VASPRINTF
634     /*
635      * Use vasprintf() if we have it, since it is simplest.
636      */
637     char *result = 0;
638     char *temp = 0;
639 
640     /* discard old destination if no length was given */
641     if (pstr && !dst_len) {
642 	if (*pstr)
643 	    FREE(*pstr);
644     }
645 
646     if (vasprintf(&temp, fmt, *ap) >= 0) {
647 	if (dst_len != 0) {
648 	    size_t src_len = strlen(temp);
649 	    size_t new_len = dst_len + src_len + 1;
650 
651 	    result = HTAlloc(pstr ? *pstr : 0, new_len);
652 	    if (result != 0) {
653 		strcpy(result + dst_len, temp);
654 	    }
655 	    (free) (temp);
656 	} else {
657 	    result = temp;
658 	    mark_malloced(temp, strlen(temp));
659 	}
660     }
661 
662     if (pstr != 0)
663 	*pstr = result;
664 
665     return result;
666 #else /* !HAVE_VASPRINTF */
667     /*
668      * If vasprintf() is not available, this works - but does not implement
669      * the POSIX '$' formatting character which may be used in some of the
670      * ".po" files.
671      */
672 #ifdef SAVE_TIME_NOT_SPACE
673     static size_t tmp_len = 0;
674     static size_t fmt_len = 0;
675     static char *tmp_ptr = NULL;
676     static char *fmt_ptr = NULL;
677 
678 #else
679     size_t tmp_len = GROW_SIZE;
680     char *tmp_ptr = 0;
681     char *fmt_ptr;
682 #endif /* SAVE_TIME_NOT_SPACE */
683     size_t have, need;
684     char *dst_ptr = *pstr;
685     const char *format = fmt;
686 
687     if (isEmpty(fmt))
688 	return 0;
689 
690     need = strlen(fmt) + 1;
691 #ifdef SAVE_TIME_NOT_SPACE
692     if (!fmt_ptr || fmt_len < need * NUM_WIDTH) {
693 	fmt_ptr = HTAlloc(fmt_ptr, fmt_len = need * NUM_WIDTH);
694     }
695     if (!tmp_ptr || tmp_len < GROW_SIZE) {
696 	tmp_ptr = HTAlloc(tmp_ptr, tmp_len = GROW_SIZE);
697     }
698 #else
699     if ((fmt_ptr = malloc(need * NUM_WIDTH)) == 0
700 	|| (tmp_ptr = malloc(tmp_len)) == 0) {
701 	outofmem(__FILE__, "StrAllocVsprintf");
702 	assert(fmt_ptr != NULL);
703 	assert(tmp_ptr != NULL);
704     }
705 #endif /* SAVE_TIME_NOT_SPACE */
706 
707     if (dst_ptr == 0) {
708 	dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
709     } else {
710 	have = strlen(dst_ptr) + 1;
711 	need += dst_len;
712 	if (have < need)
713 	    dst_ptr = HTAlloc(dst_ptr, have = GROW_SIZE + need);
714     }
715 
716     while (*fmt != '\0') {
717 	if (*fmt == '%') {
718 	    static char dummy[] = "";
719 	    PRINTF state = Flags;
720 	    char *pval = dummy;	/* avoid const-cast */
721 	    double fval = 0.0;
722 	    int done = FALSE;
723 	    int ival = 0;
724 	    int prec = -1;
725 	    int type = 0;
726 	    int used = 0;
727 	    int width = -1;
728 	    size_t f = 0;
729 
730 	    fmt_ptr[f++] = *fmt;
731 	    while (*++fmt != '\0' && !done) {
732 		fmt_ptr[f++] = *fmt;
733 
734 		if (isdigit(UCH(*fmt))) {
735 		    int num = *fmt - '0';
736 
737 		    if (state == Flags && num != 0)
738 			state = Width;
739 		    if (state == Width) {
740 			if (width < 0)
741 			    width = 0;
742 			width = (width * 10) + num;
743 		    } else if (state == Prec) {
744 			if (prec < 0)
745 			    prec = 0;
746 			prec = (prec * 10) + num;
747 		    }
748 		} else if (*fmt == '*') {
749 		    VA_INTGR(int);
750 
751 		    if (state == Flags)
752 			state = Width;
753 		    if (state == Width) {
754 			width = ival;
755 		    } else if (state == Prec) {
756 			prec = ival;
757 		    }
758 		    sprintf(&fmt_ptr[--f], "%d", ival);
759 		    f = strlen(fmt_ptr);
760 		} else if (isalpha(UCH(*fmt))) {
761 		    done = TRUE;
762 		    switch (*fmt) {
763 		    case 'Z':	/* FALLTHRU */
764 		    case 'h':	/* FALLTHRU */
765 		    case 'l':	/* FALLTHRU */
766 		    case 'L':	/* FALLTHRU */
767 			done = FALSE;
768 			type = *fmt;
769 			break;
770 		    case 'o':	/* FALLTHRU */
771 		    case 'i':	/* FALLTHRU */
772 		    case 'd':	/* FALLTHRU */
773 		    case 'u':	/* FALLTHRU */
774 		    case 'x':	/* FALLTHRU */
775 		    case 'X':	/* FALLTHRU */
776 			if (type == 'l')
777 			    VA_INTGR(long);
778 
779 			else if (type == 'Z')
780 			    VA_INTGR(size_t);
781 
782 			else
783 			    VA_INTGR(int);
784 
785 			used = 'i';
786 			break;
787 		    case 'f':	/* FALLTHRU */
788 		    case 'e':	/* FALLTHRU */
789 		    case 'E':	/* FALLTHRU */
790 		    case 'g':	/* FALLTHRU */
791 		    case 'G':	/* FALLTHRU */
792 			VA_FLOAT(double);
793 
794 			used = 'f';
795 			break;
796 		    case 'c':
797 			VA_INTGR(int);
798 
799 			used = 'c';
800 			break;
801 		    case 's':
802 			VA_POINT(char *);
803 
804 			if (prec < 0)
805 			    prec = strlen(pval);
806 			used = 's';
807 			break;
808 		    case 'p':
809 			VA_POINT(void *);
810 
811 			used = 'p';
812 			break;
813 		    case 'n':
814 			VA_POINT(int *);
815 
816 			used = 0;
817 			break;
818 		    default:
819 			CTRACE((tfp, "unknown format character '%c' in %s\n",
820 				*fmt, format));
821 			break;
822 		    }
823 		} else if (*fmt == '.') {
824 		    state = Prec;
825 		} else if (*fmt == '%') {
826 		    done = TRUE;
827 		    used = '%';
828 		}
829 	    }
830 	    fmt_ptr[f] = '\0';
831 
832 	    if (prec > 0) {
833 		switch (used) {
834 		case 'f':
835 		    if (width < prec + NUM_WIDTH)
836 			width = prec + NUM_WIDTH;
837 		    /* FALLTHRU */
838 		case 'i':
839 		    /* FALLTHRU */
840 		case 'p':
841 		    if (width < prec + 2)
842 			width = prec + 2;	/* leading sign/space/zero, "0x" */
843 		    break;
844 		case 'c':
845 		    break;
846 		case '%':
847 		    break;
848 		default:
849 		    if (width < prec)
850 			width = prec;
851 		    break;
852 		}
853 	    }
854 	    if (width >= (int) tmp_len) {
855 		tmp_len = GROW_EXPR(tmp_len + width);
856 		tmp_ptr = HTAlloc(tmp_ptr, tmp_len);
857 	    }
858 
859 	    switch (used) {
860 	    case 'i':
861 	    case 'c':
862 		sprintf(tmp_ptr, fmt_ptr, ival);
863 		break;
864 	    case 'f':
865 		sprintf(tmp_ptr, fmt_ptr, fval);
866 		break;
867 	    default:
868 		sprintf(tmp_ptr, fmt_ptr, pval);
869 		break;
870 	    }
871 	    need = dst_len + strlen(tmp_ptr) + 1;
872 	    if (need >= have) {
873 		dst_ptr = HTAlloc(dst_ptr, have = GROW_EXPR(need));
874 	    }
875 	    strcpy(dst_ptr + dst_len, tmp_ptr);
876 	    dst_len += strlen(tmp_ptr);
877 	} else {
878 	    if ((dst_len + 2) >= have) {
879 		dst_ptr = HTAlloc(dst_ptr, (have += GROW_SIZE));
880 	    }
881 	    dst_ptr[dst_len++] = *fmt++;
882 	}
883     }
884 
885 #ifndef SAVE_TIME_NOT_SPACE
886     FREE(tmp_ptr);
887     FREE(fmt_ptr);
888 #endif
889     dst_ptr[dst_len] = '\0';
890     if (pstr)
891 	*pstr = dst_ptr;
892     return (dst_ptr);
893 #endif /* HAVE_VASPRINTF */
894 }
895 #undef SAVE_TIME_NOT_SPACE
896 
897 /*
898  * Replacement for sprintf, allocates buffer on the fly according to what's
899  * needed for its arguments.  Unlike sprintf, this always concatenates to the
900  * destination buffer.
901  */
902 /* Note: if making changes, also check the memory tracking version
903  * LYLeakHTSprintf in LYLeaks.c. - kw */
904 #ifdef HTSprintf		/* if hidden by LYLeaks stuff */
905 #undef HTSprintf
906 #endif
HTSprintf(char ** pstr,const char * fmt,...)907 char *HTSprintf(char **pstr, const char *fmt,...)
908 {
909     char *result = 0;
910     size_t inuse = 0;
911     va_list ap;
912 
913     LYva_start(ap, fmt);
914     {
915 	if (pstr != 0 && *pstr != 0)
916 	    inuse = strlen(*pstr);
917 	result = StrAllocVsprintf(pstr, inuse, fmt, &ap);
918     }
919     va_end(ap);
920 
921     return (result);
922 }
923 
924 /*
925  * Replacement for sprintf, allocates buffer on the fly according to what's
926  * needed for its arguments.  Like sprintf, this always resets the destination
927  * buffer.
928  */
929 /* Note: if making changes, also check the memory tracking version
930  * LYLeakHTSprintf0 in LYLeaks.c. - kw */
931 #ifdef HTSprintf0		/* if hidden by LYLeaks stuff */
932 #undef HTSprintf0
933 #endif
HTSprintf0(char ** pstr,const char * fmt,...)934 char *HTSprintf0(char **pstr, const char *fmt,...)
935 {
936     char *result = 0;
937     va_list ap;
938 
939     LYva_start(ap, fmt);
940     {
941 	result = StrAllocVsprintf(pstr, (size_t) 0, fmt, &ap);
942     }
943     va_end(ap);
944 
945     return (result);
946 }
947 
948 /*
949  * Returns a quoted or escaped form of the given parameter, suitable for use in
950  * a command string.
951  */
952 #if USE_QUOTED_PARAMETER
953 #define S_QUOTE '\''
954 #define D_QUOTE '"'
HTQuoteParameter(const char * parameter)955 char *HTQuoteParameter(const char *parameter)
956 {
957     size_t i;
958     size_t last;
959     size_t n = 0;
960     size_t quoted = 0;
961     char *result;
962 
963     if (parameter == 0)
964 	parameter = "";
965 
966     last = strlen(parameter);
967     for (i = 0; i < last; ++i)
968 	if (strchr("\\&#$^*?(){}<>\"';`|", parameter[i]) != 0
969 	    || isspace(UCH(parameter[i])))
970 	    ++quoted;
971 
972     result = (char *) malloc(last + 5 * quoted + 3);
973     if (result == NULL)
974 	outofmem(__FILE__, "HTQuoteParameter");
975 
976     assert(result != NULL);
977 
978     n = 0;
979 #if (USE_QUOTED_PARAMETER == 1)
980     /*
981      * Only double-quotes are used in Win32/DOS -TD
982      */
983     if (quoted)
984 	result[n++] = D_QUOTE;
985     for (i = 0; i < last; i++) {
986 	result[n++] = parameter[i];
987     }
988     if (quoted)
989 	result[n++] = D_QUOTE;
990 #else
991     if (quoted)
992 	result[n++] = S_QUOTE;
993     for (i = 0; i < last; i++) {
994 	if (parameter[i] == S_QUOTE) {
995 	    result[n++] = S_QUOTE;
996 	    result[n++] = D_QUOTE;
997 	    result[n++] = parameter[i];
998 	    result[n++] = D_QUOTE;
999 	    result[n++] = S_QUOTE;
1000 	} else {
1001 	    /* Note:  No special handling of other characters, including
1002 	       backslash, since we are constructing a single-quoted string!
1003 	       Backslash has no special escape meaning within those for sh
1004 	       and compatible shells, so trying to escape a backslash by
1005 	       doubling it is unnecessary and would be interpreted by the
1006 	       shell as an additional data character. - kw 2000-05-02
1007 	     */
1008 	    result[n++] = parameter[i];
1009 	}
1010     }
1011     if (quoted)
1012 	result[n++] = S_QUOTE;
1013 #endif
1014     result[n] = '\0';
1015     return result;
1016 }
1017 #endif
1018 
1019 #define HTIsParam(string) ((string[0] == '%' && string[1] == 's'))
1020 
1021 /*
1022  * Returns the number of "%s" tokens in a system command-template.
1023  */
HTCountCommandArgs(const char * command)1024 int HTCountCommandArgs(const char *command)
1025 {
1026     int number = 0;
1027 
1028     while (command[0] != 0) {
1029 	if (HTIsParam(command))
1030 	    number++;
1031 	command++;
1032     }
1033     return number;
1034 }
1035 
1036 /*
1037  * Returns a pointer into the given string after the given parameter number
1038  */
HTAfterCommandArg(const char * command,int number)1039 static const char *HTAfterCommandArg(const char *command,
1040 				     int number)
1041 {
1042     while (number > 0) {
1043 	if (command[0] != 0) {
1044 	    if (HTIsParam(command)) {
1045 		number--;
1046 		command++;
1047 	    }
1048 	    command++;
1049 	} else {
1050 	    break;
1051 	}
1052     }
1053     return command;
1054 }
1055 
1056 /*
1057  * Like HTAddParam, but the parameter may be an environment variable, which we
1058  * will expand and append.  Do this only for things like the command-verb,
1059  * where we obtain the parameter from the user's configuration.  Any quoting
1060  * required for the environment variable has to be done within its value, e.g.,
1061  *
1062  *	setenv EDITOR 'xvile -name "No such class"'
1063  *
1064  * This is useful only when we quote parameters, of course.
1065  */
1066 #if USE_QUOTED_PARAMETER
HTAddXpand(char ** result,const char * command,int number,const char * parameter)1067 void HTAddXpand(char **result,
1068 		const char *command,
1069 		int number,
1070 		const char *parameter)
1071 {
1072     if (number > 0) {
1073 	const char *last = HTAfterCommandArg(command, number - 1);
1074 	const char *next = last;
1075 
1076 	if (number <= 1) {
1077 	    FREE(*result);
1078 	}
1079 
1080 	while (next[0] != 0) {
1081 	    if (HTIsParam(next)) {
1082 		if (next != last) {
1083 		    size_t len = ((size_t) (next - last)
1084 				  + ((*result != 0)
1085 				     ? strlen(*result)
1086 				     : 0));
1087 
1088 		    HTSACat(result, last);
1089 		    (*result)[len] = 0;
1090 		}
1091 		HTSACat(result, parameter);
1092 		CTRACE((tfp, "PARAM-EXP:%s\n", *result));
1093 		return;
1094 	    }
1095 	    next++;
1096 	}
1097     }
1098 }
1099 #endif /* USE_QUOTED_PARAMETER */
1100 
1101 /*
1102  * Append string to a system command that we are constructing, without quoting.
1103  * We're given the index of the newest parameter we're processing.  Zero
1104  * indicates none, so a value of '1' indicates that we copy from the beginning
1105  * of the command string up to the first parameter, substitute the quoted
1106  * parameter and return the result.
1107  *
1108  * Parameters are substituted at "%s" tokens, like printf.  Other printf-style
1109  * tokens are not substituted; they are passed through without change.
1110  */
HTAddToCmd(char ** result,const char * command,int number,const char * string)1111 void HTAddToCmd(char **result,
1112 		const char *command,
1113 		int number,
1114 		const char *string)
1115 {
1116     if (number > 0) {
1117 	const char *last = HTAfterCommandArg(command, number - 1);
1118 	const char *next = last;
1119 
1120 	if (number <= 1) {
1121 	    FREE(*result);
1122 	}
1123 	if (string == 0)
1124 	    string = "";
1125 	while (next[0] != 0) {
1126 	    if (HTIsParam(next)) {
1127 		if (next != last) {
1128 		    size_t len = ((size_t) (next - last)
1129 				  + ((*result != 0)
1130 				     ? strlen(*result)
1131 				     : 0));
1132 
1133 		    HTSACat(result, last);
1134 		    (*result)[len] = 0;
1135 		}
1136 		HTSACat(result, string);
1137 		CTRACE((tfp, "PARAM-ADD:%s\n", *result));
1138 		return;
1139 	    }
1140 	    next++;
1141 	}
1142     }
1143 }
1144 
1145 /*
1146  * Append string-parameter to a system command that we are constructing.  The
1147  * string is a complete parameter (which is a necessary assumption so we can
1148  * quote it properly).
1149  */
HTAddParam(char ** result,const char * command,int number,const char * parameter)1150 void HTAddParam(char **result,
1151 		const char *command,
1152 		int number,
1153 		const char *parameter)
1154 {
1155     if (number > 0) {
1156 #if USE_QUOTED_PARAMETER
1157 	char *quoted = HTQuoteParameter(parameter);
1158 
1159 	HTAddToCmd(result, command, number, quoted);
1160 	FREE(quoted);
1161 #else
1162 	HTAddToCmd(result, command, number, parameter);
1163 #endif
1164     }
1165 }
1166 
1167 /*
1168  * Append the remaining command-string to a system command (compare with
1169  * HTAddParam).  Any remaining "%s" tokens are copied as empty strings.
1170  */
HTEndParam(char ** result,const char * command,int number)1171 void HTEndParam(char **result,
1172 		const char *command,
1173 		int number)
1174 {
1175     const char *last;
1176     int count;
1177 
1178     count = HTCountCommandArgs(command);
1179     if (count < number)
1180 	number = count;
1181     last = HTAfterCommandArg(command, number);
1182     if (last[0] != 0) {
1183 	HTSACat(result, last);
1184     }
1185     CTRACE((tfp, "PARAM-END:%s\n", *result));
1186 }
1187 
1188 /* Binary-strings (may have embedded nulls).  Some modules (HTGopher) assume
1189  * there is a null on the end, anyway.
1190  */
1191 
1192 /* (Re)allocate a bstring, e.g., to increase its buffer size for ad hoc
1193  * operations.
1194  */
HTSABAlloc(bstring ** dest,int len)1195 void HTSABAlloc(bstring **dest, int len)
1196 {
1197     if (*dest == 0) {
1198 	*dest = typecalloc(bstring);
1199 
1200 	if (*dest == 0)
1201 	    outofmem(__FILE__, "HTSABAlloc");
1202     }
1203 
1204     if ((*dest)->len != len) {
1205 	(*dest)->str = typeRealloc(char, (*dest)->str, len);
1206 
1207 	if ((*dest)->str == 0)
1208 	    outofmem(__FILE__, "HTSABAlloc");
1209 
1210 	(*dest)->len = len;
1211     }
1212 }
1213 
1214 /* Allocate a new bstring, and return it.
1215 */
HTSABCopy(bstring ** dest,const char * src,int len)1216 void HTSABCopy(bstring **dest, const char *src,
1217 	       int len)
1218 {
1219     bstring *t;
1220     unsigned need = (unsigned) (len + 1);
1221 
1222     CTRACE2(TRACE_BSTRING,
1223 	    (tfp, "HTSABCopy(%p, %p, %d)\n",
1224 	     (void *) dest, (const void *) src, len));
1225     HTSABFree(dest);
1226     if (src) {
1227 	if (TRACE_BSTRING) {
1228 	    CTRACE((tfp, "===    %4d:", len));
1229 	    trace_bstring2(src, len);
1230 	    CTRACE((tfp, "\n"));
1231 	}
1232 	if ((t = (bstring *) malloc(sizeof(bstring))) == NULL)
1233 	      outofmem(__FILE__, "HTSABCopy");
1234 
1235 	assert(t != NULL);
1236 
1237 	if ((t->str = typeMallocn(char, need)) == NULL)
1238 	      outofmem(__FILE__, "HTSABCopy");
1239 
1240 	assert(t->str != NULL);
1241 
1242 	MemCpy(t->str, src, len);
1243 	t->len = len;
1244 	t->str[t->len] = '\0';
1245 	*dest = t;
1246     }
1247     if (TRACE_BSTRING) {
1248 	CTRACE((tfp, "=>     %4d:", BStrLen(*dest)));
1249 	trace_bstring(*dest);
1250 	CTRACE((tfp, "\n"));
1251     }
1252 }
1253 
1254 /*
1255  * Initialize with a null-terminated string (discards the null).
1256  */
HTSABCopy0(bstring ** dest,const char * src)1257 void HTSABCopy0(bstring **dest, const char *src)
1258 {
1259     HTSABCopy(dest, src, (int) strlen(src));
1260 }
1261 
1262 /*
1263  * Append a block of memory to a bstring.
1264  */
HTSABCat(bstring ** dest,const char * src,int len)1265 void HTSABCat(bstring **dest, const char *src,
1266 	      int len)
1267 {
1268     bstring *t = *dest;
1269 
1270     CTRACE2(TRACE_BSTRING,
1271 	    (tfp, "HTSABCat(%p, %p, %d)\n",
1272 	     (void *) dest, (const void *) src, len));
1273     if (src) {
1274 	unsigned need = (unsigned) (len + 1);
1275 
1276 	if (TRACE_BSTRING) {
1277 	    CTRACE((tfp, "===    %4d:", len));
1278 	    trace_bstring2(src, len);
1279 	    CTRACE((tfp, "\n"));
1280 	}
1281 	if (t) {
1282 	    unsigned length = (unsigned) t->len + need;
1283 
1284 	    t->str = typeRealloc(char, t->str, length);
1285 	} else {
1286 	    if ((t = typecalloc(bstring)) == NULL)
1287 		  outofmem(__FILE__, "HTSACat");
1288 
1289 	    assert(t != NULL);
1290 
1291 	    t->str = typeMallocn(char, need);
1292 	}
1293 	if (t->str == NULL)
1294 	    outofmem(__FILE__, "HTSACat");
1295 
1296 	assert(t->str != NULL);
1297 
1298 	MemCpy(t->str + t->len, src, len);
1299 	t->len += len;
1300 	t->str[t->len] = '\0';
1301 	*dest = t;
1302     }
1303     if (TRACE_BSTRING) {
1304 	CTRACE((tfp, "=>     %4d:", BStrLen(*dest)));
1305 	trace_bstring(*dest);
1306 	CTRACE((tfp, "\n"));
1307     }
1308 }
1309 
1310 /*
1311  * Append a null-terminated string (discards the null).
1312  */
HTSABCat0(bstring ** dest,const char * src)1313 void HTSABCat0(bstring **dest, const char *src)
1314 {
1315     HTSABCat(dest, src, (int) strlen(src));
1316 }
1317 
1318 /*
1319  * Compare two bstrings for equality
1320  */
HTSABEql(bstring * a,bstring * b)1321 BOOL HTSABEql(bstring *a, bstring *b)
1322 {
1323     unsigned len_a = (unsigned) ((a != 0) ? a->len : 0);
1324     unsigned len_b = (unsigned) ((b != 0) ? b->len : 0);
1325 
1326     if (len_a == len_b) {
1327 	if (len_a == 0
1328 	    || MemCmp(a->str, b->str, a->len) == 0)
1329 	    return TRUE;
1330     }
1331     return FALSE;
1332 }
1333 
1334 /*
1335  * Deallocate a bstring.
1336  */
HTSABFree(bstring ** ptr)1337 void HTSABFree(bstring **ptr)
1338 {
1339     if (*ptr != NULL) {
1340 	FREE((*ptr)->str);
1341 	FREE(*ptr);
1342 	*ptr = NULL;
1343     }
1344 }
1345 
1346 /*
1347  * Use this function to perform formatted sprintf onto the end of a bstring.
1348  * The bstring may contain embedded nulls; the formatted portions must not.
1349  */
HTBprintf(bstring ** pstr,const char * fmt,...)1350 bstring *HTBprintf(bstring **pstr, const char *fmt,...)
1351 {
1352     bstring *result = 0;
1353     char *temp = 0;
1354     va_list ap;
1355 
1356     LYva_start(ap, fmt);
1357     {
1358 	temp = StrAllocVsprintf(&temp, (size_t) 0, fmt, &ap);
1359 	if (non_empty(temp)) {
1360 	    HTSABCat(pstr, temp, (int) strlen(temp));
1361 	}
1362 	FREE(temp);
1363 	result = *pstr;
1364     }
1365     va_end(ap);
1366 
1367     return (result);
1368 }
1369 
1370 /*
1371  * Write binary-data to the logfile, making it safe for most editors to view.
1372  * That is most, since we do not restrict line-length.  Nulls and other
1373  * non-printing characters are addressed.
1374  */
trace_bstring2(const char * text,int size)1375 void trace_bstring2(const char *text,
1376 		    int size)
1377 {
1378     int n;
1379 
1380     if (text != 0) {
1381 	for (n = 0; n < size; ++n) {
1382 	    int ch = UCH(text[n]);
1383 
1384 	    switch (ch) {
1385 	    case '\\':
1386 		fputs("\\\\", tfp);
1387 		break;
1388 	    case '\r':
1389 		fputs("\\r", tfp);
1390 		break;
1391 	    case '\t':
1392 		fputs("\\t", tfp);
1393 		break;
1394 	    case '\f':
1395 		fputs("\\f", tfp);
1396 		break;
1397 	    default:
1398 		if (isprint(ch) || isspace(ch)) {
1399 		    fputc(ch, tfp);
1400 		} else {
1401 		    fprintf(tfp, "\\%03o", ch);
1402 		}
1403 		break;
1404 	    }
1405 	}
1406     }
1407 }
1408 
trace_bstring(bstring * data)1409 void trace_bstring(bstring *data)
1410 {
1411     trace_bstring2(BStrData(data), BStrLen(data));
1412 }
1413