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