Shorten bindup()
[privoxy.git] / miscutil.c
1 const char miscutil_rcs[] = "$Id: miscutil.c,v 1.70 2012/03/04 11:48:34 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    duplicate = (char *)malloc(len);
474    if (NULL != duplicate)
475    {
476       memcpy(duplicate, string, len);
477    }
478
479    return duplicate;
480
481 }
482
483
484 /*********************************************************************
485  *
486  * Function    :  make_path
487  *
488  * Description :  Takes a directory name and a file name, returns
489  *                the complete path.  Handles windows/unix differences.
490  *                If the file name is already an absolute path, or if
491  *                the directory name is NULL or empty, it returns
492  *                the filename.
493  *
494  * Parameters  :
495  *          1  :  dir: Name of directory or NULL for none.
496  *          2  :  file: Name of file.  Should not be NULL or empty.
497  *
498  * Returns     :  "dir/file" (Or on windows, "dir\file").
499  *                It allocates the string on the heap.  Caller frees.
500  *                Returns NULL in error (i.e. NULL file or out of
501  *                memory)
502  *
503  *********************************************************************/
504 char * make_path(const char * dir, const char * file)
505 {
506 #ifdef AMIGA
507    char path[512];
508
509    if(dir)
510    {
511       if(dir[0] == '.')
512       {
513          if(dir[1] == '/')
514          {
515             strncpy(path,dir+2,512);
516          }
517          else
518          {
519             strncpy(path,dir+1,512);
520          }
521       }
522       else
523       {
524          strncpy(path,dir,512);
525       }
526       path[511]=0;
527    }
528    else
529    {
530       path[0]=0;
531    }
532    if(AddPart(path,file,512))
533    {
534       return strdup(path);
535    }
536    else
537    {
538       return NULL;
539    }
540 #else /* ndef AMIGA */
541
542    if ((file == NULL) || (*file == '\0'))
543    {
544       return NULL; /* Error */
545    }
546
547    if ((dir == NULL) || (*dir == '\0') /* No directory specified */
548 #if defined(_WIN32) || defined(__OS2__)
549       || (*file == '\\') || (file[1] == ':') /* Absolute path (DOS) */
550 #else /* ifndef _WIN32 || __OS2__ */
551       || (*file == '/') /* Absolute path (U*ix) */
552 #endif /* ifndef _WIN32 || __OS2__  */
553       )
554    {
555       return strdup(file);
556    }
557    else
558    {
559       char * path;
560       size_t path_size = strlen(dir) + strlen(file) + 2; /* +2 for trailing (back)slash and \0 */
561
562 #if defined(unix)
563       if ( *dir != '/' && basedir && *basedir )
564       {
565          /*
566           * Relative path, so start with the base directory.
567           */
568          path_size += strlen(basedir) + 1; /* +1 for the slash */
569          path = malloc(path_size);
570          if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
571          strlcpy(path, basedir, path_size);
572          strlcat(path, "/", path_size);
573          strlcat(path, dir, path_size);
574       }
575       else
576 #endif /* defined unix */
577       {
578          path = malloc(path_size);
579          if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
580          strlcpy(path, dir, path_size);
581       }
582
583       assert(NULL != path);
584 #if defined(_WIN32) || defined(__OS2__)
585       if(path[strlen(path)-1] != '\\')
586       {
587          strlcat(path, "\\", path_size);
588       }
589 #else /* ifndef _WIN32 || __OS2__ */
590       if(path[strlen(path)-1] != '/')
591       {
592          strlcat(path, "/", path_size);
593       }
594 #endif /* ifndef _WIN32 || __OS2__ */
595       strlcat(path, file, path_size);
596
597       return path;
598    }
599 #endif /* ndef AMIGA */
600 }
601
602
603 /*********************************************************************
604  *
605  * Function    :  pick_from_range
606  *
607  * Description :  Pick a positive number out of a given range.
608  *                Should only be used if randomness would be nice,
609  *                but isn't really necessary.
610  *
611  * Parameters  :
612  *          1  :  range: Highest possible number to pick.
613  *
614  * Returns     :  Picked number.
615  *
616  *********************************************************************/
617 long int pick_from_range(long int range)
618 {
619    long int number;
620 #ifdef _WIN32
621    static unsigned long seed = 0;
622 #endif /* def _WIN32 */
623
624    assert(range != 0);
625    assert(range > 0);
626
627    if (range <= 0) return 0;
628
629 #ifdef HAVE_RANDOM
630    number = random() % range + 1;
631 #elif defined(MUTEX_LOCKS_AVAILABLE)
632    privoxy_mutex_lock(&rand_mutex);
633 #ifdef _WIN32
634    if (!seed)
635    {
636       seed = (unsigned long)(GetCurrentThreadId()+GetTickCount());
637    }
638    srand(seed);
639    seed = (unsigned long)((rand() << 16) + rand());
640 #endif /* def _WIN32 */
641    number = (unsigned long)((rand() << 16) + (rand())) % (unsigned long)(range + 1);
642    privoxy_mutex_unlock(&rand_mutex);
643 #else
644    /*
645     * XXX: Which platforms reach this and are there
646     * better options than just using rand() and hoping
647     * that it's safe?
648     */
649    log_error(LOG_LEVEL_INFO, "No thread-safe PRNG available? Header time randomization "
650       "might cause crashes, predictable results or even combine these fine options.");
651    number = rand() % (long int)(range + 1);
652
653 #endif /* (def HAVE_RANDOM) */
654
655    return number;
656 }
657
658
659 #ifdef USE_PRIVOXY_STRLCPY
660 /*********************************************************************
661  *
662  * Function    :  privoxy_strlcpy
663  *
664  * Description :  strlcpy(3) look-alike for those without decent libc.
665  *
666  * Parameters  :
667  *          1  :  destination: buffer to copy into.
668  *          2  :  source: String to copy.
669  *          3  :  size: Size of destination buffer.
670  *
671  * Returns     :  The length of the string that privoxy_strlcpy() tried to create.
672  *
673  *********************************************************************/
674 size_t privoxy_strlcpy(char *destination, const char *source, const size_t size)
675 {
676    if (0 < size)
677    {
678       snprintf(destination, size, "%s", source);
679       /*
680        * Platforms that lack strlcpy() also tend to have
681        * a broken snprintf implementation that doesn't
682        * guarantee nul termination.
683        *
684        * XXX: the configure script should detect and reject those.
685        */
686       destination[size-1] = '\0';
687    }
688    return strlen(source);
689 }
690 #endif /* def USE_PRIVOXY_STRLCPY */
691
692
693 #ifndef HAVE_STRLCAT
694 /*********************************************************************
695  *
696  * Function    :  privoxy_strlcat
697  *
698  * Description :  strlcat(3) look-alike for those without decent libc.
699  *
700  * Parameters  :
701  *          1  :  destination: C string.
702  *          2  :  source: String to copy.
703  *          3  :  size: Size of destination buffer.
704  *
705  * Returns     :  The length of the string that privoxy_strlcat() tried to create.
706  *
707  *********************************************************************/
708 size_t privoxy_strlcat(char *destination, const char *source, const size_t size)
709 {
710    const size_t old_length = strlen(destination);
711    return old_length + strlcpy(destination + old_length, source, size - old_length);
712 }
713 #endif /* ndef HAVE_STRLCAT */
714
715
716 #if !defined(HAVE_TIMEGM) && defined(HAVE_TZSET) && defined(HAVE_PUTENV)
717 /*********************************************************************
718  *
719  * Function    :  timegm
720  *
721  * Description :  libc replacement function for the inverse of gmtime().
722  *                Copyright (C) 2004 Free Software Foundation, Inc.
723  *
724  *                Code originally copied from GnuPG, modifications done
725  *                for Privoxy: style changed, #ifdefs for _WIN32 added
726  *                to have it work on mingw32.
727  *
728  *                XXX: It's very unlikely to happen, but if the malloc()
729  *                call fails the time zone will be permanently set to UTC.
730  *
731  * Parameters  :
732  *          1  :  tm: Broken-down time struct.
733  *
734  * Returns     :  tm converted into time_t seconds.
735  *
736  *********************************************************************/
737 time_t timegm(struct tm *tm)
738 {
739    time_t answer;
740    char *zone;
741
742    zone = getenv("TZ");
743    putenv("TZ=UTC");
744    tzset();
745    answer = mktime(tm);
746    if (zone)
747    {
748       char *old_zone;
749
750       old_zone = malloc(3 + strlen(zone) + 1);
751       if (old_zone)
752       {
753          strcpy(old_zone, "TZ=");
754          strcat(old_zone, zone);
755          putenv(old_zone);
756 #ifdef _WIN32
757          free(old_zone);
758 #endif /* def _WIN32 */
759       }
760    }
761    else
762    {
763 #ifdef HAVE_UNSETENV
764       unsetenv("TZ");
765 #elif defined(_WIN32)
766       putenv("TZ=");
767 #else
768       putenv("TZ");
769 #endif
770    }
771    tzset();
772
773    return answer;
774 }
775 #endif /* !defined(HAVE_TIMEGM) && defined(HAVE_TZSET) && defined(HAVE_PUTENV) */
776
777
778 #ifndef HAVE_SNPRINTF
779 /*
780  * What follows is a portable snprintf routine, written by Mark Martinec.
781  * See: http://www.ijs.si/software/snprintf/
782
783                                   snprintf.c
784                    - a portable implementation of snprintf,
785        including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
786
787    snprintf is a routine to convert numeric and string arguments to
788    formatted strings. It is similar to sprintf(3) provided in a system's
789    C library, yet it requires an additional argument - the buffer size -
790    and it guarantees never to store anything beyond the given buffer,
791    regardless of the format or arguments to be formatted. Some newer
792    operating systems do provide snprintf in their C library, but many do
793    not or do provide an inadequate (slow or idiosyncratic) version, which
794    calls for a portable implementation of this routine.
795
796 Author
797
798    Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
799    Copyright Â© 1999, Mark Martinec
800
801  */
802
803 #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
804 #define PORTABLE_SNPRINTF_VERSION_MINOR 2
805
806 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
807 # if defined(NEED_SNPRINTF_ONLY)
808 # undef NEED_SNPRINTF_ONLY
809 # endif
810 # if !defined(PREFER_PORTABLE_SNPRINTF)
811 # define PREFER_PORTABLE_SNPRINTF
812 # endif
813 #endif
814
815 #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
816 #define SOLARIS_COMPATIBLE
817 #endif
818
819 #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
820 #define HPUX_COMPATIBLE
821 #endif
822
823 #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
824 #define DIGITAL_UNIX_COMPATIBLE
825 #endif
826
827 #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
828 #define PERL_COMPATIBLE
829 #endif
830
831 #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
832 #define LINUX_COMPATIBLE
833 #endif
834
835 #include <sys/types.h>
836 #include <string.h>
837 #include <stdlib.h>
838 #include <stdio.h>
839 #include <stdarg.h>
840 #include <assert.h>
841 #include <errno.h>
842
843 #ifdef isdigit
844 #undef isdigit
845 #endif
846 #define isdigit(c) ((c) >= '0' && (c) <= '9')
847
848 /* For copying strings longer or equal to 'breakeven_point'
849  * it is more efficient to call memcpy() than to do it inline.
850  * The value depends mostly on the processor architecture,
851  * but also on the compiler and its optimization capabilities.
852  * The value is not critical, some small value greater than zero
853  * will be just fine if you don't care to squeeze every drop
854  * of performance out of the code.
855  *
856  * Small values favor memcpy, large values favor inline code.
857  */
858 #if defined(__alpha__) || defined(__alpha)
859 #  define breakeven_point   2    /* AXP (DEC Alpha)     - gcc or cc or egcs */
860 #endif
861 #if defined(__i386__)  || defined(__i386)
862 #  define breakeven_point  12    /* Intel Pentium/Linux - gcc 2.96 */
863 #endif
864 #if defined(__hppa)
865 #  define breakeven_point  10    /* HP-PA               - gcc */
866 #endif
867 #if defined(__sparc__) || defined(__sparc)
868 #  define breakeven_point  33    /* Sun Sparc 5         - gcc 2.8.1 */
869 #endif
870
871 /* some other values of possible interest: */
872 /* #define breakeven_point  8 */ /* VAX 4000          - vaxc */
873 /* #define breakeven_point 19 */ /* VAX 4000          - gcc 2.7.0 */
874
875 #ifndef breakeven_point
876 #  define breakeven_point   6    /* some reasonable one-size-fits-all value */
877 #endif
878
879 #define fast_memcpy(d,s,n) \
880   { register size_t nn = (size_t)(n); \
881     if (nn >= breakeven_point) memcpy((d), (s), nn); \
882     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
883       register char *dd; register const char *ss; \
884       for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
885
886 #define fast_memset(d,c,n) \
887   { register size_t nn = (size_t)(n); \
888     if (nn >= breakeven_point) memset((d), (int)(c), nn); \
889     else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
890       register char *dd; register const int cc=(int)(c); \
891       for (dd=(d); nn>0; nn--) *dd++ = cc; } }
892
893 /* prototypes */
894
895 #if defined(NEED_ASPRINTF)
896 int asprintf   (char **ptr, const char *fmt, /*args*/ ...);
897 #endif
898 #if defined(NEED_VASPRINTF)
899 int vasprintf  (char **ptr, const char *fmt, va_list ap);
900 #endif
901 #if defined(NEED_ASNPRINTF)
902 int asnprintf  (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
903 #endif
904 #if defined(NEED_VASNPRINTF)
905 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
906 #endif
907
908 #if defined(HAVE_SNPRINTF)
909 /* declare our portable snprintf  routine under name portable_snprintf  */
910 /* declare our portable vsnprintf routine under name portable_vsnprintf */
911 #else
912 /* declare our portable routines under names snprintf and vsnprintf */
913 #define portable_snprintf snprintf
914 #if !defined(NEED_SNPRINTF_ONLY)
915 #define portable_vsnprintf vsnprintf
916 #endif
917 #endif
918
919 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
920 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
921 #if !defined(NEED_SNPRINTF_ONLY)
922 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
923 #endif
924 #endif
925
926 /* declarations */
927
928 static char credits[] = "\n\
929 @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
930 @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
931 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
932
933 #if defined(NEED_ASPRINTF)
934 int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
935   va_list ap;
936   size_t str_m;
937   int str_l;
938
939   *ptr = NULL;
940   va_start(ap, fmt);                            /* measure the required size */
941   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
942   va_end(ap);
943   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
944   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
945   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
946   else {
947     int str_l2;
948     va_start(ap, fmt);
949     str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
950     va_end(ap);
951     assert(str_l2 == str_l);
952   }
953   return str_l;
954 }
955 #endif
956
957 #if defined(NEED_VASPRINTF)
958 int vasprintf(char **ptr, const char *fmt, va_list ap) {
959   size_t str_m;
960   int str_l;
961
962   *ptr = NULL;
963   { va_list ap2;
964     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
965     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
966     va_end(ap2);
967   }
968   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
969   *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
970   if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
971   else {
972     int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
973     assert(str_l2 == str_l);
974   }
975   return str_l;
976 }
977 #endif
978
979 #if defined(NEED_ASNPRINTF)
980 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
981   va_list ap;
982   int str_l;
983
984   *ptr = NULL;
985   va_start(ap, fmt);                            /* measure the required size */
986   str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
987   va_end(ap);
988   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
989   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
990   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
991   if (str_m == 0) {  /* not interested in resulting string, just return size */
992   } else {
993     *ptr = (char *) malloc(str_m);
994     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
995     else {
996       int str_l2;
997       va_start(ap, fmt);
998       str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
999       va_end(ap);
1000       assert(str_l2 == str_l);
1001     }
1002   }
1003   return str_l;
1004 }
1005 #endif
1006
1007 #if defined(NEED_VASNPRINTF)
1008 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
1009   int str_l;
1010
1011   *ptr = NULL;
1012   { va_list ap2;
1013     va_copy(ap2, ap);  /* don't consume the original ap, we'll need it again */
1014     str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1015     va_end(ap2);
1016   }
1017   assert(str_l >= 0);        /* possible integer overflow if str_m > INT_MAX */
1018   if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1;      /* truncate */
1019   /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1020   if (str_m == 0) {  /* not interested in resulting string, just return size */
1021   } else {
1022     *ptr = (char *) malloc(str_m);
1023     if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1024     else {
1025       int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1026       assert(str_l2 == str_l);
1027     }
1028   }
1029   return str_l;
1030 }
1031 #endif
1032
1033 /*
1034  * If the system does have snprintf and the portable routine is not
1035  * specifically required, this module produces no code for snprintf/vsnprintf.
1036  */
1037 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1038
1039 #if !defined(NEED_SNPRINTF_ONLY)
1040 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1041   va_list ap;
1042   int str_l;
1043
1044   va_start(ap, fmt);
1045   str_l = portable_vsnprintf(str, str_m, fmt, ap);
1046   va_end(ap);
1047   return str_l;
1048 }
1049 #endif
1050
1051 #if defined(NEED_SNPRINTF_ONLY)
1052 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1053 #else
1054 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
1055 #endif
1056
1057 #if defined(NEED_SNPRINTF_ONLY)
1058   va_list ap;
1059 #endif
1060   size_t str_l = 0;
1061   const char *p = fmt;
1062
1063 /* In contrast with POSIX, the ISO C99 now says
1064  * that str can be NULL and str_m can be 0.
1065  * This is more useful than the old:  if (str_m < 1) return -1; */
1066
1067 #if defined(NEED_SNPRINTF_ONLY)
1068   va_start(ap, fmt);
1069 #endif
1070   if (!p) p = "";
1071   while (*p) {
1072     if (*p != '%') {
1073    /* if (str_l < str_m) str[str_l++] = *p++;    -- this would be sufficient */
1074    /* but the following code achieves better performance for cases
1075     * where format string is long and contains few conversions */
1076       const char *q = strchr(p+1,'%');
1077       size_t n = !q ? strlen(p) : (q-p);
1078       if (str_l < str_m) {
1079         size_t avail = str_m-str_l;
1080         fast_memcpy(str+str_l, p, (n>avail?avail:n));
1081       }
1082       p += n; str_l += n;
1083     } else {
1084       const char *starting_p;
1085       size_t min_field_width = 0, precision = 0;
1086       int zero_padding = 0, precision_specified = 0, justify_left = 0;
1087       int alternate_form = 0, force_sign = 0;
1088       int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
1089                                      the ' ' flag should be ignored. */
1090       char length_modifier = '\0';            /* allowed values: \0, h, l, L */
1091       char tmp[32];/* temporary buffer for simple numeric->string conversion */
1092
1093       const char *str_arg;      /* string address in case of string argument */
1094       size_t str_arg_l;         /* natural field width of arg without padding
1095                                    and sign */
1096       unsigned char uchar_arg;
1097         /* unsigned char argument value - only defined for c conversion.
1098            N.B. standard explicitly states the char argument for
1099            the c conversion is unsigned */
1100
1101       size_t number_of_zeros_to_pad = 0;
1102         /* number of zeros to be inserted for numeric conversions
1103            as required by the precision or minimal field width */
1104
1105       size_t zero_padding_insertion_ind = 0;
1106         /* index into tmp where zero padding is to be inserted */
1107
1108       char fmt_spec = '\0';
1109         /* current conversion specifier character */
1110
1111       str_arg = credits;/* just to make compiler happy (defined but not used)*/
1112       str_arg = NULL;
1113       starting_p = p; p++;  /* skip '%' */
1114    /* parse flags */
1115       while (*p == '0' || *p == '-' || *p == '+' ||
1116              *p == ' ' || *p == '#' || *p == '\'') {
1117         switch (*p) {
1118         case '0': zero_padding = 1; break;
1119         case '-': justify_left = 1; break;
1120         case '+': force_sign = 1; space_for_positive = 0; break;
1121         case ' ': force_sign = 1;
1122      /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
1123 #ifdef PERL_COMPATIBLE
1124      /* ... but in Perl the last of ' ' and '+' applies */
1125                   space_for_positive = 1;
1126 #endif
1127                   break;
1128         case '#': alternate_form = 1; break;
1129         case '\'': break;
1130         }
1131         p++;
1132       }
1133    /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
1134
1135    /* parse field width */
1136       if (*p == '*') {
1137         int j;
1138         p++; j = va_arg(ap, int);
1139         if (j >= 0) min_field_width = j;
1140         else { min_field_width = -j; justify_left = 1; }
1141       } else if (isdigit((int)(*p))) {
1142         /* size_t could be wider than unsigned int;
1143            make sure we treat argument like common implementations do */
1144         unsigned int uj = *p++ - '0';
1145         while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1146         min_field_width = uj;
1147       }
1148    /* parse precision */
1149       if (*p == '.') {
1150         p++; precision_specified = 1;
1151         if (*p == '*') {
1152           int j = va_arg(ap, int);
1153           p++;
1154           if (j >= 0) precision = j;
1155           else {
1156             precision_specified = 0; precision = 0;
1157          /* NOTE:
1158           *   Solaris 2.6 man page claims that in this case the precision
1159           *   should be set to 0.  Digital Unix 4.0, HPUX 10 and BSD man page
1160           *   claim that this case should be treated as unspecified precision,
1161           *   which is what we do here.
1162           */
1163           }
1164         } else if (isdigit((int)(*p))) {
1165           /* size_t could be wider than unsigned int;
1166              make sure we treat argument like common implementations do */
1167           unsigned int uj = *p++ - '0';
1168           while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1169           precision = uj;
1170         }
1171       }
1172    /* parse 'h', 'l' and 'll' length modifiers */
1173       if (*p == 'h' || *p == 'l') {
1174         length_modifier = *p; p++;
1175         if (length_modifier == 'l' && *p == 'l') {   /* double l = long long */
1176 #ifdef SNPRINTF_LONGLONG_SUPPORT
1177           length_modifier = '2';                  /* double l encoded as '2' */
1178 #else
1179           length_modifier = 'l';                 /* treat it as a single 'l' */
1180 #endif
1181           p++;
1182         }
1183       }
1184       fmt_spec = *p;
1185    /* common synonyms: */
1186       switch (fmt_spec) {
1187       case 'i': fmt_spec = 'd'; break;
1188       case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
1189       case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
1190       case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
1191       default: break;
1192       }
1193    /* get parameter value, do initial processing */
1194       switch (fmt_spec) {
1195       case '%': /* % behaves similar to 's' regarding flags and field widths */
1196       case 'c': /* c behaves similar to 's' regarding flags and field widths */
1197       case 's':
1198         length_modifier = '\0';          /* wint_t and wchar_t not supported */
1199      /* the result of zero padding flag with non-numeric conversion specifier*/
1200      /* is undefined. Solaris and HPUX 10 does zero padding in this case,    */
1201      /* Digital Unix and Linux does not. */
1202 #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1203         zero_padding = 0;    /* turn zero padding off for string conversions */
1204 #endif
1205         str_arg_l = 1;
1206         switch (fmt_spec) {
1207         case '%':
1208           str_arg = p; break;
1209         case 'c': {
1210           int j = va_arg(ap, int);
1211           uchar_arg = (unsigned char) j;   /* standard demands unsigned char */
1212           str_arg = (const char *) &uchar_arg;
1213           break;
1214         }
1215         case 's':
1216           str_arg = va_arg(ap, const char *);
1217           if (!str_arg) str_arg_l = 0;
1218        /* make sure not to address string beyond the specified precision !!! */
1219           else if (!precision_specified) str_arg_l = strlen(str_arg);
1220        /* truncate string if necessary as requested by precision */
1221           else if (precision == 0) str_arg_l = 0;
1222           else {
1223        /* memchr on HP does not like n > 2^31  !!! */
1224             const char *q = memchr(str_arg, '\0',
1225                              precision <= 0x7fffffff ? precision : 0x7fffffff);
1226             str_arg_l = !q ? precision : (q-str_arg);
1227           }
1228           break;
1229         default: break;
1230         }
1231         break;
1232       case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
1233         /* NOTE: the u, o, x, X and p conversion specifiers imply
1234                  the value is unsigned;  d implies a signed value */
1235
1236         int arg_sign = 0;
1237           /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
1238             +1 if greater than zero (or nonzero for unsigned arguments),
1239             -1 if negative (unsigned argument is never negative) */
1240
1241         int int_arg = 0;  unsigned int uint_arg = 0;
1242           /* only defined for length modifier h, or for no length modifiers */
1243
1244         long int long_arg = 0;  unsigned long int ulong_arg = 0;
1245           /* only defined for length modifier l */
1246
1247         void *ptr_arg = NULL;
1248           /* pointer argument value -only defined for p conversion */
1249
1250 #ifdef SNPRINTF_LONGLONG_SUPPORT
1251         long long int long_long_arg = 0;
1252         unsigned long long int ulong_long_arg = 0;
1253           /* only defined for length modifier ll */
1254 #endif
1255         if (fmt_spec == 'p') {
1256         /* HPUX 10: An l, h, ll or L before any other conversion character
1257          *   (other than d, i, u, o, x, or X) is ignored.
1258          * Digital Unix:
1259          *   not specified, but seems to behave as HPUX does.
1260          * Solaris: If an h, l, or L appears before any other conversion
1261          *   specifier (other than d, i, u, o, x, or X), the behavior
1262          *   is undefined. (Actually %hp converts only 16-bits of address
1263          *   and %llp treats address as 64-bit data which is incompatible
1264          *   with (void *) argument on a 32-bit system).
1265          */
1266 #ifdef SOLARIS_COMPATIBLE
1267 #  ifdef SOLARIS_BUG_COMPATIBLE
1268           /* keep length modifiers even if it represents 'll' */
1269 #  else
1270           if (length_modifier == '2') length_modifier = '\0';
1271 #  endif
1272 #else
1273           length_modifier = '\0';
1274 #endif
1275           ptr_arg = va_arg(ap, void *);
1276           if (ptr_arg != NULL) arg_sign = 1;
1277         } else if (fmt_spec == 'd') {  /* signed */
1278           switch (length_modifier) {
1279           case '\0':
1280           case 'h':
1281          /* It is non-portable to specify a second argument of char or short
1282           * to va_arg, because arguments seen by the called function
1283           * are not char or short.  C converts char and short arguments
1284           * to int before passing them to a function.
1285           */
1286             int_arg = va_arg(ap, int);
1287             if      (int_arg > 0) arg_sign =  1;
1288             else if (int_arg < 0) arg_sign = -1;
1289             break;
1290           case 'l':
1291             long_arg = va_arg(ap, long int);
1292             if      (long_arg > 0) arg_sign =  1;
1293             else if (long_arg < 0) arg_sign = -1;
1294             break;
1295 #ifdef SNPRINTF_LONGLONG_SUPPORT
1296           case '2':
1297             long_long_arg = va_arg(ap, long long int);
1298             if      (long_long_arg > 0) arg_sign =  1;
1299             else if (long_long_arg < 0) arg_sign = -1;
1300             break;
1301 #endif
1302           }
1303         } else {  /* unsigned */
1304           switch (length_modifier) {
1305           case '\0':
1306           case 'h':
1307             uint_arg = va_arg(ap, unsigned int);
1308             if (uint_arg) arg_sign = 1;
1309             break;
1310           case 'l':
1311             ulong_arg = va_arg(ap, unsigned long int);
1312             if (ulong_arg) arg_sign = 1;
1313             break;
1314 #ifdef SNPRINTF_LONGLONG_SUPPORT
1315           case '2':
1316             ulong_long_arg = va_arg(ap, unsigned long long int);
1317             if (ulong_long_arg) arg_sign = 1;
1318             break;
1319 #endif
1320           }
1321         }
1322         str_arg = tmp; str_arg_l = 0;
1323      /* NOTE:
1324       *   For d, i, u, o, x, and X conversions, if precision is specified,
1325       *   the '0' flag should be ignored. This is so with Solaris 2.6,
1326       *   Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
1327       */
1328 #ifndef PERL_COMPATIBLE
1329         if (precision_specified) zero_padding = 0;
1330 #endif
1331         if (fmt_spec == 'd') {
1332           if (force_sign && arg_sign >= 0)
1333             tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1334          /* leave negative numbers for sprintf to handle,
1335             to avoid handling tricky cases like (short int)(-32768) */
1336 #ifdef LINUX_COMPATIBLE
1337         } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
1338           tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1339 #endif
1340         } else if (alternate_form) {
1341           if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
1342             { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
1343          /* alternate form should have no effect for p conversion, but ... */
1344 #ifdef HPUX_COMPATIBLE
1345           else if (fmt_spec == 'p'
1346          /* HPUX 10: for an alternate form of p conversion,
1347           *          a nonzero result is prefixed by 0x. */
1348 #ifndef HPUX_BUG_COMPATIBLE
1349          /* Actually it uses 0x prefix even for a zero value. */
1350                    && arg_sign != 0
1351 #endif
1352                   ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
1353 #endif
1354         }
1355         zero_padding_insertion_ind = str_arg_l;
1356         if (!precision_specified) precision = 1;   /* default precision is 1 */
1357         if (precision == 0 && arg_sign == 0
1358 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1359             && fmt_spec != 'p'
1360          /* HPUX 10 man page claims: With conversion character p the result of
1361           * converting a zero value with a precision of zero is a null string.
1362           * Actually HP returns all zeroes, and Linux returns "(nil)". */
1363 #endif
1364         ) {
1365          /* converted to null string */
1366          /* When zero value is formatted with an explicit precision 0,
1367             the resulting formatted string is empty (d, i, u, o, x, X, p).   */
1368         } else {
1369           char f[5]; int f_l = 0;
1370           f[f_l++] = '%';    /* construct a simple format string for sprintf */
1371           if (!length_modifier) { }
1372           else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
1373           else f[f_l++] = length_modifier;
1374           f[f_l++] = fmt_spec; f[f_l++] = '\0';
1375           if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
1376           else if (fmt_spec == 'd') {  /* signed */
1377             switch (length_modifier) {
1378             case '\0':
1379             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg);  break;
1380             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
1381 #ifdef SNPRINTF_LONGLONG_SUPPORT
1382             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
1383 #endif
1384             }
1385           } else {  /* unsigned */
1386             switch (length_modifier) {
1387             case '\0':
1388             case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg);  break;
1389             case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
1390 #ifdef SNPRINTF_LONGLONG_SUPPORT
1391             case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
1392 #endif
1393             }
1394           }
1395          /* include the optional minus sign and possible "0x"
1396             in the region before the zero padding insertion point */
1397           if (zero_padding_insertion_ind < str_arg_l &&
1398               tmp[zero_padding_insertion_ind] == '-') {
1399             zero_padding_insertion_ind++;
1400           }
1401           if (zero_padding_insertion_ind+1 < str_arg_l &&
1402               tmp[zero_padding_insertion_ind]   == '0' &&
1403              (tmp[zero_padding_insertion_ind+1] == 'x' ||
1404               tmp[zero_padding_insertion_ind+1] == 'X') ) {
1405             zero_padding_insertion_ind += 2;
1406           }
1407         }
1408         { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
1409           if (alternate_form && fmt_spec == 'o'
1410 #ifdef HPUX_COMPATIBLE                                  /* ("%#.o",0) -> ""  */
1411               && (str_arg_l > 0)
1412 #endif
1413 #ifdef DIGITAL_UNIX_BUG_COMPATIBLE                      /* ("%#o",0) -> "00" */
1414 #else
1415               /* unless zero is already the first character */
1416               && !(zero_padding_insertion_ind < str_arg_l
1417                    && tmp[zero_padding_insertion_ind] == '0')
1418 #endif
1419           ) {        /* assure leading zero for alternate-form octal numbers */
1420             if (!precision_specified || precision < num_of_digits+1) {
1421              /* precision is increased to force the first character to be zero,
1422                 except if a zero value is formatted with an explicit precision
1423                 of zero */
1424               precision = num_of_digits+1; precision_specified = 1;
1425             }
1426           }
1427        /* zero padding to specified precision? */
1428           if (num_of_digits < precision)
1429             number_of_zeros_to_pad = precision - num_of_digits;
1430         }
1431      /* zero padding to specified minimal field width? */
1432         if (!justify_left && zero_padding) {
1433           int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1434           if (n > 0) number_of_zeros_to_pad += n;
1435         }
1436         break;
1437       }
1438       default: /* unrecognized conversion specifier, keep format string as-is*/
1439         zero_padding = 0;  /* turn zero padding off for non-numeric convers. */
1440 #ifndef DIGITAL_UNIX_COMPATIBLE
1441         justify_left = 1; min_field_width = 0;                /* reset flags */
1442 #endif
1443 #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1444      /* keep the entire format string unchanged */
1445         str_arg = starting_p; str_arg_l = p - starting_p;
1446      /* well, not exactly so for Linux, which does something between,
1447       * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y"  */
1448 #else
1449      /* discard the unrecognized conversion, just keep *
1450       * the unrecognized conversion character          */
1451         str_arg = p; str_arg_l = 0;
1452 #endif
1453         if (*p) str_arg_l++;  /* include invalid conversion specifier unchanged
1454                                  if not at end-of-string */
1455         break;
1456       }
1457       if (*p) p++;      /* step over the just processed conversion specifier */
1458    /* insert padding to the left as requested by min_field_width;
1459       this does not include the zero padding in case of numerical conversions*/
1460       if (!justify_left) {                /* left padding with blank or zero */
1461         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1462         if (n > 0) {
1463           if (str_l < str_m) {
1464             size_t avail = str_m-str_l;
1465             fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
1466           }
1467           str_l += n;
1468         }
1469       }
1470    /* zero padding as requested by the precision or by the minimal field width
1471     * for numeric conversions required? */
1472       if (number_of_zeros_to_pad <= 0) {
1473      /* will not copy first part of numeric right now, *
1474       * force it to be copied later in its entirety    */
1475         zero_padding_insertion_ind = 0;
1476       } else {
1477      /* insert first part of numerics (sign or '0x') before zero padding */
1478         int n = zero_padding_insertion_ind;
1479         if (n > 0) {
1480           if (str_l < str_m) {
1481             size_t avail = str_m-str_l;
1482             fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
1483           }
1484           str_l += n;
1485         }
1486      /* insert zero padding as requested by the precision or min field width */
1487         n = number_of_zeros_to_pad;
1488         if (n > 0) {
1489           if (str_l < str_m) {
1490             size_t avail = str_m-str_l;
1491             fast_memset(str+str_l, '0', (n>avail?avail:n));
1492           }
1493           str_l += n;
1494         }
1495       }
1496    /* insert formatted string
1497     * (or as-is conversion specifier for unknown conversions) */
1498       { int n = str_arg_l - zero_padding_insertion_ind;
1499         if (n > 0) {
1500           if (str_l < str_m) {
1501             size_t avail = str_m-str_l;
1502             fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
1503                         (n>avail?avail:n));
1504           }
1505           str_l += n;
1506         }
1507       }
1508    /* insert right padding */
1509       if (justify_left) {          /* right blank padding to the field width */
1510         int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1511         if (n > 0) {
1512           if (str_l < str_m) {
1513             size_t avail = str_m-str_l;
1514             fast_memset(str+str_l, ' ', (n>avail?avail:n));
1515           }
1516           str_l += n;
1517         }
1518       }
1519     }
1520   }
1521 #if defined(NEED_SNPRINTF_ONLY)
1522   va_end(ap);
1523 #endif
1524   if (str_m > 0) { /* make sure the string is null-terminated
1525                       even at the expense of overwriting the last character
1526                       (shouldn't happen, but just in case) */
1527     str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
1528   }
1529   /* Return the number of characters formatted (excluding trailing null
1530    * character), that is, the number of characters that would have been
1531    * written to the buffer if it were large enough.
1532    *
1533    * The value of str_l should be returned, but str_l is of unsigned type
1534    * size_t, and snprintf is int, possibly leading to an undetected
1535    * integer overflow, resulting in a negative return value, which is illegal.
1536    * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
1537    * Should errno be set to EOVERFLOW and EOF returned in this case???
1538    */
1539   return (int) str_l;
1540 }
1541 #endif
1542 #endif /* ndef HAVE_SNPRINTF */
1543 /*
1544   Local Variables:
1545   tab-width: 3
1546   end:
1547 */