Initial revision
[ssr] / StraySrc / Libraries / Steel / c / vsscanf
1 /*
2 * vsscanf
3 *
4 * the function that ANSI forgot...
5 *
6 * © 1994-1998 Straylight
7 */
8
9 /*----- Licensing note ----------------------------------------------------*
10 *
11 * This file is part of Straylight's Steel library.
12 *
13 * Steel is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2, or (at your option)
16 * any later version.
17 *
18 * Steel is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with Steel. If not, write to the Free Software Foundation,
25 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 */
27
28 #include <stdio.h>
29 #include <stdarg.h>
30 #include <stdlib.h>
31 #include <ctype.h>
32 #include "vsscanf.h"
33
34 #ifndef BOOL
35 #define BOOL int
36 #define TRUE 1
37 #define FALSE 0
38 #endif
39
40 typedef enum
41 {
42 vsscanf__DEFAULT,
43 vsscanf__SHORT,
44 vsscanf__LONG,
45 vsscanf__VERY_LONG
46 }
47 vsscanf__length;
48
49 /* This union type is to avoid problems with converting between signed and
50 unsigned longs. */
51
52 typedef union
53 {
54 unsigned long ul;
55 signed long l;
56 }
57 vsscanf__genlongu;
58
59 static int vsscanf__getWidth(char **p)
60 {
61 char *q=(*p);
62 for (;isdigit(**p);(*p)++)
63 /* blank loop */;
64 return (atoi(q));
65 }
66
67 static void vsscanf__skipSpace(char **p)
68 {
69 while (isspace(**p))
70 (*p)++;
71 }
72
73 static void vsscanf__readFloat
74 (
75 char **p,
76 BOOL ignore,
77 int *processed,
78 vsscanf__length length,
79 va_list ap
80 )
81 {
82
83 /*-------------------------------------------------------------------------*/
84 /* Note: this code relies on the fact that long double and double are */
85 /* identical on this architecture. This can't be chacked at compile time, */
86 /* and it is pointless checking at run-time. The reason for this is that */
87 /* I don't have a function for turning a string into a long double. */
88 /*-------------------------------------------------------------------------*/
89
90 double result;
91 vsscanf__skipSpace(p);
92 result=strtod(*p,p);
93 if (!ignore)
94 {
95 switch (length)
96 {
97 case vsscanf__DEFAULT:
98 case vsscanf__SHORT:
99 *va_arg(ap,float *)=(float)result;
100 break;
101 case vsscanf__LONG:
102 case vsscanf__VERY_LONG:
103 *va_arg(ap,double *)=(double)result;
104 break;
105 }
106 (*processed)++;
107 }
108 }
109
110 static void vsscanf__readScanset
111 (
112 char **p,
113 char **q,
114 int width,
115 BOOL ignore,
116 int *processed,
117 va_list ap
118 )
119 {
120 char trans[256]; /* A trt (translate and test) table */
121 int i;
122 BOOL reversed;
123 int done=0;
124 BOOL finished=FALSE;
125 char *dest=0;
126 for (i=0;i<256;i++)
127 trans[i]=FALSE;
128 (*q)++; /* Move past the opening '[' */
129 if (**q=='^')
130 {
131 reversed=TRUE;
132 (*q)++;
133 }
134 else
135 reversed=FALSE;
136 if (**q==']')
137 {
138 trans[']']=TRUE; /* euch using a char as an index, but it works */
139 (*q)++;
140 }
141 while (**q!=']' && **q!=0)/* results of incorrect syntax are 'undefined' */
142 {
143 trans[**q]=TRUE;
144 (*q)++;
145 }
146 if (!ignore)
147 dest=va_arg(ap,char *);
148 while (!finished)
149 {
150 if (**p!=0 && (width==-1 || width!=done) && (trans[**p] ^ reversed))
151 {
152 if (!ignore)
153 *dest=**p;
154 (*p)++;
155 dest++;
156 done++;
157 }
158 else
159 finished=TRUE;
160 }
161 if (!ignore)
162 *dest=0;
163 if (done && !ignore)
164 (*processed)++;
165 }
166
167 static void vsscanf__readString
168 (
169 char **p,
170 int width,
171 BOOL ignore,
172 BOOL chars,
173 int *processed,
174 va_list ap
175 )
176 {
177 char *dest=0;
178 int done=0;
179 BOOL finished=FALSE;
180 if (!ignore)
181 dest=va_arg(ap,char *);
182 if (!chars)
183 vsscanf__skipSpace(p);
184 else
185 {
186 if (width==-1)
187 width=1;
188 }
189 while (!finished)
190 {
191 if ((!isspace(**p) || chars) && **p && (done!=width || width==-1))
192 {
193 if (!ignore)
194 *dest=**p;
195 (*p)++;
196 dest++;
197 done++;
198 }
199 else
200 finished=TRUE;
201 }
202 if (!ignore && !chars)
203 *dest=0; /* Finish off the string */
204 if (done && !ignore)
205 (*processed)++;
206 }
207
208 static void vsscanf__readInteger
209 (
210 char **p,
211 BOOL ignore,
212 int *processed,
213 BOOL isSigned,
214 int base,
215 vsscanf__length length,
216 va_list ap
217 )
218 {
219 char *start;
220 vsscanf__genlongu result;
221 vsscanf__skipSpace(p);
222 start=*p;
223 if (base==0)
224 {
225 if (**p=='0')
226 {
227 if (tolower(*(++(*p)))=='x')
228 {
229 base=16;
230 (*p)++;
231 }
232 else
233 base=8;
234 }
235 else
236 base=10;
237 }
238 if (isSigned)
239 result.l=strtol(start,p,base);
240 else
241 result.ul=strtoul(start,p,base);
242 if (!ignore)
243 {
244 switch (length)
245 {
246 case vsscanf__DEFAULT:
247 case vsscanf__VERY_LONG: /* Ignore silly option */
248 *va_arg(ap,int *)=(int)result.l;
249 break;
250 case vsscanf__LONG:
251 *va_arg(ap,long *)=(long)result.l;
252 /* This is machine dependant - r.r[2] is an int, but longs are the same */
253 /* width */
254 break;
255 case vsscanf__SHORT:
256 *va_arg(ap,short *)=(short)result.l;
257 /* If the number is too big, the results are wierd. */
258 break;
259 }
260 (*processed)++;
261 }
262 }
263
264 static BOOL vsscanf__processFormat
265 (
266 char **p,
267 char **q,
268 int *processed,
269 va_list ap
270 )
271 {
272 BOOL ignore=FALSE;
273 vsscanf__length length=vsscanf__DEFAULT;
274 int width=-1;
275 if (**q=='*')
276 {
277 ignore=TRUE;
278 (*q)++;
279 }
280 if (isdigit(**q))
281 width=vsscanf__getWidth(q);
282 if (**q=='h')
283 {
284 length=vsscanf__SHORT;
285 (*q)++;
286 }
287 else if (**q=='l')
288 {
289 length=vsscanf__LONG;
290 (*q)++;
291 }
292 else if (**q=='L')
293 {
294 length=vsscanf__VERY_LONG;
295 (*q)++;
296 }
297 /* Right - now we come to the business end of the thing. **q is the */
298 /* actual format specifier. */
299 switch (**q)
300 {
301 case 'n':
302 if (!ignore)
303 *(va_arg(ap,int *))=*processed;
304 (*q)++;
305 break;
306 case 's':
307 vsscanf__readString(p,width,ignore,FALSE,processed,ap);
308 (*q)++;
309 break;
310 case 'c':
311 vsscanf__readString(p,width,ignore,TRUE,processed,ap);
312 (*q)++;
313 break;
314 case 'd':
315 vsscanf__readInteger(p,ignore,processed,TRUE,10,length,ap);
316 (*q)++;
317 break;
318 case 'x':
319 case 'X':
320 case 'p': /* Flat memory model - pointer==hex number */
321 vsscanf__readInteger(p,ignore,processed,FALSE,16,length,ap);
322 (*q)++;
323 break;
324 case 'o':
325 vsscanf__readInteger(p,ignore,processed,FALSE,8,length,ap);
326 (*q)++;
327 break;
328 case 'u':
329 vsscanf__readInteger(p,ignore,processed,FALSE,10,length,ap);
330 (*q)++;
331 break;
332 case 'i':
333 vsscanf__readInteger(p,ignore,processed,TRUE,0,length,ap);
334 (*q)++;
335 break;
336 case 'e':
337 case 'f':
338 case 'g':
339 vsscanf__readFloat(p,ignore,processed,length,ap);
340 (*q)++;
341 break;
342 case '[':
343 vsscanf__readScanset(p,q,width,ignore,processed,ap);
344 (*q)++;
345 break;
346 default:
347 vsscanf__skipSpace(p);
348 if (*((*q)++)!=*((*p)++))
349 return (TRUE);
350 }
351 return (FALSE);
352 }
353
354 int vsscanf(char *string,char *format,va_list ap)
355 {
356 char *p=string;
357 char *q=format;
358 int processed=0;
359 char c;
360 while (c=*(q++),c)
361 {
362 if (isspace(c)) /* Skip whitespace in format and source */
363 {
364 vsscanf__skipSpace(&p);
365 vsscanf__skipSpace(&q);
366 }
367 else if (c=='%') /* A format specifier */
368 {
369 if (vsscanf__processFormat(&p,&q,&processed,ap))
370 return (processed);
371 }
372 else /* R&D (Read and Discard) */
373 {
374 vsscanf__skipSpace(&p);
375 if (c!=*(p++))
376 return (processed);
377 }
378 if (*p==0) /* This is the dreaded EOF (well, EOL) */
379 {
380 if (!processed)
381 return (EOF);
382 else
383 return (processed);
384 }
385 }
386 return (processed);
387 }