blob: f1ab5b863a38fd60c8822d806020dca774546534 [file] [log] [blame]
Daniel Veillard92ad2102001-03-27 12:47:33 +00001/*************************************************************************
2 *
3 * $Id$
4 *
5 * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
12 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
13 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
14 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
15 *
16 ************************************************************************/
17
Bjorn Reese70a9da52001-04-21 16:57:29 +000018/*
19 * TODO
20 * - StrToLongDouble
Daniel Veillard92ad2102001-03-27 12:47:33 +000021 */
22
23static const char rcsid[] = "@(#)$Id$";
24
Bjorn Reese906ec8a2001-06-05 12:46:33 +000025#if defined(unix) || defined(__xlC__) || defined(__QNX__)
Daniel Veillard92ad2102001-03-27 12:47:33 +000026# define PLATFORM_UNIX
27#elif defined(WIN32) || defined(_WIN32)
28# define PLATFORM_WIN32
29#elif defined(AMIGA) && defined(__GNUC__)
30# define PLATFORM_UNIX
31#endif
32
33#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
34# define TRIO_C99
35#endif
36
37#include "strio.h"
38#include <string.h>
Bjorn Reese906ec8a2001-06-05 12:46:33 +000039#include <locale.h>
Daniel Veillard92ad2102001-03-27 12:47:33 +000040#include <ctype.h>
41#include <stdarg.h>
42#include <time.h>
43#include <math.h>
44#ifndef DEBUG
45# define NDEBUG
46#endif
47#include <assert.h>
48
49#ifndef NULL
50# define NULL 0
51#endif
52#define NIL ((char)0)
53#ifndef FALSE
54# define FALSE (1 == 0)
55# define TRUE (! FALSE)
56#endif
57
58#define VALID(x) (NULL != (x))
59#define INVALID(x) (NULL == (x))
60
61#if defined(PLATFORM_UNIX)
62# define USE_STRCASECMP
63# define USE_STRNCASECMP
64# define USE_STRERROR
Bjorn Reese906ec8a2001-06-05 12:46:33 +000065# if defined(__QNX__)
66# define strcasecmp(x,y) stricmp(x,y)
67# define strncasecmp(x,y,n) strnicmp(x,y,n)
68# endif
Daniel Veillard92ad2102001-03-27 12:47:33 +000069#elif defined(PLATFORM_WIN32)
Bjorn Reese906ec8a2001-06-05 12:46:33 +000070# define USE_STRCASECMP
71# define strcasecmp(x,y) strcmpi(x,y)
Daniel Veillard92ad2102001-03-27 12:47:33 +000072#endif
73
74/*************************************************************************
75 * StrAppendMax
76 */
77char *StrAppendMax(char *target, size_t max, const char *source)
78{
79 assert(VALID(target));
80 assert(VALID(source));
81 assert(max > 0);
82
83 max -= StrLength(target) + 1;
84 return (max > 0) ? strncat(target, source, max) : target;
85}
86
87/*************************************************************************
88 * StrCopyMax
89 */
90char *StrCopyMax(char *target, size_t max, const char *source)
91{
92 assert(VALID(target));
93 assert(VALID(source));
94 assert(max > 0); /* Includes != 0 */
95
96 target = strncpy(target, source, max - 1);
97 target[max - 1] = (char)0;
98 return target;
99}
100
101/*************************************************************************
102 * StrDuplicate
103 */
104char *StrDuplicate(const char *source)
105{
106 char *target;
107
108 assert(VALID(source));
109
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000110 target = StrAlloc(StrLength(source) + 1);
Daniel Veillard92ad2102001-03-27 12:47:33 +0000111 if (target)
112 {
113 StrCopy(target, source);
114 }
115 return target;
116}
117
118/*************************************************************************
119 * StrDuplicateMax
120 */
121char *StrDuplicateMax(const char *source, size_t max)
122{
123 char *target;
124 size_t len;
125
126 assert(VALID(source));
127 assert(max > 0);
128
129 /* Make room for string plus a terminating zero */
130 len = StrLength(source) + 1;
131 if (len > max)
132 {
133 len = max;
134 }
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000135 target = StrAlloc(len);
Daniel Veillard92ad2102001-03-27 12:47:33 +0000136 if (target)
137 {
138 StrCopyMax(target, len, source);
139 }
140 return target;
141}
142
143/*************************************************************************
144 * StrEqual
145 */
146int StrEqual(const char *first, const char *second)
147{
148 assert(VALID(first));
149 assert(VALID(second));
150
151 if (VALID(first) && VALID(second))
152 {
153#if defined(USE_STRCASECMP)
154 return (0 == strcasecmp(first, second));
Daniel Veillard92ad2102001-03-27 12:47:33 +0000155#else
156 while ((*first != NIL) && (*second != NIL))
157 {
158 if (toupper(*first) != toupper(*second))
159 {
160 break;
161 }
162 first++;
163 second++;
164 }
165 return ((*first == NIL) && (*second == NIL));
166#endif
167 }
168 return FALSE;
169}
170
171/*************************************************************************
172 * StrEqualCase
173 */
174int StrEqualCase(const char *first, const char *second)
175{
176 assert(VALID(first));
177 assert(VALID(second));
178
179 if (VALID(first) && VALID(second))
180 {
181 return (0 == strcmp(first, second));
182 }
183 return FALSE;
184}
185
186/*************************************************************************
187 * StrEqualCaseMax
188 */
189int StrEqualCaseMax(const char *first, size_t max, const char *second)
190{
191 assert(VALID(first));
192 assert(VALID(second));
193
194 if (VALID(first) && VALID(second))
195 {
196 return (0 == strncmp(first, second, max));
197 }
198 return FALSE;
199}
200
201/*************************************************************************
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000202 * StrEqualLocale
203 */
204int StrEqualLocale(const char *first, const char *second)
205{
206 assert(VALID(first));
207 assert(VALID(second));
208
209#if defined(LC_COLLATE)
210 return (strcoll(first, second) == 0);
211#else
212 return StrEqual(first, second);
213#endif
214}
215
216/*************************************************************************
Daniel Veillard92ad2102001-03-27 12:47:33 +0000217 * StrEqualMax
218 */
219int StrEqualMax(const char *first, size_t max, const char *second)
220{
221 assert(VALID(first));
222 assert(VALID(second));
223
224 if (VALID(first) && VALID(second))
225 {
226#if defined(USE_STRNCASECMP)
227 return (0 == strncasecmp(first, second, max));
228#else
229 /* Not adequately tested yet */
230 size_t cnt = 0;
231 while ((*first != NIL) && (*second != NIL) && (cnt <= max))
232 {
233 if (toupper(*first) != toupper(*second))
234 {
235 break;
236 }
237 first++;
238 second++;
239 cnt++;
240 }
241 return ((cnt == max) || ((*first == NIL) && (*second == NIL)));
242#endif
243 }
244 return FALSE;
245}
246
247/*************************************************************************
248 * StrError
249 */
250const char *StrError(int errorNumber)
251{
252#if defined(USE_STRERROR)
253 return strerror(errorNumber);
254#else
255 return "unknown";
256#endif
257}
258
259/*************************************************************************
260 * StrFormatDate
261 */
262size_t StrFormatDateMax(char *target,
263 size_t max,
264 const char *format,
265 const struct tm *datetime)
266{
267 assert(VALID(target));
268 assert(VALID(format));
269 assert(VALID(datetime));
270 assert(max > 0);
271
272 return strftime(target, max, format, datetime);
273}
274
275/*************************************************************************
276 * StrHash
277 */
278unsigned long StrHash(const char *string, int type)
279{
280 unsigned long value = 0L;
281 char ch;
282
283 assert(VALID(string));
284
285 switch (type)
286 {
287 case STRIO_HASH_PLAIN:
288 while ( (ch = *string++) != NIL )
289 {
290 value *= 31;
291 value += (unsigned long)ch;
292 }
293 break;
294 default:
295 assert(FALSE);
296 break;
297 }
298 return value;
299}
300
301/*************************************************************************
302 * StrMatch
303 */
304int StrMatch(char *string, char *pattern)
305{
306 assert(VALID(string));
307 assert(VALID(pattern));
308
309 for (; ('*' != *pattern); ++pattern, ++string)
310 {
311 if (NIL == *string)
312 {
313 return (NIL == *pattern);
314 }
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000315 if ((toupper((int)*string) != toupper((int)*pattern))
Daniel Veillard92ad2102001-03-27 12:47:33 +0000316 && ('?' != *pattern))
317 {
318 return FALSE;
319 }
320 }
321 /* two-line patch to prevent *too* much recursiveness: */
322 while ('*' == pattern[1])
323 pattern++;
324
325 do
326 {
327 if ( StrMatch(string, &pattern[1]) )
328 {
329 return TRUE;
330 }
331 }
332 while (*string++);
333
334 return FALSE;
335}
336
337/*************************************************************************
338 * StrMatchCase
339 */
340int StrMatchCase(char *string, char *pattern)
341{
342 assert(VALID(string));
343 assert(VALID(pattern));
344
345 for (; ('*' != *pattern); ++pattern, ++string)
346 {
347 if (NIL == *string)
348 {
349 return (NIL == *pattern);
350 }
351 if ((*string != *pattern)
352 && ('?' != *pattern))
353 {
354 return FALSE;
355 }
356 }
357 /* two-line patch to prevent *too* much recursiveness: */
358 while ('*' == pattern[1])
359 pattern++;
360
361 do
362 {
363 if ( StrMatchCase(string, &pattern[1]) )
364 {
365 return TRUE;
366 }
367 }
368 while (*string++);
369
370 return FALSE;
371}
372
373/*************************************************************************
374 * StrSpanFunction
375 *
376 * Untested
377 */
378size_t StrSpanFunction(char *source, int (*Function)(int))
379{
380 size_t count = 0;
381
382 assert(VALID(source));
383 assert(VALID(Function));
384
385 while (*source != NIL)
386 {
387 if (Function(*source))
388 break; /* while */
389 source++;
390 count++;
391 }
392 return count;
393}
394
395/*************************************************************************
396 * StrSubstringMax
397 */
398char *StrSubstringMax(const char *string, size_t max, const char *find)
399{
400 size_t count;
401 size_t size;
402 char *result = NULL;
403
404 assert(VALID(string));
405 assert(VALID(find));
406
407 size = StrLength(find);
Bjorn Reese70a9da52001-04-21 16:57:29 +0000408 if (size <= max)
Daniel Veillard92ad2102001-03-27 12:47:33 +0000409 {
Bjorn Reese70a9da52001-04-21 16:57:29 +0000410 for (count = 0; count <= max - size; count++)
Daniel Veillard92ad2102001-03-27 12:47:33 +0000411 {
412 if (StrEqualMax(find, size, &string[count]))
413 {
414 result = (char *)&string[count];
415 break;
416 }
417 }
418 }
419 return result;
420}
421
422/*************************************************************************
423 * StrToDouble
424 *
425 * double ::= [ <sign> ]
426 * ( <number> |
427 * <number> <decimal_point> <number> |
428 * <decimal_point> <number> )
429 * [ <exponential> [ <sign> ] <number> ]
430 * number ::= 1*( <digit> )
431 * digit ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
432 * exponential ::= ( 'e' | 'E' )
433 * sign ::= ( '-' | '+' )
434 * decimal_point ::= '.'
435 */
436double StrToDouble(const char *source, const char **endp)
437{
438#if defined(TRIO_C99)
439 return strtod(source, endp);
440#else
441 /* Preliminary code */
442 int isNegative = FALSE;
443 int isExponentNegative = FALSE;
444 unsigned long integer = 0;
445 unsigned long fraction = 0;
446 unsigned long fracdiv = 1;
447 unsigned long exponent = 0;
448 double value = 0.0;
449
450 /* First try hex-floats */
451 if ((source[0] == '0') && ((source[1] == 'x') || (source[1] == 'X')))
452 {
453 source += 2;
454 while (isxdigit((int)*source))
455 {
456 integer *= 16;
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000457 integer += (isdigit((int)*source)
458 ? (*source - '0')
459 : 10 + (toupper((int)*source) - 'A'));
Daniel Veillard92ad2102001-03-27 12:47:33 +0000460 source++;
461 }
462 if (*source == '.')
463 {
464 source++;
465 while (isxdigit((int)*source))
466 {
467 fraction *= 16;
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000468 fraction += (isdigit((int)*source)
469 ? (*source - '0')
470 : 10 + (toupper((int)*source) - 'A'));
Daniel Veillard92ad2102001-03-27 12:47:33 +0000471 fracdiv *= 16;
472 source++;
473 }
474 if ((*source == 'p') || (*source == 'P'))
475 {
476 source++;
477 if ((*source == '+') || (*source == '-'))
478 {
479 isExponentNegative = (*source == '-');
480 source++;
481 }
482 while (isdigit((int)*source))
483 {
484 exponent *= 10;
485 exponent += (*source - '0');
486 source++;
487 }
488 }
489 }
490 }
491 else /* Then try normal decimal floats */
492 {
493 isNegative = (*source == '-');
494 /* Skip sign */
495 if ((*source == '+') || (*source == '-'))
496 source++;
497
498 /* Integer part */
499 while (isdigit((int)*source))
500 {
501 integer *= 10;
502 integer += (*source - '0');
503 source++;
504 }
505
506 if (*source == '.')
507 {
508 source++; /* skip decimal point */
509 while (isdigit((int)*source))
510 {
511 fraction *= 10;
512 fraction += (*source - '0');
513 fracdiv *= 10;
514 source++;
515 }
516 }
517 if ((*source == 'e') || (*source == 'E'))
518 {
519 source++; /* Skip exponential indicator */
520 isExponentNegative = (*source == '-');
521 if ((*source == '+') || (*source == '-'))
522 source++;
523 while (isdigit((int)*source))
524 {
525 exponent *= 10;
526 exponent += (*source - '0');
527 source++;
528 }
529 }
530 }
531
532 value = (double)integer;
533 if (fraction != 0)
534 {
535 value += (double)fraction / (double)fracdiv;
536 }
537 if (exponent != 0)
538 {
539 if (isExponentNegative)
540 value /= pow((double)10, (double)exponent);
541 else
542 value *= pow((double)10, (double)exponent);
543 }
544 if (isNegative)
545 value = -value;
546
547 if (endp)
548 *endp = source;
549 return value;
550#endif
551}
552
553/*************************************************************************
554 * StrToFloat
555 */
556float StrToFloat(const char *source, const char **endp)
557{
558#if defined(TRIO_C99)
559 return strtof(source, endp);
560#else
561 return (float)StrToDouble(source, endp);
562#endif
563}
564
565/*************************************************************************
566 * StrToUpper
567 */
568int StrToUpper(char *target)
569{
570 int i = 0;
571
572 assert(VALID(target));
573
574 while (NIL != *target)
575 {
Bjorn Reese906ec8a2001-06-05 12:46:33 +0000576 *target = toupper((int)*target);
Daniel Veillard92ad2102001-03-27 12:47:33 +0000577 target++;
578 i++;
579 }
580 return i;
581}