xref: /freebsd/contrib/ncurses/ncurses/base/safe_sprintf.c (revision 4f29da19bd44f0e99f021510460a81bf754c21d2)
1 /****************************************************************************
2  * Copyright (c) 1998,1999,2000 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 
29 /****************************************************************************
30  *  Author: Thomas E. Dickey <dickey@clark.net> 1997                        *
31  ****************************************************************************/
32 
33 #include <curses.priv.h>
34 #include <ctype.h>
35 
36 MODULE_ID("$Id: safe_sprintf.c,v 1.14 2001/07/08 00:58:34 tom Exp $")
37 
38 #if USE_SAFE_SPRINTF
39 
40 typedef enum {
41     Flags, Width, Prec, Type, Format
42 } PRINTF;
43 
44 #define VA_INTGR(type) ival = va_arg(ap, type)
45 #define VA_FLOAT(type) fval = va_arg(ap, type)
46 #define VA_POINT(type) pval = (void *)va_arg(ap, type)
47 
48 /*
49  * Scan a variable-argument list for printf to determine the number of
50  * characters that would be emitted.
51  */
52 static int
53 _nc_printf_length(const char *fmt, va_list ap)
54 {
55     size_t length = BUFSIZ;
56     char *buffer;
57     char *format;
58     int len = 0;
59 
60     if (fmt == 0 || *fmt == '\0')
61 	return -1;
62     if ((format = typeMalloc(char, strlen(fmt) + 1)) == 0)
63 	  return -1;
64     if ((buffer = typeMalloc(char, length)) == 0) {
65 	free(format);
66 	return -1;
67     }
68 
69     while (*fmt != '\0') {
70 	if (*fmt == '%') {
71 	    static char dummy[] = "";
72 	    PRINTF state = Flags;
73 	    char *pval = dummy;	/* avoid const-cast */
74 	    double fval = 0.0;
75 	    int done = FALSE;
76 	    int ival = 0;
77 	    int prec = -1;
78 	    int type = 0;
79 	    int used = 0;
80 	    int width = -1;
81 	    size_t f = 0;
82 
83 	    format[f++] = *fmt;
84 	    while (*++fmt != '\0' && len >= 0 && !done) {
85 		format[f++] = *fmt;
86 
87 		if (isdigit(UChar(*fmt))) {
88 		    int num = *fmt - '0';
89 		    if (state == Flags && num != 0)
90 			state = Width;
91 		    if (state == Width) {
92 			if (width < 0)
93 			    width = 0;
94 			width = (width * 10) + num;
95 		    } else if (state == Prec) {
96 			if (prec < 0)
97 			    prec = 0;
98 			prec = (prec * 10) + num;
99 		    }
100 		} else if (*fmt == '*') {
101 		    VA_INTGR(int);
102 		    if (state == Flags)
103 			state = Width;
104 		    if (state == Width) {
105 			width = ival;
106 		    } else if (state == Prec) {
107 			prec = ival;
108 		    }
109 		    sprintf(&format[--f], "%d", ival);
110 		    f = strlen(format);
111 		} else if (isalpha(UChar(*fmt))) {
112 		    done = TRUE;
113 		    switch (*fmt) {
114 		    case 'Z':	/* FALLTHRU */
115 		    case 'h':	/* FALLTHRU */
116 		    case 'l':	/* FALLTHRU */
117 			done = FALSE;
118 			type = *fmt;
119 			break;
120 		    case 'i':	/* FALLTHRU */
121 		    case 'd':	/* FALLTHRU */
122 		    case 'u':	/* FALLTHRU */
123 		    case 'x':	/* FALLTHRU */
124 		    case 'X':	/* FALLTHRU */
125 			if (type == 'l')
126 			    VA_INTGR(long);
127 			else if (type == 'Z')
128 			    VA_INTGR(size_t);
129 			else
130 			    VA_INTGR(int);
131 			used = 'i';
132 			break;
133 		    case 'f':	/* FALLTHRU */
134 		    case 'e':	/* FALLTHRU */
135 		    case 'E':	/* FALLTHRU */
136 		    case 'g':	/* FALLTHRU */
137 		    case 'G':	/* FALLTHRU */
138 			VA_FLOAT(double);
139 			used = 'f';
140 			break;
141 		    case 'c':
142 			VA_INTGR(int);
143 			used = 'i';
144 			break;
145 		    case 's':
146 			VA_POINT(char *);
147 			if (prec < 0)
148 			    prec = strlen(pval);
149 			if (prec > (int) length) {
150 			    length = length + prec;
151 			    buffer = typeRealloc(char, length, buffer);
152 			    if (buffer == 0) {
153 				free(format);
154 				return -1;
155 			    }
156 			}
157 			used = 'p';
158 			break;
159 		    case 'p':
160 			VA_POINT(void *);
161 			used = 'p';
162 			break;
163 		    case 'n':
164 			VA_POINT(int *);
165 			used = 0;
166 			break;
167 		    default:
168 			break;
169 		    }
170 		} else if (*fmt == '.') {
171 		    state = Prec;
172 		} else if (*fmt == '%') {
173 		    done = TRUE;
174 		    used = 'p';
175 		}
176 	    }
177 	    format[f] = '\0';
178 	    switch (used) {
179 	    case 'i':
180 		sprintf(buffer, format, ival);
181 		break;
182 	    case 'f':
183 		sprintf(buffer, format, fval);
184 		break;
185 	    default:
186 		sprintf(buffer, format, pval);
187 		break;
188 	    }
189 	    len += (int) strlen(buffer);
190 	} else {
191 	    fmt++;
192 	    len++;
193 	}
194     }
195 
196     free(buffer);
197     free(format);
198     return len;
199 }
200 #endif
201 
202 /*
203  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
204  */
205 NCURSES_EXPORT(char *)
206 _nc_printf_string
207 (const char *fmt, va_list ap)
208 {
209 #if USE_SAFE_SPRINTF
210     char *buf = 0;
211     int len = _nc_printf_length(fmt, ap);
212 
213     if (len > 0) {
214 	if ((buf = typeMalloc(char, len + 1)) == 0)
215 	      return (0);
216 	vsprintf(buf, fmt, ap);
217     }
218 #else
219     static int rows, cols;
220     static char *buf;
221     static size_t len;
222 
223     if (screen_lines > rows || screen_columns > cols) {
224 	if (screen_lines > rows)
225 	    rows = screen_lines;
226 	if (screen_columns > cols)
227 	    cols = screen_columns;
228 	len = (rows * (cols + 1)) + 1;
229 	buf = typeRealloc(char, len, buf);
230 	if (buf == 0) {
231 	    return (0);
232 	}
233     }
234 
235     if (buf != 0) {
236 # if HAVE_VSNPRINTF
237 	vsnprintf(buf, len, fmt, ap);	/* GNU extension */
238 # else
239 	vsprintf(buf, fmt, ap);	/* ANSI */
240 # endif
241     }
242 #endif
243     return buf;
244 }
245