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