• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * vsscanf.c
3  *
4  * vsscanf(), from which the rest of the scanf()
5  * family is built
6  */
7 
8 #include <ctype.h>
9 #include <stdarg.h>
10 #include <stddef.h>
11 #include <inttypes.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <stdio.h>
15 #include <sys/bitops.h>
16 
17 #ifndef LONG_BIT
18 #define LONG_BIT (CHAR_BIT*sizeof(long))
19 #endif
20 
21 enum flags {
22     FL_SPLAT = 0x01,		/* Drop the value, do not assign */
23     FL_WIDTH = 0x02,		/* Field width specified */
24     FL_MINUS = 0x04,		/* Negative number */
25 };
26 
27 enum ranks {
28     rank_char = -2,
29     rank_short = -1,
30     rank_int = 0,
31     rank_long = 1,
32     rank_longlong = 2,
33     rank_ptr = INT_MAX		/* Special value used for pointers */
34 };
35 
36 #define MIN_RANK	rank_char
37 #define MAX_RANK	rank_longlong
38 
39 #define INTMAX_RANK	rank_longlong
40 #define SIZE_T_RANK	rank_long
41 #define PTRDIFF_T_RANK	rank_long
42 
43 enum bail {
44     bail_none = 0,		/* No error condition */
45     bail_eof,			/* Hit EOF */
46     bail_err			/* Conversion mismatch */
47 };
48 
vsscanf(const char * buffer,const char * format,va_list ap)49 int vsscanf(const char *buffer, const char *format, va_list ap)
50 {
51     const char *p = format;
52     char ch;
53     const char *q = buffer;
54     const char *qq;
55     uintmax_t val = 0;
56     int rank = rank_int;	/* Default rank */
57     unsigned int width = UINT_MAX;
58     int base;
59     enum flags flags = 0;
60     enum {
61 	st_normal,		/* Ground state */
62 	st_flags,		/* Special flags */
63 	st_width,		/* Field width */
64 	st_modifiers,		/* Length or conversion modifiers */
65 	st_match_init,		/* Initial state of %[ sequence */
66 	st_match,		/* Main state of %[ sequence */
67 	st_match_range,		/* After - in a %[ sequence */
68     } state = st_normal;
69     char *sarg = NULL;		/* %s %c or %[ string argument */
70     enum bail bail = bail_none;
71     int converted = 0;		/* Successful conversions */
72     unsigned long matchmap[((1 << CHAR_BIT) + (LONG_BIT - 1)) / LONG_BIT];
73     int matchinv = 0;		/* Is match map inverted? */
74     unsigned char range_start = 0;
75 
76     while ((ch = *p++) && !bail) {
77 	switch (state) {
78 	case st_normal:
79 	    if (ch == '%') {
80 		state = st_flags;
81 		flags = 0;
82 		rank = rank_int;
83 		width = UINT_MAX;
84 	    } else if (isspace((unsigned char)ch)) {
85 		q = skipspace(q);
86 	    } else {
87 		if (*q == ch)
88 		    q++;
89 		else
90 		    bail = bail_err;	/* Match failure */
91 	    }
92 	    break;
93 
94 	case st_flags:
95 	    switch (ch) {
96 	    case '*':
97 		flags |= FL_SPLAT;
98 		break;
99 	    case '0' ... '9':
100 		width = (ch - '0');
101 		state = st_width;
102 		flags |= FL_WIDTH;
103 		break;
104 	    default:
105 		state = st_modifiers;
106 		p--;		/* Process this character again */
107 		break;
108 	    }
109 	    break;
110 
111 	case st_width:
112 	    if (ch >= '0' && ch <= '9') {
113 		width = width * 10 + (ch - '0');
114 	    } else {
115 		state = st_modifiers;
116 		p--;		/* Process this character again */
117 	    }
118 	    break;
119 
120 	case st_modifiers:
121 	    switch (ch) {
122 		/* Length modifiers - nonterminal sequences */
123 	    case 'h':
124 		rank--;		/* Shorter rank */
125 		break;
126 	    case 'l':
127 		rank++;		/* Longer rank */
128 		break;
129 	    case 'j':
130 		rank = INTMAX_RANK;
131 		break;
132 	    case 'z':
133 		rank = SIZE_T_RANK;
134 		break;
135 	    case 't':
136 		rank = PTRDIFF_T_RANK;
137 		break;
138 	    case 'L':
139 	    case 'q':
140 		rank = rank_longlong;	/* long double/long long */
141 		break;
142 
143 	    default:
144 		/* Output modifiers - terminal sequences */
145 		state = st_normal;	/* Next state will be normal */
146 		if (rank < MIN_RANK)	/* Canonicalize rank */
147 		    rank = MIN_RANK;
148 		else if (rank > MAX_RANK)
149 		    rank = MAX_RANK;
150 
151 		switch (ch) {
152 		case 'P':	/* Upper case pointer */
153 		case 'p':	/* Pointer */
154 #if 0				/* Enable this to allow null pointers by name */
155 		    q = skipspace(q);
156 		    if (!isdigit((unsigned char)*q)) {
157 			static const char *const nullnames[] =
158 			    { "null", "nul", "nil", "(null)", "(nul)", "(nil)",
159 0 };
160 			const char *const *np;
161 
162 			/* Check to see if it's a null pointer by name */
163 			for (np = nullnames; *np; np++) {
164 			    if (!strncasecmp(q, *np, strlen(*np))) {
165 				val = (uintmax_t) ((void *)NULL);
166 				goto set_integer;
167 			    }
168 			}
169 			/* Failure */
170 			bail = bail_err;
171 			break;
172 		    }
173 		    /* else */
174 #endif
175 		    rank = rank_ptr;
176 		    base = 0;
177 		    goto scan_int;
178 
179 		case 'i':	/* Base-independent integer */
180 		    base = 0;
181 		    goto scan_int;
182 
183 		case 'd':	/* Decimal integer */
184 		    base = 10;
185 		    goto scan_int;
186 
187 		case 'o':	/* Octal integer */
188 		    base = 8;
189 		    goto scan_int;
190 
191 		case 'u':	/* Unsigned decimal integer */
192 		    base = 10;
193 		    goto scan_int;
194 
195 		case 'x':	/* Hexadecimal integer */
196 		case 'X':
197 		    base = 16;
198 		    goto scan_int;
199 
200 		case 'n':	/* Number of characters consumed */
201 		    val = (q - buffer);
202 		    goto set_integer;
203 
204 scan_int:
205 		    q = skipspace(q);
206 		    if (!*q) {
207 			bail = bail_eof;
208 			break;
209 		    }
210 		    val = strntoumax(q, (char **)&qq, base, width);
211 		    if (qq == q) {
212 			bail = bail_err;
213 			break;
214 		    }
215 		    q = qq;
216 		    converted++;
217 		    /* fall through */
218 
219 set_integer:
220 		    if (!(flags & FL_SPLAT)) {
221 			switch (rank) {
222 			case rank_char:
223 			    *va_arg(ap, unsigned char *) = (unsigned char)val;
224 			    break;
225 			case rank_short:
226 			    *va_arg(ap, unsigned short *) = (unsigned short)val;
227 			    break;
228 			case rank_int:
229 			    *va_arg(ap, unsigned int *) = (unsigned int)val;
230 			    break;
231 			case rank_long:
232 			    *va_arg(ap, unsigned long *) = (unsigned long)val;
233 			    break;
234 			case rank_longlong:
235 			    *va_arg(ap, unsigned long long *) =
236 				(unsigned long long)val;
237 			    break;
238 			case rank_ptr:
239 			    *va_arg(ap, void **) = (void *)(uintptr_t) val;
240 			    break;
241 			}
242 		    }
243 		    break;
244 
245 		case 'c':	/* Character */
246 		    width = (flags & FL_WIDTH) ? width : 1;	/* Default width == 1 */
247 		    sarg = va_arg(ap, char *);
248 		    while (width--) {
249 			if (!*q) {
250 			    bail = bail_eof;
251 			    break;
252 			}
253 			*sarg++ = *q++;
254 		    }
255 		    if (!bail)
256 			converted++;
257 		    break;
258 
259 		case 's':	/* String */
260 		    {
261 			char *sp;
262 			sp = sarg = va_arg(ap, char *);
263 			while (width-- && *q && !isspace((unsigned char)*q)) {
264 			    *sp++ = *q++;
265 			}
266 			if (sarg != sp) {
267 			    *sp = '\0';	/* Terminate output */
268 			    converted++;
269 			} else {
270 			    bail = bail_eof;
271 			}
272 		    }
273 		    break;
274 
275 		case '[':	/* Character range */
276 		    sarg = va_arg(ap, char *);
277 		    state = st_match_init;
278 		    matchinv = 0;
279 		    memset(matchmap, 0, sizeof matchmap);
280 		    break;
281 
282 		case '%':	/* %% sequence */
283 		    if (*q == '%')
284 			q++;
285 		    else
286 			bail = bail_err;
287 		    break;
288 
289 		default:	/* Anything else */
290 		    bail = bail_err;	/* Unknown sequence */
291 		    break;
292 		}
293 	    }
294 	    break;
295 
296 	case st_match_init:	/* Initial state for %[ match */
297 	    if (ch == '^' && !matchinv) {
298 		matchinv = 1;
299 	    } else {
300 		range_start = (unsigned char)ch;
301 		set_bit((unsigned char)ch, matchmap);
302 		state = st_match;
303 	    }
304 	    break;
305 
306 	case st_match:		/* Main state for %[ match */
307 	    if (ch == ']') {
308 		goto match_run;
309 	    } else if (ch == '-') {
310 		state = st_match_range;
311 	    } else {
312 		range_start = (unsigned char)ch;
313 		set_bit((unsigned char)ch, matchmap);
314 	    }
315 	    break;
316 
317 	case st_match_range:	/* %[ match after - */
318 	    if (ch == ']') {
319 		set_bit((unsigned char)'-', matchmap);	/* - was last character */
320 		goto match_run;
321 	    } else {
322 		int i;
323 		for (i = range_start; i <= (unsigned char)ch; i++)
324 		    set_bit(i, matchmap);
325 		state = st_match;
326 	    }
327 	    break;
328 
329 match_run:			/* Match expression finished */
330 	    qq = q;
331 	    while (width && *q
332 		   && test_bit((unsigned char)*q, matchmap) ^ matchinv) {
333 		*sarg++ = *q++;
334 	    }
335 	    if (q != qq) {
336 		*sarg = '\0';
337 		converted++;
338 	    } else {
339 		bail = *q ? bail_err : bail_eof;
340 	    }
341 	    break;
342 	}
343     }
344 
345     if (bail == bail_eof && !converted)
346 	converted = -1;		/* Return EOF (-1) */
347 
348     return converted;
349 }
350