… | |
… | |
20 | * This file provides replacements for some handy functions that aren't |
20 | * This file provides replacements for some handy functions that aren't |
21 | * available on all systems, like most of the <string.h> functions. They |
21 | * available on all systems, like most of the <string.h> functions. They |
22 | * should behave exactly as their counterparts. There are also extensions |
22 | * should behave exactly as their counterparts. There are also extensions |
23 | * that aren't portable at all (like strirstr etc.). |
23 | * that aren't portable at all (like strirstr etc.). |
24 | * The proper behaviour in a configure script is as follows: |
24 | * The proper behaviour in a configure script is as follows: |
25 | * AC_CHECK_FUNC(strrchr,AC_DEFINE(strrchr,_FP_strrchr)) |
25 | * AC_CHECK_FUNC(strrchr,AC_DEFINE(strrchr,FP_strrchr)) |
26 | * This way, the (probably less efficient) replacements will only be used |
26 | * This way, the (probably less efficient) replacements will only be used |
27 | * where it is not provided by the default libraries. Be aware that this |
27 | * where it is not provided by the default libraries. Be aware that this |
28 | * does not work with replacements that just shadow wrong behaviour (like |
28 | * does not work with replacements that just shadow wrong behaviour (like |
29 | * _FP_free) or provide extended functionality (_FP_gets). |
29 | * FP_free) or provide extended functionality (FP_gets). |
30 | * The above is not used in the uuenview/uudeview configuration script, |
30 | * The above is not used in the uuenview/uudeview configuration script, |
31 | * since both only use the replacement functions in non-performance-cri- |
31 | * since both only use the replacement functions in non-performance-cri- |
32 | * tical sections (except for _FP_tempnam and _FP_strerror, where some |
32 | * tical sections (except for FP_tempnam and FP_strerror, where some |
33 | * functionality of the original would be lost). |
33 | * functionality of the original would be lost). |
34 | */ |
34 | */ |
35 | |
35 | |
36 | #include <stdio.h> |
36 | #include <stdio.h> |
37 | #include <ctype.h> |
37 | #include <ctype.h> |
38 | |
|
|
39 | #ifdef STDC_HEADERS |
|
|
40 | #include <stdlib.h> |
38 | #include <stdlib.h> |
41 | #include <string.h> |
39 | #include <string.h> |
42 | #endif |
40 | |
43 | #ifdef HAVE_MALLOC_H |
|
|
44 | #include <malloc.h> |
|
|
45 | #endif |
|
|
46 | #ifdef HAVE_UNISTD_H |
41 | #ifdef HAVE_UNISTD_H |
47 | #include <unistd.h> |
42 | #include <unistd.h> |
48 | #endif |
|
|
49 | #ifdef HAVE_MEMORY_H |
|
|
50 | #include <memory.h> |
|
|
51 | #endif |
43 | #endif |
52 | |
44 | |
53 | #include <fptools.h> |
45 | #include <fptools.h> |
54 | |
46 | |
55 | #if 0 |
47 | #if 0 |
… | |
… | |
62 | return TRUE; |
54 | return TRUE; |
63 | } |
55 | } |
64 | #endif |
56 | #endif |
65 | #endif |
57 | #endif |
66 | |
58 | |
67 | char * fptools_id = "$Id: fptools.c,v 1.3.2.5 2004/04/18 19:55:46 root Exp $"; |
|
|
68 | |
|
|
69 | /* |
59 | /* |
70 | * some versions of free can't handle a NULL pointer properly |
60 | * some versions of free can't handle a NULL pointer properly |
71 | * (ANSI says, free ignores a NULL pointer, but some machines |
61 | * (ANSI says, free ignores a NULL pointer, but some machines |
72 | * prefer to SIGSEGV on it) |
62 | * prefer to SIGSEGV on it) |
73 | */ |
63 | */ |
74 | |
64 | |
75 | void TOOLEXPORT |
65 | void TOOLEXPORT |
76 | _FP_free (void *ptr) |
66 | FP_free (void *ptr) |
77 | { |
67 | { |
78 | if (ptr) free (ptr); |
68 | if (ptr) free (ptr); |
79 | } |
69 | } |
80 | |
70 | |
81 | /* |
71 | /* |
82 | * This is non-standard, so I'm defining my own |
72 | * This is non-standard, so I'm defining my own |
83 | */ |
73 | */ |
84 | |
74 | |
85 | char * TOOLEXPORT |
75 | char * TOOLEXPORT |
86 | _FP_strdup (char *string) |
76 | FP_strdup (char *string) |
87 | { |
77 | { |
88 | char *result; |
78 | char *result; |
89 | |
79 | |
90 | if (string == NULL) |
80 | if (string == NULL) |
91 | return NULL; |
81 | return NULL; |
… | |
… | |
102 | * the original in that the dest string is always terminated with a |
92 | * the original in that the dest string is always terminated with a |
103 | * NULL character. |
93 | * NULL character. |
104 | */ |
94 | */ |
105 | |
95 | |
106 | char * TOOLEXPORT |
96 | char * TOOLEXPORT |
107 | _FP_strncpy (char *dest, char *src, int length) |
97 | FP_strncpy (char *dest, char *src, int length) |
108 | { |
98 | { |
109 | char *odest=dest; |
99 | char *odest=dest; |
110 | if (src == NULL || dest == NULL || length-- <= 0) |
100 | if (src == NULL || dest == NULL || length-- <= 0) |
111 | return dest; |
101 | return dest; |
112 | |
102 | |
… | |
… | |
120 | /* |
110 | /* |
121 | * duplicate a memory area |
111 | * duplicate a memory area |
122 | */ |
112 | */ |
123 | |
113 | |
124 | void * TOOLEXPORT |
114 | void * TOOLEXPORT |
125 | _FP_memdup (void *ptr, int len) |
115 | FP_memdup (const void *ptr, int len) |
126 | { |
116 | { |
127 | void *result; |
117 | void *result; |
128 | |
118 | |
129 | if (ptr == NULL) |
119 | if (ptr == NULL) |
130 | return NULL; |
120 | return NULL; |
… | |
… | |
138 | |
128 | |
139 | /* |
129 | /* |
140 | * case-insensitive compare |
130 | * case-insensitive compare |
141 | */ |
131 | */ |
142 | |
132 | |
|
|
133 | #ifndef FP_stricmp |
143 | int TOOLEXPORT |
134 | int TOOLEXPORT |
144 | _FP_stricmp (char *str1, char *str2) |
135 | FP_stricmp (const char *str1, const char *str2) |
145 | { |
136 | { |
146 | if (str1==NULL || str2==NULL) |
137 | if (str1==NULL || str2==NULL) |
147 | return -1; |
138 | return -1; |
148 | |
139 | |
149 | while (*str1) { |
140 | while (*str1) { |
… | |
… | |
152 | str1++; |
143 | str1++; |
153 | str2++; |
144 | str2++; |
154 | } |
145 | } |
155 | return (tolower (*str1) - tolower (*str2)); |
146 | return (tolower (*str1) - tolower (*str2)); |
156 | } |
147 | } |
|
|
148 | #endif |
157 | |
149 | |
|
|
150 | #ifndef FP_strnicmp |
158 | int TOOLEXPORT |
151 | int TOOLEXPORT |
159 | _FP_strnicmp (char *str1, char *str2, int count) |
152 | FP_strnicmp (const char *str1, const char *str2, int count) |
160 | { |
153 | { |
161 | if (str1==NULL || str2==NULL) |
154 | int d; |
|
|
155 | |
|
|
156 | if (!str1 || !str2) |
162 | return -1; |
157 | return -1; |
163 | |
158 | |
164 | while (*str1 && count) { |
159 | while (count--) { |
|
|
160 | if (!*str1) |
|
|
161 | return -1; |
|
|
162 | |
165 | if (tolower(*str1) != tolower(*str2)) |
163 | d = tolower (*str1) - tolower (*str2); |
166 | break; |
164 | if (d) |
|
|
165 | return d; |
|
|
166 | |
167 | str1++; |
167 | str1++; |
168 | str2++; |
168 | str2++; |
169 | count--; |
|
|
170 | } |
169 | } |
171 | return count ? (tolower (*str1) - tolower (*str2)) : 0; |
|
|
172 | } |
|
|
173 | |
170 | |
174 | /* |
171 | return 0; |
175 | * autoconf says this function might be a compatibility problem |
172 | } |
176 | */ |
173 | #endif |
177 | |
174 | |
178 | char * TOOLEXPORT |
175 | int TOOLEXPORT |
179 | _FP_strstr (char *str1, char *str2) |
176 | FP_strnicmp_fast (const char *str1, const char *str2, int count) |
180 | { |
177 | { |
181 | char *ptr1, *ptr2; |
178 | if (!str1 || !str2) |
182 | |
|
|
183 | if (str1==NULL) |
|
|
184 | return NULL; |
|
|
185 | if (str2==NULL) |
|
|
186 | return str1; |
179 | return -1; |
187 | |
180 | |
188 | while (*(ptr1=str1)) { |
181 | while (count--) { |
189 | for (ptr2=str2; |
182 | if (!*str1) |
190 | *ptr1 && *ptr2 && *ptr1==*ptr2; |
|
|
191 | ptr1++, ptr2++) |
|
|
192 | /* empty loop */ ; |
|
|
193 | |
|
|
194 | if (*ptr2 == '\0') |
|
|
195 | return str1; |
183 | return -1; |
|
|
184 | |
|
|
185 | if ((*str1 ^ *str2) & 0xdf) |
|
|
186 | return (*str1 & 0xdf) - (*str2 & 0xdf); |
|
|
187 | |
196 | str1++; |
188 | str1++; |
|
|
189 | str2++; |
197 | } |
190 | } |
|
|
191 | |
198 | return NULL; |
192 | return 0; |
199 | } |
193 | } |
200 | |
194 | |
201 | char * TOOLEXPORT |
195 | char * TOOLEXPORT |
202 | _FP_strpbrk (char *str, char *accept) |
196 | FP_strpbrk (char *str, char *accept) |
203 | { |
197 | { |
204 | char *ptr; |
198 | char *ptr; |
205 | |
199 | |
206 | if (str == NULL) |
200 | if (str == NULL) |
207 | return NULL; |
201 | return NULL; |
… | |
… | |
219 | /* |
213 | /* |
220 | * autoconf also complains about this one |
214 | * autoconf also complains about this one |
221 | */ |
215 | */ |
222 | |
216 | |
223 | char * TOOLEXPORT |
217 | char * TOOLEXPORT |
224 | _FP_strtok (char *str1, char *str2) |
218 | FP_strtok (char *str1, char *str2) |
225 | { |
219 | { |
226 | static char *optr; |
220 | static char *optr; |
227 | char *ptr; |
221 | char *ptr; |
228 | |
222 | |
229 | if (str2 == NULL) |
223 | if (str2 == NULL) |
… | |
… | |
255 | |
249 | |
256 | /* |
250 | /* |
257 | * case insensitive strstr. |
251 | * case insensitive strstr. |
258 | */ |
252 | */ |
259 | |
253 | |
|
|
254 | #ifndef FP_stristr |
260 | char * TOOLEXPORT |
255 | char * TOOLEXPORT |
261 | _FP_stristr (char *str1, char *str2) |
256 | FP_stristr (char *str1, char *str2) |
262 | { |
257 | { |
263 | char *ptr1, *ptr2; |
258 | char *ptr1, *ptr2; |
264 | |
259 | |
265 | if (str1==NULL) |
260 | if (str1==NULL) |
266 | return NULL; |
261 | return NULL; |
… | |
… | |
277 | return str1; |
272 | return str1; |
278 | str1++; |
273 | str1++; |
279 | } |
274 | } |
280 | return NULL; |
275 | return NULL; |
281 | } |
276 | } |
|
|
277 | #endif |
282 | |
278 | |
283 | /* |
279 | /* |
284 | * Nice fake of the real (non-standard) one |
280 | * Nice fake of the real (non-standard) one |
285 | */ |
281 | */ |
286 | |
282 | |
287 | char * TOOLEXPORT |
283 | char * TOOLEXPORT |
288 | _FP_strrstr (char *ptr, char *str) |
284 | FP_strrstr (const char *ptr, const char *str) |
289 | { |
285 | { |
290 | char *found=NULL, *new, *iter=ptr; |
286 | const char *found=NULL, *new, *iter=ptr; |
291 | |
287 | |
292 | if (ptr==NULL || str==NULL) |
288 | if (ptr==NULL || str==NULL) |
293 | return NULL; |
289 | return NULL; |
294 | |
290 | |
|
|
291 | if (*str == '\0') |
|
|
292 | return (char *)ptr; |
|
|
293 | |
|
|
294 | while ((new = FP_strstr (iter, str)) != NULL) { |
|
|
295 | found = new; |
|
|
296 | iter = new + 1; |
|
|
297 | } |
|
|
298 | |
|
|
299 | return (char *)found; |
|
|
300 | } |
|
|
301 | |
|
|
302 | char * TOOLEXPORT |
|
|
303 | FP_strirstr (char *ptr, char *str) |
|
|
304 | { |
|
|
305 | char *found=NULL, *iter=ptr, *new; |
|
|
306 | |
|
|
307 | if (ptr==NULL || str==NULL) |
|
|
308 | return NULL; |
295 | if (*str == '\0') |
309 | if (*str == '\0') |
296 | return ptr; |
310 | return ptr; |
297 | |
311 | |
298 | while ((new = _FP_strstr (iter, str)) != NULL) { |
312 | while ((new = FP_stristr (iter, str)) != NULL) { |
299 | found = new; |
313 | found = new; |
300 | iter = new + 1; |
314 | iter = new + 1; |
301 | } |
315 | } |
302 | return found; |
316 | return found; |
303 | } |
317 | } |
304 | |
318 | |
305 | char * TOOLEXPORT |
|
|
306 | _FP_strirstr (char *ptr, char *str) |
|
|
307 | { |
|
|
308 | char *found=NULL, *iter=ptr, *new; |
|
|
309 | |
|
|
310 | if (ptr==NULL || str==NULL) |
|
|
311 | return NULL; |
|
|
312 | if (*str == '\0') |
|
|
313 | return ptr; |
|
|
314 | |
|
|
315 | while ((new = _FP_stristr (iter, str)) != NULL) { |
|
|
316 | found = new; |
|
|
317 | iter = new + 1; |
|
|
318 | } |
|
|
319 | return found; |
|
|
320 | } |
|
|
321 | |
|
|
322 | /* |
319 | /* |
323 | * convert whole string to case |
320 | * convert whole string to case |
324 | */ |
321 | */ |
325 | |
322 | |
326 | char * TOOLEXPORT |
323 | char * TOOLEXPORT |
327 | _FP_stoupper (char *input) |
324 | FP_stoupper (char *input) |
328 | { |
325 | { |
329 | char *iter = input; |
326 | char *iter = input; |
330 | |
327 | |
331 | if (input == NULL) |
328 | if (input == NULL) |
332 | return NULL; |
329 | return NULL; |
… | |
… | |
337 | } |
334 | } |
338 | return input; |
335 | return input; |
339 | } |
336 | } |
340 | |
337 | |
341 | char * TOOLEXPORT |
338 | char * TOOLEXPORT |
342 | _FP_stolower (char *input) |
339 | FP_stolower (char *input) |
343 | { |
340 | { |
344 | char *iter = input; |
341 | char *iter = input; |
345 | |
342 | |
346 | if (input == NULL) |
343 | if (input == NULL) |
347 | return NULL; |
344 | return NULL; |
… | |
… | |
356 | /* |
353 | /* |
357 | * string matching with wildcards |
354 | * string matching with wildcards |
358 | */ |
355 | */ |
359 | |
356 | |
360 | int TOOLEXPORT |
357 | int TOOLEXPORT |
361 | _FP_strmatch (char *string, char *pattern) |
358 | FP_strmatch (char *string, char *pattern) |
362 | { |
359 | { |
363 | char *p1 = string, *p2 = pattern; |
360 | char *p1 = string, *p2 = pattern; |
364 | |
361 | |
365 | if (pattern==NULL || string==NULL) |
362 | if (pattern==NULL || string==NULL) |
366 | return 0; |
363 | return 0; |
… | |
… | |
386 | |
383 | |
387 | return 1; |
384 | return 1; |
388 | } |
385 | } |
389 | |
386 | |
390 | char * TOOLEXPORT |
387 | char * TOOLEXPORT |
391 | _FP_strrchr (char *string, int tc) |
388 | FP_strrchr (const char *string, int tc) |
392 | { |
389 | { |
393 | char *ptr; |
390 | const char *ptr; |
394 | |
391 | |
395 | if (string == NULL || !*string) |
392 | if (string == NULL || !*string) |
396 | return NULL; |
393 | return NULL; |
397 | |
394 | |
398 | ptr = string + strlen (string) - 1; |
395 | ptr = string + strlen (string) - 1; |
399 | |
396 | |
400 | while (ptr != string && *ptr != tc) |
397 | while (ptr != string && *ptr != tc) |
401 | ptr--; |
398 | ptr--; |
402 | |
399 | |
403 | if (*ptr == tc) |
400 | if (*ptr == tc) |
404 | return ptr; |
401 | return (char *)ptr; |
405 | |
402 | |
406 | return NULL; |
403 | return NULL; |
407 | } |
404 | } |
408 | |
405 | |
409 | /* |
406 | /* |
410 | * strip directory information from a filename. Works only on DOS and |
407 | * strip directory information from a filename. Works only on DOS and |
411 | * Unix systems so far ... |
408 | * Unix systems so far ... |
412 | */ |
409 | */ |
413 | |
410 | |
414 | char * TOOLEXPORT |
411 | char * TOOLEXPORT |
415 | _FP_cutdir (char *filename) |
412 | FP_cutdir (char *filename) |
416 | { |
413 | { |
417 | char *ptr; |
414 | char *ptr; |
418 | |
415 | |
419 | if (filename == NULL) |
416 | if (filename == NULL) |
420 | return NULL; |
417 | return NULL; |
421 | |
418 | |
422 | if ((ptr = _FP_strrchr (filename, '/')) != NULL) |
419 | if ((ptr = FP_strrchr (filename, '/')) != NULL) |
423 | ptr++; |
420 | ptr++; |
424 | else if ((ptr = _FP_strrchr (filename, '\\')) != NULL) |
421 | else if ((ptr = FP_strrchr (filename, '\\')) != NULL) |
425 | ptr++; |
422 | ptr++; |
426 | else |
423 | else |
427 | ptr = filename; |
424 | ptr = filename; |
428 | |
425 | |
429 | return ptr; |
426 | return ptr; |
430 | } |
427 | } |
431 | |
428 | |
432 | /* |
429 | /* |
433 | * My own fgets function. It handles all kinds of line terminators |
430 | * My own fgets function. It handles all kinds of line terminators |
434 | * properly: LF (Unix), CRLF (DOS) and CR (Mac). In all cases, the |
431 | * properly: LF (Unix), CRLF (DOS) and CR (Mac). |
435 | * terminator is replaced by a single LF |
|
|
436 | */ |
432 | */ |
437 | |
433 | /* (schmorp) the buffer is always written to, and no LF is stored at the end */ |
|
|
434 | /* also, if the buffer is too short, the remaining line is skipped */ |
438 | char * TOOLEXPORT |
435 | ecb_hot char * TOOLEXPORT |
439 | _FP_fgets (char *buf, int n, FILE *stream) |
436 | FP_fgets (char *buf, int n, FILE *stream) |
440 | { |
437 | { |
441 | char *obp = buf; |
438 | char *ptr = buf; |
442 | int c; |
439 | char *end = buf + n - 1; |
443 | |
440 | |
444 | if (feof (stream)) |
441 | /* shield against buffer overflows caused by "255 - bytes_left"-kind of bugs when bytes_left > 255 */ |
|
|
442 | if (n <= 0) |
445 | return NULL; |
443 | return 0; |
446 | |
444 | |
447 | while (--n && !feof (stream)) { |
445 | for (;;) |
448 | if ((c = fgetc (stream)) == EOF) { |
446 | { |
449 | if (ferror (stream)) |
447 | int c = FP_getc (stream); |
450 | return NULL; |
448 | |
|
|
449 | if (ecb_expect_false (c <= '\015')) /* EOF is < 0x20, too */ |
451 | else { |
450 | { |
452 | if (obp == buf) |
451 | /* ctlchar */ |
453 | return NULL; |
452 | |
454 | *buf = '\0'; |
453 | if (c == '\012') |
455 | return obp; |
454 | /* LF, nothing following */ |
|
|
455 | break; |
|
|
456 | else if (c == '\015') |
|
|
457 | { |
|
|
458 | /* CR, possibly CRLF, skip following LF */ |
|
|
459 | c = FP_getc (stream); |
|
|
460 | |
|
|
461 | if (c != '\012') /* CR LF? */ |
|
|
462 | ungetc (c, stream); |
|
|
463 | |
|
|
464 | break; |
|
|
465 | } |
|
|
466 | else if (c == EOF) |
|
|
467 | { |
|
|
468 | *ptr = 0; |
|
|
469 | return 0; |
|
|
470 | } |
456 | } |
471 | } |
|
|
472 | |
|
|
473 | *ptr = c; |
|
|
474 | ptr += ptr < end; /* this is hopefully branch-free, and fast */ |
457 | } |
475 | } |
458 | if (c == '\015') { /* CR */ |
|
|
459 | /* |
|
|
460 | * Peek next character. If it's no LF, push it back. |
|
|
461 | * ungetc(EOF, stream) is handled correctly according |
|
|
462 | * to the manual page |
|
|
463 | */ |
|
|
464 | if ((c = fgetc (stream)) != '\012') |
|
|
465 | if (!feof (stream)) |
|
|
466 | ungetc (c, stream); |
|
|
467 | *buf++ = '\012'; |
|
|
468 | *buf = '\0'; |
|
|
469 | return obp; |
|
|
470 | } |
|
|
471 | else if (c == '\012') { /* LF */ |
|
|
472 | *buf++ = '\012'; |
|
|
473 | *buf = '\0'; |
|
|
474 | return obp; |
|
|
475 | } |
|
|
476 | /* |
|
|
477 | * just another standard character |
|
|
478 | */ |
|
|
479 | *buf++ = c; |
|
|
480 | } |
|
|
481 | |
476 | |
482 | /* |
477 | *ptr = 0; |
483 | * n-1 characters already transferred |
|
|
484 | */ |
|
|
485 | |
|
|
486 | *buf = '\0'; |
|
|
487 | |
|
|
488 | /* |
|
|
489 | * If a line break is coming up, read it |
|
|
490 | */ |
|
|
491 | |
|
|
492 | if (!feof (stream)) { |
|
|
493 | if ((c = fgetc (stream)) == '\015' && !feof (stream)) { |
|
|
494 | if ((c = fgetc (stream)) != '\012' && !feof (stream)) { |
|
|
495 | ungetc (c, stream); |
|
|
496 | } |
|
|
497 | } |
|
|
498 | else if (c != '\012' && !feof (stream)) { |
|
|
499 | ungetc (c, stream); |
|
|
500 | } |
|
|
501 | } |
|
|
502 | |
|
|
503 | return obp; |
478 | return buf; |
504 | } |
479 | } |
505 | |
480 | |
506 | /* |
|
|
507 | * A replacement strerror function that just returns the error code |
|
|
508 | */ |
|
|
509 | |
|
|
510 | char * TOOLEXPORT |
|
|
511 | _FP_strerror (int errcode) |
|
|
512 | { |
|
|
513 | static char number[8]; |
|
|
514 | |
|
|
515 | sprintf (number, "%03d", errcode); |
|
|
516 | |
|
|
517 | return number; |
|
|
518 | } |
|
|
519 | |
|
|
520 | /* |
|
|
521 | * tempnam is not ANSI, but tmpnam is. Ignore the prefix here. |
|
|
522 | */ |
|
|
523 | |
|
|
524 | char * TOOLEXPORT |
|
|
525 | _FP_tempnam (char *dir, char *pfx) |
|
|
526 | { |
|
|
527 | return _FP_strdup (tmpnam (NULL)); |
|
|
528 | } |
|
|