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