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