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