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
|
|
9 #include <config.h>
|
|
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 */
|
|
18 char_t *th_stralloc(const size_t l)
|
|
19 {
|
|
20 assert(l > 0);
|
|
21 return th_malloc(sizeof(char_t) * l);
|
|
22 }
|
|
23
|
|
24
|
|
25 char_t *th_strrealloc(char_t * s, const size_t l)
|
|
26 {
|
|
27 assert(l > 0);
|
|
28 return th_realloc(s, sizeof(char_t) * l);
|
|
29 }
|
|
30
|
|
31
|
|
32 /* Calculate the length of a string [strlen]
|
|
33 */
|
|
34 size_t th_strlen(char_t * pStr)
|
|
35 {
|
|
36 size_t l = 0;
|
|
37 char_t *s = pStr;
|
|
38 assert(pStr);
|
|
39
|
|
40 while (*s) {
|
|
41 s++;
|
|
42 l++;
|
|
43 }
|
|
44
|
|
45 return l;
|
|
46 }
|
|
47
|
|
48
|
|
49 /* Duplicate a string [strdup]
|
|
50 */
|
|
51 char_t *th_strdup(char_t * pStr)
|
|
52 {
|
|
53 char_t *pResult, *s, *d;
|
|
54
|
|
55 if (!pStr) return NULL;
|
|
56
|
|
57 /* Allocate memory for destination */
|
|
58 pResult = th_stralloc(th_strlen(pStr) + 1);
|
|
59 if (!pResult)
|
|
60 return NULL;
|
|
61
|
|
62 /* Copy to the destination */
|
|
63 s = pStr;
|
|
64 d = pResult;
|
|
65 while (*s) {
|
|
66 *(d++) = *(s++);
|
|
67 }
|
|
68 *d = 0;
|
|
69
|
|
70 return pResult;
|
|
71 }
|
|
72
|
|
73
|
|
74 /* Concatenate a string [strcat]
|
|
75 */
|
|
76 char_t *th_strcat(char_t * pDest, char_t * pSource)
|
|
77 {
|
|
78 char_t *s, *d;
|
|
79 assert(pSource);
|
|
80 assert(pDest);
|
|
81
|
|
82 /* Copy to the destination */
|
|
83 s = pSource;
|
|
84 d = pDest;
|
|
85
|
|
86 while (*d) d++;
|
|
87
|
|
88 while (*s) {
|
|
89 *(d++) = *(s++);
|
|
90 }
|
|
91 *d = 0;
|
|
92
|
|
93 return pDest;
|
|
94 }
|
|
95
|
|
96
|
|
97 /* Copy a string [strcpy, strncpy]
|
|
98 */
|
|
99 char_t *th_strcpy(char_t * pDest, char_t * pSource)
|
|
100 {
|
|
101 char_t *s, *d;
|
|
102 assert(pSource);
|
|
103 assert(pDest);
|
|
104
|
|
105 /* Copy to the destination */
|
|
106 s = pSource;
|
|
107 d = pDest;
|
|
108
|
|
109 while (*s) {
|
|
110 *(d++) = *(s++);
|
|
111 }
|
|
112 *d = 0;
|
|
113
|
|
114 return pDest;
|
|
115 }
|
|
116
|
|
117
|
|
118 char_t *th_strncpy(char_t * pDest, char_t * pSource, size_t n)
|
|
119 {
|
|
120 char_t *s, *d;
|
|
121 size_t i;
|
|
122 assert(pSource);
|
|
123 assert(pDest);
|
|
124
|
|
125 /* Copy to the destination */
|
|
126 i = n;
|
|
127 s = pSource;
|
|
128 d = pDest;
|
|
129 while (*s && (i > 0)) {
|
|
130 *(d++) = *(s++);
|
|
131 i--;
|
|
132 }
|
|
133
|
|
134 /* Fill rest of space with zeros */
|
|
135 while (i > 0) {
|
|
136 *(d++) = 0;
|
|
137 i--;
|
|
138 }
|
|
139
|
|
140 /* Ensure that last is always zero */
|
|
141 pDest[n - 1] = 0;
|
|
142
|
|
143 return pDest;
|
|
144 }
|
|
145
|
|
146
|
|
147 /* Compare given strings [strcmp, strncmp]
|
|
148 */
|
|
149 int th_strcmp(char_t * pStr1, char_t * pStr2)
|
|
150 {
|
|
151 char_t *s1, *s2;
|
|
152 assert(pStr1);
|
|
153 assert(pStr2);
|
|
154
|
|
155 /* Check the string pointers */
|
|
156 if (pStr1 == pStr2)
|
|
157 return 0;
|
|
158
|
|
159 /* Go through the string */
|
|
160 s1 = pStr1;
|
|
161 s2 = pStr2;
|
|
162 while (*s1 && *s2 && (*s1 == *s2)) {
|
|
163 s1++;
|
|
164 s2++;
|
|
165 }
|
|
166
|
|
167 return ((*s1) - (*s2));
|
|
168 }
|
|
169
|
|
170
|
|
171 int th_strncmp(char_t * pStr1, char_t * pStr2, size_t n)
|
|
172 {
|
|
173 char_t *s1, *s2;
|
|
174 assert(pStr1);
|
|
175 assert(pStr2);
|
|
176
|
|
177 /* Check the string pointers */
|
|
178 if (pStr1 == pStr2)
|
|
179 return 0;
|
|
180
|
|
181 /* Go through the string */
|
|
182 s1 = pStr1;
|
|
183 s2 = pStr2;
|
|
184 while ((n > 0) && *s1 && *s2 && (*s1 == *s2)) {
|
|
185 s1++;
|
|
186 s2++;
|
|
187 n--;
|
|
188 }
|
|
189
|
|
190 if (n > 0)
|
|
191 return ((*s1) - (*s2));
|
|
192 else
|
|
193 return 0;
|
|
194 }
|
|
195
|
|
196
|
|
197 /* Compare two strings ignoring case [strcasecmp, strncasecmp]
|
|
198 */
|
|
199 int th_strcasecmp(char_t * pStr1, char_t * pStr2)
|
|
200 {
|
|
201 char_t *s1, *s2;
|
|
202 assert(pStr1);
|
|
203 assert(pStr2);
|
|
204
|
|
205 /* Check the string pointers */
|
|
206 if (pStr1 == pStr2)
|
|
207 return 0;
|
|
208
|
|
209 /* Go through the string */
|
|
210 s1 = pStr1;
|
|
211 s2 = pStr2;
|
|
212 while (*s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
|
|
213 s1++;
|
|
214 s2++;
|
|
215 }
|
|
216
|
|
217 return (th_tolower(*s1) - th_tolower(*s2));
|
|
218 }
|
|
219
|
|
220
|
|
221 int th_strncasecmp(char_t * pStr1, char_t * pStr2, size_t n)
|
|
222 {
|
|
223 char_t *s1, *s2;
|
|
224 assert(pStr1);
|
|
225 assert(pStr2);
|
|
226
|
|
227 /* Check the string pointers */
|
|
228 if (pStr1 == pStr2)
|
|
229 return 0;
|
|
230
|
|
231 /* Go through the string */
|
|
232 s1 = pStr1;
|
|
233 s2 = pStr2;
|
|
234 while ((n > 0) && *s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
|
|
235 s1++;
|
|
236 s2++;
|
|
237 n--;
|
|
238 }
|
|
239
|
|
240 if (n > 0)
|
|
241 return (th_tolower(*s1) - th_tolower(*s2));
|
|
242 else
|
|
243 return 0;
|
|
244 }
|
|
245
|
|
246
|
|
247 /* Remove all occurences of control characters, in-place.
|
|
248 * Resulting string is always shorter or same length than original.
|
|
249 */
|
|
250 void th_strip_ctrlchars(char_t * pStr)
|
|
251 {
|
|
252 char_t *i, *j;
|
|
253 assert(pStr);
|
|
254
|
|
255 i = pStr;
|
|
256 j = pStr;
|
|
257 while (*i) {
|
|
258 if (!th_iscntrl(*i))
|
|
259 *(j++) = *i;
|
|
260 i++;
|
|
261 }
|
|
262
|
|
263 *j = 0;
|
|
264 }
|
|
265
|
|
266
|
|
267 /* Locate a substring [strstr]
|
|
268 */
|
|
269 char_t *th_strstr(char_t * haystack, char_t * needle)
|
|
270 {
|
|
271 char_t *h, *n, *s;
|
|
272 assert(haystack);
|
|
273 assert(needle);
|
|
274
|
|
275 /* If 'needle' is empty, we return 'haystack' */
|
|
276 if (!*needle)
|
|
277 return haystack;
|
|
278
|
|
279 /* Search for 'needle' in 'haystack' */
|
|
280 h = haystack;
|
|
281 n = needle;
|
|
282 while (*h) {
|
|
283 /* Find possible start of 'needle' */
|
|
284 while (*h && (*h != *n))
|
|
285 h++;
|
|
286
|
|
287 if (*h == *n) {
|
|
288 /* Found, check rest */
|
|
289 s = h;
|
|
290 n = needle;
|
|
291 while (*h) {
|
|
292 if (!*n)
|
|
293 return s;
|
|
294 else
|
|
295 if (*h != *n)
|
|
296 break;
|
|
297 n++;
|
|
298 h++;
|
|
299 }
|
|
300 h = s;
|
|
301 h++;
|
|
302 }
|
|
303 }
|
|
304
|
|
305 return NULL;
|
|
306 }
|
|
307
|
|
308
|
|
309 /* Copy a given string over in *ppResult.
|
|
310 */
|
|
311 int th_pstrcpy(char_t ** ppResult, char_t * pStr)
|
|
312 {
|
|
313 assert(ppResult);
|
|
314
|
|
315 /* Check the string pointers */
|
|
316 if (!pStr)
|
|
317 return -1;
|
|
318
|
|
319 /* Allocate memory for destination */
|
|
320 th_free(*ppResult);
|
|
321 *ppResult = th_stralloc(th_strlen(pStr) + 1);
|
|
322 if (!*ppResult)
|
|
323 return -2;
|
|
324
|
|
325 /* Copy to the destination */
|
|
326 th_strcpy(*ppResult, pStr);
|
|
327
|
|
328 return 0;
|
|
329 }
|
|
330
|
|
331
|
|
332 /* Concatenates a given string into string pointed by *ppResult.
|
|
333 */
|
|
334 int th_pstrcat(char_t ** ppResult, char_t * pStr)
|
|
335 {
|
|
336 assert(ppResult);
|
|
337
|
|
338 /* Check the string pointers */
|
|
339 if (!pStr)
|
|
340 return -1;
|
|
341
|
|
342 if (*ppResult != NULL) {
|
|
343 *ppResult = th_strrealloc(*ppResult, th_strlen(*ppResult) + th_strlen(pStr) + 1);
|
|
344 if (*ppResult == NULL)
|
|
345 return -1;
|
|
346
|
|
347 th_strcat(*ppResult, pStr);
|
|
348 } else {
|
|
349 *ppResult = th_stralloc(th_strlen(pStr) + 1);
|
|
350 if (*ppResult == NULL)
|
|
351 return -1;
|
|
352
|
|
353 th_strcpy(*ppResult, pStr);
|
|
354 }
|
|
355
|
|
356 return 0;
|
|
357 }
|
|
358
|
|
359
|
|
360 /* Find next non-whitespace character in string.
|
|
361 * Updates iPos into the position of such character and
|
|
362 * returns pointer to the string.
|
|
363 */
|
|
364 char_t *th_findnext(char_t * pStr, size_t * iPos)
|
|
365 {
|
|
366 assert(pStr);
|
|
367
|
|
368 /* Terminating NULL-character is not whitespace! */
|
|
369 while (th_isspace(pStr[*iPos]))
|
|
370 (*iPos)++;
|
|
371 return &pStr[*iPos];
|
|
372 }
|
|
373
|
|
374
|
|
375 /* Find next chSep-character from string
|
|
376 */
|
|
377 char_t *th_findsep(char_t * pStr, size_t * iPos, char_t chSep)
|
|
378 {
|
|
379 assert(pStr);
|
|
380
|
|
381 /* Terminating NULL-character is not digit! */
|
|
382 while (pStr[*iPos] && (pStr[*iPos] != chSep))
|
|
383 (*iPos)++;
|
|
384 return &pStr[*iPos];
|
|
385 }
|
|
386
|
|
387
|
|
388 /* Find next chSep- or whitespace from string
|
|
389 */
|
|
390 char_t *th_findseporspace(char_t * pStr, size_t * iPos, char_t chSep)
|
|
391 {
|
|
392 assert(pStr);
|
|
393
|
|
394 /* Terminating NULL-character is not digit! */
|
|
395 while (!th_isspace(pStr[*iPos]) && (pStr[*iPos] != chSep))
|
|
396 (*iPos)++;
|
|
397 return &pStr[*iPos];
|
|
398 }
|
|
399
|
|
400
|
|
401 /* Compare a string to a pattern. Case-SENSITIVE version.
|
|
402 * The matching pattern can consist of any normal characters plus
|
|
403 * wildcards ? and *. "?" matches any character and "*" matches
|
|
404 * any number of characters.
|
|
405 */
|
|
406 BOOL th_strmatch(char_t * pStr, char_t * pPattern)
|
|
407 {
|
|
408 BOOL didMatch, isAnyMode, isEnd;
|
|
409 char_t *tmpPattern;
|
|
410
|
|
411 /* Check given pattern and string */
|
|
412 if (!pStr)
|
|
413 return FALSE;
|
|
414 if (!pPattern)
|
|
415 return FALSE;
|
|
416
|
|
417 /* Initialize */
|
|
418 tmpPattern = NULL;
|
|
419 didMatch = TRUE;
|
|
420 isEnd = FALSE;
|
|
421 isAnyMode = FALSE;
|
|
422
|
|
423 /* Start comparision */
|
|
424 do {
|
|
425 didMatch = FALSE;
|
|
426 switch (*pPattern) {
|
|
427 case '?':
|
|
428 /* Any single character matches */
|
|
429 if (*pStr) {
|
|
430 didMatch = TRUE;
|
|
431 pPattern++;
|
|
432 pStr++;
|
|
433 }
|
|
434 break;
|
|
435
|
|
436 case '*':
|
|
437 didMatch = TRUE;
|
|
438 pPattern++;
|
|
439 if (!*pPattern)
|
|
440 isEnd = TRUE;
|
|
441 isAnyMode = TRUE;
|
|
442 tmpPattern = pPattern;
|
|
443 break;
|
|
444
|
|
445 case 0:
|
|
446 if (isAnyMode) {
|
|
447 if (*pStr)
|
|
448 pStr++;
|
|
449 else
|
|
450 isEnd = TRUE;
|
|
451 } else {
|
|
452 if (*pStr) {
|
|
453 if (tmpPattern) {
|
|
454 isAnyMode = TRUE;
|
|
455 pPattern = tmpPattern;
|
|
456 } else
|
|
457 didMatch = FALSE;
|
|
458 } else
|
|
459 isEnd = TRUE;
|
|
460 }
|
|
461 break;
|
|
462 default:
|
|
463 if (isAnyMode) {
|
|
464 if ((*pPattern) == (*pStr)) {
|
|
465 isAnyMode = FALSE;
|
|
466 didMatch = TRUE;
|
|
467 } else {
|
|
468 if (*pStr) {
|
|
469 didMatch = TRUE;
|
|
470 pStr++;
|
|
471 }
|
|
472 }
|
|
473 } else {
|
|
474 if ((*pPattern) == (*pStr)) {
|
|
475 didMatch = TRUE;
|
|
476 if (*pPattern)
|
|
477 pPattern++;
|
|
478 if (*pStr)
|
|
479 pStr++;
|
|
480 } else {
|
|
481 if (tmpPattern) {
|
|
482 didMatch = TRUE;
|
|
483 isAnyMode = TRUE;
|
|
484 pPattern = tmpPattern;
|
|
485 }
|
|
486 }
|
|
487 }
|
|
488
|
|
489 if (!*pStr && !*pPattern)
|
|
490 isEnd = TRUE;
|
|
491 break;
|
|
492
|
|
493 } /* switch */
|
|
494
|
|
495 } while ((didMatch) && (!isEnd));
|
|
496
|
|
497 return didMatch;
|
|
498 }
|
|
499
|
|
500
|
|
501 /* Compare a string to a pattern. Case-INSENSITIVE version.
|
|
502 */
|
|
503 BOOL th_strcasematch(char_t * pStr, char_t * pPattern)
|
|
504 {
|
|
505 BOOL didMatch, isAnyMode, isEnd;
|
|
506 char_t *tmpPattern;
|
|
507
|
|
508 /* Check given pattern and string */
|
|
509 if (!pStr)
|
|
510 return FALSE;
|
|
511 if (!pPattern)
|
|
512 return FALSE;
|
|
513
|
|
514 /* Initialize */
|
|
515 tmpPattern = NULL;
|
|
516 didMatch = TRUE;
|
|
517 isEnd = FALSE;
|
|
518 isAnyMode = FALSE;
|
|
519
|
|
520 /* Start comparision */
|
|
521 do {
|
|
522 switch (*pPattern) {
|
|
523 case '?':
|
|
524 /* Any single character matches */
|
|
525 if (*pStr) {
|
|
526 pPattern++;
|
|
527 pStr++;
|
|
528 } else
|
|
529 didMatch = FALSE;
|
|
530 break;
|
|
531
|
|
532 case '*':
|
|
533 pPattern++;
|
|
534 if (!*pPattern || (*pPattern == '?'))
|
|
535 isEnd = TRUE;
|
|
536 isAnyMode = TRUE;
|
|
537 tmpPattern = pPattern;
|
|
538 break;
|
|
539
|
|
540 case 0:
|
|
541 if (isAnyMode) {
|
|
542 if (*pStr)
|
|
543 pStr++;
|
|
544 else
|
|
545 isEnd = TRUE;
|
|
546 } else {
|
|
547 if (*pStr) {
|
|
548 if (tmpPattern) {
|
|
549 isAnyMode = TRUE;
|
|
550 pPattern = tmpPattern;
|
|
551 } else
|
|
552 didMatch = FALSE;
|
|
553 } else
|
|
554 isEnd = TRUE;
|
|
555 }
|
|
556 break;
|
|
557
|
|
558 default:
|
|
559 if (isAnyMode) {
|
|
560 if (th_tolower(*pPattern) == th_tolower(*pStr)) {
|
|
561 isAnyMode = FALSE;
|
|
562 } else {
|
|
563 if (*pStr)
|
|
564 pStr++;
|
|
565 else
|
|
566 didMatch = FALSE;
|
|
567 }
|
|
568 } else {
|
|
569 if (th_tolower(*pPattern) == th_tolower(*pStr)) {
|
|
570 if (*pPattern)
|
|
571 pPattern++;
|
|
572 if (*pStr)
|
|
573 pStr++;
|
|
574 } else {
|
|
575 if (tmpPattern) {
|
|
576 isAnyMode = TRUE;
|
|
577 pPattern = tmpPattern;
|
|
578 } else
|
|
579 didMatch = FALSE;
|
|
580 }
|
|
581 }
|
|
582
|
|
583 if (!*pStr && !*pPattern)
|
|
584 isEnd = TRUE;
|
|
585 break;
|
|
586
|
|
587 } /* switch */
|
|
588
|
|
589 } while ((didMatch) && (!isEnd));
|
|
590
|
|
591 return didMatch;
|
|
592 }
|
|
593
|
|
594
|
|
595 /*
|
|
596 * Handling of string-lists and hashes
|
|
597 */
|
|
598 t_str_node *th_strnode_new(char_t * pcStr, ulint_t nUsed, void *pData)
|
|
599 {
|
|
600 t_str_node *pResult;
|
|
601
|
|
602 /* Allocate memory for new node */
|
|
603 pResult = (t_str_node *) th_calloc(1, sizeof(t_str_node));
|
|
604 if (!pResult)
|
|
605 return NULL;
|
|
606
|
|
607 /* Set fields */
|
|
608 th_pstrcpy(&pResult->pcStr, pcStr);
|
|
609 pResult->nUsed = nUsed;
|
|
610 pResult->pData = pData;
|
|
611
|
|
612 return pResult;
|
|
613 }
|
|
614
|
|
615
|
|
616 void th_strnode_free(t_str_node * pNode)
|
|
617 {
|
|
618 assert(pNode);
|
|
619
|
|
620 th_free(pNode->pcStr);
|
|
621 th_free(pNode);
|
|
622 }
|
|
623
|
|
624
|
|
625 /* Insert a new node into strlist
|
|
626 */
|
|
627 void th_strlist_insert(t_str_node ** strList, t_str_node * pNode)
|
|
628 {
|
|
629 assert(strList);
|
|
630 assert(pNode);
|
|
631
|
|
632 /* Insert into linked list */
|
|
633 if (*strList) {
|
|
634 /* The first node's pPrev points to last node */
|
|
635 LPREV = (*strList)->pPrev; /* New node's prev = Previous last node */
|
|
636 (*strList)->pPrev->pNext = pNode; /* Previous last node's next = New node */
|
|
637 (*strList)->pPrev = pNode; /* New last node = New node */
|
|
638 LNEXT = NULL; /* But next is NULL! */
|
|
639 } else {
|
|
640 (*strList) = pNode; /* First node ... */
|
|
641 LPREV = pNode; /* ... it's also last */
|
|
642 LNEXT = NULL; /* But next is NULL! */
|
|
643 }
|
|
644
|
|
645 }
|
|
646
|
|
647
|
|
648 /* Free a given strlist
|
|
649 */
|
|
650 void th_strlist_free(t_str_node * strList)
|
|
651 {
|
|
652 t_str_node *pNode, *nNode;
|
|
653
|
|
654 pNode = strList;
|
|
655 while (pNode) {
|
|
656 nNode = pNode->pNext;
|
|
657 th_strnode_free(pNode);
|
|
658 pNode = nNode;
|
|
659 }
|
|
660 }
|
|
661
|
|
662
|
|
663 /* Create a strIndex from strlist
|
|
664 */
|
|
665 t_str_index *th_strlist_makeindex(t_str_node * strList)
|
|
666 {
|
|
667 t_str_index *pResult;
|
|
668 t_str_node *pCurr;
|
|
669 ulint_t n;
|
|
670 assert(strList);
|
|
671
|
|
672 /* Computer number of nodes */
|
|
673 for (n = 0, pCurr = strList; pCurr; pCurr = pCurr->pNext)
|
|
674 n++;
|
|
675
|
|
676 /* Check number of nodes */
|
|
677 if (n == 0)
|
|
678 return NULL;
|
|
679
|
|
680 /* Allocate memory for index */
|
|
681 pResult = (t_str_index *) th_calloc(1, sizeof(t_str_index));
|
|
682 if (!pResult)
|
|
683 return NULL;
|
|
684
|
|
685 pResult->n = n;
|
|
686 pResult->ppIndex = (t_str_node **) th_calloc(n, sizeof(t_str_node *));
|
|
687 if (!pResult->ppIndex) {
|
|
688 th_free(pResult);
|
|
689 return NULL;
|
|
690 }
|
|
691
|
|
692 /* Create the index */
|
|
693 for (n = 0, pCurr = strList; pCurr && (n < pResult->n); pCurr = pCurr->pNext)
|
|
694 pResult->ppIndex[n++] = pCurr;
|
|
695
|
|
696 return pResult;
|
|
697 }
|
|
698
|
|
699
|
|
700 /* Insert a node into given strhash
|
|
701 */
|
|
702 int th_strhash_insert(t_str_hash strHash, t_str_node * pNode, BOOL ignoreCase)
|
|
703 {
|
|
704 int i;
|
|
705 assert(strHash);
|
|
706 assert(pNode);
|
|
707 assert(pNode->pcStr);
|
|
708
|
|
709 if (ignoreCase)
|
|
710 i = th_tolower(pNode->pcStr[0]);
|
|
711 else
|
|
712 i = pNode->pcStr[0];
|
|
713
|
|
714 /* Check the hashcode */
|
|
715 if ((i < 0) && (i >= SET_HASH_MAXINDEX))
|
|
716 return -1;
|
|
717
|
|
718 if (strHash[i]) {
|
|
719 /* The first node's pPrev points to last node */
|
|
720 pNode->pPrev = strHash[i]->pPrev; /* New node's prev = Previous last node */
|
|
721 strHash[i]->pPrev->pNext = pNode; /* Previous last node's next = New node */
|
|
722 strHash[i]->pPrev = pNode; /* New last node = New node */
|
|
723 pNode->pNext = NULL; /* But next is NULL! */
|
|
724 } else {
|
|
725 strHash[i] = pNode; /* First node */
|
|
726 pNode->pPrev = pNode; /* But also last */
|
|
727 pNode->pNext = NULL; /* But next is NULL! */
|
|
728 }
|
|
729
|
|
730 return 0;
|
|
731 }
|
|
732
|
|
733
|
|
734 /* Free a given strhash
|
|
735 */
|
|
736 void th_strhash_free(t_str_hash strHash)
|
|
737 {
|
|
738 int i;
|
|
739 assert(strHash);
|
|
740
|
|
741 for (i = 0; i < SET_HASH_MAXINDEX; i++)
|
|
742 th_strlist_free(strHash[i]);
|
|
743 }
|
|
744
|
|
745
|
|
746 /* Change pData for matching entries to new value
|
|
747 */
|
|
748 void th_strhash_change_pdata(t_str_hash strHash, void *pFind, void *pNew)
|
|
749 {
|
|
750 t_str_node *pCurr;
|
|
751 int i;
|
|
752 assert(strHash);
|
|
753
|
|
754 for (i = 0; i < SET_HASH_MAXINDEX; i++) {
|
|
755 /* Find from linked list */
|
|
756 pCurr = strHash[i];
|
|
757 while (pCurr) {
|
|
758 if (pCurr->pData == pFind)
|
|
759 pCurr->pData = pNew;
|
|
760
|
|
761 pCurr = pCurr->pNext;
|
|
762 }
|
|
763 }
|
|
764 }
|
|
765
|
|
766
|
|
767 /* Search a string from a given stringhash, either case-sensitive or insensitive
|
|
768 */
|
|
769 t_str_node *th_strhash_search(t_str_hash strHash, char_t * findStr, BOOL ignoreCase)
|
|
770 {
|
|
771 t_str_node *pCurr;
|
|
772 int i;
|
|
773 BOOL isFound;
|
|
774 assert(strHash);
|
|
775 assert(findStr);
|
|
776
|
|
777 isFound = FALSE;
|
|
778 pCurr = NULL;
|
|
779
|
|
780 /* Check hashcode */
|
|
781 if (ignoreCase)
|
|
782 i = ((unsigned char) th_tolower(findStr[0]));
|
|
783 else
|
|
784 i = ((unsigned char) findStr[0]);
|
|
785
|
|
786 if ((i < 0) && (i >= SET_HASH_MAXINDEX))
|
|
787 return NULL;
|
|
788
|
|
789 /* Find from linked list */
|
|
790 pCurr = strHash[i];
|
|
791
|
|
792 if (ignoreCase) {
|
|
793 /* Case in-sensitive search */
|
|
794 while (pCurr && !isFound) {
|
|
795 if (th_strcasecmp(findStr, pCurr->pcStr) == 0)
|
|
796 isFound = TRUE;
|
|
797 else
|
|
798 pCurr = pCurr->pNext;
|
|
799 }
|
|
800 } else {
|
|
801 /* Case sensitive search */
|
|
802 while (pCurr && !isFound) {
|
|
803 if (th_strcmp(findStr, pCurr->pcStr) == 0)
|
|
804 isFound = TRUE;
|
|
805 else
|
|
806 pCurr = pCurr->pNext;
|
|
807 }
|
|
808 }
|
|
809
|
|
810 /* Return result */
|
|
811 if (isFound)
|
|
812 return pCurr;
|
|
813 else
|
|
814 return NULL;
|
|
815 }
|
|
816
|
|
817
|
|
818 /* Create a strIndex from strHash
|
|
819 */
|
|
820 t_str_index *th_strhash_makeindex(t_str_hash strHash)
|
|
821 {
|
|
822 t_str_index *pResult;
|
|
823 t_str_node *pCurr;
|
|
824 unsigned int n, i;
|
|
825 assert(strHash);
|
|
826
|
|
827 /* Computer number of nodes */
|
|
828 for (n = i = 0; i < SET_HASH_MAXINDEX; i++) {
|
|
829 pCurr = strHash[i];
|
|
830 while (pCurr) {
|
|
831 n++;
|
|
832 pCurr = pCurr->pNext;
|
|
833 }
|
|
834 }
|
|
835
|
|
836 /* Check number of nodes */
|
|
837 if (n <= 0)
|
|
838 return NULL;
|
|
839
|
|
840 /* Allocate memory for index */
|
|
841 pResult = (t_str_index *) th_calloc(1, sizeof(t_str_index));
|
|
842 if (!pResult)
|
|
843 return NULL;
|
|
844
|
|
845 pResult->n = n;
|
|
846 pResult->ppIndex = (t_str_node **) th_calloc(n, sizeof(t_str_node *));
|
|
847 if (!pResult->ppIndex) {
|
|
848 th_free(pResult);
|
|
849 return NULL;
|
|
850 }
|
|
851
|
|
852 /* Create the index */
|
|
853 for (n = i = 0; (i < SET_HASH_MAXINDEX) && (n < pResult->n); i++) {
|
|
854 pCurr = strHash[i];
|
|
855 while (pCurr && (n < pResult->n)) {
|
|
856 pResult->ppIndex[n++] = pCurr;
|
|
857 pCurr = pCurr->pNext;
|
|
858 }
|
|
859 }
|
|
860
|
|
861 return pResult;
|
|
862 }
|
|
863
|
|
864
|
|
865 /* Free a given strIndex
|
|
866 */
|
|
867 void th_strindex_free(t_str_index * strIndex)
|
|
868 {
|
|
869 if (strIndex) {
|
|
870 th_free(strIndex->ppIndex);
|
|
871 th_free(strIndex);
|
|
872 }
|
|
873 }
|
|
874
|
|
875
|
|
876 /* Compare two t_str_nodes by nUsed
|
|
877 */
|
|
878 int th_strindex_cmp_used(const void *pNode1, const void *pNode2)
|
|
879 {
|
|
880 t_str_node *pStr1, *pStr2;
|
|
881
|
|
882 pStr1 = *(t_str_node **) pNode1;
|
|
883 pStr2 = *(t_str_node **) pNode2;
|
|
884
|
|
885 if (pStr1->nUsed > pStr2->nUsed)
|
|
886 return -1;
|
|
887 else if (pStr1->nUsed < pStr2->nUsed)
|
|
888 return 1;
|
|
889 else
|
|
890 return 0;
|
|
891 }
|
|
892
|
|
893
|
|
894 /* Sort an strIndex by nUsed, using th_strindex_cmp_used()
|
|
895 */
|
|
896 void th_strindex_sort_nused(t_str_index * strIndex)
|
|
897 {
|
|
898 assert(strIndex);
|
|
899 assert(strIndex->ppIndex);
|
|
900
|
|
901 qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_used);
|
|
902 }
|
|
903
|
|
904
|
|
905 /* Compare two t_str_nodes via strcmp()
|
|
906 */
|
|
907 int th_strindex_cmp_alpha(const void *pNode1, const void *pNode2)
|
|
908 {
|
|
909 t_str_node *pStr1, *pStr2;
|
|
910
|
|
911 pStr1 = *(t_str_node **) pNode1;
|
|
912 pStr2 = *(t_str_node **) pNode2;
|
|
913
|
|
914 return th_strcmp(pStr1->pcStr, pStr2->pcStr);
|
|
915 }
|
|
916
|
|
917
|
|
918 /* Sort an strIndex by nUsed, using th_strindex_cmp_used()
|
|
919 */
|
|
920 void th_strindex_sort_alpha(t_str_index * strIndex)
|
|
921 {
|
|
922 assert(strIndex);
|
|
923 assert(strIndex->ppIndex);
|
|
924
|
|
925 qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_alpha);
|
|
926 }
|