1 const char miscutil_rcs[] = "$Id: miscutil.c,v 1.39 2006/07/18 14:48:46 david__schmidt Exp $";
2 /*********************************************************************
4 * File : $Source: /cvsroot/ijbswa/current/miscutil.c,v $
6 * Purpose : zalloc, hash_string, safe_strerror, strcmpic,
7 * strncmpic, chomp, and MinGW32 strdup
9 * These are each too small to deserve their own file
10 * but don't really fit in any other file.
12 * Copyright : Written by and Copyright (C) 2001 the SourceForge
13 * Privoxy team. http://www.privoxy.org/
15 * Based on the Internet Junkbuster originally written
16 * by and Copyright (C) 1997 Anonymous Coders and
17 * Junkbusters Corporation. http://www.junkbusters.com
19 * This program is free software; you can redistribute it
20 * and/or modify it under the terms of the GNU General
21 * Public License as published by the Free Software
22 * Foundation; either version 2 of the License, or (at
23 * your option) any later version.
25 * This program is distributed in the hope that it will
26 * be useful, but WITHOUT ANY WARRANTY; without even the
27 * implied warranty of MERCHANTABILITY or FITNESS FOR A
28 * PARTICULAR PURPOSE. See the GNU General Public
29 * License for more details.
31 * The GNU General Public License should be included with
32 * this file. If not, you can view it at
33 * http://www.gnu.org/copyleft/gpl.html
34 * or write to the Free Software Foundation, Inc., 59
35 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
38 * $Log: miscutil.c,v $
39 * Revision 1.39 2006/07/18 14:48:46 david__schmidt
40 * Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
41 * with what was really the latest development (the v_3_0_branch branch)
43 * Revision 1.37.2.4 2003/12/01 14:45:14 oes
44 * Fixed two more problems with wildcarding in simplematch()
46 * Revision 1.37.2.3 2003/11/20 11:39:24 oes
47 * Bugfix: The "?" wildcard for domain names had never been implemented. Ooops\!
49 * Revision 1.37.2.2 2002/11/12 14:28:18 oes
50 * Proper backtracking in simplematch; fixes bug #632888
52 * Revision 1.37.2.1 2002/09/25 12:58:51 oes
53 * Made strcmpic and strncmpic safe against NULL arguments
54 * (which are now treated as empty strings).
56 * Revision 1.37 2002/04/26 18:29:43 jongfoster
57 * Fixing this Visual C++ warning:
58 * miscutil.c(710) : warning C4090: '=' : different 'const' qualifiers
60 * Revision 1.36 2002/04/26 12:55:38 oes
61 * New function string_toupper
63 * Revision 1.35 2002/03/26 22:29:55 swa
64 * we have a new homepage!
66 * Revision 1.34 2002/03/24 13:25:43 swa
67 * name change related issues
69 * Revision 1.33 2002/03/07 03:46:53 oes
70 * Fixed compiler warnings etc
72 * Revision 1.32 2002/03/06 23:02:57 jongfoster
75 * Revision 1.31 2002/03/05 04:52:42 oes
76 * Deleted non-errlog debugging code
78 * Revision 1.30 2002/03/04 18:27:42 oes
79 * - Deleted deletePidFile
80 * - Made write_pid_file use the --pidfile option value
81 * (or no PID file, if the option was absent)
82 * - Played styleguide police
84 * Revision 1.29 2002/03/04 02:08:02 david__schmidt
85 * Enable web editing of actions file on OS/2 (it had been broken all this time!)
87 * Revision 1.28 2002/03/03 09:18:03 joergs
88 * Made jumbjuster work on AmigaOS again.
90 * Revision 1.27 2002/01/21 00:52:32 jongfoster
91 * Adding string_join()
93 * Revision 1.26 2001/12/30 14:07:32 steudten
94 * - Add signal handling (unix)
95 * - Add SIGHUP handler (unix)
96 * - Add creation of pidfile (unix)
97 * - Add action 'top' in rc file (RH)
98 * - Add entry 'SIGNALS' to manpage
99 * - Add exit message to logfile (unix)
101 * Revision 1.25 2001/11/13 00:16:38 jongfoster
102 * Replacing references to malloc.h with the standard stdlib.h
103 * (See ANSI or K&R 2nd Ed)
105 * Revision 1.24 2001/11/05 21:41:43 steudten
106 * Add changes to be a real daemon just for unix os.
107 * (change cwd to /, detach from controlling tty, set
108 * process group and session leader to the own process.
110 * Add some fatal-error log message for failed malloc().
111 * Add '-d' if compiled with 'configure --with-debug' to
112 * enable debug output.
114 * Revision 1.23 2001/10/29 03:48:10 david__schmidt
115 * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted
116 * by and __OS2__ ifdef.
118 * Revision 1.22 2001/10/26 17:39:38 oes
119 * Moved ijb_isspace and ijb_tolower to project.h
121 * Revision 1.21 2001/10/23 21:27:50 jongfoster
122 * Standardising error codes in string_append
123 * make_path() no longer adds '\\' if the dir already ends in '\\' (this
124 * is just copying a UNIX-specific fix to the Windows-specific part)
126 * Revision 1.20 2001/10/22 15:33:56 david__schmidt
127 * Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in
128 * filters.c. Added a FIXME in front of the offending code. I'll gladly
129 * put in a better/more robust fix for all parties if one is presented...
130 * It seems that just returning 200 instead of 404 would pretty much fix
131 * it for everyone, but I don't know all the history of the problem.
133 * Revision 1.19 2001/10/14 22:02:57 jongfoster
134 * New function string_append() which is like strsav(), but running
135 * out of memory isn't automatically FATAL.
137 * Revision 1.18 2001/09/20 13:33:43 steudten
139 * change long to int as return value in hash_string(). Remember the wraparound
140 * for int = long = sizeof(4) - thats maybe not what we want.
142 * Revision 1.17 2001/09/13 20:51:29 jongfoster
143 * Fixing potential problems with characters >=128 in simplematch()
144 * This was also a compiler warning.
146 * Revision 1.16 2001/09/10 10:56:59 oes
147 * Silenced compiler warnings
149 * Revision 1.15 2001/07/13 14:02:24 oes
150 * Removed vim-settings
152 * Revision 1.14 2001/06/29 21:45:41 oes
153 * Indentation, CRLF->LF, Tab-> Space
155 * Revision 1.13 2001/06/29 13:32:14 oes
156 * Removed logentry from cancelled commit
158 * Revision 1.12 2001/06/09 10:55:28 jongfoster
159 * Changing BUFSIZ ==> BUFFER_SIZE
161 * Revision 1.11 2001/06/07 23:09:19 jongfoster
162 * Cosmetic indentation changes.
164 * Revision 1.10 2001/06/07 14:51:38 joergs
165 * make_path() no longer adds '/' if the dir already ends in '/'.
167 * Revision 1.9 2001/06/07 14:43:17 swa
168 * slight mistake in make_path, unix path style is /.
170 * Revision 1.8 2001/06/05 22:32:01 jongfoster
171 * New function make_path() to splice directory and file names together.
173 * Revision 1.7 2001/06/03 19:12:30 oes
174 * introduced bindup()
176 * Revision 1.6 2001/06/01 18:14:49 jongfoster
177 * Changing the calls to strerr() to check HAVE_STRERR (which is defined
178 * in config.h if appropriate) rather than the NO_STRERR macro.
180 * Revision 1.5 2001/06/01 10:31:51 oes
181 * Added character class matching to trivimatch; renamed to simplematch
183 * Revision 1.4 2001/05/31 17:32:31 oes
185 * - Enhanced domain part globbing with infix and prefix asterisk
186 * matching and optional unanchored operation
188 * Revision 1.3 2001/05/29 23:10:09 oes
191 * - Introduced chomp()
192 * - Moved strsav() from showargs to miscutil
194 * Revision 1.2 2001/05/29 09:50:24 jongfoster
195 * Unified blocklist/imagelist/permissionslist.
196 * File format is still under discussion, but the internal changes
199 * Also modified interceptor behaviour:
200 * - We now intercept all URLs beginning with one of the following
201 * prefixes (and *only* these prefixes):
203 * * http://ijbswa.sf.net/config/
204 * * http://ijbswa.sourceforge.net/config/
205 * - New interceptors "home page" - go to http://i.j.b/ to see it.
206 * - Internal changes so that intercepted and fast redirect pages
207 * are not replaced with an image.
208 * - Interceptors now have the option to send a binary page direct
209 * to the client. (i.e. ijb-send-banner uses this)
210 * - Implemented show-url-info interceptor. (Which is why I needed
211 * the above interceptors changes - a typical URL is
212 * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
213 * The previous mechanism would not have intercepted that, and
214 * if it had been intercepted then it then it would have replaced
217 * Revision 1.1.1.1 2001/05/15 13:59:00 oes
218 * Initial import of version 2.9.3 source tree
221 *********************************************************************/
227 #include <sys/types.h>
229 #if !defined(_WIN32) && !defined(__OS2__)
231 #endif /* #if !defined(_WIN32) && !defined(__OS2__) */
238 #endif /* #ifndef HAVE_TIMEGM */
241 #include "miscutil.h"
245 const char miscutil_h_rcs[] = MISCUTIL_H_VERSION;
247 /*********************************************************************
251 * Description : Malloc some memory and set it to '\0'.
252 * The way calloc() ought to be -acjc
255 * 1 : size = Size of memory chunk to return.
257 * Returns : Pointer to newly malloc'd memory chunk.
259 *********************************************************************/
260 void *zalloc(size_t size)
264 if ((ret = (void *)malloc(size)) != NULL)
266 memset(ret, 0, size);
275 /*********************************************************************
277 * Function : write_pid_file
279 * Description : Writes a pid file with the pid of the main process
285 *********************************************************************/
286 void write_pid_file(void)
291 * If no --pidfile option was given,
292 * we can live without one.
294 if (pidfile == NULL) return;
296 if ((fp = fopen(pidfile, "w")) == NULL)
298 log_error(LOG_LEVEL_INFO, "can't open pidfile '%s': %E", pidfile);
302 fprintf(fp, "%u\n", (unsigned int) getpid());
308 #endif /* def unix */
311 /*********************************************************************
313 * Function : hash_string
315 * Description : Take a string and compute a (hopefuly) unique numeric
316 * integer value. This has several uses, but being able
317 * to "switch" a string the one of my favorites.
320 * 1 : s : string to be hashed.
322 * Returns : an unsigned long variable with the hashed value.
324 *********************************************************************/
325 unsigned int hash_string( const char* s )
340 /*********************************************************************
344 * Description : For some reason (which is beyond me), gcc and WIN32
345 * don't like strdup. When a "free" is executed on a
346 * strdup'd ptr, it can at times freez up! So I just
347 * replaced it and problem was solved.
350 * 1 : s = string to duplicate
352 * Returns : Pointer to newly malloc'ed copy of the string.
354 *********************************************************************/
355 char *strdup( const char *s )
357 char * result = (char *)malloc( strlen(s)+1 );
367 #endif /* def __MINGW32__ */
371 /*********************************************************************
373 * Function : safe_strerror
375 * Description : Variant of the library routine strerror() which will
376 * work on systems without the library routine, and
377 * which should never return NULL.
380 * 1 : err = the `errno' of the last operation.
382 * Returns : An "English" string of the last `errno'. Allocated
383 * with strdup(), so caller frees. May be NULL if the
384 * system is out of memory.
386 *********************************************************************/
387 char *safe_strerror(int err)
390 char buf[BUFFER_SIZE];
395 #endif /* HAVE_STRERROR */
399 sprintf(buf, "(errno = %d)", err);
408 /*********************************************************************
410 * Function : strcmpic
412 * Description : Case insensitive string comparison
415 * 1 : s1 = string 1 to compare
416 * 2 : s2 = string 2 to compare
418 * Returns : 0 if s1==s2, Negative if s1<s2, Positive if s1>s2
420 *********************************************************************/
421 int strcmpic(const char *s1, const char *s2)
428 if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
434 return(ijb_tolower(*s1) - ijb_tolower(*s2));
439 /*********************************************************************
441 * Function : strncmpic
443 * Description : Case insensitive string comparison (upto n characters)
446 * 1 : s1 = string 1 to compare
447 * 2 : s2 = string 2 to compare
448 * 3 : n = maximum characters to compare
450 * Returns : 0 if s1==s2, Negative if s1<s2, Positive if s1>s2
452 *********************************************************************/
453 int strncmpic(const char *s1, const char *s2, size_t n)
455 if (n <= 0) return(0);
461 if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
470 return(ijb_tolower(*s1) - ijb_tolower(*s2));
475 /*********************************************************************
479 * Description : In-situ-eliminate all leading and trailing whitespace
483 * 1 : s : string to be chomped.
485 * Returns : chomped string
487 *********************************************************************/
488 char *chomp(char *string)
493 * strip trailing whitespace
495 p = string + strlen(string);
496 while (p > string && ijb_isspace(*(p-1)))
503 * find end of leading whitespace
506 while (*q && ijb_isspace(*q))
512 * if there was any, move the rest forwards
527 /*********************************************************************
531 * Description : Reallocate "old" and append text to it. This makes
532 * it easier to append to malloc'd strings.
533 * Running out of memory is a FATAL error.
536 * 1 : old = Old text that is to be extended. Will be
537 * free()d by this routine. May be NULL.
538 * 2 : text_to_append = Text to be appended to old.
541 * Returns : Pointer to newly malloc'ed appended string.
542 * If there is no text to append, return old. Caller
545 *********************************************************************/
546 char *strsav(char *old, const char *text_to_append)
548 size_t old_len, new_len = 0;
551 if ((text_to_append == NULL) || (*text_to_append == '\0'))
558 if ((p = strdup(text_to_append)) == NULL)
560 log_error(LOG_LEVEL_FATAL, "strdup() failed!");
561 /* Never get here - LOG_LEVEL_FATAL causes program exit */
566 old_len = strlen(old);
567 new_len = old_len + strlen(text_to_append) + 1;
569 if ((p = realloc(old, new_len)) == NULL)
571 log_error(LOG_LEVEL_FATAL, "realloc(%d) bytes failed!", new_len);
572 /* Never get here - LOG_LEVEL_FATAL causes program exit */
575 strcpy(p + old_len, text_to_append);
580 /*********************************************************************
582 * Function : string_append
584 * Description : Reallocate target_string and append text to it.
585 * This makes it easier to append to malloc'd strings.
586 * This is similar to the (removed) strsav(), but
587 * running out of memory isn't catastrophic.
591 * The following style provides sufficient error
592 * checking for this routine, with minimal clutter
593 * in the source code. It is recommended if you
594 * have many calls to this function:
596 * char * s = strdup(...); // don't check for error
597 * string_append(&s, ...); // don't check for error
598 * string_append(&s, ...); // don't check for error
599 * string_append(&s, ...); // don't check for error
600 * if (NULL == s) { ... handle error ... }
604 * char * s = strdup(...); // don't check for error
605 * string_append(&s, ...); // don't check for error
606 * string_append(&s, ...); // don't check for error
607 * if (string_append(&s, ...)) {... handle error ...}
610 * 1 : target_string = Pointer to old text that is to be
611 * extended. *target_string will be free()d by this
612 * routine. target_string must be non-NULL.
613 * If *target_string is NULL, this routine will
614 * do nothing and return with an error - this allows
615 * you to make many calls to this routine and only
616 * check for errors after the last one.
617 * 2 : text_to_append = Text to be appended to old.
620 * Returns : JB_ERR_OK on success, and sets *target_string
621 * to newly malloc'ed appended string. Caller
622 * must free(*target_string).
623 * JB_ERR_MEMORY on out-of-memory. (And free()s
624 * *target_string and sets it to NULL).
625 * JB_ERR_MEMORY if *target_string is NULL.
627 *********************************************************************/
628 jb_err string_append(char **target_string, const char *text_to_append)
633 assert(target_string);
634 assert(text_to_append);
636 if (*target_string == NULL)
638 return JB_ERR_MEMORY;
641 if (*text_to_append == '\0')
646 old_len = strlen(*target_string);
648 if (NULL == (new_string = realloc(*target_string,
649 strlen(text_to_append) + old_len + 1)))
651 free(*target_string);
653 *target_string = NULL;
654 return JB_ERR_MEMORY;
657 strcpy(new_string + old_len, text_to_append);
659 *target_string = new_string;
664 /*********************************************************************
666 * Function : string_join
668 * Description : Join two strings together. Frees BOTH the original
669 * strings. If either or both input strings are NULL,
670 * fails as if it had run out of memory.
672 * For comparison, string_append requires that the
673 * second string is non-NULL, and doesn't free it.
675 * Rationale: Too often, we want to do
676 * string_append(s, html_encode(s2)). That assert()s
677 * if s2 is NULL or if html_encode() runs out of memory.
678 * It also leaks memory. Proper checking is cumbersome.
679 * The solution: string_join(s, html_encode(s2)) is safe,
680 * and will free the memory allocated by html_encode().
683 * 1 : target_string = Pointer to old text that is to be
684 * extended. *target_string will be free()d by this
685 * routine. target_string must be non-NULL.
686 * 2 : text_to_append = Text to be appended to old.
688 * Returns : JB_ERR_OK on success, and sets *target_string
689 * to newly malloc'ed appended string. Caller
690 * must free(*target_string).
691 * JB_ERR_MEMORY on out-of-memory, or if
692 * *target_string or text_to_append is NULL. (In
693 * this case, frees *target_string and text_to_append,
694 * sets *target_string to NULL).
696 *********************************************************************/
697 jb_err string_join(char **target_string, char *text_to_append)
701 assert(target_string);
703 if (text_to_append == NULL)
705 freez(*target_string);
706 return JB_ERR_MEMORY;
709 err = string_append(target_string, text_to_append);
711 free(text_to_append);
717 /*********************************************************************
719 * Function : string_toupper
721 * Description : Produce a copy of string with all convertible
722 * characters converted to uppercase.
725 * 1 : string = string to convert
727 * Returns : Uppercase copy of string if possible,
728 * NULL on out-of-memory or if string was NULL.
730 *********************************************************************/
731 char *string_toupper(const char *string)
736 if (!string || ((result = (char *) zalloc(strlen(string) + 1)) == NULL))
746 *p++ = toupper(*q++);
754 /*********************************************************************
756 * Function : simplematch
758 * Description : String matching, with a (greedy) '*' wildcard that
759 * stands for zero or more arbitrary characters and
760 * character classes in [], which take both enumerations
764 * 1 : pattern = pattern for matching
765 * 2 : text = text to be matched
767 * Returns : 0 if match, else nonzero
769 *********************************************************************/
770 int simplematch(char *pattern, char *text)
772 unsigned char *pat = (unsigned char *) pattern;
773 unsigned char *txt = (unsigned char *) text;
774 unsigned char *fallback = pat;
777 unsigned char lastchar = 'a';
779 unsigned char charmap[32];
784 /* EOF pattern but !EOF text? */
797 /* '*' in the pattern? */
801 /* The pattern ends afterwards? Speed up the return. */
807 /* Else, set wildcard mode and remember position after '*' */
812 /* Character range specification? */
815 memset(charmap, '\0', sizeof(charmap));
817 while (*++pat != ']')
823 else if (*pat == '-')
825 if ((*++pat == ']') || *pat == '\0')
829 for(i = lastchar; i <= *pat; i++)
831 charmap[i / 8] |= (1 << (i % 8));
836 charmap[*pat / 8] |= (1 << (*pat % 8));
840 } /* -END- if Character range specification */
844 * Char match, or char range match?
848 || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))) )
858 * No match && no wildcard: No luck
862 else if (pat != fallback)
865 * Wildcard mode && nonmatch beyond fallback: Rewind pattern
872 /* Cut off extra '*'s */
873 if(*pat == '*') pat++;
875 /* If this is the pattern's end, fine! */
881 /*********************************************************************
885 * Description : Duplicate the first n characters of a string that may
886 * contain '\0' characters.
889 * 1 : string = string to be duplicated
890 * 2 : len = number of bytes to duplicate
892 * Returns : pointer to copy, or NULL if failiure
894 *********************************************************************/
895 char *bindup(const char *string, size_t len)
899 if (NULL == (duplicate = (char *)malloc(len)))
905 memcpy(duplicate, string, len);
913 /*********************************************************************
915 * Function : make_path
917 * Description : Takes a directory name and a file name, returns
918 * the complete path. Handles windows/unix differences.
919 * If the file name is already an absolute path, or if
920 * the directory name is NULL or empty, it returns
924 * 1 : dir: Name of directory or NULL for none.
925 * 2 : file: Name of file. Should not be NULL or empty.
927 * Returns : "dir/file" (Or on windows, "dir\file").
928 * It allocates the string on the heap. Caller frees.
929 * Returns NULL in error (i.e. NULL file or out of
932 *********************************************************************/
933 char * make_path(const char * dir, const char * file)
944 strncpy(path,dir+2,512);
948 strncpy(path,dir+1,512);
953 strncpy(path,dir,512);
959 if(AddPart(path,file,512))
965 #else /* ndef AMIGA */
967 if ((file == NULL) || (*file == '\0'))
969 return NULL; /* Error */
972 if ((dir == NULL) || (*dir == '\0') /* No directory specified */
973 #if defined(_WIN32) || defined(__OS2__)
974 || (*file == '\\') || (file[1] == ':') /* Absolute path (DOS) */
975 #else /* ifndef _WIN32 || __OS2__ */
976 || (*file == '/') /* Absolute path (U*ix) */
977 #endif /* ifndef _WIN32 || __OS2__ */
987 if ( *dir != '/' && basedir && *basedir )
989 path = malloc( strlen( basedir ) + strlen(dir) + strlen(file) + 3);
990 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
991 strcpy(path, basedir);
997 path = malloc(strlen(dir) + strlen(file) + 2);
998 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
1003 path = malloc(strlen(dir) + strlen(file) + 2);
1004 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
1007 #endif /* defined unix */
1009 #if defined(_WIN32) || defined(__OS2__)
1010 if(path[strlen(path)-1] != '\\')
1014 #else /* ifndef _WIN32 || __OS2__ */
1015 if(path[strlen(path)-1] != '/')
1019 #endif /* ifndef _WIN32 || __OS2__ */
1024 #endif /* ndef AMIGA */
1028 /*********************************************************************
1030 * Function : pick_from_range
1032 * Description : Pick a positive number out of a given range.
1033 * Should only be used if randomness would be nice,
1034 * but isn't really necessary.
1037 * 1 : range: Highest possible number to pick.
1039 * Returns : Picked number.
1041 *********************************************************************/
1043 long int pick_from_range(long int range)
1047 unsigned int weak_seed;
1049 weak_seed = (unsigned int)(time(NULL) | range);
1052 * Some rand implementations aren't that random and return mostly
1053 * lower numbers. Low entropy doesn't matter for the header times,
1054 * but higher "random" factors are prefered.
1056 number = (rand() * 12345) % (long int)(range + 1);
1057 /* Overflows don't matter either, positive numbers do. */
1063 number = random() % range + 1;
1064 #endif /* (ifndef HAVE_RANDOM) */
1070 /*********************************************************************
1074 * Description : libc replacement function for the inverse of gmtime()
1075 * Copyright (C) 2004 Free Software Foundation, Inc.
1076 * Code copied from GnuPG with minor style changes.
1079 * 1 : tm: Broken-down time struct.
1081 * Returns : tm converted into time_t seconds.
1083 *********************************************************************/
1085 time_t timegm(struct tm *tm)
1098 old_zone=malloc(3+strlen(zone)+1);
1101 strcpy(old_zone,"TZ=");
1102 strcat(old_zone,zone);
1108 #ifdef HAVE_UNSETENV
1117 #endif /* (ifndef HAVE_TIMEGM) */
1121 * What follows is a portable snprintf routine, written by Mark Martinec.
1122 * See: http://www.ijs.si/software/snprintf/
1123 * Anyone who needs it can add a define for themselves... so far, only
1124 * OS/2 (native) lacks snprintf.
1127 - a portable implementation of snprintf,
1128 including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
1130 snprintf is a routine to convert numeric and string arguments to
1131 formatted strings. It is similar to sprintf(3) provided in a system's
1132 C library, yet it requires an additional argument - the buffer size -
1133 and it guarantees never to store anything beyond the given buffer,
1134 regardless of the format or arguments to be formatted. Some newer
1135 operating systems do provide snprintf in their C library, but many do
1136 not or do provide an inadequate (slow or idiosyncratic) version, which
1137 calls for a portable implementation of this routine.
1141 Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
1142 Copyright © 1999, Mark Martinec
1148 #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
1149 #define PORTABLE_SNPRINTF_VERSION_MINOR 2
1151 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
1152 # if defined(NEED_SNPRINTF_ONLY)
1153 # undef NEED_SNPRINTF_ONLY
1155 # if !defined(PREFER_PORTABLE_SNPRINTF)
1156 # define PREFER_PORTABLE_SNPRINTF
1160 #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
1161 #define SOLARIS_COMPATIBLE
1164 #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1165 #define HPUX_COMPATIBLE
1168 #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
1169 #define DIGITAL_UNIX_COMPATIBLE
1172 #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
1173 #define PERL_COMPATIBLE
1176 #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
1177 #define LINUX_COMPATIBLE
1180 #include <sys/types.h>
1191 #define isdigit(c) ((c) >= '0' && (c) <= '9')
1193 /* For copying strings longer or equal to 'breakeven_point'
1194 * it is more efficient to call memcpy() than to do it inline.
1195 * The value depends mostly on the processor architecture,
1196 * but also on the compiler and its optimization capabilities.
1197 * The value is not critical, some small value greater than zero
1198 * will be just fine if you don't care to squeeze every drop
1199 * of performance out of the code.
1201 * Small values favor memcpy, large values favor inline code.
1203 #if defined(__alpha__) || defined(__alpha)
1204 # define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */
1206 #if defined(__i386__) || defined(__i386)
1207 # define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */
1210 # define breakeven_point 10 /* HP-PA - gcc */
1212 #if defined(__sparc__) || defined(__sparc)
1213 # define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */
1216 /* some other values of possible interest: */
1217 /* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
1218 /* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
1220 #ifndef breakeven_point
1221 # define breakeven_point 6 /* some reasonable one-size-fits-all value */
1224 #define fast_memcpy(d,s,n) \
1225 { register size_t nn = (size_t)(n); \
1226 if (nn >= breakeven_point) memcpy((d), (s), nn); \
1227 else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1228 register char *dd; register const char *ss; \
1229 for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
1231 #define fast_memset(d,c,n) \
1232 { register size_t nn = (size_t)(n); \
1233 if (nn >= breakeven_point) memset((d), (int)(c), nn); \
1234 else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1235 register char *dd; register const int cc=(int)(c); \
1236 for (dd=(d); nn>0; nn--) *dd++ = cc; } }
1240 #if defined(NEED_ASPRINTF)
1241 int asprintf (char **ptr, const char *fmt, /*args*/ ...);
1243 #if defined(NEED_VASPRINTF)
1244 int vasprintf (char **ptr, const char *fmt, va_list ap);
1246 #if defined(NEED_ASNPRINTF)
1247 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
1249 #if defined(NEED_VASNPRINTF)
1250 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
1253 #if defined(HAVE_SNPRINTF)
1254 /* declare our portable snprintf routine under name portable_snprintf */
1255 /* declare our portable vsnprintf routine under name portable_vsnprintf */
1257 /* declare our portable routines under names snprintf and vsnprintf */
1258 #define portable_snprintf snprintf
1259 #if !defined(NEED_SNPRINTF_ONLY)
1260 #define portable_vsnprintf vsnprintf
1264 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1265 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
1266 #if !defined(NEED_SNPRINTF_ONLY)
1267 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
1273 static char credits[] = "\n\
1274 @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
1275 @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
1276 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
1278 #if defined(NEED_ASPRINTF)
1279 int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
1285 va_start(ap, fmt); /* measure the required size */
1286 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1288 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1289 *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1290 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1294 str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1296 assert(str_l2 == str_l);
1302 #if defined(NEED_VASPRINTF)
1303 int vasprintf(char **ptr, const char *fmt, va_list ap) {
1309 va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
1310 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1313 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1314 *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1315 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1317 int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1318 assert(str_l2 == str_l);
1324 #if defined(NEED_ASNPRINTF)
1325 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
1330 va_start(ap, fmt); /* measure the required size */
1331 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1333 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1334 if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
1335 /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1336 if (str_m == 0) { /* not interested in resulting string, just return size */
1338 *ptr = (char *) malloc(str_m);
1339 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1343 str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1345 assert(str_l2 == str_l);
1352 #if defined(NEED_VASNPRINTF)
1353 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
1358 va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
1359 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1362 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1363 if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
1364 /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1365 if (str_m == 0) { /* not interested in resulting string, just return size */
1367 *ptr = (char *) malloc(str_m);
1368 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1370 int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1371 assert(str_l2 == str_l);
1379 * If the system does have snprintf and the portable routine is not
1380 * specifically required, this module produces no code for snprintf/vsnprintf.
1382 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1384 #if !defined(NEED_SNPRINTF_ONLY)
1385 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1390 str_l = portable_vsnprintf(str, str_m, fmt, ap);
1396 #if defined(NEED_SNPRINTF_ONLY)
1397 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1399 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
1402 #if defined(NEED_SNPRINTF_ONLY)
1406 const char *p = fmt;
1408 /* In contrast with POSIX, the ISO C99 now says
1409 * that str can be NULL and str_m can be 0.
1410 * This is more useful than the old: if (str_m < 1) return -1; */
1412 #if defined(NEED_SNPRINTF_ONLY)
1418 /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */
1419 /* but the following code achieves better performance for cases
1420 * where format string is long and contains few conversions */
1421 const char *q = strchr(p+1,'%');
1422 size_t n = !q ? strlen(p) : (q-p);
1423 if (str_l < str_m) {
1424 size_t avail = str_m-str_l;
1425 fast_memcpy(str+str_l, p, (n>avail?avail:n));
1429 const char *starting_p;
1430 size_t min_field_width = 0, precision = 0;
1431 int zero_padding = 0, precision_specified = 0, justify_left = 0;
1432 int alternate_form = 0, force_sign = 0;
1433 int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
1434 the ' ' flag should be ignored. */
1435 char length_modifier = '\0'; /* allowed values: \0, h, l, L */
1436 char tmp[32];/* temporary buffer for simple numeric->string conversion */
1438 const char *str_arg; /* string address in case of string argument */
1439 size_t str_arg_l; /* natural field width of arg without padding
1441 unsigned char uchar_arg;
1442 /* unsigned char argument value - only defined for c conversion.
1443 N.B. standard explicitly states the char argument for
1444 the c conversion is unsigned */
1446 size_t number_of_zeros_to_pad = 0;
1447 /* number of zeros to be inserted for numeric conversions
1448 as required by the precision or minimal field width */
1450 size_t zero_padding_insertion_ind = 0;
1451 /* index into tmp where zero padding is to be inserted */
1453 char fmt_spec = '\0';
1454 /* current conversion specifier character */
1456 str_arg = credits;/* just to make compiler happy (defined but not used)*/
1458 starting_p = p; p++; /* skip '%' */
1460 while (*p == '0' || *p == '-' || *p == '+' ||
1461 *p == ' ' || *p == '#' || *p == '\'') {
1463 case '0': zero_padding = 1; break;
1464 case '-': justify_left = 1; break;
1465 case '+': force_sign = 1; space_for_positive = 0; break;
1466 case ' ': force_sign = 1;
1467 /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
1468 #ifdef PERL_COMPATIBLE
1469 /* ... but in Perl the last of ' ' and '+' applies */
1470 space_for_positive = 1;
1473 case '#': alternate_form = 1; break;
1478 /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
1480 /* parse field width */
1483 p++; j = va_arg(ap, int);
1484 if (j >= 0) min_field_width = j;
1485 else { min_field_width = -j; justify_left = 1; }
1486 } else if (isdigit((int)(*p))) {
1487 /* size_t could be wider than unsigned int;
1488 make sure we treat argument like common implementations do */
1489 unsigned int uj = *p++ - '0';
1490 while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1491 min_field_width = uj;
1493 /* parse precision */
1495 p++; precision_specified = 1;
1497 int j = va_arg(ap, int);
1499 if (j >= 0) precision = j;
1501 precision_specified = 0; precision = 0;
1503 * Solaris 2.6 man page claims that in this case the precision
1504 * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page
1505 * claim that this case should be treated as unspecified precision,
1506 * which is what we do here.
1509 } else if (isdigit((int)(*p))) {
1510 /* size_t could be wider than unsigned int;
1511 make sure we treat argument like common implementations do */
1512 unsigned int uj = *p++ - '0';
1513 while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1517 /* parse 'h', 'l' and 'll' length modifiers */
1518 if (*p == 'h' || *p == 'l') {
1519 length_modifier = *p; p++;
1520 if (length_modifier == 'l' && *p == 'l') { /* double l = long long */
1521 #ifdef SNPRINTF_LONGLONG_SUPPORT
1522 length_modifier = '2'; /* double l encoded as '2' */
1524 length_modifier = 'l'; /* treat it as a single 'l' */
1530 /* common synonyms: */
1532 case 'i': fmt_spec = 'd'; break;
1533 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
1534 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
1535 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
1538 /* get parameter value, do initial processing */
1540 case '%': /* % behaves similar to 's' regarding flags and field widths */
1541 case 'c': /* c behaves similar to 's' regarding flags and field widths */
1543 length_modifier = '\0'; /* wint_t and wchar_t not supported */
1544 /* the result of zero padding flag with non-numeric conversion specifier*/
1545 /* is undefined. Solaris and HPUX 10 does zero padding in this case, */
1546 /* Digital Unix and Linux does not. */
1547 #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1548 zero_padding = 0; /* turn zero padding off for string conversions */
1555 int j = va_arg(ap, int);
1556 uchar_arg = (unsigned char) j; /* standard demands unsigned char */
1557 str_arg = (const char *) &uchar_arg;
1561 str_arg = va_arg(ap, const char *);
1562 if (!str_arg) str_arg_l = 0;
1563 /* make sure not to address string beyond the specified precision !!! */
1564 else if (!precision_specified) str_arg_l = strlen(str_arg);
1565 /* truncate string if necessary as requested by precision */
1566 else if (precision == 0) str_arg_l = 0;
1568 /* memchr on HP does not like n > 2^31 !!! */
1569 const char *q = memchr(str_arg, '\0',
1570 precision <= 0x7fffffff ? precision : 0x7fffffff);
1571 str_arg_l = !q ? precision : (q-str_arg);
1577 case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
1578 /* NOTE: the u, o, x, X and p conversion specifiers imply
1579 the value is unsigned; d implies a signed value */
1582 /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
1583 +1 if greater than zero (or nonzero for unsigned arguments),
1584 -1 if negative (unsigned argument is never negative) */
1586 int int_arg = 0; unsigned int uint_arg = 0;
1587 /* only defined for length modifier h, or for no length modifiers */
1589 long int long_arg = 0; unsigned long int ulong_arg = 0;
1590 /* only defined for length modifier l */
1592 void *ptr_arg = NULL;
1593 /* pointer argument value -only defined for p conversion */
1595 #ifdef SNPRINTF_LONGLONG_SUPPORT
1596 long long int long_long_arg = 0;
1597 unsigned long long int ulong_long_arg = 0;
1598 /* only defined for length modifier ll */
1600 if (fmt_spec == 'p') {
1601 /* HPUX 10: An l, h, ll or L before any other conversion character
1602 * (other than d, i, u, o, x, or X) is ignored.
1604 * not specified, but seems to behave as HPUX does.
1605 * Solaris: If an h, l, or L appears before any other conversion
1606 * specifier (other than d, i, u, o, x, or X), the behavior
1607 * is undefined. (Actually %hp converts only 16-bits of address
1608 * and %llp treats address as 64-bit data which is incompatible
1609 * with (void *) argument on a 32-bit system).
1611 #ifdef SOLARIS_COMPATIBLE
1612 # ifdef SOLARIS_BUG_COMPATIBLE
1613 /* keep length modifiers even if it represents 'll' */
1615 if (length_modifier == '2') length_modifier = '\0';
1618 length_modifier = '\0';
1620 ptr_arg = va_arg(ap, void *);
1621 if (ptr_arg != NULL) arg_sign = 1;
1622 } else if (fmt_spec == 'd') { /* signed */
1623 switch (length_modifier) {
1626 /* It is non-portable to specify a second argument of char or short
1627 * to va_arg, because arguments seen by the called function
1628 * are not char or short. C converts char and short arguments
1629 * to int before passing them to a function.
1631 int_arg = va_arg(ap, int);
1632 if (int_arg > 0) arg_sign = 1;
1633 else if (int_arg < 0) arg_sign = -1;
1636 long_arg = va_arg(ap, long int);
1637 if (long_arg > 0) arg_sign = 1;
1638 else if (long_arg < 0) arg_sign = -1;
1640 #ifdef SNPRINTF_LONGLONG_SUPPORT
1642 long_long_arg = va_arg(ap, long long int);
1643 if (long_long_arg > 0) arg_sign = 1;
1644 else if (long_long_arg < 0) arg_sign = -1;
1648 } else { /* unsigned */
1649 switch (length_modifier) {
1652 uint_arg = va_arg(ap, unsigned int);
1653 if (uint_arg) arg_sign = 1;
1656 ulong_arg = va_arg(ap, unsigned long int);
1657 if (ulong_arg) arg_sign = 1;
1659 #ifdef SNPRINTF_LONGLONG_SUPPORT
1661 ulong_long_arg = va_arg(ap, unsigned long long int);
1662 if (ulong_long_arg) arg_sign = 1;
1667 str_arg = tmp; str_arg_l = 0;
1669 * For d, i, u, o, x, and X conversions, if precision is specified,
1670 * the '0' flag should be ignored. This is so with Solaris 2.6,
1671 * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
1673 #ifndef PERL_COMPATIBLE
1674 if (precision_specified) zero_padding = 0;
1676 if (fmt_spec == 'd') {
1677 if (force_sign && arg_sign >= 0)
1678 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1679 /* leave negative numbers for sprintf to handle,
1680 to avoid handling tricky cases like (short int)(-32768) */
1681 #ifdef LINUX_COMPATIBLE
1682 } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
1683 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1685 } else if (alternate_form) {
1686 if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
1687 { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
1688 /* alternate form should have no effect for p conversion, but ... */
1689 #ifdef HPUX_COMPATIBLE
1690 else if (fmt_spec == 'p'
1691 /* HPUX 10: for an alternate form of p conversion,
1692 * a nonzero result is prefixed by 0x. */
1693 #ifndef HPUX_BUG_COMPATIBLE
1694 /* Actually it uses 0x prefix even for a zero value. */
1697 ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
1700 zero_padding_insertion_ind = str_arg_l;
1701 if (!precision_specified) precision = 1; /* default precision is 1 */
1702 if (precision == 0 && arg_sign == 0
1703 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1705 /* HPUX 10 man page claims: With conversion character p the result of
1706 * converting a zero value with a precision of zero is a null string.
1707 * Actually HP returns all zeroes, and Linux returns "(nil)". */
1710 /* converted to null string */
1711 /* When zero value is formatted with an explicit precision 0,
1712 the resulting formatted string is empty (d, i, u, o, x, X, p). */
1714 char f[5]; int f_l = 0;
1715 f[f_l++] = '%'; /* construct a simple format string for sprintf */
1716 if (!length_modifier) { }
1717 else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
1718 else f[f_l++] = length_modifier;
1719 f[f_l++] = fmt_spec; f[f_l++] = '\0';
1720 if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
1721 else if (fmt_spec == 'd') { /* signed */
1722 switch (length_modifier) {
1724 case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break;
1725 case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
1726 #ifdef SNPRINTF_LONGLONG_SUPPORT
1727 case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
1730 } else { /* unsigned */
1731 switch (length_modifier) {
1733 case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break;
1734 case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
1735 #ifdef SNPRINTF_LONGLONG_SUPPORT
1736 case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
1740 /* include the optional minus sign and possible "0x"
1741 in the region before the zero padding insertion point */
1742 if (zero_padding_insertion_ind < str_arg_l &&
1743 tmp[zero_padding_insertion_ind] == '-') {
1744 zero_padding_insertion_ind++;
1746 if (zero_padding_insertion_ind+1 < str_arg_l &&
1747 tmp[zero_padding_insertion_ind] == '0' &&
1748 (tmp[zero_padding_insertion_ind+1] == 'x' ||
1749 tmp[zero_padding_insertion_ind+1] == 'X') ) {
1750 zero_padding_insertion_ind += 2;
1753 { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
1754 if (alternate_form && fmt_spec == 'o'
1755 #ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */
1758 #ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */
1760 /* unless zero is already the first character */
1761 && !(zero_padding_insertion_ind < str_arg_l
1762 && tmp[zero_padding_insertion_ind] == '0')
1764 ) { /* assure leading zero for alternate-form octal numbers */
1765 if (!precision_specified || precision < num_of_digits+1) {
1766 /* precision is increased to force the first character to be zero,
1767 except if a zero value is formatted with an explicit precision
1769 precision = num_of_digits+1; precision_specified = 1;
1772 /* zero padding to specified precision? */
1773 if (num_of_digits < precision)
1774 number_of_zeros_to_pad = precision - num_of_digits;
1776 /* zero padding to specified minimal field width? */
1777 if (!justify_left && zero_padding) {
1778 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1779 if (n > 0) number_of_zeros_to_pad += n;
1783 default: /* unrecognized conversion specifier, keep format string as-is*/
1784 zero_padding = 0; /* turn zero padding off for non-numeric convers. */
1785 #ifndef DIGITAL_UNIX_COMPATIBLE
1786 justify_left = 1; min_field_width = 0; /* reset flags */
1788 #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1789 /* keep the entire format string unchanged */
1790 str_arg = starting_p; str_arg_l = p - starting_p;
1791 /* well, not exactly so for Linux, which does something inbetween,
1792 * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */
1794 /* discard the unrecognized conversion, just keep *
1795 * the unrecognized conversion character */
1796 str_arg = p; str_arg_l = 0;
1798 if (*p) str_arg_l++; /* include invalid conversion specifier unchanged
1799 if not at end-of-string */
1802 if (*p) p++; /* step over the just processed conversion specifier */
1803 /* insert padding to the left as requested by min_field_width;
1804 this does not include the zero padding in case of numerical conversions*/
1805 if (!justify_left) { /* left padding with blank or zero */
1806 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1808 if (str_l < str_m) {
1809 size_t avail = str_m-str_l;
1810 fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
1815 /* zero padding as requested by the precision or by the minimal field width
1816 * for numeric conversions required? */
1817 if (number_of_zeros_to_pad <= 0) {
1818 /* will not copy first part of numeric right now, *
1819 * force it to be copied later in its entirety */
1820 zero_padding_insertion_ind = 0;
1822 /* insert first part of numerics (sign or '0x') before zero padding */
1823 int n = zero_padding_insertion_ind;
1825 if (str_l < str_m) {
1826 size_t avail = str_m-str_l;
1827 fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
1831 /* insert zero padding as requested by the precision or min field width */
1832 n = number_of_zeros_to_pad;
1834 if (str_l < str_m) {
1835 size_t avail = str_m-str_l;
1836 fast_memset(str+str_l, '0', (n>avail?avail:n));
1841 /* insert formatted string
1842 * (or as-is conversion specifier for unknown conversions) */
1843 { int n = str_arg_l - zero_padding_insertion_ind;
1845 if (str_l < str_m) {
1846 size_t avail = str_m-str_l;
1847 fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
1853 /* insert right padding */
1854 if (justify_left) { /* right blank padding to the field width */
1855 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1857 if (str_l < str_m) {
1858 size_t avail = str_m-str_l;
1859 fast_memset(str+str_l, ' ', (n>avail?avail:n));
1866 #if defined(NEED_SNPRINTF_ONLY)
1869 if (str_m > 0) { /* make sure the string is null-terminated
1870 even at the expense of overwriting the last character
1871 (shouldn't happen, but just in case) */
1872 str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
1874 /* Return the number of characters formatted (excluding trailing null
1875 * character), that is, the number of characters that would have been
1876 * written to the buffer if it were large enough.
1878 * The value of str_l should be returned, but str_l is of unsigned type
1879 * size_t, and snprintf is int, possibly leading to an undetected
1880 * integer overflow, resulting in a negative return value, which is illegal.
1881 * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
1882 * Should errno be set to EOVERFLOW and EOF returned in this case???
1887 #endif /* __OS2__ */