chiark / gitweb /
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 }