ncurses 6.0 - patch 20170311
[ncurses.git] / ncurses / base / safe_sprintf.c
1 /****************************************************************************
2  * Copyright (c) 1998-2012,2013 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        1997-on                                 *
31  ****************************************************************************/
32
33 #include <curses.priv.h>
34 #include <ctype.h>
35
36 MODULE_ID("$Id: safe_sprintf.c,v 1.27 2013/01/20 01:04:32 tom Exp $")
37
38 #if USE_SAFE_SPRINTF
39
40 typedef 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  */
52 static 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                     _nc_SPRINTF(fmt_arg,
113                                 _nc_SLIMIT(sizeof(fmt_arg))
114                                 "%d", ival);
115                     fmt_len += strlen(fmt_arg);
116                     if ((format = _nc_doalloc(format, fmt_len)) == 0) {
117                         free(buffer);
118                         return -1;
119                     }
120                     --f;
121                     _nc_STRCPY(&format[f], fmt_arg, fmt_len - f);
122                     f = strlen(format);
123                 } else if (isalpha(UChar(*fmt))) {
124                     done = TRUE;
125                     switch (*fmt) {
126                     case 'Z':   /* FALLTHRU */
127                     case 'h':   /* FALLTHRU */
128                     case 'l':   /* FALLTHRU */
129                         done = FALSE;
130                         type = *fmt;
131                         break;
132                     case 'i':   /* FALLTHRU */
133                     case 'd':   /* FALLTHRU */
134                     case 'u':   /* FALLTHRU */
135                     case 'x':   /* FALLTHRU */
136                     case 'X':   /* FALLTHRU */
137                         if (type == 'l')
138                             VA_INTGR(long);
139                         else if (type == 'Z')
140                             VA_INTGR(size_t);
141                         else
142                             VA_INTGR(int);
143                         used = 'i';
144                         break;
145                     case 'f':   /* FALLTHRU */
146                     case 'e':   /* FALLTHRU */
147                     case 'E':   /* FALLTHRU */
148                     case 'g':   /* FALLTHRU */
149                     case 'G':   /* FALLTHRU */
150                         VA_FLOAT(double);
151                         used = 'f';
152                         break;
153                     case 'c':
154                         VA_INTGR(int);
155                         used = 'i';
156                         break;
157                     case 's':
158                         VA_POINT(char *);
159                         if (prec < 0)
160                             prec = strlen(pval);
161                         if (prec > (int) length) {
162                             length = length + prec;
163                             buffer = typeRealloc(char, length, buffer);
164                             if (buffer == 0) {
165                                 free(format);
166                                 return -1;
167                             }
168                         }
169                         used = 'p';
170                         break;
171                     case 'p':
172                         VA_POINT(void *);
173                         used = 'p';
174                         break;
175                     case 'n':
176                         VA_POINT(int *);
177                         used = 0;
178                         break;
179                     default:
180                         break;
181                     }
182                 } else if (*fmt == '.') {
183                     state = Prec;
184                 } else if (*fmt == '%') {
185                     done = TRUE;
186                     used = 'p';
187                 }
188             }
189             format[f] = '\0';
190             switch (used) {
191             case 'i':
192                 _nc_SPRINTF(buffer, _nc_SLIMIT(length) format, ival);
193                 break;
194             case 'f':
195                 _nc_SPRINTF(buffer, _nc_SLIMIT(length) format, fval);
196                 break;
197             default:
198                 _nc_SPRINTF(buffer, _nc_SLIMIT(length) format, pval);
199                 break;
200             }
201             len += (int) strlen(buffer);
202         } else {
203             fmt++;
204             len++;
205         }
206     }
207
208     free(buffer);
209     free(format);
210     return len;
211 }
212 #endif
213
214 #define my_buffer _nc_globals.safeprint_buf
215 #define my_length _nc_globals.safeprint_used
216
217 /*
218  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
219  */
220 NCURSES_EXPORT(char *)
221 NCURSES_SP_NAME(_nc_printf_string) (NCURSES_SP_DCLx
222                                     const char *fmt,
223                                     va_list ap)
224 {
225     char *result = 0;
226
227     if (fmt != 0) {
228 #if USE_SAFE_SPRINTF
229         va_list ap2;
230         int len;
231
232         begin_va_copy(ap2, ap);
233         len = _nc_printf_length(fmt, ap2);
234         end_va_copy(ap2);
235
236         if ((int) my_length < len + 1) {
237             my_length = 2 * (len + 1);
238             my_buffer = typeRealloc(char, my_length, my_buffer);
239         }
240         if (my_buffer != 0) {
241             *my_buffer = '\0';
242             if (len >= 0) {
243                 vsprintf(my_buffer, fmt, ap);
244             }
245             result = my_buffer;
246         }
247 #else
248 #define MyCols _nc_globals.safeprint_cols
249 #define MyRows _nc_globals.safeprint_rows
250
251         if (screen_lines(SP_PARM) > MyRows || screen_columns(SP_PARM) > MyCols) {
252             if (screen_lines(SP_PARM) > MyRows)
253                 MyRows = screen_lines(SP_PARM);
254             if (screen_columns(SP_PARM) > MyCols)
255                 MyCols = screen_columns(SP_PARM);
256             my_length = (size_t) (MyRows * (MyCols + 1)) + 1;
257             my_buffer = typeRealloc(char, my_length, my_buffer);
258         }
259
260         if (my_buffer != 0) {
261 # if HAVE_VSNPRINTF
262             vsnprintf(my_buffer, my_length, fmt, ap);   /* GNU extension */
263 # else
264             vsprintf(my_buffer, fmt, ap);       /* ANSI */
265 # endif
266             result = my_buffer;
267         }
268 #endif
269     } else if (my_buffer != 0) {        /* see _nc_freeall() */
270         free(my_buffer);
271         my_buffer = 0;
272         my_length = 0;
273     }
274     return result;
275 }
276
277 #if NCURSES_SP_FUNCS
278 NCURSES_EXPORT(char *)
279 _nc_printf_string(const char *fmt, va_list ap)
280 {
281     return NCURSES_SP_NAME(_nc_printf_string) (CURRENT_SCREEN, fmt, ap);
282 }
283 #endif