xref: /openbsd-src/lib/libcurses/base/safe_sprintf.c (revision 92dd1ec0a89df25171bc5d61a3d95ea1a68cef0b)
1 /*	$OpenBSD: safe_sprintf.c,v 1.1 1999/01/18 19:10:07 millert Exp $	*/
2 
3 /****************************************************************************
4  * Copyright (c) 1998 Free Software Foundation, Inc.                        *
5  *                                                                          *
6  * Permission is hereby granted, free of charge, to any person obtaining a  *
7  * copy of this software and associated documentation files (the            *
8  * "Software"), to deal in the Software without restriction, including      *
9  * without limitation the rights to use, copy, modify, merge, publish,      *
10  * distribute, distribute with modifications, sublicense, and/or sell       *
11  * copies of the Software, and to permit persons to whom the Software is    *
12  * furnished to do so, subject to the following conditions:                 *
13  *                                                                          *
14  * The above copyright notice and this permission notice shall be included  *
15  * in all copies or substantial portions of the Software.                   *
16  *                                                                          *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
20  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
21  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
22  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
23  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
24  *                                                                          *
25  * Except as contained in this notice, the name(s) of the above copyright   *
26  * holders shall not be used in advertising or otherwise to promote the     *
27  * sale, use or other dealings in this Software without prior written       *
28  * authorization.                                                           *
29  ****************************************************************************/
30 
31 /****************************************************************************
32  *  Author: Thomas E. Dickey <dickey@clark.net> 1997                        *
33  ****************************************************************************/
34 
35 #include <curses.priv.h>
36 #include <ctype.h>
37 
38 MODULE_ID("$From: safe_sprintf.c,v 1.9 1998/08/15 23:58:49 tom Exp $")
39 
40 #if USE_SAFE_SPRINTF
41 
42 typedef enum { Flags, Width, Prec, Type, Format } 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 = malloc(strlen(fmt)+1)) == 0)
63 		return -1;
64 	if ((buffer = malloc(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(*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(*fmt)) {
112 					done = TRUE;
113 					switch (*fmt) {
114 					case 'Z': /* FALLTHRU */
115 					case 'h': /* FALLTHRU */
116 					case 'l': /* FALLTHRU */
117 					case 'L': /* FALLTHRU */
118 						done = FALSE;
119 						type = *fmt;
120 						break;
121 					case 'i': /* FALLTHRU */
122 					case 'd': /* FALLTHRU */
123 					case 'u': /* FALLTHRU */
124 					case 'x': /* FALLTHRU */
125 					case 'X': /* FALLTHRU */
126 						if (type == 'l')
127 							VA_INTGR(long);
128 						else if (type == 'Z')
129 							VA_INTGR(size_t);
130 						else
131 							VA_INTGR(int);
132 						used = 'i';
133 						break;
134 					case 'f': /* FALLTHRU */
135 					case 'e': /* FALLTHRU */
136 					case 'E': /* FALLTHRU */
137 					case 'g': /* FALLTHRU */
138 					case 'G': /* FALLTHRU */
139 						if (type == 'L')
140 							VA_FLOAT(long double);
141 						else
142 							VA_FLOAT(double);
143 						used = 'f';
144 						break;
145 					case 'c':
146 						VA_INTGR(int);
147 						used = 'i';
148 						break;
149 					case 's':
150 						VA_POINT(char *);
151 						if (prec < 0)
152 							prec = strlen(pval);
153 						if (prec > (int)length) {
154 							length = length + prec;
155 							buffer = (char *)_nc_doalloc(buffer, length);
156 							if (buffer == 0) {
157 								free(format);
158 								return -1;
159 							}
160 						}
161 						used = 'p';
162 						break;
163 					case 'p':
164 						VA_POINT(void *);
165 						used = 'p';
166 						break;
167 					case 'n':
168 						VA_POINT(int *);
169 						used = 0;
170 						break;
171 					default:
172 						break;
173 					}
174 				} else if (*fmt == '.') {
175 					state = Prec;
176 				} else if (*fmt == '%') {
177 					done = TRUE;
178 					used = 'p';
179 				}
180 			}
181 			format[f] = '\0';
182 			switch (used) {
183 			case 'i':
184 				sprintf(buffer, format, ival);
185 				break;
186 			case 'f':
187 				sprintf(buffer, format, fval);
188 				break;
189 			default:
190 				sprintf(buffer, format, pval);
191 				break;
192 			}
193 			len += (int)strlen(buffer);
194 		} else {
195 			fmt++;
196 			len++;
197 		}
198 	}
199 
200 	free(buffer);
201 	free(format);
202 	return len;
203 }
204 #endif
205 
206 /*
207  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
208  */
209 char *
210 _nc_printf_string(const char *fmt, va_list ap)
211 {
212 #if USE_SAFE_SPRINTF
213 	char *buf = 0;
214 	int len = _nc_printf_length(fmt, ap);
215 
216 	if (len > 0) {
217 		if ((buf = malloc(len+1)) == 0)
218 			return(0);
219 		vsprintf(buf, fmt, ap);
220 	}
221 #else
222 	static int rows, cols;
223 	static char *buf;
224 	static size_t len;
225 
226 	if (screen_lines > rows || screen_columns > cols) {
227 		if (screen_lines   > rows) rows = screen_lines;
228 		if (screen_columns > cols) cols = screen_columns;
229 		len = (rows * (cols + 1)) + 1;
230 		buf = (char *)_nc_doalloc(buf, len);
231 		if (buf == 0) {
232 			return(0);
233 		}
234 	}
235 
236 	if (buf != 0) {
237 # if HAVE_VSNPRINTF
238 		vsnprintf(buf, len, fmt, ap);	/* GNU extension */
239 # else
240 		vsprintf(buf, fmt, ap);		/* ANSI */
241 # endif
242 	}
243 #endif
244 	return buf;
245 }
246