~ubuntu-branches/ubuntu/karmic/gnustep-base/karmic

« back to all changes in this revision

Viewing changes to Source/o_vscanf.c

  • Committer: Bazaar Package Importer
  • Author(s): Eric Heintzmann
  • Date: 2005-04-17 00:14:38 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050417001438-enf0y07c9tku85z1
Tags: 1.10.3-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* Implementation of o_vscanf for GNUstep Base Library
2
 
   
3
 
   Reworked by:  Andrew Kachites McCallum <mccallum@gnu.ai.mit.edu>
4
 
   Date: July 1994
5
 
   
6
 
   This file is part of the GNUstep Base Library.
7
 
 
8
 
   This library is free software; you can redistribute it and/or
9
 
   modify it under the terms of the GNU Library General Public
10
 
   License as published by the Free Software Foundation; either
11
 
   version 2 of the License, or (at your option) any later version.
12
 
   
13
 
   This library is distributed in the hope that it will be useful,
14
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16
 
   Library General Public License for more details.
17
 
   
18
 
   You should have received a copy of the GNU Library General Public
19
 
   License along with this library; if not, write to the Free
20
 
   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
21
 
   */ 
22
 
 
23
 
/* Copyright (C) 1991, 1992, 1993, 1996 Free Software Foundation, Inc.
24
 
This file is part of the GNU C Library.
25
 
 
26
 
The GNU C Library is free software; you can redistribute it and/or
27
 
modify it under the terms of the GNU Library General Public License as
28
 
published by the Free Software Foundation; either version 2 of the
29
 
License, or (at your option) any later version.
30
 
 
31
 
The GNU C Library is distributed in the hope that it will be useful,
32
 
but WITHOUT ANY WARRANTY; without even the implied warranty of
33
 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
34
 
Library General Public License for more details.
35
 
 
36
 
You should have received a copy of the GNU Library General Public
37
 
License along with the GNU C Library; see the file COPYING.LIB.  If
38
 
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
39
 
Cambridge, MA 02111, USA.  */
40
 
 
41
 
 
42
 
 
43
 
/* Reworked from glibc by Andrew McCallum:
44
 
   Fixed bug by adding "*f == 'a'" to type modifier checking.
45
 
   Declared extern strtod and strtol.
46
 
   Use function pointer argument to get next character.
47
 
   Use objc_malloc() instead of malloc()
48
 
   Use objc_realloc() instead of realloc()
49
 
 
50
 
   This is a solution for StdioStream and MemoryStream.
51
 
   It isn't ideal.  Anyone have other suggestions? */
52
 
 
53
 
/* #include <ansidecl.h> */
54
 
/* #include <localeinfo.h> */
55
 
#include <errno.h>
56
 
#include <limits.h>
57
 
#include <ctype.h>
58
 
#include <stdarg.h>
59
 
#include <stdio.h>
60
 
#include <stdlib.h>
61
 
#include <string.h>
62
 
#include <objc/objc-api.h>
63
 
 
64
 
extern double strtod(const char *str, char **ptr);
65
 
extern long strtol(const char *str, char** ptr, int base);
66
 
 
67
 
#ifdef  __GNUC__
68
 
#define HAVE_LONGLONG
69
 
#define LONGLONG        long long
70
 
#define CONST const
71
 
#define LONG_DOUBLE long double
72
 
#else
73
 
#define LONGLONG        long
74
 
#define CONST
75
 
#endif
76
 
 
77
 
 
78
 
/* #define      inchar()        ((c = getc(s)) == EOF ? EOF : (++read_in, c))*/
79
 
#define inchar()  ((c = (*inchar_func)(stream)) == EOF ? EOF : (++read_in, c))
80
 
 
81
 
/* #define      conv_error()    return ((c == EOF || ungetc(c, s)), done) */
82
 
//#define       conv_error() return ((c == EOF || ((*unchar_func)(stream,c),c)), done)
83
 
#define conv_error() return (c==EOF ? done : ((*unchar_func)(stream,c), done))
84
 
 
85
 
#define input_error()   return (done == 0 ? EOF : done)
86
 
#define memory_error()  return ((errno = ENOMEM), EOF)
87
 
 
88
 
 
89
 
/* Read formatted input from STREAM according to the format string
90
 
   FORMAT, using the argument list in ARGPTR.
91
 
   Return the number of assignments made, or -1 for an input error.  */
92
 
int
93
 
o_vscanf (void *stream, 
94
 
                int (*inchar_func)(void*), 
95
 
                void (*unchar_func)(void*,int),
96
 
                const char *format, va_list arg)
