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