1 /* $OpenBSD: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $ */
2
3 /****************************************************************************
4 * Copyright 2020 Thomas E. Dickey *
5 * Copyright 1998-2004,2012 Free Software Foundation, Inc. *
6 * *
7 * Permission is hereby granted, free of charge, to any person obtaining a *
8 * copy of this software and associated documentation files (the *
9 * "Software"), to deal in the Software without restriction, including *
10 * without limitation the rights to use, copy, modify, merge, publish, *
11 * distribute, distribute with modifications, sublicense, and/or sell *
12 * copies of the Software, and to permit persons to whom the Software is *
13 * furnished to do so, subject to the following conditions: *
14 * *
15 * The above copyright notice and this permission notice shall be included *
16 * in all copies or substantial portions of the Software. *
17 * *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
25 * *
26 * Except as contained in this notice, the name(s) of the above copyright *
27 * holders shall not be used in advertising or otherwise to promote the *
28 * sale, use or other dealings in this Software without prior written *
29 * authorization. *
30 ****************************************************************************/
31
32 /****************************************************************************
33 * State-machine fallback written by Thomas E. Dickey 2002 *
34 ****************************************************************************/
35
36 /*
37 * This function is needed to support vwscanw
38 */
39
40 #include <curses.priv.h>
41
42 #if !HAVE_VSSCANF
43
44 MODULE_ID("$Id: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $")
45
46 #if !(HAVE_VFSCANF || HAVE__DOSCAN)
47
48 #include <ctype.h>
49
50 #define L_SQUARE '['
51 #define R_SQUARE ']'
52
53 typedef enum {
54 cUnknown
55 ,cError /* anything that isn't ANSI */
56 ,cAssigned
57 ,cChar
58 ,cInt
59 ,cFloat
60 ,cDouble
61 ,cPointer
62 ,cLong
63 ,cShort
64 ,cRange
65 ,cString
66 } ChunkType;
67
68 typedef enum {
69 oUnknown
70 ,oShort
71 ,oLong
72 } OtherType;
73
74 typedef enum {
75 sUnknown
76 ,sPercent /* last was '%' beginning a format */
77 ,sNormal /* ...somewhere in the middle */
78 ,sLeft /* last was left square bracket beginning a range */
79 ,sRange /* ...somewhere in the middle */
80 ,sFinal /* last finished a format */
81 } ScanState;
82
83 static ChunkType
final_ch(int ch,OtherType other)84 final_ch(int ch, OtherType other)
85 {
86 ChunkType result = cUnknown;
87
88 switch (ch) {
89 case 'c':
90 if (other == oUnknown)
91 result = cChar;
92 else
93 result = cError;
94 break;
95 case 'd':
96 case 'i':
97 case 'X':
98 case 'x':
99 switch (other) {
100 case oUnknown:
101 result = cInt;
102 break;
103 case oShort:
104 result = cShort;
105 break;
106 case oLong:
107 result = cLong;
108 break;
109 }
110 break;
111 case 'E':
112 case 'e':
113 case 'f':
114 case 'g':
115 switch (other) {
116 case oUnknown:
117 result = cFloat;
118 break;
119 case oShort:
120 result = cError;
121 break;
122 case oLong:
123 result = cDouble;
124 break;
125 }
126 break;
127 case 'n':
128 if (other == oUnknown)
129 result = cAssigned;
130 else
131 result = cError;
132 break;
133 case 'p':
134 if (other == oUnknown)
135 result = cPointer;
136 else
137 result = cError;
138 break;
139 case 's':
140 if (other == oUnknown)
141 result = cString;
142 else
143 result = cError;
144 break;
145 }
146 return result;
147 }
148
149 static OtherType
other_ch(int ch)150 other_ch(int ch)
151 {
152 OtherType result = oUnknown;
153 switch (ch) {
154 case 'h':
155 result = oShort;
156 break;
157 case 'l':
158 result = oLong;
159 break;
160 }
161 return result;
162 }
163 #endif
164
165 /*VARARGS2*/
166 NCURSES_EXPORT(int)
vsscanf(const char * str,const char * format,va_list ap)167 vsscanf(const char *str, const char *format, va_list ap)
168 {
169 #if HAVE_VFSCANF || HAVE__DOSCAN
170 /*
171 * This code should work on anything descended from AT&T SVr1.
172 */
173 FILE strbuf;
174
175 strbuf._flag = _IOREAD;
176 strbuf._ptr = strbuf._base = (unsigned char *) str;
177 strbuf._cnt = strlen(str);
178 strbuf._file = _NFILE;
179
180 #if HAVE_VFSCANF
181 return (vfscanf(&strbuf, format, ap));
182 #else
183 return (_doscan(&strbuf, format, ap));
184 #endif
185 #else
186 static int can_convert = -1;
187
188 int assigned = 0;
189 int consumed = 0;
190
191 T((T_CALLED("vsscanf(%s,%s,...)"),
192 _nc_visbuf2(1, str),
193 _nc_visbuf2(2, format)));
194
195 /*
196 * This relies on having a working "%n" format conversion. Check if it
197 * works. Only very old C libraries do not support it.
198 *
199 * FIXME: move this check into the configure script.
200 */
201 if (can_convert < 0) {
202 int check1;
203 int check2;
204 if (sscanf("123", "%d%n", &check1, &check2) > 0
205 && check1 == 123
206 && check2 == 3) {
207 can_convert = 1;
208 } else {
209 can_convert = 0;
210 }
211 }
212
213 if (can_convert) {
214 size_t len_fmt = strlen(format) + 32;
215 char *my_fmt = malloc(len_fmt);
216 ChunkType chunk, ctest;
217 OtherType other, otest;
218 ScanState state;
219 unsigned n;
220 int eaten;
221 void *pointer;
222
223 if (my_fmt != 0) {
224 /*
225 * Split the original format into chunks, adding a "%n" to the end
226 * of each (except of course if it used %n), and use that
227 * information to decide where to start scanning the next chunk.
228 *
229 * FIXME: does %n count bytes or characters? If the latter, this
230 * will require further work for multibyte strings.
231 */
232 while (*format != '\0') {
233 /* find a chunk */
234 state = sUnknown;
235 chunk = cUnknown;
236 other = oUnknown;
237 pointer = 0;
238 for (n = 0; format[n] != 0 && state != sFinal; ++n) {
239 my_fmt[n] = format[n];
240 switch (state) {
241 case sUnknown:
242 if (format[n] == '%')
243 state = sPercent;
244 break;
245 case sPercent:
246 if (format[n] == '%') {
247 state = sUnknown;
248 } else if (format[n] == L_SQUARE) {
249 state = sLeft;
250 } else {
251 state = sNormal;
252 --n;
253 }
254 break;
255 case sLeft:
256 state = sRange;
257 if (format[n] == '^') {
258 ++n;
259 my_fmt[n] = format[n];
260 }
261 break;
262 case sRange:
263 if (format[n] == R_SQUARE) {
264 state = sFinal;
265 chunk = cRange;
266 }
267 break;
268 case sNormal:
269 if (format[n] == '*') {
270 state = sUnknown;
271 } else {
272 if ((ctest = final_ch(format[n], other)) != cUnknown) {
273 state = sFinal;
274 chunk = ctest;
275 } else if ((otest = other_ch(format[n])) != oUnknown) {
276 other = otest;
277 } else if (isalpha(UChar(format[n]))) {
278 state = sFinal;
279 chunk = cError;
280 }
281 }
282 break;
283 case sFinal:
284 break;
285 }
286 }
287 my_fmt[n] = '\0';
288 format += n;
289
290 if (chunk == cUnknown
291 || chunk == cError) {
292 if (assigned == 0)
293 assigned = EOF;
294 break;
295 }
296
297 /* add %n, if the format was not that */
298 if (chunk != cAssigned) {
299 _nc_STRCAT(my_fmt, "%n", len_fmt);
300 }
301
302 switch (chunk) {
303 case cAssigned:
304 _nc_STRCAT(my_fmt, "%n", len_fmt);
305 pointer = &eaten;
306 break;
307 case cInt:
308 pointer = va_arg(ap, int *);
309 break;
310 case cShort:
311 pointer = va_arg(ap, short *);
312 break;
313 case cFloat:
314 pointer = va_arg(ap, float *);
315 break;
316 case cDouble:
317 pointer = va_arg(ap, double *);
318 break;
319 case cLong:
320 pointer = va_arg(ap, long *);
321 break;
322 case cPointer:
323 pointer = va_arg(ap, void *);
324 break;
325 case cChar:
326 case cRange:
327 case cString:
328 pointer = va_arg(ap, char *);
329 break;
330 case cError:
331 case cUnknown:
332 break;
333 }
334 /* do the conversion */
335 T(("...converting chunk #%d type %d(%s,%s)",
336 assigned + 1, chunk,
337 _nc_visbuf2(1, str + consumed),
338 _nc_visbuf2(2, my_fmt)));
339 if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
340 consumed += eaten;
341 else
342 break;
343 ++assigned;
344 }
345 free(my_fmt);
346 }
347 }
348 returnCode(assigned);
349 #endif
350 }
351 #else
352 extern
353 NCURSES_EXPORT(void)
354 _nc_vsscanf(void); /* quiet's gcc warning */
355 NCURSES_EXPORT(void)
_nc_vsscanf(void)356 _nc_vsscanf(void)
357 {
358 } /* nonempty for strict ANSI compilers */
359 #endif /* !HAVE_VSSCANF */
360