1/****************************************************************************
2 * Copyright 2018,2020 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
37MODULE_ID("$Id: safe_sprintf.c,v 1.33 2020/02/02 23:34:34 tom Exp $")
38
39#if USE_SAFE_SPRINTF
40
41typedef 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 */
53static int
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 */
221NCURSES_EXPORT(char *)
222NCURSES_SP_NAME(_nc_printf_string) (NCURSES_SP_DCLx
223				    const char *fmt,
224				    va_list ap)
225{
226    char *result = 0;
227
228    if (SP_PARM != 0 && fmt != 0) {
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 != 0) {
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	    my_buffer = typeRealloc(char, my_length, my_buffer);
259	}
260
261	if (my_buffer != 0) {
262# if HAVE_VSNPRINTF
263	    vsnprintf(my_buffer, my_length, fmt, ap);	/* SUSv2, 1997 */
264# else
265	    vsprintf(my_buffer, fmt, ap);	/* ISO/ANSI C, 1989 */
266# endif
267	    result = my_buffer;
268	}
269#endif
270    } else if (my_buffer != 0) {	/* see _nc_freeall() */
271	free(my_buffer);
272	my_buffer = 0;
273	my_length = 0;
274    }
275    return result;
276}
277
278#if NCURSES_SP_FUNCS
279NCURSES_EXPORT(char *)
280_nc_printf_string(const char *fmt, va_list ap)
281{
282    return NCURSES_SP_NAME(_nc_printf_string) (CURRENT_SCREEN, fmt, ap);
283}
284#endif
285