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