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