97
 
{
98
 
  register CONST char *f = format;
99
 
  register char fc;             /* Current character of the format.  */
100
 
  register size_t done = 0;     /* Assignments done.  */
101
 
  register size_t read_in = 0;  /* Chars read in.  */
102
 
  register int c;               /* Last char read.  */
103
 
  register int do_assign;       /* Whether to do an assignment.  */
104
 
  register int width;           /* Maximum field width.  */
105
 
 
106
 
  /* Type modifiers.  */
107
 
  char is_short, is_long, is_long_double;
108
 
#ifdef  HAVE_LONGLONG
109
 
  /* We use the `L' modifier for `long long int'.  */
110
 
#define is_longlong     is_long_double
111
 
#else
112
 
#define is_longlong     0
113
 
#endif
114
 
  int malloc_string;            /* Args are char ** to be filled in.  */
115
 
  /* Status for reading F-P nums.  */
116
 
  char got_dot, got_e;
117
 
  /* If a [...] is a [^...].  */
118
 
  char not_in;
119
 
  /* Base for integral numbers.  */
120
 
  int base;
121
 
  /* Signedness for integral numbers.  */
122
 
  int number_signed;
123
 
  /* Integral holding variables.  */
124
 
  long int num;
125
 
  unsigned long int unum;
126
 
  /* Floating-point holding variable.  */
127
 
  LONG_DOUBLE fp_num;
128
 
  /* Character-buffer pointer.  */
129
 
  register char *str, **strptr;
130
 
  size_t strsize;
131
 
  /* Workspace.  */
132
 
  char work[200];
133
 
  char *w;                      /* Pointer into WORK.  */
134
 
  wchar_t decimal;              /* Decimal point character.  */
135
 
 
136
 
#if 0
137
 
  if (!__validfp(s) || !s->__mode.__read || format == NULL)
138
 
    {
139
 
      errno = EINVAL;
140
 
      return EOF;
141
 
    }
142
 
#endif
143
 
 
144
 
  /* Figure out the decimal point character.  */
145
 
#if 0
146
 
  if (mbtowc(&decimal, _numeric_info->decimal_point,
147
 
             strlen(_numeric_info->decimal_point)) <= 0)
148
 
    decimal = (wchar_t) *_numeric_info->decimal_point;
149
 
#else
150
 
  decimal = '.';
151
 
#endif
152
 
 
153
 
  c = inchar();
154
 
 
155
 
  /* Run through the format string.  */
156
 
  while (*f != '\0')
157
 
    {
158
 
      if (!isascii(*f))
159
 
        {
160
 
          /* Non-ASCII, may be a multibyte.  */
161
 
          int len = mblen(f, strlen(f));
162
 
          if (len > 0)
163
 
            {
164
 
              while (len-- > 0)
165
 
                if (c == EOF)
166
 
                  input_error();
167
 
                else if (c == *f++)
168
 
                  (void) inchar();
169
 
                else
170
 
                  conv_error();
171
 
              continue;
172
 
            }
173
 
        }
174
 
 
175
 
      fc = *f++;
176
 
      if (fc != '%')
177
 
        {
178
 
          /* Characters other than format specs must just match.  */
179
 
          if (c == EOF)
180
 
            input_error();
181
 
          if (isspace(fc))
182
 
            {
183
 
              /* Whitespace characters match any amount of whitespace.  */
184
 
              while (isspace (c))
185
 
                inchar ();
186
 
              continue;
187
 
            }
188
 
          else if (c == fc)
189
 
            (void) inchar();
190
 
          else
191
 
            conv_error();
192
 
          continue;
193
 
        }
194
 
 
195
 
      /* Check for the assignment-suppressant.  */
196
 
      if (*f == '*')
197
 
        {
198
 
          do_assign = 0;
199
 
          ++f;
200
 
        }
201
 
      else
202
 
        do_assign = 1;
203
 
                
204
 
      /* Find the maximum field width.  */
205
 
      width = 0;
206
 
      while (isdigit(*f))
207
 
        {
208
 
          width *= 10;
209
 
          width += *f++ - '0';
210
 
        }
211
 
      if (width == 0)
212
 
        width = -1;
213
 
 
214
 
      /* Check for type modifiers.  */
215
 
      is_short = is_long = is_long_double = malloc_string = 0;
216
 
      while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'a')
217
 
        switch (*f++)
218
 
          {
219
 
          case 'h':
220
 
            /* int's are short int's.  */
221
 
            is_short = 1;
222
 
            break;
223
 
          case 'l':
224
 
            if (is_long)
225
 
              /* A double `l' is equivalent to an `L'.  */
226
 
              is_longlong = 1;
227
 
            else
228
 
              /* int's are long int's.  */
229
 
              is_long = 1;
230
 
            break;
231
 
          case 'L':
232
 
            /* double's are long double's, and int's are long long int's.  */
233
 
            is_long_double = 1;
234
 
            break;
235
 
          case 'a':
236
 
            /* String conversions (%s, %[) take a `char **'
237
 
               arg and fill it in with a malloc'd pointer.  */
238
 
            malloc_string = 1;
239
 
            break;
240
 
          }
241
 
 
242
 
      /* End of the format string?  */
243
 
      if (*f == '\0')
244
 
        conv_error();
245
 
 
246
 
      /* Find the conversion specifier.  */
247
 
      w = work;
248
 
      fc = *f++;
249
 
      if (fc != '[' && fc != 'c' && fc != 'n')
250
 
        /* Eat whitespace.  */
251
 
        while (isspace(c))
252
 
          (void) inchar();
253
 
      switch (fc)
254
 
        {
255
 
        case '%':       /* Must match a literal '%'.  */
256
 
          if (c != fc)
257
 
            conv_error();
258
 
          break;
259
 
 
260
 
        case 'n':       /* Answer number of assignments done.  */
261
 
          if (do_assign)
262
 
            *va_arg(arg, int *) = read_in;
263
 
          break;
264
 
 
265
 
        case 'c':       /* Match characters.  */
266
 
          if (do_assign)
267
 
            {
268
 
              str = va_arg (arg, char *);
269
 
              if (str == NULL)
270
 
                conv_error ();
271
 
            }
272
 
 
273
 
          if (c == EOF)
274
 
            input_error();
275
 
 
276
 
          if (width == -1)
277
 
            width = 1;
278
 
 
279
 
          if (do_assign)
280
 
            {
281
 
              do
282
 
                *str++ = c;
283
 
              while (inchar() != EOF && --width > 0);
284
 
            }
285
 
          else
286
 
            while (inchar() != EOF && width > 0)
287
 
              --width;
288
 
 
289
 
          if (do_assign)
290
 
            ++done;
291
 
 
292
 
          break;
293
 
 
294
 
        case 's':               /* Read a string.  */
295
 
#define STRING_ARG                                                            \
296
 
          if (do_assign)                                                      \
297
 
            {                                                                 \
298
 
              if (malloc_string)                                              \
299
 
                {                                                             \
300
 
                  /* The string is to be stored in a malloc'd buffer.  */     \
301
 
                  strptr = va_arg (arg, char **);                             \
302
 
                  if (strptr == NULL)                                         \
303
 
                    conv_error ();                                            \
304
 
                  /* Allocate an initial buffer.  */                          \
305
 
                  strsize = 100;                                              \
306
 
                  *strptr = str = objc_malloc (strsize);                      \
307
 
                }                                                             \
308
 
              else                                                            \
309
 
                str = va_arg (arg, char *);                                   \
310
 
              if (str == NULL)                                                \
311
 
                conv_error ();                                                \
312
 
            }
313
 
          STRING_ARG;
314
 
 
315
 
          if (c == EOF)
316
 
            input_error ();
317
 
 
318
 
          do
319
 
            {
320
 
              if (isspace (c))
321
 
                break;
322
 
#define STRING_ADD_CHAR(c)                                                    \
323
 
              if (do_assign)                                                  \
324
 
                {                                                             \
325
 
                  *str++ = c;                                                 \
326
 
                  if (malloc_string && str == *strptr + strsize)              \
327
 
                    {                                                         \
328
 
                      /* Enlarge the buffer.  */                              \
329
 
                      str = objc_realloc (*strptr, strsize * 2);              \
330
 
                      if (str == NULL)                                        \
331
 
                        {                                                     \
332
 
                          /* Can't allocate that much.  Last-ditch effort.  */\
333
 
                          str = objc_realloc (*strptr, strsize + 1);          \
334
 
                          if (str == NULL)                                    \
335
 
                            {                                                 \
336
 
                              /* We lose.  Oh well.                           \
337
 
                                 Terminate the string and stop converting,    \
338
 
                                 so at least we don't swallow any input.  */  \
339
 
                              (*strptr)[strsize] = '\0';                      \
340
 
                              ++done;                                         \
341
 
                              conv_error ();                                  \
342
 
                            }                                                 \
343
 
                          else                                                \
344
 
                            {                                                 \
345
 
                              *strptr = str;                                  \
346
 
                              str += strsize;                                 \
347
 
                              ++strsize;                                      \
348
 
                            }                                                 \
349
 
                        }                                                     \
350
 
                      else                                                    \
351
 
                        {                                                     \
352
 
                          *strptr = str;                                      \
353
 
                          str += strsize;                                     \
354
 
                          strsize *= 2;                                       \
355
 
                        }                                                     \
356
 
                    }                                                         \
357
 
                }
358
 
              STRING_ADD_CHAR (c);
359
 
            } while (inchar () != EOF && (width <= 0 || --width > 0));
360
 
 
361
 
          if (do_assign)
362
 
            {
363
 
              *str = '\0';
364
 
              ++done;
365
 
            }
366
 
          break;
367
 
 
368
 
        case 'x':       /* Hexadecimal integer.  */
369
 
        case 'X':       /* Ditto.  */ 
370
 
          base = 16;
371
 
          number_signed = 0;
372
 
          goto number;
373
 
 
374
 
        case 'o':       /* Octal integer.  */
375
 
          base = 8;
376
 
          number_signed = 0;
377
 
          goto number;
378
 
 
379
 
        case 'u':       /* Unsigned decimal integer.  */
380
 
          base = 10;
381
 
          number_signed = 0;
382
 
          goto number;
383
 
 
384
 
        case 'd':       /* Signed decimal integer.  */
385
 
          base = 10;
386
 
          number_signed = 1;
387
 
          goto number;
388
 
 
389
 
        case 'i':       /* Generic number.  */
390
 
          base = 0;
391
 
          number_signed = 1;
392
 
 
393
 
        number:
394
 
          if (c == EOF)
395
 
            input_error();
396
 
 
397
 
          /* Check for a sign.  */
398
 
          if (c == '-' || c == '+')
399
 
            {
400
 
              *w++ = c;
401
 
              if (width > 0)
402
 
                --width;
403
 
              (void) inchar();
404
 
            }
405
 
 
406
 
          /* Look for a leading indication of base.  */
407
 
          if (c == '0')
408
 
            {
409
 
              if (width > 0)
410
 
                --width;
411
 
              *w++ = '0';
412
 
 
413
 
              (void) inchar();
414
 
 
415
 
              if (tolower(c) == 'x')
416
 
                {
417
 
                  if (base == 0)
418
 
                    base = 16;
419
 
                  if (base == 16)
420
 
                    {
421
 
                      if (width > 0)
422
 
                        --width;
423
 
                      (void) inchar();
424
 
                    }
425
 
                }
426
 
              else if (base == 0)
427
 
                base = 8;
428
 
            }
429
 
 
430
 
          if (base == 0)
431
 
            base = 10;
432
 
 
433
 
          /* Read the number into WORK.  */
434
 
          do
435
 
            {
436
 
              if (base == 16 ? !isxdigit(c) :
437
 
                  (!isdigit(c) || c - '0' >= base))
438
 
                break;
439
 
              *w++ = c;
440
 
              if (width > 0)
441
 
                --width;
442
 
            } while (inchar() != EOF && width != 0);
443
 
 
444
 
          if (w == work ||
445
 
              (w - work == 1 && (work[0] == '+' || work[0] == '-')))
446
 
            /* There was on number.  */
447
 
            conv_error();
448
 
 
449
 
          /* Convert the number.  */
450
 
          *w = '\0';
451
 
          if (number_signed)
452
 
            num = strtol (work, &w, base);
453
 
          else
454
 
#if HAVE_STRTOUL
455
 
            unum = strtoul (work, &w, base);
456
 
#else
457
 
            unum = (unsigned long) strtol (work, &w, base);
458
 
#endif
459
 
          if (w == work)
460
 
            conv_error ();
461
 
 
462
 
          if (do_assign)
463
 
            {
464
 
              if (! number_signed)
465
 
                {
466
 
                  if (is_longlong)
467
 
                    *va_arg (arg, unsigned LONGLONG int *) = unum;
468
 
                  else if (is_long)
469
 
                    *va_arg (arg, unsigned long int *) = unum;
470
 
                  else if (is_short)
471
 
                    *va_arg (arg, unsigned short int *)
472
 
                      = (unsigned short int) unum;
473
 
                  else
474
 
                    *va_arg(arg, unsigned int *) = (unsigned int) unum;
475
 
                }
476
 
              else
477
 
                {
478
 
                  if (is_longlong)
479
 
                    *va_arg(arg, LONGLONG int *) = num;
480
 
                  else if (is_long)
481
 
                    *va_arg(arg, long int *) = num;
482
 
                  else if (is_short)
483
 
                    *va_arg(arg, short int *) = (short int) num;
484
 
                  else
485
 
                    *va_arg(arg, int *) = (int) num;
486
 
                }
487
 
              ++done;
488
 
            }
489
 
          break;
490
 
 
491
 
        case 'e':       /* Floating-point numbers.  */
492
 
        case 'E':
493
 
        case 'f':
494
 
        case 'g':
495
 
        case 'G':
496
 
          if (c == EOF)
497
 
            input_error();
498
 
 
499
 
          /* Check for a sign.  */
500
 
          if (c == '-' || c == '+')
501
 
            {
502
 
              *w++ = c;
503
 
              if (inchar() == EOF)
504
 
                /* EOF is only an input error before we read any chars.  */
505
 
                conv_error();
506
 
              if (width > 0)
507
 
                --width;
508
 
            }
509
 
 
510
 
          got_dot = got_e = 0;
511
 
          do
512
 
            {
513
 
              if (isdigit(c))
514
 
                *w++ = c;
515
 
              else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
516
 
                *w++ = c;
517
 
              else if (!got_e && tolower(c) == 'e')
518
 
                {
519
 
                  *w++ = 'e';
520
 
                  got_e = got_dot = 1;
521
 
                }
522
 
              else if (c == decimal && !got_dot)
523
 
                {
524
 
                  *w++ = c;
525
 
                  got_dot = 1;
526
 
                }
527
 
              else
528
 
                break;
529
 
              if (width > 0)
530
 
                --width;
531
 
            } while (inchar() != EOF && width != 0);
532
 
 
533
 
          if (w == work)
534
 
            conv_error();
535
 
          if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
536
 
            conv_error();
537
 
 
538
 
#ifndef MIB_HACKS
539
 
          /* Convert the number.  */
540
 
          *w = '\0';
541
 
          fp_num = strtod(work, &w);
542
 
          if (w == work)
543
 
            conv_error();
544
 
 
545
 
          if (do_assign)
546
 
            {
547
 
              if (is_long_double)
548
 
                *va_arg(arg, LONG_DOUBLE *) = fp_num;
549
 
              else if (is_long)
550
 
                *va_arg(arg, double *) = (double) fp_num;
551
 
              else
552
 
                *va_arg(arg, float *) = (float) fp_num;
553
 
              ++done;
554
 
            }
555
 
          break;
556
 
#endif /* MIB_HACKS */
557
 
 
558
 
        case '[':       /* Character class.  */
559
 
          STRING_ARG;
560
 
 
561
 
          if (c == EOF)
562
 
            input_error();
563
 
 
564
 
          if (*f == '^')
565
 
            {
566
 
              ++f;
567
 
              not_in = 1;
568
 
            }
569
 
          else
570
 
            not_in = 0;
571
 
 
572
 
          while ((fc = *f++) != '\0' && fc != ']')
573
 
            {
574
 
              if (fc == '-' && *f != '\0' && *f != ']' &&
575
 
                  w > work && w[-1] <= *f)
576
 
                /* Add all characters from the one before the '-'
577
 
                   up to (but not including) the next format char.  */
578
 
                for (fc = w[-1] + 1; fc < *f; ++fc)
579
 
                  *w++ = fc;
580
 
              else
581
 
                /* Add the character to the list.  */
582
 
                *w++ = fc;
583
 
            }
584
 
          if (fc == '\0')
585
 
            conv_error();
586
 
 
587
 
          *w = '\0';
588
 
          unum = read_in;
589
 
          do
590
 
            {
591
 
              if ((strchr (work, c) == NULL) != not_in)
592
 
                break;
593
 
              STRING_ADD_CHAR (c);
594
 
              if (width > 0)
595
 
                --width;
596
 
            } while (inchar () != EOF && width != 0);
597
 
          if (read_in == unum)
598
 
            conv_error ();
599
 
 
600
 
          if (do_assign)
601
 
            {
602
 
              *str = '\0';
603
 
              ++done;
604
 
            }
605
 
          break;
606
 
 
607
 
        case 'p':       /* Generic pointer.  */
608
 
          base = 16;
609
 
          /* A PTR must be the same size as a `long int'.  */
610
 
          is_long = 1;
611
 
          goto number;
612
 
        }
613
 
    }
614
 
 
615
 
  conv_error();
616
 
}