0
|
1 /*
|
|
2 * Miscellaneous string-handling related utility-functions
|
|
3 * Programmed and designed by Matti 'ccr' Hamalainen
|
|
4 * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
|
|
5 *
|
|
6 * Please read file 'COPYING' for information on license and distribution.
|
|
7 */
|
|
8 #ifdef HAVE_CONFIG_H
|
2
|
9 #include "config.h"
|
0
|
10 #endif
|
|
11 #include "th_string.h"
|
|
12
|
|
13 #define LPREV (pNode->pPrev)
|
|
14 #define LNEXT (pNode->pNext)
|
|
15
|
79
|
16
|
|
17 /* strdup with a NULL check
|
|
18 */
|
|
19 char *th_strdup(const char *s)
|
|
20 {
|
|
21 char *res;
|
|
22 if (s == NULL)
|
|
23 return NULL;
|
|
24
|
|
25 if ((res = th_malloc(strlen(s) + 1)) == NULL)
|
|
26 return NULL;
|
|
27
|
|
28 strcpy(res, s);
|
|
29 return res;
|
|
30 }
|
|
31
|
|
32
|
0
|
33 /* Allocate memory for a string with given length
|
|
34 */
|
11
|
35 char *th_stralloc(const size_t l)
|
0
|
36 {
|
|
37 assert(l > 0);
|
11
|
38 return th_malloc(sizeof(char) * l);
|
0
|
39 }
|
|
40
|
|
41
|
11
|
42 char *th_strrealloc(char * s, const size_t l)
|
0
|
43 {
|
11
|
44 assert(l > 0);
|
|
45 return th_realloc(s, sizeof(char) * l);
|
0
|
46 }
|
|
47
|
|
48
|
47
|
49 char *th_strncpy(char * dst, const char * src, size_t n)
|
0
|
50 {
|
47
|
51 const char *s = src;
|
|
52 char *d = dst;
|
0
|
53 size_t i;
|
47
|
54 assert(src != NULL);
|
|
55 assert(dst != NULL);
|
0
|
56
|
|
57 /* Copy to the destination */
|
|
58 i = n;
|
|
59 while (*s && (i > 0)) {
|
|
60 *(d++) = *(s++);
|
|
61 i--;
|
|
62 }
|
|
63
|
|
64 /* Fill rest of space with zeros */
|
|
65 while (i > 0) {
|
|
66 *(d++) = 0;
|
|
67 i--;
|
|
68 }
|
|
69
|
|
70 /* Ensure that last is always zero */
|
11
|
71 dst[n - 1] = 0;
|
0
|
72
|
11
|
73 return dst;
|
0
|
74 }
|
|
75
|
|
76
|
11
|
77 int th_strncmp(char * str1, char * str2, size_t n)
|
0
|
78 {
|
11
|
79 char *s1, *s2;
|
47
|
80 assert(str1 != NULL);
|
|
81 assert(str2 != NULL);
|
0
|
82
|
|
83 /* Check the string pointers */
|
11
|
84 if (str1 == str2)
|
0
|
85 return 0;
|
|
86
|
|
87 /* Go through the string */
|
11
|
88 s1 = str1;
|
|
89 s2 = str2;
|
0
|
90 while ((n > 0) && *s1 && *s2 && (*s1 == *s2)) {
|
|
91 s1++;
|
|
92 s2++;
|
|
93 n--;
|
|
94 }
|
|
95
|
|
96 if (n > 0)
|
|
97 return ((*s1) - (*s2));
|
|
98 else
|
|
99 return 0;
|
|
100 }
|
|
101
|
|
102
|
|
103 /* Compare two strings ignoring case [strcasecmp, strncasecmp]
|
|
104 */
|
11
|
105 int th_strcasecmp(char * str1, char * str2)
|
0
|
106 {
|
11
|
107 char *s1 = str1, *s2 = str2;
|
47
|
108 assert(str1 != NULL);
|
|
109 assert(str2 != NULL);
|
0
|
110
|
|
111 /* Check the string pointers */
|
11
|
112 if (str1 == str2)
|
0
|
113 return 0;
|
|
114
|
|
115 /* Go through the string */
|
|
116 while (*s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
|
|
117 s1++;
|
|
118 s2++;
|
|
119 }
|
|
120
|
|
121 return (th_tolower(*s1) - th_tolower(*s2));
|
|
122 }
|
|
123
|
|
124
|
11
|
125 int th_strncasecmp(char * str1, char * str2, size_t n)
|
0
|
126 {
|
11
|
127 char *s1 = str1, *s2 = str2;
|
47
|
128 assert(str1 != NULL);
|
|
129 assert(str2 != NULL);
|
0
|
130
|
|
131 /* Check the string pointers */
|
11
|
132 if (str1 == str2)
|
0
|
133 return 0;
|
|
134
|
|
135 /* Go through the string */
|
|
136 while ((n > 0) && *s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
|
|
137 s1++;
|
|
138 s2++;
|
|
139 n--;
|
|
140 }
|
|
141
|
|
142 if (n > 0)
|
|
143 return (th_tolower(*s1) - th_tolower(*s2));
|
|
144 else
|
|
145 return 0;
|
|
146 }
|
|
147
|
|
148
|
|
149 /* Remove all occurences of control characters, in-place.
|
|
150 * Resulting string is always shorter or same length than original.
|
|
151 */
|
11
|
152 void th_strip_ctrlchars(char * str)
|
0
|
153 {
|
11
|
154 char *i, *j;
|
47
|
155 assert(str != NULL);
|
0
|
156
|
11
|
157 i = str;
|
|
158 j = str;
|
0
|
159 while (*i) {
|
|
160 if (!th_iscntrl(*i))
|
|
161 *(j++) = *i;
|
|
162 i++;
|
|
163 }
|
|
164
|
|
165 *j = 0;
|
|
166 }
|
|
167
|
|
168
|
11
|
169 /* Copy a given string over in *result.
|
0
|
170 */
|
11
|
171 int th_pstrcpy(char ** result, char * str)
|
0
|
172 {
|
47
|
173 assert(result != NULL);
|
0
|
174
|
|
175 /* Check the string pointers */
|
47
|
176 if (str == NULL)
|
0
|
177 return -1;
|
|
178
|
|
179 /* Allocate memory for destination */
|
11
|
180 th_free(*result);
|
|
181 *result = th_stralloc(strlen(str) + 1);
|
|
182 if (!*result)
|
0
|
183 return -2;
|
|
184
|
|
185 /* Copy to the destination */
|
11
|
186 strcpy(*result, str);
|
0
|
187
|
|
188 return 0;
|
|
189 }
|
|
190
|
|
191
|
11
|
192 /* Concatenates a given string into string pointed by *result.
|
0
|
193 */
|
11
|
194 int th_pstrcat(char ** result, char * str)
|
0
|
195 {
|
47
|
196 assert(result != NULL);
|
0
|
197
|
|
198 /* Check the string pointers */
|
47
|
199 if (str == NULL)
|
0
|
200 return -1;
|
|
201
|
11
|
202 if (*result != NULL) {
|
|
203 *result = th_strrealloc(*result, strlen(*result) + strlen(str) + 1);
|
|
204 if (*result == NULL)
|
0
|
205 return -1;
|
|
206
|
11
|
207 strcat(*result, str);
|
0
|
208 } else {
|
11
|
209 *result = th_stralloc(strlen(str) + 1);
|
|
210 if (*result == NULL)
|
0
|
211 return -1;
|
|
212
|
11
|
213 strcpy(*result, str);
|
0
|
214 }
|
|
215
|
|
216 return 0;
|
|
217 }
|
|
218
|
|
219
|
|
220 /* Find next non-whitespace character in string.
|
|
221 * Updates iPos into the position of such character and
|
|
222 * returns pointer to the string.
|
|
223 */
|
11
|
224 char *th_findnext(char * str, size_t * iPos)
|
0
|
225 {
|
47
|
226 assert(str != NULL);
|
0
|
227
|
|
228 /* Terminating NULL-character is not whitespace! */
|
11
|
229 while (th_isspace(str[*iPos]))
|
0
|
230 (*iPos)++;
|
11
|
231 return &str[*iPos];
|
0
|
232 }
|
|
233
|
|
234
|
|
235 /* Find next chSep-character from string
|
|
236 */
|
11
|
237 char *th_findsep(char * str, size_t * iPos, char chSep)
|
0
|
238 {
|
47
|
239 assert(str != NULL);
|
0
|
240
|
|
241 /* Terminating NULL-character is not digit! */
|
11
|
242 while (str[*iPos] && (str[*iPos] != chSep))
|
0
|
243 (*iPos)++;
|
11
|
244 return &str[*iPos];
|
0
|
245 }
|
|
246
|
|
247
|
|
248 /* Find next chSep- or whitespace from string
|
|
249 */
|
11
|
250 char *th_findseporspace(char * str, size_t * iPos, char chSep)
|
0
|
251 {
|
47
|
252 assert(str != NULL);
|
0
|
253
|
|
254 /* Terminating NULL-character is not digit! */
|
11
|
255 while (!th_isspace(str[*iPos]) && (str[*iPos] != chSep))
|
0
|
256 (*iPos)++;
|
11
|
257 return &str[*iPos];
|
0
|
258 }
|
|
259
|
|
260
|
|
261 /* Compare a string to a pattern. Case-SENSITIVE version.
|
|
262 * The matching pattern can consist of any normal characters plus
|
|
263 * wildcards ? and *. "?" matches any character and "*" matches
|
|
264 * any number of characters.
|
|
265 */
|
11
|
266 BOOL th_strmatch(char * str, char * pattern)
|
0
|
267 {
|
47
|
268 BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE;
|
|
269 char *tmpPattern = NULL;
|
0
|
270
|
|
271 /* Check given pattern and string */
|
47
|
272 if (str == NULL || pattern == NULL)
|
0
|
273 return FALSE;
|
|
274
|
|
275 /* Start comparision */
|
|
276 do {
|
|
277 didMatch = FALSE;
|
11
|
278 switch (*pattern) {
|
0
|
279 case '?':
|
|
280 /* Any single character matches */
|
11
|
281 if (*str) {
|
0
|
282 didMatch = TRUE;
|
11
|
283 pattern++;
|
|
284 str++;
|
0
|
285 }
|
|
286 break;
|
|
287
|
|
288 case '*':
|
|
289 didMatch = TRUE;
|
11
|
290 pattern++;
|
|
291 if (!*pattern)
|
0
|
292 isEnd = TRUE;
|
|
293 isAnyMode = TRUE;
|
11
|
294 tmpPattern = pattern;
|
0
|
295 break;
|
|
296
|
|
297 case 0:
|
|
298 if (isAnyMode) {
|
11
|
299 if (*str)
|
|
300 str++;
|
0
|
301 else
|
|
302 isEnd = TRUE;
|
|
303 } else {
|
11
|
304 if (*str) {
|
0
|
305 if (tmpPattern) {
|
|
306 isAnyMode = TRUE;
|
11
|
307 pattern = tmpPattern;
|
0
|
308 } else
|
|
309 didMatch = FALSE;
|
|
310 } else
|
|
311 isEnd = TRUE;
|
|
312 }
|
|
313 break;
|
|
314 default:
|
|
315 if (isAnyMode) {
|
47
|
316 if (*pattern == *str) {
|
0
|
317 isAnyMode = FALSE;
|
|
318 didMatch = TRUE;
|
|
319 } else {
|
11
|
320 if (*str) {
|
0
|
321 didMatch = TRUE;
|
11
|
322 str++;
|
0
|
323 }
|
|
324 }
|
|
325 } else {
|
47
|
326 if (*pattern == *str) {
|
0
|
327 didMatch = TRUE;
|
11
|
328 if (*pattern)
|
|
329 pattern++;
|
|
330 if (*str)
|
|
331 str++;
|
0
|
332 } else {
|
|
333 if (tmpPattern) {
|
|
334 didMatch = TRUE;
|
|
335 isAnyMode = TRUE;
|
11
|
336 pattern = tmpPattern;
|
0
|
337 }
|
|
338 }
|
|
339 }
|
|
340
|
11
|
341 if (!*str && !*pattern)
|
0
|
342 isEnd = TRUE;
|
|
343 break;
|
|
344
|
|
345 } /* switch */
|
|
346
|
47
|
347 } while (didMatch && !isEnd);
|
0
|
348
|
|
349 return didMatch;
|
|
350 }
|
|
351
|
|
352
|
|
353 /* Compare a string to a pattern. Case-INSENSITIVE version.
|
|
354 */
|
11
|
355 BOOL th_strcasematch(char * str, char * pattern)
|
0
|
356 {
|
47
|
357 BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE;
|
|
358 char *tmpPattern = NULL;
|
0
|
359
|
|
360 /* Check given pattern and string */
|
47
|
361 if (str == NULL || pattern == NULL)
|
0
|
362 return FALSE;
|
|
363
|
|
364 /* Start comparision */
|
|
365 do {
|
11
|
366 switch (*pattern) {
|
0
|
367 case '?':
|
|
368 /* Any single character matches */
|
11
|
369 if (*str) {
|
|
370 pattern++;
|
|
371 str++;
|
0
|
372 } else
|
|
373 didMatch = FALSE;
|
|
374 break;
|
|
375
|
|
376 case '*':
|
11
|
377 pattern++;
|
47
|
378 if (!*pattern || *pattern == '?')
|
0
|
379 isEnd = TRUE;
|
|
380 isAnyMode = TRUE;
|
11
|
381 tmpPattern = pattern;
|
0
|
382 break;
|
|
383
|
|
384 case 0:
|
|
385 if (isAnyMode) {
|
11
|
386 if (*str)
|
|
387 str++;
|
0
|
388 else
|
|
389 isEnd = TRUE;
|
|
390 } else {
|
11
|
391 if (*str) {
|
0
|
392 if (tmpPattern) {
|
|
393 isAnyMode = TRUE;
|
11
|
394 pattern = tmpPattern;
|
0
|
395 } else
|
|
396 didMatch = FALSE;
|
|
397 } else
|
|
398 isEnd = TRUE;
|
|
399 }
|
|
400 break;
|
|
401
|
|
402 default:
|
|
403 if (isAnyMode) {
|
11
|
404 if (th_tolower(*pattern) == th_tolower(*str)) {
|
0
|
405 isAnyMode = FALSE;
|
|
406 } else {
|
11
|
407 if (*str)
|
|
408 str++;
|
0
|
409 else
|
|
410 didMatch = FALSE;
|
|
411 }
|
|
412 } else {
|
11
|
413 if (th_tolower(*pattern) == th_tolower(*str)) {
|
|
414 if (*pattern)
|
|
415 pattern++;
|
|
416 if (*str)
|
|
417 str++;
|
0
|
418 } else {
|
|
419 if (tmpPattern) {
|
|
420 isAnyMode = TRUE;
|
11
|
421 pattern = tmpPattern;
|
0
|
422 } else
|
|
423 didMatch = FALSE;
|
|
424 }
|
|
425 }
|
|
426
|
11
|
427 if (!*str && !*pattern)
|
0
|
428 isEnd = TRUE;
|
|
429 break;
|
|
430
|
|
431 } /* switch */
|
|
432
|
47
|
433 } while (didMatch && !isEnd);
|
0
|
434
|
|
435 return didMatch;
|
|
436 }
|
|
437
|