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