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