xref: /freebsd/contrib/ncurses/ncurses/base/safe_sprintf.c (revision 4cf49a43559ed9fdad601bdcccd2c55963008675)
1 /****************************************************************************
2  * Copyright (c) 1998 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 
36 MODULE_ID("$Id: safe_sprintf.c,v 1.10 1999/02/27 19:56:37 tom Exp $")
37 
38 #if USE_SAFE_SPRINTF
39 
40 typedef enum { Flags, Width, Prec, Type, Format } PRINTF;
41 
42 #define VA_INTGR(type) ival = va_arg(ap, type)
43 #define VA_FLOAT(type) fval = va_arg(ap, type)
44 #define VA_POINT(type) pval = (void *)va_arg(ap, type)
45 
46 /*
47  * Scan a variable-argument list for printf to determine the number of
48  * characters that would be emitted.
49  */
50 static int
51 _nc_printf_length(const char *fmt, va_list ap)
52 {
53 	size_t length = BUFSIZ;
54 	char *buffer;
55 	char *format;
56 	int len = 0;
57 
58 	if (fmt == 0 || *fmt == '\0')
59 		return -1;
60 	if ((format = typeMalloc(char, strlen(fmt)+1)) == 0)
61 		return -1;
62 	if ((buffer = typeMalloc(char, length)) == 0) {
63 		free(format);
64 		return -1;
65 	}
66 
67 	while (*fmt != '\0') {
68 		if (*fmt == '%') {
69 			static char dummy[] = "";
70 			PRINTF state = Flags;
71 			char *pval   = dummy;	/* avoid const-cast */
72 			double fval  = 0.0;
73 			int done     = FALSE;
74 			int ival     = 0;
75 			int prec     = -1;
76 			int type     = 0;
77 			int used     = 0;
78 			int width    = -1;
79 			size_t f     = 0;
80 
81 			format[f++] = *fmt;
82 			while (*++fmt != '\0' && len >= 0 && !done) {
83 				format[f++] = *fmt;
84 
85 				if (isdigit(*fmt)) {
86 					int num = *fmt - '0';
87 					if (state == Flags && num != 0)
88 						state = Width;
89 					if (state == Width) {
90 						if (width < 0)
91 							width = 0;
92 						width = (width * 10) + num;
93 					} else if (state == Prec) {
94 						if (prec < 0)
95 							prec = 0;
96 						prec = (prec * 10) + num;
97 					}
98 				} else if (*fmt == '*') {
99 					VA_INTGR(int);
100 					if (state == Flags)
101 						state = Width;
102 					if (state == Width) {
103 						width = ival;
104 					} else if (state == Prec) {
105 						prec = ival;
106 					}
107 					sprintf(&format[--f], "%d", ival);
108 					f = strlen(format);
109 				} else if (isalpha(*fmt)) {
110 					done = TRUE;
111 					switch (*fmt) {
112 					case 'Z': /* FALLTHRU */
113 					case 'h': /* FALLTHRU */
114 					case 'l': /* FALLTHRU */
115 					case 'L': /* FALLTHRU */
116 						done = FALSE;
117 						type = *fmt;
118 						break;
119 					case 'i': /* FALLTHRU */
120 					case 'd': /* FALLTHRU */
121 					case 'u': /* FALLTHRU */
122 					case 'x': /* FALLTHRU */
123 					case 'X': /* FALLTHRU */
124 						if (type == 'l')
125 							VA_INTGR(long);
126 						else if (type == 'Z')
127 							VA_INTGR(size_t);
128 						else
129 							VA_INTGR(int);
130 						used = 'i';
131 						break;
132 					case 'f': /* FALLTHRU */
133 					case 'e': /* FALLTHRU */
134 					case 'E': /* FALLTHRU */
135 					case 'g': /* FALLTHRU */
136 					case 'G': /* FALLTHRU */
137 						if (type == 'L')
138 							VA_FLOAT(long double);
139 						else
140 							VA_FLOAT(double);
141 						used = 'f';
142 						break;
143 					case 'c':
144 						VA_INTGR(int);
145 						used = 'i';
146 						break;
147 					case 's':
148 						VA_POINT(char *);
149 						if (prec < 0)
150 							prec = strlen(pval);
151 						if (prec > (int)length) {
152 							length = length + prec;
153 							buffer = typeRealloc(char, length, buffer);
154 							if (buffer == 0) {
155 								free(format);
156 								return -1;
157 							}
158 						}
159 						used = 'p';
160 						break;
161 					case 'p':
162 						VA_POINT(void *);
163 						used = 'p';
164 						break;
165 					case 'n':
166 						VA_POINT(int *);
167 						used = 0;
168 						break;
169 					default:
170 						break;
171 					}
172 				} else if (*fmt == '.') {
173 					state = Prec;
174 				} else if (*fmt == '%') {
175 					done = TRUE;
176 					used = 'p';
177 				}
178 			}
179 			format[f] = '\0';
180 			switch (used) {
181 			case 'i':
182 				sprintf(buffer, format, ival);
183 				break;
184 			case 'f':
185 				sprintf(buffer, format, fval);
186 				break;
187 			default:
188 				sprintf(buffer, format, pval);
189 				break;
190 			}
191 			len += (int)strlen(buffer);
192 		} else {
193 			fmt++;
194 			len++;
195 		}
196 	}
197 
198 	free(buffer);
199 	free(format);
200 	return len;
201 }
202 #endif
203 
204 /*
205  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
206  */
207 char *
208 _nc_printf_string(const char *fmt, va_list ap)
209 {
210 #if USE_SAFE_SPRINTF
211 	char *buf = 0;
212 	int len = _nc_printf_length(fmt, ap);
213 
214 	if (len > 0) {
215 		if ((buf = typeMalloc(char, len+1)) == 0)
216 			return(0);
217 		vsprintf(buf, fmt, ap);
218 	}
219 #else
220 	static int rows, cols;
221 	static char *buf;
222 	static size_t len;
223 
224 	if (screen_lines > rows || screen_columns > cols) {
225 		if (screen_lines   > rows) rows = screen_lines;
226 		if (screen_columns > cols) cols = screen_columns;
227 		len = (rows * (cols + 1)) + 1;
228 		buf = typeRealloc(char, len, buf);
229 		if (buf == 0) {
230 			return(0);
231 		}
232 	}
233 
234 	if (buf != 0) {
235 # if HAVE_VSNPRINTF
236 		vsnprintf(buf, len, fmt, ap);	/* GNU extension */
237 # else
238 		vsprintf(buf, fmt, ap);		/* ANSI */
239 # endif
240 	}
241 #endif
242 	return buf;
243 }
244