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