1 const char miscutil_rcs[] = "$Id: miscutil.c,v 1.34 2002/03/24 13:25:43 swa 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.34 2002/03/24 13:25:43 swa
40 * name change related issues
42 * Revision 1.33 2002/03/07 03:46:53 oes
43 * Fixed compiler warnings etc
45 * Revision 1.32 2002/03/06 23:02:57 jongfoster
48 * Revision 1.31 2002/03/05 04:52:42 oes
49 * Deleted non-errlog debugging code
51 * Revision 1.30 2002/03/04 18:27:42 oes
52 * - Deleted deletePidFile
53 * - Made write_pid_file use the --pidfile option value
54 * (or no PID file, if the option was absent)
55 * - Played styleguide police
57 * Revision 1.29 2002/03/04 02:08:02 david__schmidt
58 * Enable web editing of actions file on OS/2 (it had been broken all this time!)
60 * Revision 1.28 2002/03/03 09:18:03 joergs
61 * Made jumbjuster work on AmigaOS again.
63 * Revision 1.27 2002/01/21 00:52:32 jongfoster
64 * Adding string_join()
66 * Revision 1.26 2001/12/30 14:07:32 steudten
67 * - Add signal handling (unix)
68 * - Add SIGHUP handler (unix)
69 * - Add creation of pidfile (unix)
70 * - Add action 'top' in rc file (RH)
71 * - Add entry 'SIGNALS' to manpage
72 * - Add exit message to logfile (unix)
74 * Revision 1.25 2001/11/13 00:16:38 jongfoster
75 * Replacing references to malloc.h with the standard stdlib.h
76 * (See ANSI or K&R 2nd Ed)
78 * Revision 1.24 2001/11/05 21:41:43 steudten
79 * Add changes to be a real daemon just for unix os.
80 * (change cwd to /, detach from controlling tty, set
81 * process group and session leader to the own process.
83 * Add some fatal-error log message for failed malloc().
84 * Add '-d' if compiled with 'configure --with-debug' to
85 * enable debug output.
87 * Revision 1.23 2001/10/29 03:48:10 david__schmidt
88 * OS/2 native needed a snprintf() routine. Added one to miscutil, brackedted
89 * by and __OS2__ ifdef.
91 * Revision 1.22 2001/10/26 17:39:38 oes
92 * Moved ijb_isspace and ijb_tolower to project.h
94 * Revision 1.21 2001/10/23 21:27:50 jongfoster
95 * Standardising error codes in string_append
96 * make_path() no longer adds '\\' if the dir already ends in '\\' (this
97 * is just copying a UNIX-specific fix to the Windows-specific part)
99 * Revision 1.20 2001/10/22 15:33:56 david__schmidt
100 * Special-cased OS/2 out of the Netscape-abort-on-404-in-js problem in
101 * filters.c. Added a FIXME in front of the offending code. I'll gladly
102 * put in a better/more robust fix for all parties if one is presented...
103 * It seems that just returning 200 instead of 404 would pretty much fix
104 * it for everyone, but I don't know all the history of the problem.
106 * Revision 1.19 2001/10/14 22:02:57 jongfoster
107 * New function string_append() which is like strsav(), but running
108 * out of memory isn't automatically FATAL.
110 * Revision 1.18 2001/09/20 13:33:43 steudten
112 * change long to int as return value in hash_string(). Remember the wraparound
113 * for int = long = sizeof(4) - thats maybe not what we want.
115 * Revision 1.17 2001/09/13 20:51:29 jongfoster
116 * Fixing potential problems with characters >=128 in simplematch()
117 * This was also a compiler warning.
119 * Revision 1.16 2001/09/10 10:56:59 oes
120 * Silenced compiler warnings
122 * Revision 1.15 2001/07/13 14:02:24 oes
123 * Removed vim-settings
125 * Revision 1.14 2001/06/29 21:45:41 oes
126 * Indentation, CRLF->LF, Tab-> Space
128 * Revision 1.13 2001/06/29 13:32:14 oes
129 * Removed logentry from cancelled commit
131 * Revision 1.12 2001/06/09 10:55:28 jongfoster
132 * Changing BUFSIZ ==> BUFFER_SIZE
134 * Revision 1.11 2001/06/07 23:09:19 jongfoster
135 * Cosmetic indentation changes.
137 * Revision 1.10 2001/06/07 14:51:38 joergs
138 * make_path() no longer adds '/' if the dir already ends in '/'.
140 * Revision 1.9 2001/06/07 14:43:17 swa
141 * slight mistake in make_path, unix path style is /.
143 * Revision 1.8 2001/06/05 22:32:01 jongfoster
144 * New function make_path() to splice directory and file names together.
146 * Revision 1.7 2001/06/03 19:12:30 oes
147 * introduced bindup()
149 * Revision 1.6 2001/06/01 18:14:49 jongfoster
150 * Changing the calls to strerr() to check HAVE_STRERR (which is defined
151 * in config.h if appropriate) rather than the NO_STRERR macro.
153 * Revision 1.5 2001/06/01 10:31:51 oes
154 * Added character class matching to trivimatch; renamed to simplematch
156 * Revision 1.4 2001/05/31 17:32:31 oes
158 * - Enhanced domain part globbing with infix and prefix asterisk
159 * matching and optional unanchored operation
161 * Revision 1.3 2001/05/29 23:10:09 oes
164 * - Introduced chomp()
165 * - Moved strsav() from showargs to miscutil
167 * Revision 1.2 2001/05/29 09:50:24 jongfoster
168 * Unified blocklist/imagelist/permissionslist.
169 * File format is still under discussion, but the internal changes
172 * Also modified interceptor behaviour:
173 * - We now intercept all URLs beginning with one of the following
174 * prefixes (and *only* these prefixes):
176 * * http://ijbswa.sf.net/config/
177 * * http://ijbswa.sourceforge.net/config/
178 * - New interceptors "home page" - go to http://i.j.b/ to see it.
179 * - Internal changes so that intercepted and fast redirect pages
180 * are not replaced with an image.
181 * - Interceptors now have the option to send a binary page direct
182 * to the client. (i.e. ijb-send-banner uses this)
183 * - Implemented show-url-info interceptor. (Which is why I needed
184 * the above interceptors changes - a typical URL is
185 * "http://i.j.b/show-url-info?url=www.somesite.com/banner.gif".
186 * The previous mechanism would not have intercepted that, and
187 * if it had been intercepted then it then it would have replaced
190 * Revision 1.1.1.1 2001/05/15 13:59:00 oes
191 * Initial import of version 2.9.3 source tree
194 *********************************************************************/
200 #include <sys/types.h>
202 #if !defined(_WIN32) && !defined(__OS2__)
204 #endif /* #if !defined(_WIN32) && !defined(__OS2__) */
210 #include "miscutil.h"
214 const char miscutil_h_rcs[] = MISCUTIL_H_VERSION;
216 /*********************************************************************
220 * Description : Malloc some memory and set it to '\0'.
221 * The way calloc() ought to be -acjc
224 * 1 : size = Size of memory chunk to return.
226 * Returns : Pointer to newly malloc'd memory chunk.
228 *********************************************************************/
229 void *zalloc(size_t size)
233 if ((ret = (void *)malloc(size)) != NULL)
235 memset(ret, 0, size);
244 /*********************************************************************
246 * Function : write_pid_file
248 * Description : Writes a pid file with the pid of the main process
254 *********************************************************************/
255 void write_pid_file(void)
260 * If no --pidfile option was given,
261 * we can live without one.
263 if (pidfile == NULL) return;
265 if ((fp = fopen(pidfile, "w")) == NULL)
267 log_error(LOG_LEVEL_INFO, "can't open pidfile '%s': %E", pidfile);
271 fprintf(fp, "%u\n", (unsigned int) getpid());
277 #endif /* def unix */
280 /*********************************************************************
282 * Function : hash_string
284 * Description : Take a string and compute a (hopefuly) unique numeric
285 * integer value. This has several uses, but being able
286 * to "switch" a string the one of my favorites.
289 * 1 : s : string to be hashed.
291 * Returns : an unsigned long variable with the hashed value.
293 *********************************************************************/
294 unsigned int hash_string( const char* s )
309 /*********************************************************************
313 * Description : For some reason (which is beyond me), gcc and WIN32
314 * don't like strdup. When a "free" is executed on a
315 * strdup'd ptr, it can at times freez up! So I just
316 * replaced it and problem was solved.
319 * 1 : s = string to duplicate
321 * Returns : Pointer to newly malloc'ed copy of the string.
323 *********************************************************************/
324 char *strdup( const char *s )
326 char * result = (char *)malloc( strlen(s)+1 );
336 #endif /* def __MINGW32__ */
340 /*********************************************************************
342 * Function : safe_strerror
344 * Description : Variant of the library routine strerror() which will
345 * work on systems without the library routine, and
346 * which should never return NULL.
349 * 1 : err = the `errno' of the last operation.
351 * Returns : An "English" string of the last `errno'. Allocated
352 * with strdup(), so caller frees. May be NULL if the
353 * system is out of memory.
355 *********************************************************************/
356 char *safe_strerror(int err)
359 char buf[BUFFER_SIZE];
364 #endif /* HAVE_STRERROR */
368 sprintf(buf, "(errno = %d)", err);
377 /*********************************************************************
379 * Function : strcmpic
381 * Description : Case insensitive string comparison
384 * 1 : s1 = string 1 to compare
385 * 2 : s2 = string 2 to compare
387 * Returns : 0 if s1==s2, Negative if s1<s2, Positive if s1>s2
389 *********************************************************************/
390 int strcmpic(const char *s1, const char *s2)
394 if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
400 return(ijb_tolower(*s1) - ijb_tolower(*s2));
405 /*********************************************************************
407 * Function : strncmpic
409 * Description : Case insensitive string comparison (upto n characters)
412 * 1 : s1 = string 1 to compare
413 * 2 : s2 = string 2 to compare
414 * 3 : n = maximum characters to compare
416 * Returns : 0 if s1==s2, Negative if s1<s2, Positive if s1>s2
418 *********************************************************************/
419 int strncmpic(const char *s1, const char *s2, size_t n)
421 if (n <= 0) return(0);
425 if ( ( *s1 != *s2 ) && ( ijb_tolower(*s1) != ijb_tolower(*s2) ) )
434 return(ijb_tolower(*s1) - ijb_tolower(*s2));
439 /*********************************************************************
443 * Description : In-situ-eliminate all leading and trailing whitespace
447 * 1 : s : string to be chomped.
449 * Returns : chomped string
451 *********************************************************************/
452 char *chomp(char *string)
457 * strip trailing whitespace
459 p = string + strlen(string);
460 while (p > string && ijb_isspace(*(p-1)))
467 * find end of leading whitespace
470 while (*q && ijb_isspace(*q))
476 * if there was any, move the rest forwards
491 /*********************************************************************
495 * Description : Reallocate "old" and append text to it. This makes
496 * it easier to append to malloc'd strings.
497 * Running out of memory is a FATAL error.
500 * 1 : old = Old text that is to be extended. Will be
501 * free()d by this routine. May be NULL.
502 * 2 : text_to_append = Text to be appended to old.
505 * Returns : Pointer to newly malloc'ed appended string.
506 * If there is no text to append, return old. Caller
509 *********************************************************************/
510 char *strsav(char *old, const char *text_to_append)
512 size_t old_len, new_len = 0;
515 if ((text_to_append == NULL) || (*text_to_append == '\0'))
522 if ((p = strdup(text_to_append)) == NULL)
524 log_error(LOG_LEVEL_FATAL, "strdup() failed!");
525 /* Never get here - LOG_LEVEL_FATAL causes program exit */
530 old_len = strlen(old);
531 new_len = old_len + strlen(text_to_append) + 1;
533 if ((p = realloc(old, new_len)) == NULL)
535 log_error(LOG_LEVEL_FATAL, "realloc(%d) bytes failed!", new_len);
536 /* Never get here - LOG_LEVEL_FATAL causes program exit */
539 strcpy(p + old_len, text_to_append);
544 /*********************************************************************
546 * Function : string_append
548 * Description : Reallocate target_string and append text to it.
549 * This makes it easier to append to malloc'd strings.
550 * This is similar to the (removed) strsav(), but
551 * running out of memory isn't catastrophic.
555 * The following style provides sufficient error
556 * checking for this routine, with minimal clutter
557 * in the source code. It is recommended if you
558 * have many calls to this function:
560 * char * s = strdup(...); // don't check for error
561 * string_append(&s, ...); // don't check for error
562 * string_append(&s, ...); // don't check for error
563 * string_append(&s, ...); // don't check for error
564 * if (NULL == s) { ... handle error ... }
568 * char * s = strdup(...); // don't check for error
569 * string_append(&s, ...); // don't check for error
570 * string_append(&s, ...); // don't check for error
571 * if (string_append(&s, ...)) {... handle error ...}
574 * 1 : target_string = Pointer to old text that is to be
575 * extended. *target_string will be free()d by this
576 * routine. target_string must be non-NULL.
577 * If *target_string is NULL, this routine will
578 * do nothing and return with an error - this allows
579 * you to make many calls to this routine and only
580 * check for errors after the last one.
581 * 2 : text_to_append = Text to be appended to old.
584 * Returns : JB_ERR_OK on success, and sets *target_string
585 * to newly malloc'ed appended string. Caller
586 * must free(*target_string).
587 * JB_ERR_MEMORY on out-of-memory. (And free()s
588 * *target_string and sets it to NULL).
589 * JB_ERR_MEMORY if *target_string is NULL.
591 *********************************************************************/
592 jb_err string_append(char **target_string, const char *text_to_append)
597 assert(target_string);
598 assert(text_to_append);
600 if (*target_string == NULL)
602 return JB_ERR_MEMORY;
605 if (*text_to_append == '\0')
610 old_len = strlen(*target_string);
612 if (NULL == (new_string = realloc(*target_string,
613 strlen(text_to_append) + old_len + 1)))
615 free(*target_string);
617 *target_string = NULL;
618 return JB_ERR_MEMORY;
621 strcpy(new_string + old_len, text_to_append);
623 *target_string = new_string;
628 /*********************************************************************
630 * Function : string_join
632 * Description : Join two strings together. Frees BOTH the original
633 * strings. If either or both input strings are NULL,
634 * fails as if it had run out of memory.
636 * For comparison, string_append requires that the
637 * second string is non-NULL, and doesn't free it.
639 * Rationale: Too often, we want to do
640 * string_append(s, html_encode(s2)). That assert()s
641 * if s2 is NULL or if html_encode() runs out of memory.
642 * It also leaks memory. Proper checking is cumbersome.
643 * The solution: string_join(s, html_encode(s2)) is safe,
644 * and will free the memory allocated by html_encode().
647 * 1 : target_string = Pointer to old text that is to be
648 * extended. *target_string will be free()d by this
649 * routine. target_string must be non-NULL.
650 * 2 : text_to_append = Text to be appended to old.
652 * Returns : JB_ERR_OK on success, and sets *target_string
653 * to newly malloc'ed appended string. Caller
654 * must free(*target_string).
655 * JB_ERR_MEMORY on out-of-memory, or if
656 * *target_string or text_to_append is NULL. (In
657 * this case, frees *target_string and text_to_append,
658 * sets *target_string to NULL).
660 *********************************************************************/
661 jb_err string_join(char **target_string, char *text_to_append)
665 assert(target_string);
667 if (text_to_append == NULL)
669 freez(*target_string);
670 return JB_ERR_MEMORY;
673 err = string_append(target_string, text_to_append);
675 free(text_to_append);
681 /*********************************************************************
683 * Function : simplematch
685 * Description : String matching, with a (greedy) '*' wildcard that
686 * stands for zero or more arbitrary characters and
687 * character classes in [], which take both enumerations
691 * 1 : pattern = pattern for matching
692 * 2 : text = text to be matched
694 * Returns : 0 if match, else nonzero
696 *********************************************************************/
697 int simplematch(char *pattern, char *text)
699 unsigned char *pat = (unsigned char *) pattern;
700 unsigned char *txt = (unsigned char *) text;
701 unsigned char *fallback = pat;
704 unsigned char lastchar = 'a';
706 unsigned char charmap[32];
712 /* EOF pattern but !EOF text? */
718 /* '*' in the pattern? */
722 /* The pattern ends afterwards? Speed up the return. */
728 /* Else, set wildcard mode and remember position after '*' */
733 /* Character range specification? */
736 memset(charmap, '\0', sizeof(charmap));
738 while (*++pat != ']')
744 else if (*pat == '-')
746 if ((*++pat == ']') || *pat == '\0')
750 for(i = lastchar; i <= *pat; i++)
752 charmap[i / 8] |= (1 << (i % 8));
757 charmap[*pat / 8] |= (1 << (*pat % 8));
761 } /* -END- if Character range specification */
764 /* Compare: Char match, or char range match*/
766 || ((*pat == ']') && (charmap[*txt / 8] & (1 << (*txt % 8)))) )
768 /* Sucess, go ahead */
773 /* In wildcard mode, just try again after failiure */
788 /* Cut off extra '*'s */
789 if(*pat == '*') pat++;
791 /* If this is the pattern's end, fine! */
797 /*********************************************************************
801 * Description : Duplicate the first n characters of a string that may
802 * contain '\0' characters.
805 * 1 : string = string to be duplicated
806 * 2 : len = number of bytes to duplicate
808 * Returns : pointer to copy, or NULL if failiure
810 *********************************************************************/
811 char *bindup(const char *string, size_t len)
815 if (NULL == (duplicate = (char *)malloc(len)))
821 memcpy(duplicate, string, len);
829 /*********************************************************************
831 * Function : make_path
833 * Description : Takes a directory name and a file name, returns
834 * the complete path. Handles windows/unix differences.
835 * If the file name is already an absolute path, or if
836 * the directory name is NULL or empty, it returns
840 * 1 : dir: Name of directory or NULL for none.
841 * 2 : file: Name of file. Should not be NULL or empty.
843 * Returns : "dir/file" (Or on windows, "dir\file").
844 * It allocates the string on the heap. Caller frees.
845 * Returns NULL in error (i.e. NULL file or out of
848 *********************************************************************/
849 char * make_path(const char * dir, const char * file)
860 strncpy(path,dir+2,512);
864 strncpy(path,dir+1,512);
869 strncpy(path,dir,512);
875 if(AddPart(path,file,512))
881 #else /* ndef AMIGA */
883 if ((file == NULL) || (*file == '\0'))
885 return NULL; /* Error */
888 if ((dir == NULL) || (*dir == '\0') /* No directory specified */
889 #if defined(_WIN32) || defined(__OS2__)
890 || (*file == '\\') || (file[1] == ':') /* Absolute path (DOS) */
891 #else /* ifndef _WIN32 || __OS2__ */
892 || (*file == '/') /* Absolute path (U*ix) */
893 #endif /* ifndef _WIN32 || __OS2__ */
903 if ( *dir != '/' && basedir && *basedir )
905 path = malloc( strlen( basedir ) + strlen(dir) + strlen(file) + 3);
906 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
907 strcpy(path, basedir);
913 path = malloc(strlen(dir) + strlen(file) + 2);
914 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
919 path = malloc(strlen(dir) + strlen(file) + 2);
920 if (!path ) log_error(LOG_LEVEL_FATAL, "malloc failed!");
923 #endif /* defined unix */
925 #if defined(_WIN32) || defined(__OS2__)
926 if(path[strlen(path)-1] != '\\')
930 #else /* ifndef _WIN32 || __OS2__ */
931 if(path[strlen(path)-1] != '/')
935 #endif /* ifndef _WIN32 || __OS2__ */
940 #endif /* ndef AMIGA */
945 * What follows is a portable snprintf routine, written by Mark Martinec.
946 * See: http://www.ijs.si/software/snprintf/
947 * Anyone who needs it can add a define for themselves... so far, only
948 * OS/2 (native) lacks snprintf.
951 - a portable implementation of snprintf,
952 including vsnprintf.c, asnprintf, vasnprintf, asprintf, vasprintf
954 snprintf is a routine to convert numeric and string arguments to
955 formatted strings. It is similar to sprintf(3) provided in a system's
956 C library, yet it requires an additional argument - the buffer size -
957 and it guarantees never to store anything beyond the given buffer,
958 regardless of the format or arguments to be formatted. Some newer
959 operating systems do provide snprintf in their C library, but many do
960 not or do provide an inadequate (slow or idiosyncratic) version, which
961 calls for a portable implementation of this routine.
965 Mark Martinec <mark.martinec@ijs.si>, April 1999, June 2000
966 Copyright © 1999, Mark Martinec
972 #define PORTABLE_SNPRINTF_VERSION_MAJOR 2
973 #define PORTABLE_SNPRINTF_VERSION_MINOR 2
975 #if defined(NEED_ASPRINTF) || defined(NEED_ASNPRINTF) || defined(NEED_VASPRINTF) || defined(NEED_VASNPRINTF)
976 # if defined(NEED_SNPRINTF_ONLY)
977 # undef NEED_SNPRINTF_ONLY
979 # if !defined(PREFER_PORTABLE_SNPRINTF)
980 # define PREFER_PORTABLE_SNPRINTF
984 #if defined(SOLARIS_BUG_COMPATIBLE) && !defined(SOLARIS_COMPATIBLE)
985 #define SOLARIS_COMPATIBLE
988 #if defined(HPUX_BUG_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
989 #define HPUX_COMPATIBLE
992 #if defined(DIGITAL_UNIX_BUG_COMPATIBLE) && !defined(DIGITAL_UNIX_COMPATIBLE)
993 #define DIGITAL_UNIX_COMPATIBLE
996 #if defined(PERL_BUG_COMPATIBLE) && !defined(PERL_COMPATIBLE)
997 #define PERL_COMPATIBLE
1000 #if defined(LINUX_BUG_COMPATIBLE) && !defined(LINUX_COMPATIBLE)
1001 #define LINUX_COMPATIBLE
1004 #include <sys/types.h>
1015 #define isdigit(c) ((c) >= '0' && (c) <= '9')
1017 /* For copying strings longer or equal to 'breakeven_point'
1018 * it is more efficient to call memcpy() than to do it inline.
1019 * The value depends mostly on the processor architecture,
1020 * but also on the compiler and its optimization capabilities.
1021 * The value is not critical, some small value greater than zero
1022 * will be just fine if you don't care to squeeze every drop
1023 * of performance out of the code.
1025 * Small values favor memcpy, large values favor inline code.
1027 #if defined(__alpha__) || defined(__alpha)
1028 # define breakeven_point 2 /* AXP (DEC Alpha) - gcc or cc or egcs */
1030 #if defined(__i386__) || defined(__i386)
1031 # define breakeven_point 12 /* Intel Pentium/Linux - gcc 2.96 */
1034 # define breakeven_point 10 /* HP-PA - gcc */
1036 #if defined(__sparc__) || defined(__sparc)
1037 # define breakeven_point 33 /* Sun Sparc 5 - gcc 2.8.1 */
1040 /* some other values of possible interest: */
1041 /* #define breakeven_point 8 */ /* VAX 4000 - vaxc */
1042 /* #define breakeven_point 19 */ /* VAX 4000 - gcc 2.7.0 */
1044 #ifndef breakeven_point
1045 # define breakeven_point 6 /* some reasonable one-size-fits-all value */
1048 #define fast_memcpy(d,s,n) \
1049 { register size_t nn = (size_t)(n); \
1050 if (nn >= breakeven_point) memcpy((d), (s), nn); \
1051 else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1052 register char *dd; register const char *ss; \
1053 for (ss=(s), dd=(d); nn>0; nn--) *dd++ = *ss++; } }
1055 #define fast_memset(d,c,n) \
1056 { register size_t nn = (size_t)(n); \
1057 if (nn >= breakeven_point) memset((d), (int)(c), nn); \
1058 else if (nn > 0) { /* proc call overhead is worth only for large strings*/\
1059 register char *dd; register const int cc=(int)(c); \
1060 for (dd=(d); nn>0; nn--) *dd++ = cc; } }
1064 #if defined(NEED_ASPRINTF)
1065 int asprintf (char **ptr, const char *fmt, /*args*/ ...);
1067 #if defined(NEED_VASPRINTF)
1068 int vasprintf (char **ptr, const char *fmt, va_list ap);
1070 #if defined(NEED_ASNPRINTF)
1071 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...);
1073 #if defined(NEED_VASNPRINTF)
1074 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap);
1077 #if defined(HAVE_SNPRINTF)
1078 /* declare our portable snprintf routine under name portable_snprintf */
1079 /* declare our portable vsnprintf routine under name portable_vsnprintf */
1081 /* declare our portable routines under names snprintf and vsnprintf */
1082 #define portable_snprintf snprintf
1083 #if !defined(NEED_SNPRINTF_ONLY)
1084 #define portable_vsnprintf vsnprintf
1088 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1089 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...);
1090 #if !defined(NEED_SNPRINTF_ONLY)
1091 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap);
1097 static char credits[] = "\n\
1098 @(#)snprintf.c, v2.2: Mark Martinec, <mark.martinec@ijs.si>\n\
1099 @(#)snprintf.c, v2.2: Copyright 1999, Mark Martinec. Frontier Artistic License applies.\n\
1100 @(#)snprintf.c, v2.2: http://www.ijs.si/software/snprintf/\n";
1102 #if defined(NEED_ASPRINTF)
1103 int asprintf(char **ptr, const char *fmt, /*args*/ ...) {
1109 va_start(ap, fmt); /* measure the required size */
1110 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1112 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1113 *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1114 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1118 str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1120 assert(str_l2 == str_l);
1126 #if defined(NEED_VASPRINTF)
1127 int vasprintf(char **ptr, const char *fmt, va_list ap) {
1133 va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
1134 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1137 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1138 *ptr = (char *) malloc(str_m = (size_t)str_l + 1);
1139 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1141 int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1142 assert(str_l2 == str_l);
1148 #if defined(NEED_ASNPRINTF)
1149 int asnprintf (char **ptr, size_t str_m, const char *fmt, /*args*/ ...) {
1154 va_start(ap, fmt); /* measure the required size */
1155 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap);
1157 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1158 if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
1159 /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1160 if (str_m == 0) { /* not interested in resulting string, just return size */
1162 *ptr = (char *) malloc(str_m);
1163 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1167 str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1169 assert(str_l2 == str_l);
1176 #if defined(NEED_VASNPRINTF)
1177 int vasnprintf (char **ptr, size_t str_m, const char *fmt, va_list ap) {
1182 va_copy(ap2, ap); /* don't consume the original ap, we'll need it again */
1183 str_l = portable_vsnprintf(NULL, (size_t)0, fmt, ap2);/*get required size*/
1186 assert(str_l >= 0); /* possible integer overflow if str_m > INT_MAX */
1187 if ((size_t)str_l + 1 < str_m) str_m = (size_t)str_l + 1; /* truncate */
1188 /* if str_m is 0, no buffer is allocated, just set *ptr to NULL */
1189 if (str_m == 0) { /* not interested in resulting string, just return size */
1191 *ptr = (char *) malloc(str_m);
1192 if (*ptr == NULL) { errno = ENOMEM; str_l = -1; }
1194 int str_l2 = portable_vsnprintf(*ptr, str_m, fmt, ap);
1195 assert(str_l2 == str_l);
1203 * If the system does have snprintf and the portable routine is not
1204 * specifically required, this module produces no code for snprintf/vsnprintf.
1206 #if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
1208 #if !defined(NEED_SNPRINTF_ONLY)
1209 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1214 str_l = portable_vsnprintf(str, str_m, fmt, ap);
1220 #if defined(NEED_SNPRINTF_ONLY)
1221 int portable_snprintf(char *str, size_t str_m, const char *fmt, /*args*/ ...) {
1223 int portable_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) {
1226 #if defined(NEED_SNPRINTF_ONLY)
1230 const char *p = fmt;
1232 /* In contrast with POSIX, the ISO C99 now says
1233 * that str can be NULL and str_m can be 0.
1234 * This is more useful than the old: if (str_m < 1) return -1; */
1236 #if defined(NEED_SNPRINTF_ONLY)
1242 /* if (str_l < str_m) str[str_l++] = *p++; -- this would be sufficient */
1243 /* but the following code achieves better performance for cases
1244 * where format string is long and contains few conversions */
1245 const char *q = strchr(p+1,'%');
1246 size_t n = !q ? strlen(p) : (q-p);
1247 if (str_l < str_m) {
1248 size_t avail = str_m-str_l;
1249 fast_memcpy(str+str_l, p, (n>avail?avail:n));
1253 const char *starting_p;
1254 size_t min_field_width = 0, precision = 0;
1255 int zero_padding = 0, precision_specified = 0, justify_left = 0;
1256 int alternate_form = 0, force_sign = 0;
1257 int space_for_positive = 1; /* If both the ' ' and '+' flags appear,
1258 the ' ' flag should be ignored. */
1259 char length_modifier = '\0'; /* allowed values: \0, h, l, L */
1260 char tmp[32];/* temporary buffer for simple numeric->string conversion */
1262 const char *str_arg; /* string address in case of string argument */
1263 size_t str_arg_l; /* natural field width of arg without padding
1265 unsigned char uchar_arg;
1266 /* unsigned char argument value - only defined for c conversion.
1267 N.B. standard explicitly states the char argument for
1268 the c conversion is unsigned */
1270 size_t number_of_zeros_to_pad = 0;
1271 /* number of zeros to be inserted for numeric conversions
1272 as required by the precision or minimal field width */
1274 size_t zero_padding_insertion_ind = 0;
1275 /* index into tmp where zero padding is to be inserted */
1277 char fmt_spec = '\0';
1278 /* current conversion specifier character */
1280 str_arg = credits;/* just to make compiler happy (defined but not used)*/
1282 starting_p = p; p++; /* skip '%' */
1284 while (*p == '0' || *p == '-' || *p == '+' ||
1285 *p == ' ' || *p == '#' || *p == '\'') {
1287 case '0': zero_padding = 1; break;
1288 case '-': justify_left = 1; break;
1289 case '+': force_sign = 1; space_for_positive = 0; break;
1290 case ' ': force_sign = 1;
1291 /* If both the ' ' and '+' flags appear, the ' ' flag should be ignored */
1292 #ifdef PERL_COMPATIBLE
1293 /* ... but in Perl the last of ' ' and '+' applies */
1294 space_for_positive = 1;
1297 case '#': alternate_form = 1; break;
1302 /* If the '0' and '-' flags both appear, the '0' flag should be ignored. */
1304 /* parse field width */
1307 p++; j = va_arg(ap, int);
1308 if (j >= 0) min_field_width = j;
1309 else { min_field_width = -j; justify_left = 1; }
1310 } else if (isdigit((int)(*p))) {
1311 /* size_t could be wider than unsigned int;
1312 make sure we treat argument like common implementations do */
1313 unsigned int uj = *p++ - '0';
1314 while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1315 min_field_width = uj;
1317 /* parse precision */
1319 p++; precision_specified = 1;
1321 int j = va_arg(ap, int);
1323 if (j >= 0) precision = j;
1325 precision_specified = 0; precision = 0;
1327 * Solaris 2.6 man page claims that in this case the precision
1328 * should be set to 0. Digital Unix 4.0, HPUX 10 and BSD man page
1329 * claim that this case should be treated as unspecified precision,
1330 * which is what we do here.
1333 } else if (isdigit((int)(*p))) {
1334 /* size_t could be wider than unsigned int;
1335 make sure we treat argument like common implementations do */
1336 unsigned int uj = *p++ - '0';
1337 while (isdigit((int)(*p))) uj = 10*uj + (unsigned int)(*p++ - '0');
1341 /* parse 'h', 'l' and 'll' length modifiers */
1342 if (*p == 'h' || *p == 'l') {
1343 length_modifier = *p; p++;
1344 if (length_modifier == 'l' && *p == 'l') { /* double l = long long */
1345 #ifdef SNPRINTF_LONGLONG_SUPPORT
1346 length_modifier = '2'; /* double l encoded as '2' */
1348 length_modifier = 'l'; /* treat it as a single 'l' */
1354 /* common synonyms: */
1356 case 'i': fmt_spec = 'd'; break;
1357 case 'D': fmt_spec = 'd'; length_modifier = 'l'; break;
1358 case 'U': fmt_spec = 'u'; length_modifier = 'l'; break;
1359 case 'O': fmt_spec = 'o'; length_modifier = 'l'; break;
1362 /* get parameter value, do initial processing */
1364 case '%': /* % behaves similar to 's' regarding flags and field widths */
1365 case 'c': /* c behaves similar to 's' regarding flags and field widths */
1367 length_modifier = '\0'; /* wint_t and wchar_t not supported */
1368 /* the result of zero padding flag with non-numeric conversion specifier*/
1369 /* is undefined. Solaris and HPUX 10 does zero padding in this case, */
1370 /* Digital Unix and Linux does not. */
1371 #if !defined(SOLARIS_COMPATIBLE) && !defined(HPUX_COMPATIBLE)
1372 zero_padding = 0; /* turn zero padding off for string conversions */
1379 int j = va_arg(ap, int);
1380 uchar_arg = (unsigned char) j; /* standard demands unsigned char */
1381 str_arg = (const char *) &uchar_arg;
1385 str_arg = va_arg(ap, const char *);
1386 if (!str_arg) str_arg_l = 0;
1387 /* make sure not to address string beyond the specified precision !!! */
1388 else if (!precision_specified) str_arg_l = strlen(str_arg);
1389 /* truncate string if necessary as requested by precision */
1390 else if (precision == 0) str_arg_l = 0;
1392 /* memchr on HP does not like n > 2^31 !!! */
1393 const char *q = memchr(str_arg, '\0',
1394 precision <= 0x7fffffff ? precision : 0x7fffffff);
1395 str_arg_l = !q ? precision : (q-str_arg);
1401 case 'd': case 'u': case 'o': case 'x': case 'X': case 'p': {
1402 /* NOTE: the u, o, x, X and p conversion specifiers imply
1403 the value is unsigned; d implies a signed value */
1406 /* 0 if numeric argument is zero (or if pointer is NULL for 'p'),
1407 +1 if greater than zero (or nonzero for unsigned arguments),
1408 -1 if negative (unsigned argument is never negative) */
1410 int int_arg = 0; unsigned int uint_arg = 0;
1411 /* only defined for length modifier h, or for no length modifiers */
1413 long int long_arg = 0; unsigned long int ulong_arg = 0;
1414 /* only defined for length modifier l */
1416 void *ptr_arg = NULL;
1417 /* pointer argument value -only defined for p conversion */
1419 #ifdef SNPRINTF_LONGLONG_SUPPORT
1420 long long int long_long_arg = 0;
1421 unsigned long long int ulong_long_arg = 0;
1422 /* only defined for length modifier ll */
1424 if (fmt_spec == 'p') {
1425 /* HPUX 10: An l, h, ll or L before any other conversion character
1426 * (other than d, i, u, o, x, or X) is ignored.
1428 * not specified, but seems to behave as HPUX does.
1429 * Solaris: If an h, l, or L appears before any other conversion
1430 * specifier (other than d, i, u, o, x, or X), the behavior
1431 * is undefined. (Actually %hp converts only 16-bits of address
1432 * and %llp treats address as 64-bit data which is incompatible
1433 * with (void *) argument on a 32-bit system).
1435 #ifdef SOLARIS_COMPATIBLE
1436 # ifdef SOLARIS_BUG_COMPATIBLE
1437 /* keep length modifiers even if it represents 'll' */
1439 if (length_modifier == '2') length_modifier = '\0';
1442 length_modifier = '\0';
1444 ptr_arg = va_arg(ap, void *);
1445 if (ptr_arg != NULL) arg_sign = 1;
1446 } else if (fmt_spec == 'd') { /* signed */
1447 switch (length_modifier) {
1450 /* It is non-portable to specify a second argument of char or short
1451 * to va_arg, because arguments seen by the called function
1452 * are not char or short. C converts char and short arguments
1453 * to int before passing them to a function.
1455 int_arg = va_arg(ap, int);
1456 if (int_arg > 0) arg_sign = 1;
1457 else if (int_arg < 0) arg_sign = -1;
1460 long_arg = va_arg(ap, long int);
1461 if (long_arg > 0) arg_sign = 1;
1462 else if (long_arg < 0) arg_sign = -1;
1464 #ifdef SNPRINTF_LONGLONG_SUPPORT
1466 long_long_arg = va_arg(ap, long long int);
1467 if (long_long_arg > 0) arg_sign = 1;
1468 else if (long_long_arg < 0) arg_sign = -1;
1472 } else { /* unsigned */
1473 switch (length_modifier) {
1476 uint_arg = va_arg(ap, unsigned int);
1477 if (uint_arg) arg_sign = 1;
1480 ulong_arg = va_arg(ap, unsigned long int);
1481 if (ulong_arg) arg_sign = 1;
1483 #ifdef SNPRINTF_LONGLONG_SUPPORT
1485 ulong_long_arg = va_arg(ap, unsigned long long int);
1486 if (ulong_long_arg) arg_sign = 1;
1491 str_arg = tmp; str_arg_l = 0;
1493 * For d, i, u, o, x, and X conversions, if precision is specified,
1494 * the '0' flag should be ignored. This is so with Solaris 2.6,
1495 * Digital UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl.
1497 #ifndef PERL_COMPATIBLE
1498 if (precision_specified) zero_padding = 0;
1500 if (fmt_spec == 'd') {
1501 if (force_sign && arg_sign >= 0)
1502 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1503 /* leave negative numbers for sprintf to handle,
1504 to avoid handling tricky cases like (short int)(-32768) */
1505 #ifdef LINUX_COMPATIBLE
1506 } else if (fmt_spec == 'p' && force_sign && arg_sign > 0) {
1507 tmp[str_arg_l++] = space_for_positive ? ' ' : '+';
1509 } else if (alternate_form) {
1510 if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X') )
1511 { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = fmt_spec; }
1512 /* alternate form should have no effect for p conversion, but ... */
1513 #ifdef HPUX_COMPATIBLE
1514 else if (fmt_spec == 'p'
1515 /* HPUX 10: for an alternate form of p conversion,
1516 * a nonzero result is prefixed by 0x. */
1517 #ifndef HPUX_BUG_COMPATIBLE
1518 /* Actually it uses 0x prefix even for a zero value. */
1521 ) { tmp[str_arg_l++] = '0'; tmp[str_arg_l++] = 'x'; }
1524 zero_padding_insertion_ind = str_arg_l;
1525 if (!precision_specified) precision = 1; /* default precision is 1 */
1526 if (precision == 0 && arg_sign == 0
1527 #if defined(HPUX_BUG_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1529 /* HPUX 10 man page claims: With conversion character p the result of
1530 * converting a zero value with a precision of zero is a null string.
1531 * Actually HP returns all zeroes, and Linux returns "(nil)". */
1534 /* converted to null string */
1535 /* When zero value is formatted with an explicit precision 0,
1536 the resulting formatted string is empty (d, i, u, o, x, X, p). */
1538 char f[5]; int f_l = 0;
1539 f[f_l++] = '%'; /* construct a simple format string for sprintf */
1540 if (!length_modifier) { }
1541 else if (length_modifier=='2') { f[f_l++] = 'l'; f[f_l++] = 'l'; }
1542 else f[f_l++] = length_modifier;
1543 f[f_l++] = fmt_spec; f[f_l++] = '\0';
1544 if (fmt_spec == 'p') str_arg_l += sprintf(tmp+str_arg_l, f, ptr_arg);
1545 else if (fmt_spec == 'd') { /* signed */
1546 switch (length_modifier) {
1548 case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, int_arg); break;
1549 case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, long_arg); break;
1550 #ifdef SNPRINTF_LONGLONG_SUPPORT
1551 case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,long_long_arg); break;
1554 } else { /* unsigned */
1555 switch (length_modifier) {
1557 case 'h': str_arg_l+=sprintf(tmp+str_arg_l, f, uint_arg); break;
1558 case 'l': str_arg_l+=sprintf(tmp+str_arg_l, f, ulong_arg); break;
1559 #ifdef SNPRINTF_LONGLONG_SUPPORT
1560 case '2': str_arg_l+=sprintf(tmp+str_arg_l,f,ulong_long_arg);break;
1564 /* include the optional minus sign and possible "0x"
1565 in the region before the zero padding insertion point */
1566 if (zero_padding_insertion_ind < str_arg_l &&
1567 tmp[zero_padding_insertion_ind] == '-') {
1568 zero_padding_insertion_ind++;
1570 if (zero_padding_insertion_ind+1 < str_arg_l &&
1571 tmp[zero_padding_insertion_ind] == '0' &&
1572 (tmp[zero_padding_insertion_ind+1] == 'x' ||
1573 tmp[zero_padding_insertion_ind+1] == 'X') ) {
1574 zero_padding_insertion_ind += 2;
1577 { size_t num_of_digits = str_arg_l - zero_padding_insertion_ind;
1578 if (alternate_form && fmt_spec == 'o'
1579 #ifdef HPUX_COMPATIBLE /* ("%#.o",0) -> "" */
1582 #ifdef DIGITAL_UNIX_BUG_COMPATIBLE /* ("%#o",0) -> "00" */
1584 /* unless zero is already the first character */
1585 && !(zero_padding_insertion_ind < str_arg_l
1586 && tmp[zero_padding_insertion_ind] == '0')
1588 ) { /* assure leading zero for alternate-form octal numbers */
1589 if (!precision_specified || precision < num_of_digits+1) {
1590 /* precision is increased to force the first character to be zero,
1591 except if a zero value is formatted with an explicit precision
1593 precision = num_of_digits+1; precision_specified = 1;
1596 /* zero padding to specified precision? */
1597 if (num_of_digits < precision)
1598 number_of_zeros_to_pad = precision - num_of_digits;
1600 /* zero padding to specified minimal field width? */
1601 if (!justify_left && zero_padding) {
1602 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1603 if (n > 0) number_of_zeros_to_pad += n;
1607 default: /* unrecognized conversion specifier, keep format string as-is*/
1608 zero_padding = 0; /* turn zero padding off for non-numeric convers. */
1609 #ifndef DIGITAL_UNIX_COMPATIBLE
1610 justify_left = 1; min_field_width = 0; /* reset flags */
1612 #if defined(PERL_COMPATIBLE) || defined(LINUX_COMPATIBLE)
1613 /* keep the entire format string unchanged */
1614 str_arg = starting_p; str_arg_l = p - starting_p;
1615 /* well, not exactly so for Linux, which does something inbetween,
1616 * and I don't feel an urge to imitate it: "%+++++hy" -> "%+y" */
1618 /* discard the unrecognized conversion, just keep *
1619 * the unrecognized conversion character */
1620 str_arg = p; str_arg_l = 0;
1622 if (*p) str_arg_l++; /* include invalid conversion specifier unchanged
1623 if not at end-of-string */
1626 if (*p) p++; /* step over the just processed conversion specifier */
1627 /* insert padding to the left as requested by min_field_width;
1628 this does not include the zero padding in case of numerical conversions*/
1629 if (!justify_left) { /* left padding with blank or zero */
1630 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1632 if (str_l < str_m) {
1633 size_t avail = str_m-str_l;
1634 fast_memset(str+str_l, (zero_padding?'0':' '), (n>avail?avail:n));
1639 /* zero padding as requested by the precision or by the minimal field width
1640 * for numeric conversions required? */
1641 if (number_of_zeros_to_pad <= 0) {
1642 /* will not copy first part of numeric right now, *
1643 * force it to be copied later in its entirety */
1644 zero_padding_insertion_ind = 0;
1646 /* insert first part of numerics (sign or '0x') before zero padding */
1647 int n = zero_padding_insertion_ind;
1649 if (str_l < str_m) {
1650 size_t avail = str_m-str_l;
1651 fast_memcpy(str+str_l, str_arg, (n>avail?avail:n));
1655 /* insert zero padding as requested by the precision or min field width */
1656 n = number_of_zeros_to_pad;
1658 if (str_l < str_m) {
1659 size_t avail = str_m-str_l;
1660 fast_memset(str+str_l, '0', (n>avail?avail:n));
1665 /* insert formatted string
1666 * (or as-is conversion specifier for unknown conversions) */
1667 { int n = str_arg_l - zero_padding_insertion_ind;
1669 if (str_l < str_m) {
1670 size_t avail = str_m-str_l;
1671 fast_memcpy(str+str_l, str_arg+zero_padding_insertion_ind,
1677 /* insert right padding */
1678 if (justify_left) { /* right blank padding to the field width */
1679 int n = min_field_width - (str_arg_l+number_of_zeros_to_pad);
1681 if (str_l < str_m) {
1682 size_t avail = str_m-str_l;
1683 fast_memset(str+str_l, ' ', (n>avail?avail:n));
1690 #if defined(NEED_SNPRINTF_ONLY)
1693 if (str_m > 0) { /* make sure the string is null-terminated
1694 even at the expense of overwriting the last character
1695 (shouldn't happen, but just in case) */
1696 str[str_l <= str_m-1 ? str_l : str_m-1] = '\0';
1698 /* Return the number of characters formatted (excluding trailing null
1699 * character), that is, the number of characters that would have been
1700 * written to the buffer if it were large enough.
1702 * The value of str_l should be returned, but str_l is of unsigned type
1703 * size_t, and snprintf is int, possibly leading to an undetected
1704 * integer overflow, resulting in a negative return value, which is illegal.
1705 * Both XSH5 and ISO C99 (at least the draft) are silent on this issue.
1706 * Should errno be set to EOVERFLOW and EOF returned in this case???
1711 #endif /* __OS2__ */