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