1 |
|
|
/* $OpenBSD: vsscanf.c,v 1.2 2015/09/27 05:25:00 guenther Exp $ */ |
2 |
|
|
|
3 |
|
|
/**************************************************************************** |
4 |
|
|
* Copyright (c) 1998-2003,2004 Free Software Foundation, Inc. * |
5 |
|
|
* * |
6 |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a * |
7 |
|
|
* copy of this software and associated documentation files (the * |
8 |
|
|
* "Software"), to deal in the Software without restriction, including * |
9 |
|
|
* without limitation the rights to use, copy, modify, merge, publish, * |
10 |
|
|
* distribute, distribute with modifications, sublicense, and/or sell * |
11 |
|
|
* copies of the Software, and to permit persons to whom the Software is * |
12 |
|
|
* furnished to do so, subject to the following conditions: * |
13 |
|
|
* * |
14 |
|
|
* The above copyright notice and this permission notice shall be included * |
15 |
|
|
* in all copies or substantial portions of the Software. * |
16 |
|
|
* * |
17 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * |
18 |
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * |
19 |
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * |
20 |
|
|
* IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * |
21 |
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * |
22 |
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * |
23 |
|
|
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. * |
24 |
|
|
* * |
25 |
|
|
* Except as contained in this notice, the name(s) of the above copyright * |
26 |
|
|
* holders shall not be used in advertising or otherwise to promote the * |
27 |
|
|
* sale, use or other dealings in this Software without prior written * |
28 |
|
|
* authorization. * |
29 |
|
|
****************************************************************************/ |
30 |
|
|
|
31 |
|
|
/**************************************************************************** |
32 |
|
|
* State-machine fallback written by Thomas E. Dickey 2002 * |
33 |
|
|
****************************************************************************/ |
34 |
|
|
|
35 |
|
|
/* |
36 |
|
|
* This function is needed to support vwscanw |
37 |
|
|
*/ |
38 |
|
|
|
39 |
|
|
#include <curses.priv.h> |
40 |
|
|
|
41 |
|
|
#if !HAVE_VSSCANF |
42 |
|
|
|
43 |
|
|
MODULE_ID("$Id: vsscanf.c,v 1.2 2015/09/27 05:25:00 guenther Exp $") |
44 |
|
|
|
45 |
|
|
#if !(HAVE_VFSCANF || HAVE__DOSCAN) |
46 |
|
|
|
47 |
|
|
#include <ctype.h> |
48 |
|
|
|
49 |
|
|
#define L_SQUARE '[' |
50 |
|
|
#define R_SQUARE ']' |
51 |
|
|
|
52 |
|
|
typedef enum { |
53 |
|
|
cUnknown |
54 |
|
|
,cError /* anything that isn't ANSI */ |
55 |
|
|
,cAssigned |
56 |
|
|
,cChar |
57 |
|
|
,cInt |
58 |
|
|
,cFloat |
59 |
|
|
,cDouble |
60 |
|
|
,cPointer |
61 |
|
|
,cLong |
62 |
|
|
,cShort |
63 |
|
|
,cRange |
64 |
|
|
,cString |
65 |
|
|
} ChunkType; |
66 |
|
|
|
67 |
|
|
typedef enum { |
68 |
|
|
oUnknown |
69 |
|
|
,oShort |
70 |
|
|
,oLong |
71 |
|
|
} OtherType; |
72 |
|
|
|
73 |
|
|
typedef enum { |
74 |
|
|
sUnknown |
75 |
|
|
,sPercent /* last was '%' beginning a format */ |
76 |
|
|
,sNormal /* ...somewhere in the middle */ |
77 |
|
|
,sLeft /* last was left square bracket beginning a range */ |
78 |
|
|
,sRange /* ...somewhere in the middle */ |
79 |
|
|
,sFinal /* last finished a format */ |
80 |
|
|
} ScanState; |
81 |
|
|
|
82 |
|
|
static ChunkType |
83 |
|
|
final_ch(int ch, OtherType other) |
84 |
|
|
{ |
85 |
|
|
ChunkType result = cUnknown; |
86 |
|
|
|
87 |
|
|
switch (ch) { |
88 |
|
|
case 'c': |
89 |
|
|
if (other == oUnknown) |
90 |
|
|
result = cChar; |
91 |
|
|
else |
92 |
|
|
result = cError; |
93 |
|
|
break; |
94 |
|
|
case 'd': |
95 |
|
|
case 'i': |
96 |
|
|
case 'X': |
97 |
|
|
case 'x': |
98 |
|
|
switch (other) { |
99 |
|
|
case oUnknown: |
100 |
|
|
result = cInt; |
101 |
|
|
break; |
102 |
|
|
case oShort: |
103 |
|
|
result = cShort; |
104 |
|
|
break; |
105 |
|
|
case oLong: |
106 |
|
|
result = cLong; |
107 |
|
|
break; |
108 |
|
|
} |
109 |
|
|
break; |
110 |
|
|
case 'E': |
111 |
|
|
case 'e': |
112 |
|
|
case 'f': |
113 |
|
|
case 'g': |
114 |
|
|
switch (other) { |
115 |
|
|
case oUnknown: |
116 |
|
|
result = cFloat; |
117 |
|
|
break; |
118 |
|
|
case oShort: |
119 |
|
|
result = cError; |
120 |
|
|
break; |
121 |
|
|
case oLong: |
122 |
|
|
result = cDouble; |
123 |
|
|
break; |
124 |
|
|
} |
125 |
|
|
break; |
126 |
|
|
case 'n': |
127 |
|
|
if (other == oUnknown) |
128 |
|
|
result = cAssigned; |
129 |
|
|
else |
130 |
|
|
result = cError; |
131 |
|
|
break; |
132 |
|
|
case 'p': |
133 |
|
|
if (other == oUnknown) |
134 |
|
|
result = cPointer; |
135 |
|
|
else |
136 |
|
|
result = cError; |
137 |
|
|
break; |
138 |
|
|
case 's': |
139 |
|
|
if (other == oUnknown) |
140 |
|
|
result = cString; |
141 |
|
|
else |
142 |
|
|
result = cError; |
143 |
|
|
break; |
144 |
|
|
} |
145 |
|
|
return result; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
static OtherType |
149 |
|
|
other_ch(int ch) |
150 |
|
|
{ |
151 |
|
|
OtherType result = oUnknown; |
152 |
|
|
switch (ch) { |
153 |
|
|
case 'h': |
154 |
|
|
result = oShort; |
155 |
|
|
break; |
156 |
|
|
case 'l': |
157 |
|
|
result = oLong; |
158 |
|
|
break; |
159 |
|
|
} |
160 |
|
|
return result; |
161 |
|
|
} |
162 |
|
|
#endif |
163 |
|
|
|
164 |
|
|
NCURSES_EXPORT(int) |
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 |
|
|
strlcat(my_fmt, "%n", len_fmt); |
298 |
|
|
} |
299 |
|
|
|
300 |
|
|
switch (chunk) { |
301 |
|
|
case cAssigned: |
302 |
|
|
strlcat(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) |
354 |
|
|
_nc_vsscanf(void) |
355 |
|
|
{ |
356 |
|
|
} /* nonempty for strict ANSI compilers */ |
357 |
|
|
#endif /* !HAVE_VSSCANF */ |