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