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