e4d525272627b7c153c99d4bdb036b21e6965254
[ncurses.git] / ncurses / base / safe_sprintf.c
1 /****************************************************************************
2  * Copyright (c) 1998,1999 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.11 1999/09/11 18:03:27 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                                                 done = FALSE;
116                                                 type = *fmt;
117                                                 break;
118                                         case 'i': /* FALLTHRU */
119                                         case 'd': /* FALLTHRU */
120                                         case 'u': /* FALLTHRU */
121                                         case 'x': /* FALLTHRU */
122                                         case 'X': /* FALLTHRU */
123                                                 if (type == 'l')
124                                                         VA_INTGR(long);
125                                                 else if (type == 'Z')
126                                                         VA_INTGR(size_t);
127                                                 else
128                                                         VA_INTGR(int);
129                                                 used = 'i';
130                                                 break;
131                                         case 'f': /* FALLTHRU */
132                                         case 'e': /* FALLTHRU */
133                                         case 'E': /* FALLTHRU */
134                                         case 'g': /* FALLTHRU */
135                                         case 'G': /* FALLTHRU */
136                                                 VA_FLOAT(double);
137                                                 used = 'f';
138                                                 break;
139                                         case 'c':
140                                                 VA_INTGR(int);
141                                                 used = 'i';
142                                                 break;
143                                         case 's':
144                                                 VA_POINT(char *);
145                                                 if (prec < 0)
146                                                         prec = strlen(pval);
147                                                 if (prec > (int)length) {
148                                                         length = length + prec;
149                                                         buffer = typeRealloc(char, length, buffer);
150                                                         if (buffer == 0) {
151                                                                 free(format);
152                                                                 return -1;
153                                                         }
154                                                 }
155                                                 used = 'p';
156                                                 break;
157                                         case 'p':
158                                                 VA_POINT(void *);
159                                                 used = 'p';
160                                                 break;
161                                         case 'n':
162                                                 VA_POINT(int *);
163                                                 used = 0;
164                                                 break;
165                                         default:
166                                                 break;
167                                         }
168                                 } else if (*fmt == '.') {
169                                         state = Prec;
170                                 } else if (*fmt == '%') {
171                                         done = TRUE;
172                                         used = 'p';
173                                 }
174                         }
175                         format[f] = '\0';
176                         switch (used) {
177                         case 'i':
178                                 sprintf(buffer, format, ival);
179                                 break;
180                         case 'f':
181                                 sprintf(buffer, format, fval);
182                                 break;
183                         default:
184                                 sprintf(buffer, format, pval);
185                                 break;
186                         }
187                         len += (int)strlen(buffer);
188                 } else {
189                         fmt++;
190                         len++;
191                 }
192         }
193
194         free(buffer);
195         free(format);
196         return len;
197 }
198 #endif
199
200 /*
201  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
202  */
203 char *
204 _nc_printf_string(const char *fmt, va_list ap)
205 {
206 #if USE_SAFE_SPRINTF
207         char *buf = 0;
208         int len = _nc_printf_length(fmt, ap);
209
210         if (len > 0) {
211                 if ((buf = typeMalloc(char, len+1)) == 0)
212                         return(0);
213                 vsprintf(buf, fmt, ap);
214         }
215 #else
216         static int rows, cols;
217         static char *buf;
218         static size_t len;
219
220         if (screen_lines > rows || screen_columns > cols) {
221                 if (screen_lines   > rows) rows = screen_lines;
222                 if (screen_columns > cols) cols = screen_columns;
223                 len = (rows * (cols + 1)) + 1;
224                 buf = typeRealloc(char, len, buf);
225                 if (buf == 0) {
226                         return(0);
227                 }
228         }
229
230         if (buf != 0) {
231 # if HAVE_VSNPRINTF
232                 vsnprintf(buf, len, fmt, ap);   /* GNU extension */
233 # else
234                 vsprintf(buf, fmt, ap);         /* ANSI */
235 # endif
236         }
237 #endif
238         return buf;
239 }