1 /****************************************************************************
2 * Copyright 2020 Thomas E. Dickey *
3 * Copyright 1998-2004,2012 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 * State-machine fallback written by Thomas E. Dickey 2002 *
32 ****************************************************************************/
33
34 /*
35 * This function is needed to support vwscanw
36 */
37
38 #include <curses.priv.h>
39
40 #if !HAVE_VSSCANF
41
42 MODULE_ID("$Id: vsscanf.c,v 1.21 2020/02/02 23:34:34 tom Exp $")
43
44 #if !(HAVE_VFSCANF || HAVE__DOSCAN)
45
46 #include <ctype.h>
47
48 #define L_SQUARE '['
49 #define R_SQUARE ']'
50
51 typedef enum {
52 cUnknown
53 ,cError /* anything that isn't ANSI */
54 ,cAssigned
55 ,cChar
56 ,cInt
57 ,cFloat
58 ,cDouble
59 ,cPointer
60 ,cLong
61 ,cShort
62 ,cRange
63 ,cString
64 } ChunkType;
65
66 typedef enum {
67 oUnknown
68 ,oShort
69 ,oLong
70 } OtherType;
71
72 typedef enum {
73 sUnknown
74 ,sPercent /* last was '%' beginning a format */
75 ,sNormal /* ...somewhere in the middle */
76 ,sLeft /* last was left square bracket beginning a range */
77 ,sRange /* ...somewhere in the middle */
78 ,sFinal /* last finished a format */
79 } ScanState;
80
81 static ChunkType
final_ch(int ch,OtherType other)82 final_ch(int ch, OtherType other)
83 {
84 ChunkType result = cUnknown;
85
86 switch (ch) {
87 case 'c':
88 if (other == oUnknown)
89 result = cChar;
90 else
91 result = cError;
92 break;
93 case 'd':
94 case 'i':
95 case 'X':
96 case 'x':
97 switch (other) {
98 case oUnknown:
99 result = cInt;
100 break;
101 case oShort:
102 result = cShort;
103 break;
104 case oLong:
105 result = cLong;
106 break;
107 }
108 break;
109 case 'E':
110 case 'e':
111 case 'f':
112 case 'g':
113 switch (other) {
114 case oUnknown:
115 result = cFloat;
116 break;
117 case oShort:
118 result = cError;
119 break;
120 case oLong:
121 result = cDouble;
122 break;
123 }
124 break;
125 case 'n':
126 if (other == oUnknown)
127 result = cAssigned;
128 else
129 result = cError;
130 break;
131 case 'p':
132 if (other == oUnknown)
133 result = cPointer;
134 else
135 result = cError;
136 break;
137 case 's':
138 if (other == oUnknown)
139 result = cString;
140 else
141 result = cError;
142 break;
143 }
144 return result;
145 }
146
147 static OtherType
other_ch(int ch)148 other_ch(int ch)
149 {
150 OtherType result = oUnknown;
151 switch (ch) {
152 case 'h':
153 result = oShort;
154 break;
155 case 'l':
156 result = oLong;
157 break;
158 }
159 return result;
160 }
161 #endif
162
163 /*VARARGS2*/
164 NCURSES_EXPORT(int)
vsscanf(const char * str,const char * format,va_list ap)165 vsscanf(const char *str, const char *format, va_list ap)
166 {
167 #if HAVE_VFSCANF || HAVE__DOSCAN
168 /*
169 * This code should work on anything descended from AT&T SVr1.
170 */
171 FILE strbuf;
172
173 strbuf._flag = _IOREAD;
174 strbuf._ptr = strbuf._base = (unsigned char *) str;
175 strbuf._cnt = strlen(str);
176 strbuf._file = _NFILE;
177
178 #if HAVE_VFSCANF
179 return (vfscanf(&strbuf, format, ap));
180 #else
181 return (_doscan(&strbuf, format, ap));
182 #endif
183 #else
184 static int can_convert = -1;
185
186 int assigned = 0;
187 int consumed = 0;
188
189 T((T_CALLED("vsscanf(%s,%s,...)"),
190 _nc_visbuf2(1, str),
191 _nc_visbuf2(2, format)));
192
193 /*
194 * This relies on having a working "%n" format conversion. Check if it
195 * works. Only very old C libraries do not support it.
196 *
197 * FIXME: move this check into the configure script.
198 */
199 if (can_convert < 0) {
200 int check1;
201 int check2;
202 if (sscanf("123", "%d%n", &check1, &check2) > 0
203 && check1 == 123
204 && check2 == 3) {
205 can_convert = 1;
206 } else {
207 can_convert = 0;
208 }
209 }
210
211 if (can_convert) {
212 size_t len_fmt = strlen(format) + 32;
213 char *my_fmt = malloc(len_fmt);
214 ChunkType chunk, ctest;
215 OtherType other, otest;
216 ScanState state;
217 unsigned n;
218 int eaten;
219 void *pointer;
220
221 if (my_fmt != 0) {
222 /*
223 * Split the original format into chunks, adding a "%n" to the end
224 * of each (except of course if it used %n), and use that
225 * information to decide where to start scanning the next chunk.
226 *
227 * FIXME: does %n count bytes or characters? If the latter, this
228 * will require further work for multibyte strings.
229 */
230 while (*format != '\0') {
231 /* find a chunk */
232 state = sUnknown;
233 chunk = cUnknown;
234 other = oUnknown;
235 pointer = 0;
236 for (n = 0; format[n] != 0 && state != sFinal; ++n) {
237 my_fmt[n] = format[n];
238 switch (state) {
239 case sUnknown:
240 if (format[n] == '%')
241 state = sPercent;
242 break;
243 case sPercent:
244 if (format[n] == '%') {
245 state = sUnknown;
246 } else if (format[n] == L_SQUARE) {
247 state = sLeft;
248 } else {
249 state = sNormal;
250 --n;
251 }
252 break;
253 case sLeft:
254 state = sRange;
255 if (format[n] == '^') {
256 ++n;
257 my_fmt[n] = format[n];
258 }
259 break;
260 case sRange:
261 if (format[n] == R_SQUARE) {
262 state = sFinal;
263 chunk = cRange;
264 }
265 break;
266 case sNormal:
267 if (format[n] == '*') {
268 state = sUnknown;
269 } else {
270 if ((ctest = final_ch(format[n], other)) != cUnknown) {
271 state = sFinal;
272 chunk = ctest;
273 } else if ((otest = other_ch(format[n])) != oUnknown) {
274 other = otest;
275 } else if (isalpha(UChar(format[n]))) {
276 state = sFinal;
277 chunk = cError;
278 }
279 }
280 break;
281 case sFinal:
282 break;
283 }
284 }
285 my_fmt[n] = '\0';
286 format += n;
287
288 if (chunk == cUnknown
289 || chunk == cError) {
290 if (assigned == 0)
291 assigned = EOF;
292 break;
293 }
294
295 /* add %n, if the format was not that */
296 if (chunk != cAssigned) {
297 _nc_STRCAT(my_fmt, "%n", len_fmt);
298 }
299
300 switch (chunk) {
301 case cAssigned:
302 _nc_STRCAT(my_fmt, "%n", len_fmt);
303 pointer = &eaten;
304 break;
305 case cInt:
306 pointer = va_arg(ap, int *);
307 break;
308 case cShort:
309 pointer = va_arg(ap, short *);
310 break;
311 case cFloat:
312 pointer = va_arg(ap, float *);
313 break;
314 case cDouble:
315 pointer = va_arg(ap, double *);
316 break;
317 case cLong:
318 pointer = va_arg(ap, long *);
319 break;
320 case cPointer:
321 pointer = va_arg(ap, void *);
322 break;
323 case cChar:
324 case cRange:
325 case cString:
326 pointer = va_arg(ap, char *);
327 break;
328 case cError:
329 case cUnknown:
330 break;
331 }
332 /* do the conversion */
333 T(("...converting chunk #%d type %d(%s,%s)",
334 assigned + 1, chunk,
335 _nc_visbuf2(1, str + consumed),
336 _nc_visbuf2(2, my_fmt)));
337 if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
338 consumed += eaten;
339 else
340 break;
341 ++assigned;
342 }
343 free(my_fmt);
344 }
345 }
346 returnCode(assigned);
347 #endif
348 }
349 #else
350 extern
351 NCURSES_EXPORT(void)
352 _nc_vsscanf(void); /* quiet's gcc warning */
353 NCURSES_EXPORT(void)
_nc_vsscanf(void)354 _nc_vsscanf(void)
355 {
356 } /* nonempty for strict ANSI compilers */
357 #endif /* !HAVE_VSSCANF */
358