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