1 /* sscanf function for S-Lang */
2 /*
3 Copyright (C) 2004-2017,2018 John E. Davis
4 
5 This file is part of the S-Lang Library.
6 
7 The S-Lang Library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11 
12 The S-Lang Library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 USA.
21 */
22 
23 #ifndef _GNU_SOURCE
24 # define _GNU_SOURCE
25 #endif
26 #ifndef __EXTENSIONS__
27 # define __EXTENSIONS__
28 #endif
29 
30 #include "slinclud.h"
31 #include <ctype.h>
32 #include <math.h>
33 #include <errno.h>
34 
35 #ifdef HAVE_LOCALE_H
36 # include <locale.h>
37 #endif
38 
39 #ifdef HAVE_XLOCALE_H
40 # include <xlocale.h>
41 #endif
42 
43 #include "slang.h"
44 #include "_slang.h"
45 
init_map(unsigned char map[256],int base)46 static void init_map (unsigned char map[256], int base)
47 {
48    memset ((char *) map, 0xFF, 256);
49 
50    map['0'] = 0;   map['1'] = 1;   map['2'] = 2;   map['3'] = 3;
51    map['4'] = 4;   map['5'] = 5;   map['6'] = 6;   map['7'] = 7;
52    if (base == 8)
53      return;
54 
55    map['8'] = 8;   map['9'] = 9;
56    if (base == 10)
57      return;
58 
59    map['A'] = 10;   map['B'] = 11;   map['C'] = 12;   map['D'] = 13;
60    map['E'] = 14;   map['F'] = 15;   map['a'] = 10;   map['b'] = 11;
61    map['c'] = 12;   map['d'] = 13;   map['e'] = 14;   map['f'] = 15;
62 }
63 
get_sign(SLFUTURE_CONST char * s,SLFUTURE_CONST char * smax,int * sign)64 static SLFUTURE_CONST char *get_sign (SLFUTURE_CONST char *s, SLFUTURE_CONST char *smax, int *sign)
65 {
66    *sign = 1;
67    if (s + 1 < smax)
68      {
69 	if (*s == '+') s++;
70 	else if (*s == '-')
71 	  {
72 	     s++;
73 	     *sign = -1;
74 	  }
75      }
76    return s;
77 }
78 
parse_long(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,long * np,long base,unsigned char map[256])79 static int parse_long (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, long *np,
80 		       long base, unsigned char map[256])
81 {
82    SLFUTURE_CONST char *s, *s0;
83    long n;
84    int sign;
85 
86    s = s0 = get_sign (*sp, smax, &sign);
87 
88    n = 0;
89    while (s < smax)
90      {
91 	unsigned char value;
92 
93 	value = map [(unsigned char) *s];
94 	if (value == 0xFF)
95 	  break;
96 
97 	n = base * n + value;
98 	s++;
99      }
100 
101    *sp = s;
102    if (s == s0)
103      return 0;
104 
105    *np = n * sign;
106 
107    return 1;
108 }
109 
parse_int(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,int * np,long base,unsigned char map[256])110 static int parse_int (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, int *np,
111 		      long base, unsigned char map[256])
112 {
113    long n;
114    int status;
115 
116    if (1 == (status = parse_long (sp, smax, &n, base, map)))
117      *np = (int) n;
118    return status;
119 }
120 
parse_short(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,short * np,long base,unsigned char map[256])121 static int parse_short (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, short *np,
122 			long base, unsigned char map[256])
123 {
124    long n;
125    int status;
126 
127    if (1 == (status = parse_long (sp, smax, &n, base, map)))
128      *np = (short) n;
129    return status;
130 }
131 
parse_ulong(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,unsigned long * np,long base,unsigned char map[256])132 static int parse_ulong (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, unsigned long *np,
133 			long base, unsigned char map[256])
134 {
135    return parse_long (sp, smax, (long *) np, base, map);
136 }
137 
parse_uint(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,unsigned int * np,long base,unsigned char map[256])138 static int parse_uint (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, unsigned int *np,
139 		       long base, unsigned char map[256])
140 {
141    return parse_int (sp, smax, (int *) np, base, map);
142 }
143 
parse_ushort(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,unsigned short * np,long base,unsigned char map[256])144 static int parse_ushort (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, unsigned short *np,
145 			 long base, unsigned char map[256])
146 {
147    return parse_short (sp, smax, (short *) np, base, map);
148 }
149 
150 #if SLANG_HAS_FLOAT
151 /* See <pubs.opengroup.org> for information about locale_t, newlocale, etc.
152  * The usage below is based on the opengroup manpages.
153  */
154 # if defined(HAVE_STRTOD_L) && defined(HAVE_NEWLOCALE) && defined(LC_ALL_MASK)
155 #  define USE_STRTOD_L 1
156 # else
157 #  define USE_STRTOD_L 0
158 # endif
159 
160 # if USE_STRTOD_L
161 static locale_t C_Locale = (locale_t) 0;
162 # endif
do_strtod(char * buf,int sign,double * xp)163 static int do_strtod (char *buf, int sign, double *xp)
164 {
165 # if USE_STRTOD_L
166    if (C_Locale != (locale_t) 0)
167      {
168 	*xp = sign * strtod_l (buf, NULL, C_Locale);
169 	return 1;
170      }
171 
172    C_Locale = newlocale (LC_ALL_MASK, "C", (locale_t) 0);
173    if (C_Locale != (locale_t) 0)
174      {
175 	*xp = sign * strtod_l (buf, NULL, C_Locale);
176 	return 1;
177      }
178    /* drop */
179 # endif
180 
181 # ifdef HAVE_STRTOD
182    *xp = sign * strtod (buf, NULL);
183 # else
184    *xp = sign * atof (buf);
185 # endif
186    return 1;
187 }
188 
189 /*
190  * In an ideal world, strtod would be the correct function to use.  However,
191  * there may be problems relying on this function because some systems do
192  * not support and some that do get it wrong.  So, I will handle the parsing
193  * of the string and let atof or strtod handle the arithmetic.
194  */
parse_double(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,double * d)195 static int parse_double (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, double *d)
196 {
197    SLFUTURE_CONST char *s, *s0;
198    int sign;
199    int expon;
200    unsigned char map[256];
201    char buf[128];
202    int has_leading_zeros;
203    SLFUTURE_CONST char *start_pos, *sign_pos;
204    char *b, *bmax;
205    char ch;
206    const char *decimal_point = ".";
207    char *b_after_decimal_position;
208 # if !USE_STRTOD_L && defined(HAVE_LOCALECONV)
209    struct lconv *locale_data;
210 
211    if (NULL != (locale_data = localeconv()))
212      {
213 	decimal_point = locale_data->decimal_point;
214 	if (*decimal_point == 0) decimal_point = ".";
215      }
216 # endif
217 
218    start_pos = *sp;
219    s = get_sign (start_pos, smax, &sign);
220    if (s >= smax)
221      {
222 	errno = _pSLerrno_errno = EINVAL;
223 	return 0;
224      }
225 
226    ch = *s|0x20;
227    if ((ch == 'n') || (ch == 'i'))
228      {
229 	if (s + 3 <= smax)
230 	  {
231 	     if (ch == 'n')
232 	       {
233 		  if (((s[1]|0x20) == 'a') && ((s[2]|0x20) == 'n'))
234 		    {
235 		       s += 3;
236 		       s0 = s;
237 		       /* Now parse the nan(chars) form.  Here we allow
238 			*   ([a-zA-Z_0-9]*)
239 			*/
240 		       if ((s < smax) && (*s == '('))
241 			 {
242 			    s++;
243 			    while (s < smax)
244 			      {
245 				 ch = *s++;
246 				 if (isdigit ((unsigned char)ch)
247 				     || ((ch >= 'a') && (ch <= 'z'))
248 				     || ((ch >= 'A') && (ch <= 'Z'))
249 				     || (ch == '_'))
250 				   continue;
251 
252 				 if (ch == ')')
253 				   s0 = s;
254 
255 				 break;
256 			      }
257 			 }
258 		       *sp = s0;
259 		       *d = _pSLang_NaN;
260 		       return 1;
261 		    }
262 		  *sp = start_pos;
263 		  errno = _pSLerrno_errno = EINVAL;
264 		  return 0;
265 	       }
266 	     if (((s[1] | 0x20) == 'n') && ((s[2] | 0x20) == 'f'))
267 	       {
268 		  /* check for infinity */
269 		  if ((s + 8 <= smax)
270 		      && (((s[3]|0x20)=='i')&&((s[4]|0x20)=='n')&&((s[5]|0x20)=='i')
271 		     && ((s[6]|0x20)=='t')&&((s[7]|0x20)=='y')))
272 		    *sp = s + 8;
273 		  else
274 		    *sp = s + 3;
275 		  *d = _pSLang_Inf * sign;
276 		  return 1;
277 	       }
278 	  }
279 	*sp = start_pos;
280 	errno = _pSLerrno_errno = EINVAL;
281 	return 0;
282      }
283 
284    /* Prepare the buffer that will be passed to strtod */
285    /* Allow the exponent to be 5 significant digits: E+xxxxx\0 */
286    bmax = buf + (sizeof (buf) - 8);
287    b = buf;
288    *b++ = '0';
289    do
290      {
291 	*b++ = *decimal_point++;
292      }
293    while ((*decimal_point != 0) && (b < bmax));
294    b_after_decimal_position = b;
295 
296    init_map (map, 10);
297 
298    /* Skip leading 0s */
299    s0 = s;
300    while ((s < smax) && (*s == '0'))
301      s++;
302    has_leading_zeros = (s != s0);
303 
304    expon = 0;
305    while (s < smax)
306      {
307 	unsigned char value = map [(unsigned char) *s];
308 
309 	if (value == 0xFF)
310 	  break;
311 
312 	if (b < bmax)
313 	  *b++ = *s;
314 
315 	expon++;
316 	s++;
317      }
318 
319    if ((s < smax) && (*s == '.'))
320      {
321 	s++;
322 	if (b == b_after_decimal_position)	       /* nothing added yet */
323 	  {
324 	     while ((s < smax) && (*s == '0'))
325 	       {
326 		  expon--;
327 		  s++;
328 	       }
329 	  }
330 
331 	while (s < smax)
332 	  {
333 	     unsigned char value = map [(unsigned char) *s];
334 
335 	     if (value == 0xFF)
336 	       break;
337 
338 	     if (b < bmax)
339 	       *b++ = *s;
340 	     s++;
341 	  }
342      }
343 
344    if ((b == b_after_decimal_position)
345        && (has_leading_zeros == 0)
346        && (expon == 0))		       /* .0 ==> expon=-1 */
347      {
348 	*sp = start_pos;
349 	errno = EINVAL;
350 	return 0;
351      }
352 
353    if ((s + 1 < smax) && ((*s == 'E') || (*s == 'e')))
354      {
355 	int e;
356 	int esign;
357 
358 	s0 = s;
359 	s = get_sign (s + 1, smax, &esign);
360 	sign_pos = s;
361 	e = 0;
362 	while (s < smax)
363 	  {
364 	     unsigned char value = map [(unsigned char) *s];
365 	     if (value == 0xFF)
366 	       break;
367 	     if (e < 25000)	       /* avoid overflow if 16 bit */
368 	       e = 10 * e + value;
369 	     s++;
370 	  }
371 #ifdef ERANGE
372 	if (e >= 25000)
373 	  errno = ERANGE;
374 #endif
375 	if (s == sign_pos)
376 	  s = s0;		       /* ...E-X */
377 	else
378 	  {
379 	     e = esign * e;
380 	     expon += e;
381 	  }
382      }
383 
384    if (expon != 0)
385      sprintf (b, "e%d", expon);
386    else
387      *b = 0;
388 
389    *sp = s;
390 
391    return do_strtod (buf, sign, d);
392 }
393 
parse_float(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,float * d)394 static int parse_float (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, float *d)
395 {
396    double x;
397    if (1 == parse_double (sp, smax, &x))
398      {
399 	*d = (float) x;
400 	return 1;
401      }
402    return 0;
403 }
404 #endif				       /* SLANG_HAS_FLOAT */
405 
parse_string(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,char ** str)406 static int parse_string (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, char **str)
407 {
408    SLFUTURE_CONST char *s, *s0;
409 
410    s0 = s = *sp;
411    while (s < smax)
412      {
413 	if (isspace ((unsigned char)*s))
414 	  break;
415 	s++;
416      }
417    if (NULL == (*str = SLang_create_nslstring (s0, (unsigned int) (s - s0))))
418      return -1;
419 
420    *sp = s;
421    return 1;
422 }
423 
parse_bstring(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,char ** str)424 static int parse_bstring (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, char **str)
425 {
426    SLFUTURE_CONST char *s;
427 
428    s = *sp;
429    if (NULL == (*str = SLang_create_nslstring (s, (unsigned int) (smax - s))))
430      return -1;
431 
432    *sp = smax;
433    return 1;
434 }
435 
parse_range(SLFUTURE_CONST char ** sp,SLFUTURE_CONST char * smax,SLFUTURE_CONST char ** fp,char ** str)436 static int parse_range (SLFUTURE_CONST char **sp, SLFUTURE_CONST char *smax, SLFUTURE_CONST char **fp, char **str)
437 {
438    SLFUTURE_CONST char *s, *s0;
439    char *range;
440    SLFUTURE_CONST char *f;
441    unsigned char map[256];
442    unsigned char reverse;
443 
444    /* How can one represent a range with just '^'?  The naive answer is
445     * is [^].  However, this may be interpreted as meaning any character
446     * but ']' and others.  Let's assume that the user will not use a range
447     * to match '^'.
448     */
449    f = *fp;
450    /* f is a pointer to (one char after) [...]. */
451    if (*f == '^')
452      {
453 	f++;
454 	reverse = 1;
455      }
456    else reverse = 0;
457 
458    s0 = f;
459    if (*f == ']')
460      f++;
461 
462    while (1)
463      {
464 	char ch = *f;
465 
466 	if (ch == 0)
467 	  {
468 	     _pSLang_verror (SL_INVALID_PARM, "Unexpected end of range in format");
469 	     return -1;
470 	  }
471 	if (ch == ']')
472 	  break;
473 	f++;
474      }
475    if (NULL == (range = SLmake_nstring (s0, (unsigned int) (f - s0))))
476      return -1;
477    *fp = f + 1;			       /* skip ] */
478 
479    SLmake_lut (map, (unsigned char *) range, reverse);
480    SLfree (range);
481 
482    s0 = s = *sp;
483    while ((s < smax) && map [(unsigned char) *s])
484      s++;
485 
486    if (NULL == (*str = SLang_create_nslstring (s0, (unsigned int) (s - s0))))
487      return -1;
488 
489    *sp = s;
490    return 1;
491 }
492 
493 /* FIXME: This function does not handle LONG_LONG */
_pSLang_sscanf(void)494 int _pSLang_sscanf (void)
495 {
496    int num;
497    unsigned int num_refs;
498    char *format;
499    char *input_string, *input_string_max;
500    SLFUTURE_CONST char *f, *s;
501    unsigned char map8[256], map10[256], map16[256];
502 
503    if (SLang_Num_Function_Args < 2)
504      {
505 	_pSLang_verror (SL_INVALID_PARM, "Int_Type sscanf (str, format, ...)");
506 	return -1;
507      }
508 
509    num_refs = (unsigned int) SLang_Num_Function_Args;
510    if (-1 == SLreverse_stack (num_refs))
511      return -1;
512    num_refs -= 2;
513 
514    if (-1 == SLang_pop_slstring (&input_string))
515      return -1;
516 
517    if (-1 == SLang_pop_slstring (&format))
518      {
519 	SLang_free_slstring (input_string);
520 	return -1;
521      }
522 
523    f = format;
524    s = input_string;
525    input_string_max = input_string + strlen (input_string);
526 
527    init_map (map8, 8);
528    init_map (map10, 10);
529    init_map (map16, 16);
530 
531    num = 0;
532 
533    while (num_refs != 0)
534      {
535 	SLang_Object_Type obj;
536 	SLang_Ref_Type *ref;
537 	SLFUTURE_CONST char *smax;
538 	unsigned char *map;
539 	int base;
540 	int no_assign;
541 	int is_short;
542 	int is_long;
543 	int force_signed;
544 	int status;
545 	char chf;
546 	unsigned int width;
547 	int has_width;
548 
549 	chf = *f++;
550 
551 	if (chf == 0)
552 	  {
553 	     /* Hmmm....  what is the most useful thing to do?? */
554 #if 1
555 	     break;
556 #else
557 	     _pSLang_verror (SL_INVALID_PARM, "sscanf: format not big enough for output list");
558 	     goto return_error;
559 #endif
560 	  }
561 
562 	if (isspace ((unsigned char)chf))
563 	  {
564 	     char *s1 = _pSLskip_whitespace (s);
565 
566 	     /* Next line commented out since the sscanf man page allows
567 	      * whitespace to match 0 or more whitespace chars.
568 	      */
569 	     /* if (s1 == s) break; */
570 	     s = s1;
571 	     continue;
572 	  }
573 
574 	if ((chf != '%')
575 	    || ((chf = *f++) == '%'))
576 	  {
577 	     if (*s != chf)
578 	       break;
579 	     s++;
580 	     continue;
581 	  }
582 
583 	no_assign = 0;
584 	is_short = 0;
585 	is_long = 0;
586 	width = 0;
587 	smax = input_string_max;
588 
589 	/* Look for the flag character */
590 	if (chf == '*')
591 	  {
592 	     no_assign = 1;
593 	     chf = *f++;
594 	  }
595 
596 	/* Width */
597 	has_width = isdigit ((unsigned char)chf);
598 	if (has_width)
599 	  {
600 	     f--;
601 	     (void) parse_uint (&f, f + strlen(f), &width, 10, map10);
602 	     chf = *f++;
603 	  }
604 
605 	/* Now the type modifier */
606 	switch (chf)
607 	  {
608 	   case 'h':
609 	     is_short = 1;
610 	     chf = *f++;
611 	     break;
612 
613 	   case 'L':		       /* not implemented */
614 	   case 'l':
615 	     is_long = 1;
616 	     chf = *f++;
617 	     break;
618 	  }
619 
620 	status = -1;
621 
622 	if ((chf != 'c') && (chf != '['))
623 	  {
624 	     s = _pSLskip_whitespace (s);
625 	     if (*s == 0)
626 	       break;
627 	  }
628 
629 	if (has_width)
630 	  {
631 	     if (width > (unsigned int) (input_string_max - s))
632 	       width = (unsigned int) (input_string_max - s);
633 	     smax = s + width;
634 	  }
635 
636 	/* Now the format descriptor */
637 
638 	map = map10;
639 	base = 10;
640 	force_signed = 0;
641 try_again:		       /* used by i, x, and o, conversions */
642 	switch (chf)
643 	  {
644 	   case 0:
645 	     _pSLang_verror (SL_INVALID_PARM, "sscanf: Unexpected end of format");
646 	     goto return_error;
647 	   case 'D':
648 	     is_long = 1;
649 	   case 'd':
650 	     if (is_short)
651 	       {
652 		  obj.o_data_type = SLANG_SHORT_TYPE;
653 		  status = parse_short (&s, smax, &obj.v.short_val, base, map);
654 	       }
655 	     else if (is_long)
656 	       {
657 		  obj.o_data_type = SLANG_LONG_TYPE;
658 		  status = parse_long (&s, smax, &obj.v.long_val, base, map);
659 	       }
660 	     else
661 	       {
662 		  obj.o_data_type = SLANG_INT_TYPE;
663 		  status = parse_int (&s, smax, &obj.v.int_val, base, map);
664 	       }
665 	     break;
666 
667 	   case 'U':
668 	     is_long = 1;
669 	   case 'u':
670 	     if (is_short)
671 	       {
672 		  obj.o_data_type = SLANG_USHORT_TYPE;
673 		  status = parse_ushort (&s, smax, &obj.v.ushort_val, base, map);
674 	       }
675 	     else if (is_long)
676 	       {
677 		  obj.o_data_type = SLANG_ULONG_TYPE;
678 		  status = parse_ulong (&s, smax, &obj.v.ulong_val, base, map);
679 	       }
680 	     else
681 	       {
682 		  obj.o_data_type = SLANG_UINT_TYPE;
683 		  status = parse_uint (&s, smax, &obj.v.uint_val, base, map);
684 	       }
685 	     break;
686 
687 	   case 'I':
688 	     is_long = 1;
689 	     /* drop */
690 	   case 'i':
691 	     if ((s + 1 >= smax)
692 		 || (*s != '0'))
693 	       chf = 'd';
694 	     else if (((s[1] == 'x') || (s[1] == 'X'))
695 		      && (s + 2 < smax))
696 	       {
697 		  s += 2;
698 		  chf = 'x';
699 	       }
700 	     else chf = 'o';
701 	     force_signed = 1;
702 	     goto try_again;
703 
704 	   case 'O':
705 	     is_long = 1;
706 	     /* drop */
707 	   case 'o':
708 	     map = map8;
709 	     base = 8;
710 	     if (force_signed)
711 	       chf = 'd';
712 	     else
713 	       chf = 'u';
714 	     goto try_again;
715 
716 	   case 'X':
717 	     is_long = 1;
718 	     /* drop */
719 	   case 'x':
720 	     base = 16;
721 	     map = map16;
722 	     if (force_signed)
723 	       chf = 'd';
724 	     else
725 	       chf = 'u';
726 	     goto try_again;
727 
728 	   case 'E':
729 	   case 'F':
730 	     is_long = 1;
731 	     /* drop */
732 	   case 'e':
733 	   case 'f':
734 	   case 'g':
735 #if SLANG_HAS_FLOAT
736 	     if (is_long)
737 	       {
738 		  obj.o_data_type = SLANG_DOUBLE_TYPE;
739 		  status = parse_double (&s, smax, &obj.v.double_val);
740 	       }
741 	     else
742 	       {
743 		  obj.o_data_type = SLANG_FLOAT_TYPE;
744 		  status = parse_float (&s, smax, &obj.v.float_val);
745 	       }
746 #else
747 	     _pSLang_verror (SL_NOT_IMPLEMENTED,
748 			   "This version of the S-Lang does not support floating point");
749 	     status = -1;
750 #endif
751 	     break;
752 
753 	   case 's':
754 	     obj.o_data_type = SLANG_STRING_TYPE;
755 	     status = parse_string (&s, smax, &obj.v.s_val);
756 	     break;
757 
758 	   case 'c':
759 	     if (has_width == 0)
760 	       {
761 		  obj.o_data_type = SLANG_UCHAR_TYPE;
762 		  obj.v.uchar_val = *s++;
763 		  status = 1;
764 		  break;
765 	       }
766 	     obj.o_data_type = SLANG_STRING_TYPE;
767 	     status = parse_bstring (&s, smax, &obj.v.s_val);
768 	     break;
769 
770 	   case '[':
771 	     obj.o_data_type = SLANG_STRING_TYPE;
772 	     status = parse_range (&s, smax, &f, &obj.v.s_val);
773 	     break;
774 
775 	   case 'n':
776 	     obj.o_data_type = SLANG_UINT_TYPE;
777 	     obj.v.uint_val = (unsigned int) (s - input_string);
778 	     status = 1;
779 	     break;
780 
781 	   default:
782 	     status = -1;
783 	     _pSLang_verror (SL_NOT_IMPLEMENTED, "format specifier '%c' is not supported", chf);
784 	     break;
785 	  }
786 
787 	if (status == 0)
788 	  break;
789 
790 	if (status == -1)
791 	  goto return_error;
792 
793 	if (no_assign)
794 	  {
795 	     SLang_free_object (&obj);
796 	     continue;
797 	  }
798 
799 	if (-1 == SLang_pop_ref (&ref))
800 	  {
801 	     SLang_free_object (&obj);
802 	     goto return_error;
803 	  }
804 
805 	if (-1 == SLang_push (&obj))
806 	  {
807 	     SLang_free_object (&obj);
808 	     SLang_free_ref (ref);
809 	     goto return_error;
810 	  }
811 
812 	if (-1 == _pSLang_deref_assign (ref))
813 	  {
814 	     SLang_free_ref (ref);
815 	     goto return_error;
816 	  }
817 	SLang_free_ref (ref);
818 
819 	num++;
820 	num_refs--;
821      }
822 
823    if (-1 == SLdo_pop_n (num_refs))
824      goto return_error;
825 
826    SLang_free_slstring (format);
827    SLang_free_slstring (input_string);
828    return num;
829 
830    return_error:
831    /* NULLS ok */
832    SLang_free_slstring (format);
833    SLang_free_slstring (input_string);
834    return -1;
835 }
836 
837 # if SLANG_HAS_FLOAT
838 
839 #ifndef HAVE_STDLIB_H
840 /* Oh dear.  Where is the prototype for atof?  If not in stdlib, then
841  * I do not know where.  Not in math.h on some systems either.
842  */
843 extern double atof ();
844 #endif
845 
_pSLang_atof(SLFUTURE_CONST char * s)846 double _pSLang_atof (SLFUTURE_CONST char *s)
847 {
848    double x;
849 
850    s = _pSLskip_whitespace (s);
851    errno = 0;
852 
853    if (1 != parse_double (&s, s + strlen (s), &x))
854      {
855 	if ((0 == strcmp ("NaN", s))
856 	    || (0 == strcmp ("-Inf", s))
857 	    || (0 == strcmp ("Inf", s)))
858 	  return atof (s);	       /* let this deal with it */
859 #ifdef EINVAL
860 	errno = _pSLerrno_errno = EINVAL;
861 #endif
862 	return 0.0;
863      }
864    if (errno)
865      _pSLerrno_errno = errno;
866    return x;
867 }
868 #endif
869