ncurses 6.2 - patch 20200718
[ncurses.git] / ncurses / base / safe_sprintf.c
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
37 MODULE_ID("$Id: safe_sprintf.c,v 1.33 2020/02/02 23:34:34 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
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 *)
222 NCURSES_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
279 NCURSES_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