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