1 /*
2  * fptools.c, some helper functions for getcgi.c and uu(en|de)view
3  *
4  * Distributed under the terms of the GNU General Public License.
5  * Use and be happy.
6  */
7 
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11 
12 #ifdef SYSTEM_WINDLL
13 #include <windows.h>
14 #endif
15 #ifdef SYSTEM_OS2
16 #include <os2.h>
17 #endif
18 
19 /*
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
22  * should behave exactly as their counterparts. There are also extensions
23  * that aren't portable at all (like strirstr etc.).
24  * The proper behaviour in a configure script is as follows:
25  *    AC_CHECK_FUNC(strrchr,AC_DEFINE(strrchr,_FP_strrchr))
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
28  * does not work with replacements that just shadow wrong behaviour (like
29  * _FP_free) or provide extended functionality (_FP_gets).
30  * The above is not used in the uuenview/uudeview configuration script,
31  * since both only use the replacement functions in non-performance-cri-
32  * tical sections (except for _FP_tempnam and _FP_strerror, where some
33  * functionality of the original would be lost).
34  */
35 
36 #include <stdio.h>
37 #include <ctype.h>
38 
39 #ifdef STDC_HEADERS
40 #include <stdlib.h>
41 #include <string.h>
42 #endif
43 #ifdef HAVE_MALLOC_H
44 #include <malloc.h>
45 #endif
46 #ifdef HAVE_UNISTD_H
47 #include <unistd.h>
48 #endif
49 #ifdef HAVE_MEMORY_H
50 #include <memory.h>
51 #endif
52 
53 #include <fptools.h>
54 
55 #if 0
56 #ifdef SYSTEM_WINDLL
57 BOOL _export WINAPI
58 DllEntryPoint (HINSTANCE hInstance, DWORD seginfo,
59 	       LPVOID lpCmdLine)
60 {
61   /* Don't do anything, so just return true */
62   return TRUE;
63 }
64 #endif
65 #endif
66 
67 char * fptools_id = "$Id: fptools.c,v 1.8 2004/02/24 00:05:32 fp Exp $";
68 
69 /*
70  * some versions of free can't handle a NULL pointer properly
71  * (ANSI says, free ignores a NULL pointer, but some machines
72  * prefer to SIGSEGV on it)
73  */
74 
75 void TOOLEXPORT
_FP_free(void * ptr)76 _FP_free (void *ptr)
77 {
78   if (ptr) free (ptr);
79 }
80 
81 /*
82  * This is non-standard, so I'm defining my own
83  */
84 
85 char * TOOLEXPORT
_FP_strdup(char * string)86 _FP_strdup (char *string)
87 {
88   char *result;
89 
90   if (string == NULL)
91     return NULL;
92 
93   if ((result = (char *) malloc (strlen (string) + 1)) == NULL)
94     return NULL;
95 
96   strcpy (result, string);
97   return result;
98 }
99 
100 /*
101  * limited-length string copy. this function behaves differently from
102  * the original in that the dest string is always terminated with a
103  * NULL character.
104  */
105 
106 char * TOOLEXPORT
_FP_strncpy(char * dest,char * src,int length)107 _FP_strncpy (char *dest, char *src, int length)
108 {
109   char *odest=dest;
110   if (src == NULL || dest == NULL || length-- <= 0)
111     return dest;
112 
113   while (length-- && *src)
114     *dest++ = *src++;
115 
116   *dest++ = '\0';
117   return odest;
118 }
119 
120 /*
121  * duplicate a memory area
122  */
123 
124 void * TOOLEXPORT
_FP_memdup(void * ptr,int len)125 _FP_memdup (void *ptr, int len)
126 {
127   void *result;
128 
129   if (ptr == NULL)
130     return NULL;
131 
132   if ((result = malloc (len)) == NULL)
133     return NULL;
134 
135   memcpy (result, ptr, len);
136   return result;
137 }
138 
139 /*
140  * case-insensitive compare
141  */
142 
143 int TOOLEXPORT
_FP_stricmp(char * str1,char * str2)144 _FP_stricmp (char *str1, char *str2)
145 {
146   if (str1==NULL || str2==NULL)
147     return -1;
148 
149   while (*str1) {
150     if (tolower(*str1) != tolower(*str2))
151       break;
152     str1++;
153     str2++;
154   }
155   return (tolower (*str1) - tolower (*str2));
156 }
157 
158 int TOOLEXPORT
_FP_strnicmp(char * str1,char * str2,int count)159 _FP_strnicmp (char *str1, char *str2, int count)
160 {
161   if (str1==NULL || str2==NULL)
162     return -1;
163 
164   while (*str1 && count) {
165     if (tolower(*str1) != tolower(*str2))
166       break;
167     str1++;
168     str2++;
169     count--;
170   }
171   return count ? (tolower (*str1) - tolower (*str2)) : 0;
172 }
173 
174 /*
175  * autoconf says this function might be a compatibility problem
176  */
177 
178 char * TOOLEXPORT
_FP_strstr(char * str1,char * str2)179 _FP_strstr (char *str1, char *str2)
180 {
181   char *ptr1, *ptr2;
182 
183   if (str1==NULL)
184     return NULL;
185   if (str2==NULL)
186     return str1;
187 
188   while (*(ptr1=str1)) {
189     for (ptr2=str2;
190 	 *ptr1 && *ptr2 && *ptr1==*ptr2;
191 	 ptr1++, ptr2++)
192       /* empty loop */ ;
193 
194     if (*ptr2 == '\0')
195       return str1;
196     str1++;
197   }
198   return NULL;
199 }
200 
201 char * TOOLEXPORT
_FP_strpbrk(char * str,char * accept)202 _FP_strpbrk (char *str, char *accept)
203 {
204   char *ptr;
205 
206   if (str == NULL)
207     return NULL;
208   if (accept == NULL || *accept == '\0')
209     return str;
210 
211   for (; *str; str++)
212     for (ptr=accept; *ptr; ptr++)
213       if (*str == *ptr)
214 	return str;
215 
216   return NULL;
217 }
218 
219 /*
220  * autoconf also complains about this one
221  */
222 
223 char * TOOLEXPORT
_FP_strtok(char * str1,char * str2)224 _FP_strtok (char *str1, char *str2)
225 {
226   static char *optr;
227   char *ptr;
228 
229   if (str2 == NULL)
230     return NULL;
231 
232   if (str1) {
233     optr = str1;
234   }
235   else {
236     if (*optr == '\0')
237       return NULL;
238   }
239 
240   while (*optr && strchr (str2, *optr))	/* look for beginning of token */
241     optr++;
242 
243   if (*optr == '\0')			/* no token found */
244     return NULL;
245 
246   ptr = optr;
247   while (*optr && strchr (str2, *optr) == NULL) /* look for end of token */
248     optr++;
249 
250   if (*optr) {
251     *optr++ = '\0';
252   }
253   return ptr;
254 }
255 
256 /*
257  * case insensitive strstr.
258  */
259 
260 char * TOOLEXPORT
_FP_stristr(char * str1,char * str2)261 _FP_stristr (char *str1, char *str2)
262 {
263   char *ptr1, *ptr2;
264 
265   if (str1==NULL)
266     return NULL;
267   if (str2==NULL)
268     return str1;
269 
270   while (*(ptr1=str1)) {
271     for (ptr2=str2;
272 	 *ptr1 && *ptr2 && tolower(*ptr1)==tolower(*ptr2);
273 	 ptr1++, ptr2++)
274       /* empty loop */ ;
275 
276     if (*ptr2 == '\0')
277       return str1;
278     str1++;
279   }
280   return NULL;
281 }
282 
283 /*
284  * Nice fake of the real (non-standard) one
285  */
286 
287 char * TOOLEXPORT
_FP_strrstr(char * ptr,char * str)288 _FP_strrstr (char *ptr, char *str)
289 {
290   char *found=NULL, *new, *iter=ptr;
291 
292   if (ptr==NULL || str==NULL)
293     return NULL;
294 
295   if (*str == '\0')
296     return ptr;
297 
298   while ((new = _FP_strstr (iter, str)) != NULL) {
299     found = new;
300     iter  = new + 1;
301   }
302   return found;
303 }
304 
305 char * TOOLEXPORT
_FP_strirstr(char * ptr,char * str)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 /*
323  * convert whole string to case
324  */
325 
326 char * TOOLEXPORT
_FP_stoupper(char * input)327 _FP_stoupper (char *input)
328 {
329   char *iter = input;
330 
331   if (input == NULL)
332     return NULL;
333 
334   while (*iter) {
335     *iter = toupper (*iter);
336     iter++;
337   }
338   return input;
339 }
340 
341 char * TOOLEXPORT
_FP_stolower(char * input)342 _FP_stolower (char *input)
343 {
344   char *iter = input;
345 
346   if (input == NULL)
347     return NULL;
348 
349   while (*iter) {
350     *iter = tolower (*iter);
351     iter++;
352   }
353   return input;
354 }
355 
356 /*
357  * string matching with wildcards
358  */
359 
360 int TOOLEXPORT
_FP_strmatch(char * string,char * pattern)361 _FP_strmatch (char *string, char *pattern)
362 {
363   char *p1 = string, *p2 = pattern;
364 
365   if (pattern==NULL || string==NULL)
366     return 0;
367 
368   while (*p1 && *p2) {
369     if (*p2 == '?') {
370       p1++; p2++;
371     }
372     else if (*p2 == '*') {
373       if (*++p2 == '\0')
374 	return 1;
375       while (*p1 && *p1 != *p2)
376 	p1++;
377     }
378     else if (*p1 == *p2) {
379       p1++; p2++;
380     }
381     else
382       return 0;
383   }
384   if (*p1 || *p2)
385     return 0;
386 
387   return 1;
388 }
389 
390 char * TOOLEXPORT
_FP_strrchr(char * string,int tc)391 _FP_strrchr (char *string, int tc)
392 {
393   char *ptr;
394 
395   if (string == NULL || !*string)
396     return NULL;
397 
398   ptr = string + strlen (string) - 1;
399 
400   while (ptr != string && *ptr != tc)
401     ptr--;
402 
403   if (*ptr == tc)
404     return ptr;
405 
406   return NULL;
407 }
408 
409 /*
410  * strip directory information from a filename. Works only on DOS and
411  * Unix systems so far ...
412  */
413 
414 char * TOOLEXPORT
_FP_cutdir(char * filename)415 _FP_cutdir (char *filename)
416 {
417   char *ptr;
418 
419   if (filename == NULL)
420     return NULL;
421 
422   if ((ptr = _FP_strrchr (filename, '/')) != NULL)
423     ptr++;
424   else if ((ptr = _FP_strrchr (filename, '\\')) != NULL)
425     ptr++;
426   else
427     ptr = filename;
428 
429   return ptr;
430 }
431 
432 /*
433  * My own fgets function. It handles all kinds of line terminators
434  * properly: LF (Unix), CRLF (DOS) and CR (Mac). In all cases, the
435  * terminator is replaced by a single LF
436  */
437 
438 char * TOOLEXPORT
_FP_fgets(char * buf,int n,FILE * stream)439 _FP_fgets (char *buf, int n, FILE *stream)
440 {
441   char *obp = buf;
442   int c;
443 
444   if (feof (stream))
445     return NULL;
446 
447   while (--n && !feof (stream)) {
448     if ((c = fgetc (stream)) == EOF) {
449       if (ferror (stream))
450 	return NULL;
451       else {
452 	if (obp == buf)
453 	  return NULL;
454 	*buf = '\0';
455 	return obp;
456       }
457     }
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 
482   /*
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;
504 }
505 
506 /*
507  * A replacement strerror function that just returns the error code
508  */
509 
510 char * TOOLEXPORT
_FP_strerror(int errcode)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
_FP_tempnam(char * dir,char * pfx)525 _FP_tempnam (char *dir, char *pfx)
526 {
527   /* TODO: should make this patch acceptable for fitting back
528    * into the standard uudeview distribution (rather than the
529    * hack that it is now). */
530 
531   char *fname;
532   int fd;
533 
534   if ((fname = _FP_strdup ("/tmp/uuXXXXXXX")) != NULL) {
535     if ((fd = mkstemp (fname)) != -1) {
536       close (fd);
537       return fname;
538     } else {
539       _FP_free (fname);
540     }
541   }
542   return NULL;
543 }
544