1 /*
2 * Copyright (C) 1997-2004, Michael Jennings
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies of the Software, its documentation and marketing & publicity
13 * materials, and acknowledgment shall be given in the documentation, materials
14 * and software packages that this Software was used.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23
24 /**
25 * @file strings.c
26 * String Utility Routine Source File
27 *
28 * This file contains string utility functions.
29 *
30 * @author Michael Jennings <mej@eterm.org>
31 */
32
33 static const char __attribute__((unused)) cvs_ident[] = "$Id: strings.c,v 1.25 2005/03/07 22:29:07 mej Exp $";
34
35 #ifdef HAVE_CONFIG_H
36 # include <config.h>
37 #endif
38
39 #include <libast_internal.h>
40
41 #if !(HAVE_MEMMEM)
42 /* Find first occurance of bytestring needle of size needlelen in memory region
43 haystack of size haystacklen */
44 void *
memmem(const void * haystack,register size_t haystacklen,const void * needle,register size_t needlelen)45 memmem(const void *haystack, register size_t haystacklen, const void *needle, register size_t needlelen)
46 {
47 register char *hs = (char *) haystack;
48 register char *n = (char *) needle;
49 register unsigned long i;
50 register size_t len = haystacklen - needlelen;
51
52 REQUIRE_RVAL(needle != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
53 REQUIRE_RVAL(haystack != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
54 REQUIRE_RVAL(needlelen > 0, SPIF_NULL_TYPE(ptr));
55 REQUIRE_RVAL(haystacklen > 0, SPIF_NULL_TYPE(ptr));
56 REQUIRE_RVAL(haystacklen > needlelen, SPIF_NULL_TYPE(ptr));
57 for (i = 0; i < len; i++) {
58 if (!memcmp(hs + i, n, needlelen)) {
59 return (hs + i);
60 }
61 }
62 return (NULL);
63 }
64 #endif
65
66 #if !(HAVE_STRNLEN)
67 size_t
strnlen(register const char * s,size_t maxlen)68 strnlen(register const char *s, size_t maxlen)
69 {
70 register size_t n;
71
72 REQUIRE_RVAL(s, SPIF_CAST_C(size_t) 0);
73 for (n = 0; *s && n < maxlen; s++, n++);
74 return n;
75 }
76 #endif
77
78 #if !(HAVE_USLEEP)
79 void
usleep(unsigned long usec)80 usleep(unsigned long usec)
81 {
82 struct timeval delay;
83
84 delay.tv_sec = 0;
85 delay.tv_usec = usec;
86 select(0, NULL, NULL, NULL, &delay);
87
88 }
89
90 #endif
91
92 /***** Not needed ******
93 #if !(HAVE_NANOSLEEP)
94 __inline__ void
95 nanosleep(unsigned long nsec) {
96 usleep(nsec / 1000);
97 }
98 #endif
99 ************************/
100
101 #if !(HAVE_STRCASESTR)
102 char *
strcasestr(const char * haystack,register const char * needle)103 strcasestr(const char *haystack, register const char *needle)
104 {
105 register const spif_uchar_t *t;
106 register size_t len;
107
108 REQUIRE_RVAL(needle != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
109 REQUIRE_RVAL(haystack != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
110 len = strlen(needle);
111 for (t = SPIF_CAST_PTR(uchar) haystack; t && *t; t++) {
112 if (!strncasecmp(t, needle, len)) {
113 return ((char *) t);
114 }
115 }
116 return (NULL);
117 }
118 #endif
119
120 #if !(HAVE_STRCASECHR)
121 char *
strcasechr(const char * haystack,register const char needle)122 strcasechr(const char *haystack, register const char needle)
123 {
124 register const char *t;
125
126 REQUIRE_RVAL(haystack != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
127 for (t = haystack; t && *t; t++) {
128 if (tolower(*t) == tolower(needle)) {
129 return ((char *) t);
130 }
131 }
132 return (NULL);
133 }
134 #endif
135
136 #if !(HAVE_STRCASEPBRK)
137 char *
strcasepbrk(const char * haystack,register const char * needle)138 strcasepbrk(const char *haystack, register const char *needle)
139 {
140 register const char *t;
141
142 REQUIRE_RVAL(needle != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
143 REQUIRE_RVAL(haystack != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
144 for (t = haystack; t && *t; t++) {
145 if (strcasechr(needle, *t)) {
146 return ((char *) t);
147 }
148 }
149 return (NULL);
150 }
151 #endif
152
153 #if !(HAVE_STRREV)
154 char *
strrev(register char * str)155 strrev(register char *str)
156 {
157 register int i, j;
158
159 REQUIRE_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
160 i = strlen(str);
161 for (j = 0, i--; i > j; i--, j++) {
162 (void) SWAP(str[j], str[i]);
163 }
164 return (str);
165
166 }
167 #endif
168
169 #if !(HAVE_STRSEP)
170 char *
strsep(char ** str,register char * sep)171 strsep(char **str, register char *sep)
172 {
173 register char *s = *str;
174 char *sptr;
175
176 REQUIRE_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
177 REQUIRE_RVAL(sep != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
178 D_STRINGS(("strsep(%s, %s) called.\n", *str, sep));
179 sptr = s;
180 for (; *s && !strchr(sep, *s); s++);
181 if (!*s) {
182 if (s != sptr) {
183 *str = s;
184 D_STRINGS(("Reached end of string with token \"%s\" in buffer\n", sptr));
185 return (sptr);
186 } else {
187 D_STRINGS(("Reached end of string\n"));
188 return ((char *) NULL);
189 }
190 }
191 *s = 0;
192 *str = s + 1;
193 D_STRINGS(("Got token \"%s\", *str == \"%s\"\n", sptr, *str));
194 return (sptr);
195 }
196 #endif
197
198 /**
199 * Safer strncpy() with no NUL padding or wasted calculations.
200 */
201 spif_bool_t
spiftool_safe_strncpy(spif_charptr_t dest,const spif_charptr_t src,spif_int32_t size)202 spiftool_safe_strncpy(spif_charptr_t dest, const spif_charptr_t src, spif_int32_t size)
203 {
204 spif_char_t c;
205 spif_charptr_t s = src, pbuff = dest;
206 spif_charptr_t max_pbuff = dest + size - 1;
207
208 ASSERT_RVAL(!SPIF_PTR_ISNULL(dest), FALSE);
209 REQUIRE_RVAL(!SPIF_PTR_ISNULL(src), FALSE);
210 REQUIRE_RVAL(size > 0, FALSE);
211
212 for (; (c = *s) && (pbuff < max_pbuff); s++, pbuff++) {
213 *pbuff = c;
214 }
215 *pbuff = 0;
216 return ((c == 0) ? (TRUE) : (FALSE));
217 }
218
219 /**
220 * Variant of strncat() which uses the safe strncpy() replacement above.
221 */
222 spif_bool_t
spiftool_safe_strncat(spif_charptr_t dest,const spif_charptr_t src,spif_int32_t size)223 spiftool_safe_strncat(spif_charptr_t dest, const spif_charptr_t src, spif_int32_t size)
224 {
225 spif_int32_t len;
226
227 ASSERT_RVAL(!SPIF_PTR_ISNULL(dest), FALSE);
228 REQUIRE_RVAL(!SPIF_PTR_ISNULL(src), FALSE);
229 REQUIRE_RVAL(size > 0, FALSE);
230
231 len = strnlen(SPIF_CHARPTR_C(dest), size);
232 if (len >= size) {
233 return FALSE;
234 } else {
235 return spiftool_safe_strncpy(dest + len, src, size - len);
236 }
237 }
238
239 /**
240 * Returns a portion of a larger string.
241 */
242 spif_charptr_t
spiftool_substr(spif_charptr_t str,spif_int32_t idx,spif_int32_t cnt)243 spiftool_substr(spif_charptr_t str, spif_int32_t idx, spif_int32_t cnt)
244 {
245 spif_charptr_t newstr;
246 spif_uint32_t start_pos, char_count;
247 spif_uint32_t len;
248
249 REQUIRE_RVAL(str != SPIF_NULL_TYPE(charptr), SPIF_NULL_TYPE(charptr));
250
251 len = SPIF_CAST(uint32) strlen(SPIF_CHARPTR_C(str));
252
253 if (idx < 0) {
254 start_pos = len + idx;
255 } else {
256 start_pos = idx;
257 }
258 REQUIRE_RVAL(start_pos < len, SPIF_NULL_TYPE(charptr));
259
260 if (cnt <= 0) {
261 char_count = len - start_pos + cnt;
262 } else {
263 char_count = cnt;
264 }
265 UPPER_BOUND(char_count, len - start_pos);
266
267 newstr = SPIF_CAST(charptr) MALLOC(char_count + 1);
268 memcpy(newstr, str + start_pos, char_count);
269 newstr[char_count] = 0;
270 return newstr;
271 }
272
273 #if HAVE_REGEX_H
274 /**
275 * Compare a string to a regular expression.
276 */
277 spif_bool_t
spiftool_regexp_match(const spif_charptr_t str,const spif_charptr_t pattern)278 spiftool_regexp_match(const spif_charptr_t str, const spif_charptr_t pattern)
279 {
280 static regex_t *rexp = NULL;
281 register int result;
282 char errbuf[256];
283
284 if (!str) {
285 /* If we're passed a NULL str, we want to free our static storage. */
286 FREE(rexp);
287 return FALSE;
288 } else if (!rexp) {
289 /* If we don't have static storage yet, make some. */
290 rexp = (regex_t *) MALLOC(sizeof(regex_t));
291 }
292
293 if (pattern) {
294 /* We have a pattern, so we need to compile it. */
295 if ((result = regcomp(rexp, pattern, REG_EXTENDED)) != 0) {
296 regerror(result, rexp, errbuf, 256);
297 libast_print_error("Unable to compile regexp %s -- %s.\n", pattern, errbuf);
298 return (FALSE);
299 }
300 }
301
302 /* Compare the string to the compiled pattern. */
303 if (((result = regexec(rexp, str, (size_t) 0, (regmatch_t *) NULL, 0)) != 0)
304 && (result != REG_NOMATCH)) {
305 regerror(result, rexp, errbuf, 256);
306 libast_print_error("Error testing input string %s -- %s.\n", str, errbuf);
307 return (FALSE);
308 }
309 return ((result == REG_NOMATCH) ? (FALSE) : (TRUE));
310 }
311
312 /**
313 * Thread-safe way to compare a string to a regular expression.
314 */
315 spif_bool_t
spiftool_regexp_match_r(register const spif_charptr_t str,register const spif_charptr_t pattern,register regex_t ** rexp)316 spiftool_regexp_match_r(register const spif_charptr_t str, register const spif_charptr_t pattern, register regex_t **rexp)
317 {
318 register int result;
319 char errbuf[256];
320
321 ASSERT_RVAL(rexp != NULL, FALSE);
322 if (*rexp == NULL) {
323 *rexp = (regex_t *) MALLOC(sizeof(regex_t));
324 }
325
326 if (pattern) {
327 if ((result = regcomp(*rexp, pattern, REG_EXTENDED)) != 0) {
328 regerror(result, *rexp, errbuf, 256);
329 libast_print_error("Unable to compile regexp %s -- %s.\n", pattern, errbuf);
330 FREE(*rexp);
331 return (FALSE);
332 }
333 }
334
335 if (((result = regexec(*rexp, str, (size_t) 0, (regmatch_t *) NULL, 0))
336 != 0) && (result != REG_NOMATCH)) {
337 regerror(result, *rexp, errbuf, 256);
338 libast_print_error("Error testing input string %s -- %s.\n", str, errbuf);
339 return (FALSE);
340 }
341 return ((result == REG_NOMATCH) ? (FALSE) : (TRUE));
342 }
343 #endif
344
345 #define IS_DELIM(c) ((delim != NULL) ? (strchr(SPIF_CHARPTR_C(delim), (c)) != NULL) : (isspace(c)))
346 #define IS_QUOTE(c) (quote && quote == (c))
347
348 spif_charptr_t *
spiftool_split(const spif_charptr_t delim,const spif_charptr_t str)349 spiftool_split(const spif_charptr_t delim, const spif_charptr_t str)
350 {
351 spif_charptr_t *slist;
352 register spif_charptr_t pstr;
353 register spif_charptr_t pdest;
354 char quote = 0;
355 unsigned short cnt = 0;
356 unsigned long len;
357
358 REQUIRE_RVAL(str != NULL, (spif_charptr_t *) NULL);
359
360 if ((slist = (spif_charptr_t *) MALLOC(sizeof(spif_charptr_t))) == NULL) {
361 libast_print_error("split(): Unable to allocate memory -- %s\n", strerror(errno));
362 return ((spif_charptr_t *) NULL);
363 }
364
365 /* Before we do anything, skip leading "whitespace." */
366 for (pstr = SPIF_CAST(charptr) str; *pstr && IS_DELIM(*pstr); pstr++);
367
368 /* The outermost for loop is where we traverse the string. Each new
369 word brings us back to the top where we resize our string list. */
370 for (; *pstr; cnt++) {
371 /* First, resize the list to two bigger than our count. Why two?
372 One for the string we're about to do, and one for a trailing NULL. */
373 if ((slist = (spif_charptr_t *) REALLOC(slist, sizeof(spif_charptr_t) * (cnt + 2))) == NULL) {
374 libast_print_error("split(): Unable to allocate memory -- %s\n", strerror(errno));
375 return ((spif_charptr_t *) NULL);
376 }
377
378 /* The string we're about to create can't possibly be larger than the remainder
379 of the string we have yet to parse, so allocate that much space to start. */
380 len = strlen(SPIF_CHARPTR_C(pstr)) + 1;
381 if ((slist[cnt] = SPIF_CAST(charptr) MALLOC(len)) == NULL) {
382 libast_print_error("split(): Unable to allocate memory -- %s.\n", strerror(errno));
383 return ((spif_charptr_t *) NULL);
384 }
385 pdest = slist[cnt];
386
387 /* This for loop is where we process each character. */
388 for (; *pstr && (quote || !IS_DELIM(*pstr));) {
389 if (*pstr == '\"' || *pstr == '\'') {
390 /* It's a quote character, so set or reset the quote variable. */
391 if (quote) {
392 if (quote == *pstr) {
393 quote = 0;
394 } else {
395 /* It's a single quote inside double quotes, or vice versa. Leave it alone. */
396 *pdest++ = *pstr++;
397 }
398 } else {
399 quote = *pstr;
400 }
401 pstr++;
402 } else {
403 /* Handle any backslashes that are escaping delimiters or quotes. */
404 if ((*pstr == '\\') && (IS_DELIM(*(pstr + 1)) || IS_QUOTE(*(pstr + 1)))) {
405 /* Incrementing pstr here moves us past the backslash so that the line
406 below will copy the next character to the new token, no questions asked. */
407 pstr++;
408 }
409 *pdest++ = *pstr++;
410 }
411 }
412 /* Add the trailing \0 to terminate the new string. */
413 *pdest = 0;
414
415 /* Reallocate the new string to be just the right size. */
416 len = strlen(SPIF_CHARPTR_C(slist[cnt])) + 1;
417 slist[cnt] = SPIF_CAST(charptr) REALLOC(slist[cnt], len);
418
419 /* Move past any trailing "whitespace." */
420 for (; *pstr && IS_DELIM(*pstr); pstr++);
421 }
422 if (cnt == 0) {
423 FREE(slist);
424 return NULL;
425 } else {
426 /* The last element of slist[] should be NULL. */
427 slist[cnt] = 0;
428 return slist;
429 }
430 }
431
432 spif_charptr_t *
spiftool_split_regexp(const spif_charptr_t regexp,const spif_charptr_t str)433 spiftool_split_regexp(const spif_charptr_t regexp, const spif_charptr_t str)
434 {
435 USE_VAR(regexp);
436 USE_VAR(str);
437 return (NULL);
438 }
439
440 spif_charptr_t
spiftool_join(spif_charptr_t sep,spif_charptr_t * slist)441 spiftool_join(spif_charptr_t sep, spif_charptr_t *slist)
442 {
443 register unsigned long i;
444 size_t len, slen;
445 spif_charptr_t new_str;
446
447 ASSERT_RVAL(slist != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
448 REQUIRE_RVAL(*slist != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
449 if (sep == NULL) {
450 sep = SPIF_CHARPTR("");
451 }
452 slen = strlen(SPIF_CHARPTR_C(sep));
453 for (i = len = 0; slist[i]; i++) {
454 len += strlen(SPIF_CHARPTR_C(slist[i]));
455 }
456 len += slen * (i - 1);
457 new_str = SPIF_CAST(charptr) MALLOC(len);
458 strcpy(SPIF_CHARPTR_C(new_str), SPIF_CHARPTR_C(slist[0]));
459 for (i = 1; slist[i]; i++) {
460 if (slen) {
461 strcat(SPIF_CHARPTR_C(new_str), SPIF_CHARPTR_C(sep));
462 }
463 strcat(SPIF_CHARPTR_C(new_str), SPIF_CHARPTR_C(slist[i]));
464 }
465 return new_str;
466 }
467
468 /* Return malloc'd pointer to index-th word in str. "..." counts as 1 word. */
469 #undef IS_DELIM
470 #define IS_DELIM(c) (delim ? ((c) == delim) : isspace(c))
471
472 spif_charptr_t
spiftool_get_word(unsigned long index,const spif_charptr_t str)473 spiftool_get_word(unsigned long index, const spif_charptr_t str)
474 {
475 spif_charptr_t tmpstr;
476 char delim = 0;
477 register unsigned long i, j, k;
478
479 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
480 k = strlen(SPIF_CHARPTR_C(str)) + 1;
481 if ((tmpstr = SPIF_CAST(charptr) MALLOC(k)) == NULL) {
482 libast_print_error("get_word(%lu, %s): Unable to allocate memory -- %s.\n", index, str, strerror(errno));
483 return (SPIF_CAST(charptr) NULL);
484 }
485 *tmpstr = 0;
486 for (i = 0, j = 0; j < index && str[i]; j++) {
487 for (; isspace(str[i]); i++);
488 switch (str[i]) {
489 case '\"':
490 delim = '\"';
491 i++;
492 break;
493 case '\'':
494 delim = '\'';
495 i++;
496 break;
497 default:
498 delim = 0;
499 }
500 for (k = 0; str[i] && !IS_DELIM(str[i]);) {
501 if (str[i] == '\\') {
502 if (str[i + 1] == '\'' || str[i + 1] == '\"') {
503 i++;
504 }
505 }
506 tmpstr[k++] = str[i++];
507 }
508 switch (str[i]) {
509 case '\"':
510 case '\'':
511 i++;
512 break;
513 }
514 tmpstr[k] = 0;
515 }
516
517 if (j != index) {
518 FREE(tmpstr);
519 D_STRINGS(("get_word(%lu, %s) returning NULL.\n", index, str));
520 return (SPIF_CAST(charptr) NULL);
521 } else {
522 tmpstr = SPIF_CAST(charptr) REALLOC(tmpstr, strlen(SPIF_CHARPTR_C(tmpstr)) + 1);
523 D_STRINGS(("get_word(%lu, %s) returning \"%s\".\n", index, str, tmpstr));
524 return (tmpstr);
525 }
526 }
527
528 /* Return pointer into str to index-th word in str. "..." counts as 1 word. */
529 spif_charptr_t
spiftool_get_pword(unsigned long index,const spif_charptr_t str)530 spiftool_get_pword(unsigned long index, const spif_charptr_t str)
531 {
532 register spif_charptr_t tmpstr = SPIF_CAST(charptr) str;
533 register unsigned long j;
534
535 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
536 for (; isspace(*tmpstr) && *tmpstr; tmpstr++);
537 for (j = 1; j < index && *tmpstr; j++) {
538 for (; !isspace(*tmpstr) && *tmpstr; tmpstr++);
539 for (; isspace(*tmpstr) && *tmpstr; tmpstr++);
540 }
541
542 if (*tmpstr == '\"' || *tmpstr == '\'') {
543 tmpstr++;
544 }
545 if (*tmpstr == '\0') {
546 D_STRINGS(("get_pword(%lu, %s) returning NULL.\n", index, str));
547 return (SPIF_CAST(charptr) NULL);
548 } else {
549 D_STRINGS(("get_pword(%lu, %s) returning \"%s\"\n", index, str, tmpstr));
550 return SPIF_CAST(charptr) tmpstr;
551 }
552 }
553
554 /* Returns the number of words in str, for use with get_word() and get_pword(). "..." counts as 1 word. */
555 unsigned long
spiftool_num_words(const spif_charptr_t str)556 spiftool_num_words(const spif_charptr_t str)
557 {
558 register unsigned long cnt = 0;
559 char delim = 0;
560 register unsigned long i;
561
562 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_CAST_C(unsigned long) -1);
563 for (i = 0; str[i] && IS_DELIM(str[i]); i++);
564 for (; str[i]; cnt++) {
565 switch (str[i]) {
566 case '\"':
567 delim = '\"';
568 i++;
569 break;
570 case '\'':
571 delim = '\'';
572 i++;
573 break;
574 default:
575 delim = 0;
576 }
577 for (; str[i] && !IS_DELIM(str[i]); i++);
578 switch (str[i]) {
579 case '\"':
580 case '\'':
581 i++;
582 break;
583 }
584 for (; str[i] && isspace(str[i]); i++);
585 }
586
587 D_STRINGS(("num_words() returning %lu\n", cnt));
588 return (cnt);
589 }
590
591 /* chomp() removes leading and trailing whitespace from a string */
592 spif_charptr_t
spiftool_chomp(spif_charptr_t s)593 spiftool_chomp(spif_charptr_t s)
594 {
595 register spif_charptr_t front, back;
596
597 ASSERT_RVAL(s != NULL, NULL);
598 REQUIRE_RVAL(*s, s);
599
600 for (front = s; *front && isspace(*front); front++);
601 for (back = s + strlen(SPIF_CHARPTR_C(s)) - 1; *back && isspace(*back) && back > front; back--);
602
603 *(++back) = 0;
604 if (front != s) {
605 memmove(s, front, back - front + 1);
606 }
607 return (s);
608 }
609
610 spif_charptr_t
spiftool_downcase_str(spif_charptr_t str)611 spiftool_downcase_str(spif_charptr_t str)
612 {
613 register spif_charptr_t tmp;
614
615 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
616 for (tmp = str; *tmp; tmp++) {
617 *tmp = tolower(*tmp);
618 }
619 D_STRINGS(("downcase_str() returning %s\n", str));
620 return (str);
621 }
622
623 spif_charptr_t
spiftool_upcase_str(spif_charptr_t str)624 spiftool_upcase_str(spif_charptr_t str)
625 {
626 register spif_charptr_t tmp;
627
628 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
629 for (tmp = str; *tmp; tmp++) {
630 *tmp = toupper(*tmp);
631 }
632 D_STRINGS(("upcase_str() returning %s\n", str));
633 return (str);
634 }
635
636 spif_charptr_t
spiftool_condense_whitespace(spif_charptr_t s)637 spiftool_condense_whitespace(spif_charptr_t s)
638 {
639
640 register unsigned char gotspc = 0;
641 register spif_charptr_t pbuff = s, pbuff2 = s;
642
643 ASSERT_RVAL(s != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
644 D_STRINGS(("condense_whitespace(%s) called.\n", s));
645 for (; *pbuff2; pbuff2++) {
646 if (isspace(*pbuff2)) {
647 if (!gotspc) {
648 *pbuff = ' ';
649 gotspc = 1;
650 pbuff++;
651 }
652 } else {
653 *pbuff = *pbuff2;
654 gotspc = 0;
655 pbuff++;
656 }
657 }
658 if ((pbuff >= s) && (isspace(*(pbuff - 1))))
659 pbuff--;
660 *pbuff = 0;
661 D_STRINGS(("condense_whitespace() returning \"%s\".\n", s));
662 return (SPIF_CAST(charptr) REALLOC(s, strlen(SPIF_CHARPTR_C(s)) + 1));
663 }
664
665 spif_charptr_t
spiftool_safe_str(register spif_charptr_t str,unsigned short len)666 spiftool_safe_str(register spif_charptr_t str, unsigned short len)
667 {
668 register unsigned short i;
669
670 ASSERT_RVAL(str != SPIF_NULL_TYPE(ptr), SPIF_NULL_TYPE(ptr));
671 for (i = 0; i < len; i++) {
672 if (iscntrl(str[i])) {
673 str[i] = '.';
674 }
675 }
676
677 return (str);
678 }
679
680 void
spiftool_hex_dump(void * buff,register size_t count)681 spiftool_hex_dump(void *buff, register size_t count)
682 {
683 register unsigned long j, k, l;
684 register spif_charptr_t ptr;
685 spif_char_t buffr[9];
686
687 ASSERT(buff != SPIF_NULL_TYPE(ptr));
688 fprintf(stderr, " Address | Size | Offset | 00 01 02 03 04 05 06 07 | ASCII \n");
689 fprintf(stderr, "-----------+--------+---------+-------------------------+---------\n");
690 for (ptr = buff, j = 0; j < count; j += 8) {
691 fprintf(stderr, " %10p | %06lu | %07x | ", buff, (unsigned long) count, (unsigned int) j);
692 l = ((count - j < 8) ? (count - j) : (8));
693 memcpy(buffr, ptr + j, l);
694 memset(buffr + l, 0, 9 - l);
695 for (k = 0; k < l; k++) {
696 fprintf(stderr, "%02x ", buffr[k]);
697 }
698 for (; k < 8; k++) {
699 fprintf(stderr, " ");
700 }
701 fprintf(stderr, "| %-8s\n", spiftool_safe_str(SPIF_CAST(charptr) buffr, l));
702 }
703 }
704
705 #define CHAR_CLASS_MATCH(a, b) ((isalpha(a) && isalpha(b)) \
706 || (isdigit(a) && isdigit(b)) \
707 || (!isalnum(a) && !isalnum(b)))
708 spif_cmp_t
spiftool_version_compare(spif_charptr_t v1,spif_charptr_t v2)709 spiftool_version_compare(spif_charptr_t v1, spif_charptr_t v2)
710 {
711 spif_char_t buff1[128], buff2[128];
712
713 D_CONF(("Comparing version strings \"%s\" and \"%s\"\n", NONULL(v1), NONULL(v2)));
714 SPIF_COMP_CHECK_NULL(v1, v2);
715
716 for (; *v1 && *v2; ) {
717 D_CONF((" -> Looking at \"%s\" and \"%s\"\n", v1, v2));
718 if (isalpha(*v1) && isalpha(*v2)) {
719 spif_charptr_t p1 = buff1, p2 = buff2;
720 spif_int8_t ival1 = 6, ival2 = 6;
721
722 /* Compare words. First, copy each word into buffers. */
723 for (; *v1 && isalpha(*v1); v1++, p1++) *p1 = *v1;
724 for (; *v2 && isalpha(*v2); v2++, p2++) *p2 = *v2;
725 *p1 = *p2 = 0;
726
727 /* Change the buffered strings to lowercase for easier comparison. */
728 spiftool_downcase_str(buff1);
729 spiftool_downcase_str(buff2);
730 D_CONF((" -> Comparing as words \"%s\" vs. \"%s\"\n", buff1, buff2));
731
732 /* Some strings require special handling. */
733 if (!strcmp(SPIF_CHARPTR_C(buff1), "snap")) {
734 ival1 = 1;
735 } else if (!strcmp(SPIF_CHARPTR_C(buff1), "pre")) {
736 ival1 = 2;
737 } else if (!strcmp(SPIF_CHARPTR_C(buff1), "alpha")) {
738 ival1 = 3;
739 } else if (!strcmp(SPIF_CHARPTR_C(buff1), "beta")) {
740 ival1 = 4;
741 } else if (!strcmp(SPIF_CHARPTR_C(buff1), "rc")) {
742 ival1 = 5;
743 }
744 if (!strcmp(SPIF_CHARPTR_C(buff2), "snap")) {
745 ival2 = 1;
746 } else if (!strcmp(SPIF_CHARPTR_C(buff2), "pre")) {
747 ival2 = 2;
748 } else if (!strcmp(SPIF_CHARPTR_C(buff2), "alpha")) {
749 ival2 = 3;
750 } else if (!strcmp(SPIF_CHARPTR_C(buff2), "beta")) {
751 ival2 = 4;
752 } else if (!strcmp(SPIF_CHARPTR_C(buff2), "rc")) {
753 ival2 = 5;
754 }
755 if (ival1 != ival2) {
756 /* If the values are different, compare them. */
757 D_CONF((" -> %d\n", (int) SPIF_CMP_FROM_INT(ival1 - ival2)));
758 return SPIF_CMP_FROM_INT(ival1 - ival2);
759 } else if (ival1 == 6) {
760 int c;
761
762 /* Two arbitrary strings. Compare them too. */
763 if ((c = strcmp(SPIF_CHARPTR_C(buff1), SPIF_CHARPTR_C(buff2))) != 0) {
764 D_CONF((" -> %d\n", (int) SPIF_CMP_FROM_INT(c)));
765 return SPIF_CMP_FROM_INT(c);
766 }
767 }
768 } else if (isdigit(*v1) && isdigit(*v2)) {
769 spif_charptr_t p1 = buff1, p2 = buff2;
770 spif_int32_t ival1, ival2;
771 spif_cmp_t c;
772
773 /* Compare numbers. First, copy each number into buffers. */
774 for (; *v1 && isdigit(*v1); v1++, p1++) *p1 = *v1;
775 for (; *v2 && isdigit(*v2); v2++, p2++) *p2 = *v2;
776 *p1 = *p2 = 0;
777
778 /* Convert the strings into actual integers. */
779 ival1 = SPIF_CAST(int32) strtol(SPIF_CHARPTR_C(buff1), (char **) NULL, 10);
780 ival2 = SPIF_CAST(int32) strtol(SPIF_CHARPTR_C(buff2), (char **) NULL, 10);
781 D_CONF((" -> Comparing as integers %d vs. %d\n", SPIF_CAST_C(int) ival1, SPIF_CAST_C(int) ival2));
782
783 /* Compare the integers and return if not equal. */
784 c = SPIF_CMP_FROM_INT(ival1 - ival2);
785 if (!SPIF_CMP_IS_EQUAL(c)) {
786 D_CONF((" -> %d\n", (int) c));
787 return c;
788 }
789 } else if (!isalnum(*v1) && !isalnum(*v2)) {
790 spif_charptr_t p1 = buff1, p2 = buff2;
791 spif_cmp_t c;
792
793 /* Compare non-alphanumeric strings. */
794 for (; *v1 && !isalnum(*v1); v1++, p1++) *p1 = *v1;
795 for (; *v2 && !isalnum(*v2); v2++, p2++) *p2 = *v2;
796 *p1 = *p2 = 0;
797
798 D_CONF((" -> Comparing as non-alphanumeric strings \"%s\" vs. \"%s\"\n", buff1, buff2));
799 c = SPIF_CMP_FROM_INT(strcasecmp(SPIF_CHARPTR_C(buff1), SPIF_CHARPTR_C(buff2)));
800 if (!SPIF_CMP_IS_EQUAL(c)) {
801 D_CONF((" -> %d\n", (int) c));
802 return c;
803 }
804 } else {
805 D_CONF((" -> Comparing as alphanumeric strings \"%s\" vs. \"%s\"\n", buff1, buff2));
806 D_CONF((" -> %d\n", (int) SPIF_CMP_FROM_INT(strcasecmp(SPIF_CHARPTR_C(buff1), SPIF_CHARPTR_C(buff2)))));
807 return SPIF_CMP_FROM_INT(strcasecmp(SPIF_CHARPTR_C(buff1), SPIF_CHARPTR_C(buff2)));
808 }
809 }
810
811 /* We've reached the end of one of the strings. */
812 if (*v1) {
813 if (!BEG_STRCASECMP(SPIF_CHARPTR_C(v1), "snap") || !BEG_STRCASECMP(SPIF_CHARPTR_C(v1), "pre")
814 || !BEG_STRCASECMP(SPIF_CHARPTR_C(v1), "alpha") || !BEG_STRCASECMP(SPIF_CHARPTR_C(v1), "beta")) {
815 D_CONF((" -> <\n"));
816 return SPIF_CMP_LESS;
817 } else {
818 D_CONF((" -> >\n"));
819 return SPIF_CMP_GREATER;
820 }
821 } else if (*v2) {
822 if (!BEG_STRCASECMP(SPIF_CHARPTR_C(v2), "snap") || !BEG_STRCASECMP(SPIF_CHARPTR_C(v2), "pre")
823 || !BEG_STRCASECMP(SPIF_CHARPTR_C(v2), "alpha") || !BEG_STRCASECMP(SPIF_CHARPTR_C(v2), "beta")) {
824 D_CONF((" -> >\n"));
825 return SPIF_CMP_GREATER;
826 } else {
827 D_CONF((" -> <\n"));
828 return SPIF_CMP_LESS;
829 }
830 }
831 D_CONF((" -> ==\n"));
832 return SPIF_CMP_EQUAL;
833 }
834
835 /**
836 * @defgroup DOXGRP_STRINGS String Utility Routines
837 *
838 * This group of functions/defines/macros provides oft-needed string
839 * manipulation functionality which is not found (at least not
840 * portably) in libc.
841 *
842 * Over the past several years, I have had to implement some simple
843 * string routines which, for whatever reason, are not part of the
844 * Standard C Library. I have gathered these macros, functions,
845 * etc. into LibAST in the hopes that others might not have to
846 * reimplement them themselves. Unlike the memory/debugging/object
847 * stuff, there really isn't a well-defined architecture surrounding
848 * these. They're just utility functions and implementations for
849 * non-portable functionality.
850 *
851 * A small sample program demonstrating some of these routines can be
852 * found @link strings_example.c here @endlink.
853 */
854
855 /**
856 * @example strings_example.c
857 * Example code for using the string routines.
858 *
859 */
860