Just tweaking
[privoxy.git] / miscutil.c
1 const char miscutil_rcs[] = "$Id: miscutil.c,v 1.26 2001/12/30 14:07:32 steudten Exp $";
2 /*********************************************************************
3  *
4  * File        :  $Source: /cvsroot/ijbswa/current/miscutil.c,v $
5  *
6  * Purpose     :  zalloc, hash_string, safe_strerror, strcmpic,
7  *                strncmpic, chomp, and MinGW32 strdup
8  *                functions. 
9  *                These are each too small to deserve their own file
10  *                but don't really fit in any other file.
11  *
12  * Copyright   :  Written by and Copyright (C) 2001 the SourceForge
13  *                IJBSWA team.  http://ijbswa.sourceforge.net
14  *
15  *                Based on the Internet Junkbuster originally written
16  *                by and Copyright (C) 1997 Anonymous Coders and 
17  *                Junkbusters Corporation.  http://www.junkbusters.com
18  *
19  *                This program is free software; you can redistribute it 
20  *                and/or modify it under the terms of the GNU General
21  *                Public License as published by the Free Software
22  *                Foundation; either version 2 of the License, or (at
23  *                your option) any later version.
24  *
25  *                This program is distributed in the hope that it will
26  *                be useful, but WITHOUT ANY WARRANTY; without even the
27  *                implied warranty of MERCHANTABILITY or FITNESS FOR A
28  *                PARTICULAR PURPOSE.  See the GNU General Public
29  *                License for more details.
30  *
31  *                The GNU General Public License should be included with
32  *                this file.  If not, you can view it at
33  *                http://www.gnu.org/copyleft/gpl.html
34  *                or write to the Free Software Foundation, Inc., 59
35  *                Temple Place - Suite 330, Boston, MA  02111-1307, USA.
36  *
37  * Revisions   :
38  *    $Log: miscutil.c,v $
39  *    Revision 1.26  2001/12/30 14:07:32  steudten
40  *    - Add signal handling (unix)
41  *    - Add SIGHUP handler (unix)
42  *    - Add creation of pidfile (unix)
43  *    - Add action 'top' in rc file (RH)
44  *    - Add entry 'SIGNALS' to manpage
45  *    - Add exit message to logfile (unix)
46  *
47  *    Revision 1.25  2001/11/13 00:16:38  jongfoster
48  *    Replacing references to malloc.h with the standard stdlib.h
49  *    (See ANSI or K&R 2nd Ed)
50  *
51  *    Revision 1.24  2001/11/05 21:41:43  steudten
52  *    Add changes to be a real daemon just for unix os.
53  *    (change cwd to /, detach from controlling tty, set
54  *    process group and session leader to the own process.
55  *    Add DBG() Macro.
56  *    Add some fatal-error log message for failed malloc().
57  *    Add '-d' if compiled with 'configure --with-debug' to
58  *    enable debug output.
59  *
60  *    Revision 1.23  2001/10/29 03:48:10  david__schmidt
61  *    OS/2 native needed a snprintf() routine.  Added one to miscutil, brackedted
62  *    by and __OS2__ ifdef.
63  *
64  *    Revision 1.22  2001/10/26 17:39:38  oes
65  *    Moved ijb_isspace and ijb_tolower to project.h
66  *
67  *    Revision 1.21  2001/10/23 21:27:50  jongfoster
68  *    Standardising error codes in string_append
69  *    make_path() no longer adds '\\' if the dir already ends in '\\' (this
70  *    is just copying a UNIX-specific fix to the Windows-specific part)
71  *
72  *    Revision 1.20  2001/10/22 15:33:56  david__schmidt
73  *    Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in
74  *    filters.c.  Added a FIXME in front of the offending code.  I'll gladly
75  *    put in a better/more robust fix for all parties if one is presented...
76  *    It seems that just returning 200 instead of 404 would pretty much fix
77  *    it for everyone, but I don't know all the history of the problem.
78  *
79  *    Revision 1.19  2001/10/14 22:02:57  jongfoster
80  *    New function string_append() which is like strsav(), but running
81  *    out of memory isn't automatically FATAL.
82  *
83  *    Revision 1.18  2001/09/20 13:33:43  steudten
84  *
85  *    change long to int as return value in hash_string(). Remember the wraparound
86  *    for int = long = sizeof(4) - thats maybe not what we want.
87  *
88  *    Revision 1.17  2001/09/13 20:51:29  jongfoster
89  *    Fixing potential problems with characters >=128 in simplematch()
90  *    This was also a compiler warning.
91  *
92  *    Revision 1.16  2001/09/10 10:56:59  oes
93  *    Silenced compiler warnings
94  *
95  *    Revision 1.15  2001/07/13 14:02:24  oes
96  *    Removed vim-settings
97  *
98  *    Revision 1.14  2001/06/29 21:45:41  oes
99  *    Indentation, CRLF->LF, Tab-> Space
100  *
101  *    Revision 1.13  2001/06/29 13:32:14  oes
102  *    Removed logentry from cancelled commit
103  *
104  *    Revision 1.12  2001/06/09 10:55:28  jongfoster
105  *    Changing BUFSIZ ==> BUFFER_SIZE
106  *
107  *    Revision 1.11  2001/06/07 23:09:19  jongfoster
108  *    Cosmetic indentation changes.
109  *
110  *    Revision 1.10  2001/06/07 14:51:38  joergs
111  *    make_path() no longer adds '/' if the dir already ends in '/'.
112  *
113  *    Revision 1.9  2001/06/07 14:43:17  swa
114  *    slight mistake in make_path, unix path style is /.
115  *
116  *    Revision 1.8  2001/06/05 22:32:01  jongfoster
117  *    New function make_path() to splice directory and file names together.
118  *
119  *    Revision 1.7  2001/06/03 19:12:30  oes
120  *    introduced bindup()
121  *
122  *    Revision 1.6  2001/06/01 18:14:49  jongfoster
123  *    Changing the calls to strerr() to check HAVE_STRERR (which is defined
124  *    in config.h if appropriate) rather than the NO_STRERR macro.
125  *
126  *    Revision 1.5  2001/06/01 10:31:51  oes
127  *    Added character class matching to trivimatch; renamed to simplematch
128  *
129  *    Revision 1.4  2001/05/31 17:32:31  oes
130  *
131  *     - Enhanced domain part globbing with infix and prefix asterisk
132  *       matching and optional unanchored operation
133  *
134  *    Revision 1.3  2001/05/29 23:10:09  oes
135  *
136  *
137  *     - Introduced chomp()
138  *     - Moved strsav() from showargs to miscutil
139  *
140  *    Revision 1.2  2001/05/29 09:50:24  jongfoster
141  *    Unified blocklist/imagelist/permissionslist.
142  *    File format is still under discussion, but the internal changes
143  *    are (mostly) done.
144  *
145  *    Also modified interceptor behaviour:
146  *    - We now intercept all URLs beginning with one of the following
147  *      prefixes (and *only* these prefixes):
148  *        * http://i.j.b/
149  *        * http://ijbswa.sf.net/config/
150  *        * http://ijbswa.sourceforge.net/config/
151  *    - New interceptors "home page" - go to http://i.j.b/ to see it.
152  *    - Internal changes so that intercepted and fast redirect pages
153  *      are not replaced with an image.
154  *    - Interceptors now have the option to send a binary page direct
155  *      to the client. (i.e. ijb-send-banner uses this)
156  *    - Implemented show-url-info interceptor.  (Which is why I needed
157  *      the above interceptors changes - a typical URL is
158  *      "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
159  *      The previous mechanism would not have intercepted that, and
160  *      if it had been intercepted then it then it would have replaced
161  *      it with an image.)
162  *
163  *    Revision 1.1.1.1  2001/05/15 13:59:00  oes
164  *    Initial import of version 2.9.3 source tree
165  *
166  *
167  *********************************************************************/
168 \f
169
170 #include "config.h"
171
172 #include <stdio.h>
173 #include <sys/types.h>
174 #include <stdlib.h>
175 #if !defined(_WIN32) && !defined(__OS2__)
176 #include <unistd.h>
177 #endif /* #if !defined(_WIN32) && !defined(__OS2__) */
178 #include <string.h>
179 #include <ctype.h>
180 #include <assert.h>
181
182 #include "project.h"
183 #include "miscutil.h"
184 #include "errlog.h"
185
186 const char miscutil_h_rcs[] = MISCUTIL_H_VERSION;
187
188 /*********************************************************************
189  *
190  * Function    :  zalloc
191  *
192  * Description :  Malloc some memory and set it to '\0'.
193  *                The way calloc() ought to be -acjc
194  *
195  * Parameters  :
196  *          1  :  size = Size of memory chunk to return.
197  *
198  * Returns     :  Pointer to newly malloc'd memory chunk.
199  *
200  *********************************************************************/
201 void *zalloc(int size)
202 {
203    void * ret;
204
205    if ((ret = (void *)malloc(size)) != NULL)
206    {
207       memset(ret, 0, size);
208    }
209
210    return(ret);
211 }
212 #if defined(unix)
213 /*********************************************************************
214  *
215  * Function    : deletePidFile 
216  *
217  * Description :  deletes the pid file with the pid of the main process 
218  *
219  * Parameters  : -
220  *
221  * Returns     : - 
222  *
223  *********************************************************************/
224 void deletePidFile( void )
225 {
226   char pidfile[ 64 ];
227
228   snprintf( pidfile, sizeof(pidfile), "%s/%s", PID_FILE_PATH, PID_FILE_NAME);
229   unlink( pidfile );
230 }
231 /*********************************************************************
232  *
233  * Function    : writePidFile 
234  *
235  * Description :  writes the pid file with the pid of the main process 
236  *
237  * Parameters  : -
238  *
239  * Returns     : - 
240  *
241  *********************************************************************/
242 void writePidFile( void )
243 {
244   FILE   *fp;
245   char   pidfile[64];
246
247   snprintf( pidfile, sizeof(pidfile), "%s/%s", PID_FILE_PATH, PID_FILE_NAME);
248   if ((fp = fopen( pidfile,"w")) == NULL )
249   {
250       log_error(LOG_LEVEL_INFO, "can't open pidfile '%s': %E", pidfile);
251       return;
252   }
253
254   fprintf( fp,"%u\n", (unsigned int) getpid());
255   fclose ( fp );
256 }
257 #endif /* unix */
258
259 /*********************************************************************
260  *
261  * Function    :  hash_string
262  *
263  * Description :  Take a string and compute a (hopefuly) unique numeric
264  *                integer value.  This has several uses, but being able
265  *                to "switch" a string the one of my favorites.
266  *
267  * Parameters  :
268  *          1  :  s : string to be hashed.
269  *
270  * Returns     :  an unsigned long variable with the hashed value.
271  *
272  *********************************************************************/
273 unsigned int hash_string( const char* s )
274 {
275    unsigned int h = 0; 
276
277    for ( ; *s; ++s )
278    {
279       h = 5 * h + *s;
280    }
281
282    return (h);
283
284 }
285
286
287 #ifdef __MINGW32__
288 /*********************************************************************
289  *
290  * Function    :  strdup
291  *
292  * Description :  For some reason (which is beyond me), gcc and WIN32
293  *                don't like strdup.  When a "free" is executed on a
294  *                strdup'd ptr, it can at times freez up!  So I just
295  *                replaced it and problem was solved.
296  *
297  * Parameters  :
298  *          1  :  s = string to duplicate
299  *
300  * Returns     :  Pointer to newly malloc'ed copy of the string.
301  *
302  *********************************************************************/
303 char *strdup( const char *s )
304 {
305    char * result = (char *)malloc( strlen(s)+1 );
306
307    if (result != NULL)
308    {
309       strcpy( result, s );
310    }
311
312    return( result );
313 }
314
315 #endif /* def __MINGW32__ */
316
317
318
319 /*********************************************************************
320  *
321  * Function    :  safe_strerror
322  *
323  * Description :  Variant of the library routine strerror() which will
324  *                work on systems without the library routine, and
325  *                which should never return NULL.
326  *
327  * Parameters  :
328  *          1  :  err = the `errno' of the last operation.
329  *
330  * Returns     :  An "English" string of the last `errno'.  Allocated
331  *                with strdup(), so caller frees.  May be NULL if the
332  *                system is out of memory.
333  *
334  *********************************************************************/
335 char *safe_strerror(int err)
336 {
337    char *s = NULL;
338    char buf[BUFFER_SIZE];
339
340
341 #ifdef HAVE_STRERROR
342    s = strerror(err);
343 #endif /* HAVE_STRERROR */
344
345    if (s == NULL)
346    {
347       sprintf(buf, "(errno = %d)", err);
348       s = buf;
349    }
350
351    return(strdup(s));
352
353 }
354
355
356 /*********************************************************************
357  *
358  * Function    :  strcmpic
359  *
360  * Description :  Case insensitive string comparison
361  *
362  * Parameters  :
363  *          1  :  s1 = string 1 to compare
364  *          2  :  s2 = string 2 to compare
365  *
366  * Returns     :  0 if s1==s2, Negative if s1<s2, Positive if s1>s2
367  *
368  *********************************************************************/
369 int strcmpic(const char *s1, const char *s2)
370 {
371    while (*s1 && *s2)
372    {
373       if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
374       {
375          break;
376       }
377       s1++, s2++;
378    }
379    return(ijb_tolower(*s1) - ijb_tolower(*s2));
380
381 }
382
383
384 /*********************************************************************
385  *
386  * Function    :  strncmpic
387  *
388  * Description :  Case insensitive string comparison (upto n characters)
389  *
390  * Parameters  :
391  *          1  :  s1 = string 1 to compare
392  *          2  :  s2 = string 2 to compare
393  *          3  :  n = maximum characters to compare
394  *
395  * Returns     :  0 if s1==s2, Negative if s1<s2, Positive if s1>s2
396  *
397  *********************************************************************/
398 int strncmpic(const char *s1, const char *s2, size_t n)
399 {
400    if (n <= 0) return(0);
401
402    while (*s1 && *s2)
403    {
404       if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
405       {
406          break;
407       }
408
409       if (--n <= 0) break;
410
411       s1++, s2++;
412    }
413    return(ijb_tolower(*s1) - ijb_tolower(*s2));
414
415 }
416
417
418 /*********************************************************************
419  *
420  * Function    :  chomp
421  *
422  * Description :  In-situ-eliminate all leading and trailing whitespace
423  *                from a string.
424  *
425  * Parameters  :
426  *          1  :  s : string to be chomped.
427  *
428  * Returns     :  chomped string
429  *
430  *********************************************************************/
431 char *chomp(char *string)
432 {
433    char *p, *q, *r;
434
435    /* 
436     * strip trailing whitespace
437     */
438    p = string + strlen(string);
439    while (p > string && ijb_isspace(*(p-1)))
440    {
441       p--;
442    }
443    *p = '\0';
444
445    /* 
446     * find end of leading whitespace 
447     */
448    q = r = string;
449    while (*q && ijb_isspace(*q))
450    {
451       q++;
452    }
453
454    /*
455     * if there was any, move the rest forwards
456     */
457    if (q != string)
458    {
459       while (q <= p)
460       {
461          *r++ = *q++;
462       }
463    }
464
465    return(string);
466
467 }
468
469
470 /*********************************************************************
471  *
472  * Function    :  strsav
473  *
474  * Description :  Reallocate "old" and append text to it.  This makes
475  *                it easier to append to malloc'd strings.
476  *                Running out of memory is a FATAL error.
477  *
478  * Parameters  :
479  *          1  :  old = Old text that is to be extended.  Will be
480  *                free()d by this routine.  May be NULL.
481  *          2  :  text_to_append = Text to be appended to old.
482  *                May be NULL.
483  *
484  * Returns     :  Pointer to newly malloc'ed appended string.
485  *                If there is no text to append, return old.  Caller
486  *                must free().
487  *
488  *********************************************************************/
489 char *strsav(char *old, const char *text_to_append)
490 {
491    int old_len, new_len = 0;
492    char *p;
493
494    if (( text_to_append == NULL) || (*text_to_append == '\0'))
495    {
496       return(old);
497    }
498
499    if (NULL == old)
500    {
501       if ((p = strdup(text_to_append)) == NULL)
502       {
503          log_error(LOG_LEVEL_FATAL, "strdup() failed!", new_len);
504          /* Never get here - LOG_LEVEL_FATAL causes program exit */
505       }
506       return p;
507    }
508
509    old_len = strlen(old);
510    new_len = old_len + strlen(text_to_append) + 1;
511
512    if ((p = realloc(old, new_len)) == NULL)
513    {
514       log_error(LOG_LEVEL_FATAL, "realloc(%d) bytes failed!", new_len);
515       /* Never get here - LOG_LEVEL_FATAL causes program exit */
516    }
517
518    strcpy(p + old_len, text_to_append);
519    return(p);
520 }
521
522
523 /*********************************************************************
524  *
525  * Function    :  string_append
526  *
527  * Description :  Reallocate target_string and append text to it.  
528  *                This makes it easier to append to malloc'd strings.
529  *                This is similar to the (removed) strsav(), but
530  *                running out of memory isn't catastrophic.
531  *
532  *                Programming style:
533  *
534  *                The following style provides sufficient error
535  *                checking for this routine, with minimal clutter
536  *                in the source code.  It is recommended if you
537  *                have many calls to this function:
538  *
539  *                char * s = strdup(...); // don't check for error
540  *                string_append(&s, ...);  // don't check for error
541  *                string_append(&s, ...);  // don't check for error
542  *                string_append(&s, ...);  // don't check for error
543  *                if (NULL == s) { ... handle error ... }
544  *
545  *                OR, equivalently:
546  *
547  *                char * s = strdup(...); // don't check for error
548  *                string_append(&s, ...);  // don't check for error
549  *                string_append(&s, ...);  // don't check for error
550  *                if (string_append(&s, ...)) {... handle error ...}
551  *
552  * Parameters  :
553  *          1  :  target_string = Pointer to old text that is to be
554  *                extended.  *target_string will be free()d by this
555  *                routine.  target_string must be non-NULL.
556  *                If *target_string is NULL, this routine will
557  *                do nothing and return with an error - this allows
558  *                you to make many calls to this routine and only
559  *                check for errors after the last one.
560  *          2  :  text_to_append = Text to be appended to old.
561  *                Must not be NULL.
562  *
563  * Returns     :  JB_ERR_OK on success, and sets *target_string
564  *                   to newly malloc'ed appended string.  Caller
565  *                   must free(*target_string).
566  *                JB_ERR_MEMORY on out-of-memory.  (And free()s
567  *                   *target_string and sets it to NULL).
568  *                JB_ERR_MEMORY if *target_string is NULL.
569  *
570  *********************************************************************/
571 jb_err string_append(char **target_string, const char *text_to_append)
572 {
573    size_t old_len;
574    char *new_string;
575
576    assert(target_string);
577    assert(text_to_append);
578
579    if (*target_string == NULL)
580    {
581       return JB_ERR_MEMORY;
582    }
583
584    if (*text_to_append == '\0')
585    {
586       return JB_ERR_OK;
587    }
588
589    old_len = strlen(*target_string);
590
591    if (NULL == (new_string = realloc(*target_string,
592           strlen(text_to_append) + old_len + 1)))
593    {
594       free(*target_string);
595
596       *target_string = NULL;
597       return JB_ERR_MEMORY;
598    }
599
600    strcpy(new_string + old_len, text_to_append);
601
602    *target_string = new_string;
603    return JB_ERR_OK;
604 }
605
606
607 /*********************************************************************
608  *
609  * Function    :  string_join
610  *
611  * Description :  Join two strings together.  Frees BOTH the original
612  *                strings.  If either or both input strings are NULL,
613  *                fails as if it had run out of memory.
614  *
615  *                For comparison, string_append requires that the
616  *                second string is non-NULL, and doesn't free it.
617  *
618  *                Rationale: Too often, we want to do
619  *                string_append(s, html_encode(s2)).  That assert()s
620  *                if s2 is NULL or if html_encode() runs out of memory.
621  *                It also leaks memory.  Proper checking is cumbersome.
622  *                The solution: string_join(s, html_encode(s2)) is safe,
623  *                and will free the memory allocated by html_encode().
624  *
625  * Parameters  :
626  *          1  :  target_string = Pointer to old text that is to be
627  *                extended.  *target_string will be free()d by this
628  *                routine.  target_string must be non-NULL.
629  *          2  :  text_to_append = Text to be appended to old.
630  *
631  * Returns     :  JB_ERR_OK on success, and sets *target_string
632  *                   to newly malloc'ed appended string.  Caller
633  *                   must free(*target_string).
634  *                JB_ERR_MEMORY on out-of-memory, or if
635  *                   *target_string or text_to_append is NULL.  (In
636  *                   this case, frees *target_string and text_to_append,
637  *                   sets *target_string to NULL).
638  *
639  *********************************************************************/
640 jb_err string_join(char **target_string, char *text_to_append)
641 {
642    jb_err err;
643
644    assert(target_string);
645
646    if (text_to_append == NULL)
647    {
648       freez(*target_string);
649       return JB_ERR_MEMORY;
650    }
651
652    err = string_append(target_string, text_to_append);
653
654    free(text_to_append);
655
656    return err;
657 }
658
659
660 /*********************************************************************
661  *
662  * Function    :  simplematch
663  *
664  * Description :  String matching, with a (greedy) '*' wildcard that
665  *                stands for zero or more arbitrary characters and
666  *                character classes in [], which take both enumerations
667  *                and ranges.
668  *
669  * Parameters  :
670  *          1  :  pattern = pattern for matching
671  *          2  :  text    = text to be matched
672  *
673  * Returns     :  0 if match, else nonzero
674  *
675  *********************************************************************/
676 int simplematch(char *pattern, char *text)
677 {
678    unsigned char *pat = (unsigned char *) pattern;
679    unsigned char *txt = (unsigned char *) text;
680    unsigned char *fallback = pat; 
681    int wildcard = 0;
682   
683    unsigned char lastchar = 'a';
684    unsigned i;
685    unsigned char charmap[32];
686   
687   
688    while (*txt)
689    {
690
691       /* EOF pattern but !EOF text? */
692       if (*pat == '\0')
693       {
694          return 1;
695       }
696
697       /* '*' in the pattern?  */
698       if (*pat == '*') 
699       {
700      
701          /* The pattern ends afterwards? Speed up the return. */
702          if (*++pat == '\0')
703          {
704             return 0;
705          }
706      
707          /* Else, set wildcard mode and remember position after '*' */
708          wildcard = 1;
709          fallback = pat;
710       }
711
712       /* Character range specification? */
713       if (*pat == '[')
714       {
715          memset(charmap, '\0', sizeof(charmap));
716
717          while (*++pat != ']')
718          {
719             if (!*pat)
720             { 
721                return 1;
722             }
723             else if (*pat == '-')
724             {
725                if ((*++pat == ']') || *pat == '\0')
726                {
727                   return(1);
728                }
729                for(i = lastchar; i <= *pat; i++)
730                {
731                   charmap[i / 8] |= (1 << (i % 8));
732                } 
733             }
734             else
735             {
736                charmap[*pat / 8] |= (1 << (*pat % 8));
737                lastchar = *pat;
738             }
739          }
740       } /* -END- if Character range specification */
741
742
743       /* Compare: Char match, or char range match*/
744       if ((*pat == *txt)  
745       || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))) )
746       {
747          /* Sucess, go ahead */
748          pat++;
749       }
750       else
751       {
752          /* In wildcard mode, just try again after failiure */
753          if(wildcard)
754          {
755             pat = fallback;
756          }
757
758          /* Else, bad luck */
759          else
760          {
761             return 1;
762          }
763       }
764       txt++;
765    }
766
767    /* Cut off extra '*'s */
768    if(*pat == '*')  pat++;
769
770    /* If this is the pattern's end, fine! */
771    return(*pat);
772
773 }
774
775
776 /*********************************************************************
777  *
778  * Function    :  bindup
779  *
780  * Description :  Duplicate the first n characters of a string that may
781  *                contain '\0' characters.
782  *
783  * Parameters  :
784  *          1  :  string = string to be duplicated
785  *          2  :  n = number of bytes to duplicate
786  *
787  * Returns     :  pointer to copy, or NULL if failiure
788  *
789  *********************************************************************/
790 char *bindup(const char *string, int n)
791 {
792    char *dup;
793
794    if (NULL == (dup = (char *)malloc(n)))
795    {
796       return NULL;
797    }
798    else
799    {
800      memcpy(dup, string, n);
801    }
802
803    return dup;
804
805 }
806
807
808 /*********************************************************************
809  *
810  * Function    :  make_path
811  *
812  * Description :  Takes a directory name and a file name, returns 
813  *                the complete path.  Handles windows/unix differences.
814  *                If the file name is already an absolute path, or if
815  *                the directory name is NULL or empty, it returns 
816  *                the filename. 
817  *
818  * Parameters  :
819  *          1  :  dir: Name of directory or NULL for none.
820  *          2  :  file: Name of file.  Should not be NULL or empty.
821  *
822  * Returns     :  "dir/file" (Or on windows, "dir\file").
823  *                It allocates the string on the heap.  Caller frees.
824  *                Returns NULL in error (i.e. NULL file or out of
825  *                memory) 
826  *
827  *********************************************************************/
828 char * make_path(const char * dir, const char * file)
829 {
830 #ifdef AMIGA
831    char path[512];
832
833    if(dir)
834    {
835       strncpy(path,dir,512);
836       path[511]=0;
837    } else {
838       path[0]=0;
839    }
840    if(AddPart(path,file,512))
841    {
842       return strdup(path);
843    } else {
844       return NULL;
845    }
846 #else /* ndef AMIGA */
847
848    if ((file == NULL) || (*file == '\0'))
849    {
850       return NULL; /* Error */
851    }
852
853    if ((dir == NULL) || (*dir == '\0') /* No directory specified */
854 #ifdef _WIN32
855       || (*file == '\\') || (file[1] == ':') /* Absolute path (DOS) */
856 #else /* ifndef _WIN32 */
857       || (*file == '/') /* Absolute path (U*ix) */
858 #endif /* ifndef _WIN32 */
859       )
860    {
861       return strdup(file);
862    }
863    else
864    {
865       char * path;
866
867 #if defined(unix)
868       if ( *dir != '/' && basedir && *basedir )
869       {
870               path = malloc( strlen( basedir ) + strlen(dir) + strlen(file) + 3);
871               if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
872               strcpy(path, basedir);
873               strcat(path, "/");
874               strcat(path, dir);
875               DBG(1, ("make_path: path: %s\n",path) );
876       }
877       else
878       {
879               path = malloc(strlen(dir) + strlen(file) + 2);
880               if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
881               strcpy(path, dir);
882       }
883 #else
884
885       path = malloc(strlen(dir) + strlen(file) + 2);
886       if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
887       strcpy(path, dir);
888
889 #endif /* defined unix */
890
891 #ifdef _WIN32
892       if(path[strlen(path)-1] != '\\')
893       {
894          strcat(path, "\\");
895       }
896 #else /* ifndef _WIN32 */
897       if(path[strlen(path)-1] != '/')
898       {
899          strcat(path, "/");
900       }
901 #endif /* ifndef _WIN32 */
902       strcat(path, file);
903
904       return path;
905    }
906 #endif /* ndef AMIGA */
907 }
908
909
910 /*
911  * What follows is a portable snprintf routine, written by Mark Martinec.
912  * See: http://www.ijs.si/software/snprintf/
913  * Anyone who needs it can add a define for themselves... so far, only 
914  * OS/2 (native) lacks snprintf.
915
916                                   snprintf.c
917                    - a portable implementation of snprintf,
918        including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
919                                        
920    snprintf is a routine to convert numeric and string arguments to
921    formatted strings. It is similar to sprintf(3) provided in a system's
922    C library, yet it requires an additional argument - the buffer size -
923    and it guarantees never to store anything beyond the given buffer,
924    regardless of the format or arguments to be formatted. Some newer
925    operating systems do provide snprintf in their C library, but many do
926    not or do provide an inadequate (slow or idiosyncratic) version, which
927    calls for a portable implementation of this routine.
928
929 Author
930
931    Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
932    Copyright Â© 1999, Mark Martinec
933
934  */
935
936 #ifdef __OS2__
937
938 #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
939 #define PORTABLE_SNPRINTF_VERSION_MINOR 2
940
941 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
942 # if defined(NEED_SNPRINTF_ONLY)
943 # undef NEED_SNPRINTF_ONLY
944 # endif
945 # if !defined(PREFER_PORTABLE_SNPRINTF)
946 # define PREFER_PORTABLE_SNPRINTF
947 # endif
948 #endif
949
950 #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
951 #define SOLARIS_COMPATIBLE
952 #endif
953
954 #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
955 #define HPUX_COMPATIBLE
956 #endif
957
958 #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
959 #define DIGITAL_UNIX_COMPATIBLE
960 #endif
961
962 #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
963 #define PERL_COMPATIBLE
964 #endif
965
966 #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
967 #define LINUX_COMPATIBLE
968 #endif
969
970 #include <sys/types.h>
971 #include <string.h>
972 #include <stdlib.h>
973 #include <stdio.h>
974 #include <stdarg.h>
975 #include <assert.h>
976 #include <errno.h>
977
978 #ifdef isdigit
979 #undef isdigit
980 #endif
981 #define isdigit(c) ((c) >= '0' && (c) <= '9')
982
983 /* For copying strings longer or equal to 'breakeven_point'
984  * it is more efficient to call memcpy() than to do it inline.
985  * The value depends mostly on the processor architecture,
986  * but also on the compiler and its optimization capabilities.
987  * The value is not critical, some small value greater than zero
988  * will be just fine if you don't care to squeeze every drop
989  * of performance out of the code.
990  *
991  * Small values favor memcpy, large values favor inline code.
992  */
993 #if defined(__alpha__) || defined(__alpha)
994 #  define breakeven_point   2   /* AXP (DEC Alpha)     - gcc or cc or egcs */
995 #endif
996 #if defined(__i386__)  || defined(__i386)
997 #  define breakeven_point  12   /* Intel Pentium/Linux - gcc 2.96 */
998 #endif
999 #if defined(__hppa)
1000 #  define breakeven_point  10   /* HP-PA               - gcc */
1001 #endif
1002 #if defined(__sparc__) || defined(__sparc)
1003 #  define breakeven_point  33   /* Sun Sparc 5         - gcc 2.8.1 */
1004 #endif
1005
1006 /* some other values of possible interest: */
1007 /* #define breakeven_point  8 */  /* VAX 4000          - vaxc */
1008 /* #define breakeven_point 19 */  /* VAX 4000          - gcc 2.7.0 */
1009
1010 #ifndef breakeven_point
1011 #  define breakeven_point   6   /* some reasonable one-size-fits-all value */
1012 #endif
1013
1014 #define fast_memcpy(d,s,n) \
1015   { register size_t nn = (size_t)(n); \
1016     if (nn >= breakeven_point) memcpy((d), (s), nn); \
1017     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1018       register char *dd; register const char *ss; \
1019       for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
1020
1021 #define fast_memset(d,c,n) \
1022   { register size_t nn = (size_t)(n); \
1023     if (nn >= breakeven_point) memset((d), (int)(c), nn); \
1024     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1025       register char *dd; register const int cc=(int)(c); \
1026       for (dd=(d); nn>0; nn--) *dd++ = cc; } }
1027
1028 /* prototypes */
1029
1030 #if defined(NEED_ASPRINTF)
1031 int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
1032 #endif
1033 #if defined(NEED_VASPRINTF)
1034 int vasprintf  (char **ptr, const char *fmt, va_list ap);
1035 #endif
1036 #if defined(NEED_ASNPRINTF)
1037 int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
1038 #endif
1039 #if defined(NEED_VASNPRINTF)
1040 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
1041 #endif
1042
1043 #if defined(HAVE_SNPRINTF)
1044 /* declare our portable snprintf  routine under name portable_snprintf  */
1045 /* declare our portable vsnprintf routine under name portable_vsnprintf */
1046 #else
1047 /* declare our portable routines under names snprintf and vsnprintf */
1048 #define portable_snprintf snprintf
1049 #if !defined(NEED_SNPRINTF_ONLY)
1050 #define portable_vsnprintf vsnprintf
1051 #endif
1052 #endif
1053
1054 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1055 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
1056 #if !defined(NEED_SNPRINTF_ONLY)
1057 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
1058 #endif
1059 #endif
1060
1061 /* declarations */
1062
1063 static char credits[] = "\n\
1064 @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
1065 @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
1066 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
1067
1068 #if defined(NEED_ASPRINTF)
1069 int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
1070   va_list ap;
1071   size_t str_m;
1072   int str_l;
1073
1074   *ptr = NULL;
1075   va_start(ap, fmt);                            /* measure the required size */
1076   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1077   va_end(ap);
1078   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
1079   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1080   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1081   else {
1082     int str_l2;
1083     va_start(ap, fmt);
1084     str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1085     va_end(ap);
1086     assert(str_l2 == str_l);
1087   }
1088   return str_l;
1089 }
1090 #endif
1091
1092 #if defined(NEED_VASPRINTF)
1093 int vasprintf(char **ptr, const char *fmt, va_list ap) {
1094   size_t str_m;
1095   int str_l;
1096
1097   *ptr = NULL;
1098   { va_list ap2;
1099     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
1100     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1101     va_end(ap2);
1102   }
1103   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
1104   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1105   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1106   else {
1107     int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1108     assert(str_l2 == str_l);
1109   }
1110   return str_l;
1111 }
1112 #endif
1113
1114 #if defined(NEED_ASNPRINTF)
1115 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
1116   va_list ap;
1117   int str_l;
1118
1119   *ptr = NULL;
1120   va_start(ap, fmt);                            /* measure the required size */
1121   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1122   va_end(ap);
1123   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
1124   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
1125   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1126   if (str_m == 0) {  /* not interested in resulting string, just return size */
1127   } else {
1128     *ptr = (char *) malloc(str_m);
1129     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1130     else {
1131       int str_l2;
1132       va_start(ap, fmt);
1133       str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1134       va_end(ap);
1135       assert(str_l2 == str_l);
1136     }
1137   }
1138   return str_l;
1139 }
1140 #endif
1141
1142 #if defined(NEED_VASNPRINTF)
1143 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
1144   int str_l;
1145
1146   *ptr = NULL;
1147   { va_list ap2;
1148     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
1149     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1150     va_end(ap2);
1151   }
1152   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
1153   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
1154   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1155   if (str_m == 0) {  /* not interested in resulting string, just return size */
1156   } else {
1157     *ptr = (char *) malloc(str_m);
1158     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1159     else {
1160       int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1161       assert(str_l2 == str_l);
1162     }
1163   }
1164   return str_l;
1165 }
1166 #endif
1167
1168 /*
1169  * If the system does have snprintf and the portable routine is not
1170  * specifically required, this module produces no code for snprintf/vsnprintf.
1171  */
1172 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1173
1174 #if !defined(NEED_SNPRINTF_ONLY)
1175 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1176   va_list ap;
1177   int str_l;
1178
1179   va_start(ap, fmt);
1180   str_l = portable_vsnprintf(str, str_m, fmt, ap);
1181   va_end(ap);
1182   return str_l;
1183 }
1184 #endif
1185
1186 #if defined(NEED_SNPRINTF_ONLY)
1187 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1188 #else
1189 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
1190 #endif
1191
1192 #if defined(NEED_SNPRINTF_ONLY)
1193   va_list ap;
1194 #endif
1195   size_t str_l = 0;
1196   const char *p = fmt;
1197
1198 /* In contrast with POSIX, the ISO C99 now says
1199  * that str can be NULL and str_m can be 0.
1200  * This is more useful than the old:  if (str_m < 1) return -1; */
1201
1202 #if defined(NEED_SNPRINTF_ONLY)
1203   va_start(ap, fmt);
1204 #endif
1205   if (!p) p = "";
1206   while (*p) {
1207     if (*p != '%') {
1208    /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
1209    /* but the following code achieves better performance for cases
1210     * where format string is long and contains few conversions */
1211       const char *q = strchr(p+1,'%');
1212       size_t n = !q ? strlen(p) : (q-p);
1213       if (str_l < str_m) {
1214         size_t avail = str_m-str_l;
1215         fast_memcpy(str+str_l, p, (n>avail?avail:n));
1216       }
1217       p += n; str_l += n;
1218     } else {
1219       const char *starting_p;
1220       size_t min_field_width = 0, precision = 0;
1221       int zero_padding = 0, precision_specified = 0, justify_left = 0;
1222       int alternate_form = 0, force_sign = 0;
1223       int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
1224                                      the ' ' flag should be ignored. */
1225       char length_modifier = '\0';            /* allowed values: \0, h, l, L */
1226       char tmp[32];/* temporary buffer for simple numeric->string conversion */
1227
1228       const char *str_arg;      /* string address in case of string argument */
1229       size_t str_arg_l;         /* natural field width of arg without padding
1230                                    and sign */
1231       unsigned char uchar_arg;
1232         /* unsigned char argument value - only defined for c conversion.
1233            N.B. standard explicitly states the char argument for
1234            the c conversion is unsigned */
1235
1236       size_t number_of_zeros_to_pad = 0;
1237         /* number of zeros to be inserted for numeric conversions
1238            as required by the precision or minimal field width */
1239
1240       size_t zero_padding_insertion_ind = 0;
1241         /* index into tmp where zero padding is to be inserted */
1242
1243       char fmt_spec = '\0';
1244         /* current conversion specifier character */
1245
1246       str_arg = credits;/* just to make compiler happy (defined but not used)*/
1247       str_arg = NULL;
1248       starting_p = p; p++;  /* skip '%' */
1249    /* parse flags */
1250       while (*p == '0' || *p == '-' || *p == '+' ||
1251              *p == ' ' || *p == '#' || *p == '\'') {
1252         switch (*p) {
1253         case '0': zero_padding = 1; break;
1254         case '-': justify_left = 1; break;
1255         case '+': force_sign = 1; space_for_positive = 0; break;
1256         case ' ': force_sign = 1;
1257      /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
1258 #ifdef PERL_COMPATIBLE
1259      /* ... but in Perl the last of ' ' and '+' applies */
1260                   space_for_positive = 1;
1261 #endif
1262                   break;
1263         case '#': alternate_form = 1; break;
1264         case '\'': break;
1265         }
1266         p++;
1267       }
1268    /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
1269
1270    /* parse field width */
1271       if (*p == '*') {
1272         int j;
1273         p++; j = va_arg(ap, int);
1274         if (j >= 0) min_field_width = j;
1275         else { min_field_width = -j; justify_left = 1; }
1276       } else if (isdigit((int)(*p))) {
1277         /* size_t could be wider than unsigned int;
1278            make sure we treat argument like common implementations do */
1279         unsigned int uj = *p++ - '0';
1280         while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1281         min_field_width = uj;
1282       }
1283    /* parse precision */
1284       if (*p == '.') {
1285         p++; precision_specified = 1;
1286         if (*p == '*') {
1287           int j = va_arg(ap, int);
1288           p++;
1289           if (j >= 0) precision = j;
1290           else {
1291             precision_specified = 0; precision = 0;
1292          /* NOTE:
1293           *   Solaris 2.6 man page claims that in this case the precision
1294           *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
1295           *   claim that this case should be treated as unspecified precision,
1296           *   which is what we do here.
1297           */
1298           }
1299         } else if (isdigit((int)(*p))) {
1300           /* size_t could be wider than unsigned int;
1301              make sure we treat argument like common implementations do */
1302           unsigned int uj = *p++ - '0';
1303           while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1304           precision = uj;
1305         }
1306       }
1307    /* parse 'h', 'l' and 'll' length modifiers */
1308       if (*p == 'h' || *p == 'l') {
1309         length_modifier = *p; p++;
1310         if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
1311 #ifdef SNPRINTF_LONGLONG_SUPPORT
1312           length_modifier = '2';                  /* double l encoded as '2' */
1313 #else
1314           length_modifier = 'l';                 /* treat it as a single 'l' */
1315 #endif
1316           p++;
1317         }
1318       }
1319       fmt_spec = *p;
1320    /* common synonyms: */
1321       switch (fmt_spec) {
1322       case 'i': fmt_spec = 'd'; break;
1323       case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
1324       case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
1325       case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
1326       default: break;
1327       }
1328    /* get parameter value, do initial processing */
1329       switch (fmt_spec) {
1330       case '%': /* % behaves similar to 's' regarding flags and field widths */
1331       case 'c': /* c behaves similar to 's' regarding flags and field widths */
1332       case 's':
1333         length_modifier = '\0';          /* wint_t and wchar_t not supported */
1334      /* the result of zero padding flag with non-numeric conversion specifier*/
1335      /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
1336      /* Digital Unix and Linux does not. */
1337 #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1338         zero_padding = 0;    /* turn zero padding off for string conversions */
1339 #endif
1340         str_arg_l = 1;
1341         switch (fmt_spec) {
1342         case '%':
1343           str_arg = p; break;
1344         case 'c': {
1345           int j = va_arg(ap, int);
1346           uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
1347           str_arg = (const char *) &uchar_arg;
1348           break;
1349         }
1350         case 's':
1351           str_arg = va_arg(ap, const char *);
1352           if (!str_arg) str_arg_l = 0;
1353        /* make sure not to address string beyond the specified precision !!! */
1354           else if (!precision_specified) str_arg_l = strlen(str_arg);
1355        /* truncate string if necessary as requested by precision */
1356           else if (precision == 0) str_arg_l = 0;
1357           else {
1358        /* memchr on HP does not like n > 2^31  !!! */
1359             const char *q = memchr(str_arg, '\0',
1360                              precision <= 0x7fffffff ? precision : 0x7fffffff);
1361             str_arg_l = !q ? precision : (q-str_arg);
1362           }
1363           break;
1364         default: break;
1365         }
1366         break;
1367       case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
1368         /* NOTE: the u, o, x, X and p conversion specifiers imply
1369                  the value is unsigned;  d implies a signed value */
1370
1371         int arg_sign = 0;
1372           /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
1373             +1 if greater than zero (or nonzero for unsigned arguments),
1374             -1 if negative (unsigned argument is never negative) */
1375
1376         int int_arg = 0;  unsigned int uint_arg = 0;
1377           /* only defined for length modifier h, or for no length modifiers */
1378
1379         long int long_arg = 0;  unsigned long int ulong_arg = 0;
1380           /* only defined for length modifier l */
1381
1382         void *ptr_arg = NULL;
1383           /* pointer argument value -only defined for p conversion */
1384
1385 #ifdef SNPRINTF_LONGLONG_SUPPORT
1386         long long int long_long_arg = 0;
1387         unsigned long long int ulong_long_arg = 0;
1388           /* only defined for length modifier ll */
1389 #endif
1390         if (fmt_spec == 'p') {
1391         /* HPUX 10: An l, h, ll or L before any other conversion character
1392          *   (other than d, i, u, o, x, or X) is ignored.
1393          * Digital Unix:
1394          *   not specified, but seems to behave as HPUX does.
1395          * Solaris: If an h, l, or L appears before any other conversion
1396          *   specifier (other than d, i, u, o, x, or X), the behavior
1397          *   is undefined. (Actually %hp converts only 16-bits of address
1398          *   and %llp treats address as 64-bit data which is incompatible
1399          *   with (void *) argument on a 32-bit system).
1400          */
1401 #ifdef SOLARIS_COMPATIBLE
1402 #  ifdef SOLARIS_BUG_COMPATIBLE
1403           /* keep length modifiers even if it represents 'll' */
1404 #  else
1405           if (length_modifier == '2') length_modifier = '\0';
1406 #  endif
1407 #else
1408           length_modifier = '\0';
1409 #endif
1410           ptr_arg = va_arg(ap, void *);
1411           if (ptr_arg != NULL) arg_sign = 1;
1412         } else if (fmt_spec == 'd') {  /* signed */
1413           switch (length_modifier) {
1414           case '\0':
1415           case 'h':
1416          /* It is non-portable to specify a second argument of char or short
1417           * to va_arg, because arguments seen by the called function
1418           * are not char or short.  C converts char and short arguments
1419           * to int before passing them to a function.
1420           */
1421             int_arg = va_arg(ap, int);
1422             if      (int_arg > 0) arg_sign =  1;
1423             else if (int_arg < 0) arg_sign = -1;
1424             break;
1425           case 'l':
1426             long_arg = va_arg(ap, long int);
1427             if      (long_arg > 0) arg_sign =  1;
1428             else if (long_arg < 0) arg_sign = -1;
1429             break;
1430 #ifdef SNPRINTF_LONGLONG_SUPPORT
1431           case '2':
1432             long_long_arg = va_arg(ap, long long int);
1433             if      (long_long_arg > 0) arg_sign =  1;
1434             else if (long_long_arg < 0) arg_sign = -1;
1435             break;
1436 #endif
1437           }
1438         } else {  /* unsigned */
1439           switch (length_modifier) {
1440           case '\0':
1441           case 'h':
1442             uint_arg = va_arg(ap, unsigned int);
1443             if (uint_arg) arg_sign = 1;
1444             break;
1445           case 'l':
1446             ulong_arg = va_arg(ap, unsigned long int);
1447             if (ulong_arg) arg_sign = 1;
1448             break;
1449 #ifdef SNPRINTF_LONGLONG_SUPPORT
1450           case '2':
1451             ulong_long_arg = va_arg(ap, unsigned long long int);
1452             if (ulong_long_arg) arg_sign = 1;
1453             break;
1454 #endif
1455           }
1456         }
1457         str_arg = tmp; str_arg_l = 0;
1458      /* NOTE:
1459       *   For d, i, u, o, x, and X conversions, if precision is specified,
1460       *   the '0' flag should be ignored. This is so with Solaris 2.6,
1461       *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
1462       */
1463 #ifndef PERL_COMPATIBLE
1464         if (precision_specified) zero_padding = 0;
1465 #endif
1466         if (fmt_spec == 'd') {
1467           if (force_sign && arg_sign >= 0)
1468             tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1469          /* leave negative numbers for sprintf to handle,
1470             to avoid handling tricky cases like (short int)(-32768) */
1471 #ifdef LINUX_COMPATIBLE
1472         } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
1473           tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1474 #endif
1475         } else if (alternate_form) {
1476           if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
1477             { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
1478          /* alternate form should have no effect for p conversion, but ... */
1479 #ifdef HPUX_COMPATIBLE
1480           else if (fmt_spec == 'p'
1481          /* HPUX 10: for an alternate form of p conversion,
1482           *          a nonzero result is prefixed by 0x. */
1483 #ifndef HPUX_BUG_COMPATIBLE
1484          /* Actually it uses 0x prefix even for a zero value. */
1485                    && arg_sign != 0
1486 #endif
1487                   ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
1488 #endif
1489         }
1490         zero_padding_insertion_ind = str_arg_l;
1491         if (!precision_specified) precision = 1;   /* default precision is 1 */
1492         if (precision == 0 && arg_sign == 0
1493 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1494             && fmt_spec != 'p'
1495          /* HPUX 10 man page claims: With conversion character p the result of
1496           * converting a zero value with a precision of zero is a null string.
1497           * Actually HP returns all zeroes, and Linux returns "(nil)". */
1498 #endif
1499         ) {
1500          /* converted to null string */
1501          /* When zero value is formatted with an explicit precision 0,
1502             the resulting formatted string is empty (d, i, u, o, x, X, p).   */
1503         } else {
1504           char f[5]; int f_l = 0;
1505           f[f_l++] = '%';    /* construct a simple format string for sprintf */
1506           if (!length_modifier) { }
1507           else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
1508           else f[f_l++] = length_modifier;
1509           f[f_l++] = fmt_spec; f[f_l++] = '\0';
1510           if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
1511           else if (fmt_spec == 'd') {  /* signed */
1512             switch (length_modifier) {
1513             case '\0':
1514             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
1515             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
1516 #ifdef SNPRINTF_LONGLONG_SUPPORT
1517             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
1518 #endif
1519             }
1520           } else {  /* unsigned */
1521             switch (length_modifier) {
1522             case '\0':
1523             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
1524             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
1525 #ifdef SNPRINTF_LONGLONG_SUPPORT
1526             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
1527 #endif
1528             }
1529           }
1530          /* include the optional minus sign and possible "0x"
1531             in the region before the zero padding insertion point */
1532           if (zero_padding_insertion_ind < str_arg_l &&
1533               tmp[zero_padding_insertion_ind] == '-') {
1534             zero_padding_insertion_ind++;
1535           }
1536           if (zero_padding_insertion_ind+1 < str_arg_l &&
1537               tmp[zero_padding_insertion_ind]   == '0' &&
1538              (tmp[zero_padding_insertion_ind+1] == 'x' ||
1539               tmp[zero_padding_insertion_ind+1] == 'X') ) {
1540             zero_padding_insertion_ind += 2;
1541           }
1542         }
1543         { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
1544           if (alternate_form && fmt_spec == 'o'
1545 #ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
1546               && (str_arg_l > 0)
1547 #endif
1548 #ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
1549 #else
1550               /* unless zero is already the first character */
1551               && !(zero_padding_insertion_ind < str_arg_l
1552                    && tmp[zero_padding_insertion_ind] == '0')
1553 #endif
1554           ) {        /* assure leading zero for alternate-form octal numbers */
1555             if (!precision_specified || precision < num_of_digits+1) {
1556              /* precision is increased to force the first character to be zero,
1557                 except if a zero value is formatted with an explicit precision
1558                 of zero */
1559               precision = num_of_digits+1; precision_specified = 1;
1560             }
1561           }
1562        /* zero padding to specified precision? */
1563           if (num_of_digits < precision) 
1564             number_of_zeros_to_pad = precision - num_of_digits;
1565         }
1566      /* zero padding to specified minimal field width? */
1567         if (!justify_left && zero_padding) {
1568           int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1569           if (n > 0) number_of_zeros_to_pad += n;
1570         }
1571         break;
1572       }
1573       default: /* unrecognized conversion specifier, keep format string as-is*/
1574         zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
1575 #ifndef DIGITAL_UNIX_COMPATIBLE
1576         justify_left = 1; min_field_width = 0;                /* reset flags */
1577 #endif
1578 #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1579      /* keep the entire format string unchanged */
1580         str_arg = starting_p; str_arg_l = p - starting_p;
1581      /* well, not exactly so for Linux, which does something inbetween,
1582       * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
1583 #else
1584      /* discard the unrecognized conversion, just keep *
1585       * the unrecognized conversion character          */
1586         str_arg = p; str_arg_l = 0;
1587 #endif
1588         if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
1589                                  if not at end-of-string */
1590         break;
1591       }
1592       if (*p) p++;      /* step over the just processed conversion specifier */
1593    /* insert padding to the left as requested by min_field_width;
1594       this does not include the zero padding in case of numerical conversions*/
1595       if (!justify_left) {                /* left padding with blank or zero */
1596         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1597         if (n > 0) {
1598           if (str_l < str_m) {
1599             size_t avail = str_m-str_l;
1600             fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
1601           }
1602           str_l += n;
1603         }
1604       }
1605    /* zero padding as requested by the precision or by the minimal field width
1606     * for numeric conversions required? */
1607       if (number_of_zeros_to_pad <= 0) {
1608      /* will not copy first part of numeric right now, *
1609       * force it to be copied later in its entirety    */
1610         zero_padding_insertion_ind = 0;
1611       } else {
1612      /* insert first part of numerics (sign or '0x') before zero padding */
1613         int n = zero_padding_insertion_ind;
1614         if (n > 0) {
1615           if (str_l < str_m) {
1616             size_t avail = str_m-str_l;
1617             fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
1618           }
1619           str_l += n;
1620         }
1621      /* insert zero padding as requested by the precision or min field width */
1622         n = number_of_zeros_to_pad;
1623         if (n > 0) {
1624           if (str_l < str_m) {
1625             size_t avail = str_m-str_l;
1626             fast_memset(str+str_l, '0', (n>avail?avail:n));
1627           }
1628           str_l += n;
1629         }
1630       }
1631    /* insert formatted string
1632     * (or as-is conversion specifier for unknown conversions) */
1633       { int n = str_arg_l - zero_padding_insertion_ind;
1634         if (n > 0) {
1635           if (str_l < str_m) {
1636             size_t avail = str_m-str_l;
1637             fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
1638                         (n>avail?avail:n));
1639           }
1640           str_l += n;
1641         }
1642       }
1643    /* insert right padding */
1644       if (justify_left) {          /* right blank padding to the field width */
1645         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1646         if (n > 0) {
1647           if (str_l < str_m) {
1648             size_t avail = str_m-str_l;
1649             fast_memset(str+str_l, ' ', (n>avail?avail:n));
1650           }
1651           str_l += n;
1652         }
1653       }
1654     }
1655   }
1656 #if defined(NEED_SNPRINTF_ONLY)
1657   va_end(ap);
1658 #endif
1659   if (str_m > 0) { /* make sure the string is null-terminated
1660                       even at the expense of overwriting the last character
1661                       (shouldn't happen, but just in case) */
1662     str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
1663   }
1664   /* Return the number of characters formatted (excluding trailing null
1665    * character), that is, the number of characters that would have been
1666    * written to the buffer if it were large enough.
1667    *
1668    * The value of str_l should be returned, but str_l is of unsigned type
1669    * size_t, and snprintf is int, possibly leading to an undetected
1670    * integer overflow, resulting in a negative return value, which is illegal.
1671    * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
1672    * Should errno be set to EOVERFLOW and EOF returned in this case???
1673    */
1674   return (int) str_l;
1675 }
1676 #endif
1677 #endif /* __OS2__ */
1678 /*
1679   Local Variables:
1680   tab-width: 3
1681   end:
1682 */