1 /*
2  * $Id: utils.c,v 1.20 2001/02/13 23:38:06 danny Exp $
3  *
4  * Copyright � 1990, 1992, 1993, 2001 Free Software Foundation, Inc.
5  *
6  * This file is part of Oleo, the GNU Spreadsheet.
7  *
8  * Oleo is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2, or (at your option)
11  * any later version.
12  *
13  * Oleo 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
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with Oleo; see the file COPYING.  If not, write to
20  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #ifdef	WITH_DMALLOC
28 #include <dmalloc.h>
29 #endif
30 
31 #include <stdio.h>
32 #include <errno.h>
33 #include <stdlib.h>
34 
35 #include "sysdef.h"
36 #include <stdarg.h>
37 #include <fcntl.h>
38 
39 #include "cmd.h"
40 
41 /* unistd.h defines _POSIX_VERSION on POSIX.1 systems.  */
42 #if defined(DIRENT) || defined(_POSIX_VERSION)
43 #include <dirent.h>
44 #define NLENGTH(dirent) (strlen((dirent)->d_name))
45 #else /* not (DIRENT or _POSIX_VERSION) */
46 #define dirent direct
47 #define NLENGTH(dirent) ((dirent)->d_namlen)
48 #ifdef SYSNDIR
49 #include <sys/ndir.h>
50 #endif /* SYSNDIR */
51 #ifdef SYSDIR
52 #include <sys/dir.h>
53 #endif /* SYSDIR */
54 #ifdef NDIR
55 #include <ndir.h>
56 #endif /* NDIR */
57 #endif /* not (DIRENT or _POSIX_VERSION) */
58 
59 #include <ctype.h>
60 #include "utils.h"
61 
62 #ifndef F_OK
63 #define F_OK 0
64 #endif
65 #ifndef _IOSTRG
66 #define _IOSTRG 0
67 #endif
68 
69 // extern int sys_nerr;
70 
71 struct id
72   {
73     int flag;
74     FILE *fp;
75     char *name;
76   };
77 
78 struct id *__id_s;
79 int __id_n;
80 int __id_f;
81 
82 int __make_backups;
83 int __backup_by_copying;
84 
85 /* Blow chunks! */
86 void
panic(const char * s,...)87 panic (const char *s,...)
88 {
89   va_list iggy;
90 
91   va_start (iggy, s);
92   fprintf (stderr, "%s %s:", GNU_PACKAGE, VERSION);
93   vfprintf (stderr, s, iggy);
94   putc ('\n', stderr);
95   va_end (iggy);
96   exit (2);
97 }
98 
99 /* Given a file name, come up with a backup file name. . . */
100 char *
backup_file_name(char * file_name)101 backup_file_name (char *file_name)
102 {
103   char *dir_name, *dir_end;
104 
105   DIR *dir;
106   register struct dirent *dp;
107   int len;
108   int max_fnum;
109   int cur_fnum;
110 
111   char *tmp_ptr;
112 
113   char *return_value;
114 
115   dir_end = (char *)rindex (file_name, '/');
116   if (dir_end)
117     {
118       dir_name = file_name;
119       file_name = dir_end + 1;
120       *dir_end = '\0';
121     }
122   else
123     {
124       dir_name = ".";
125     }
126   len = strlen (file_name);
127 
128   dir = opendir (dir_name);
129   if (dir == 0)
130     {
131       if (dir_end)
132 	*dir_end = '/';
133       return (char *) 0;
134     }
135 
136   max_fnum = 0;
137   while ((dp = readdir (dir)))
138     {
139       if (!dp->d_ino
140 	  || NLENGTH (dp) <= len
141 	  || strncmp (dp->d_name, file_name, len)
142 	  || dp->d_name[len] != '.'
143 	  || dp->d_name[len + 1] != '~'
144 	  || dp->d_name[NLENGTH(dp) - 1] != '~')
145 	continue;
146 
147       tmp_ptr = &(dp->d_name[len + 2]);
148       for (cur_fnum = 0; isdigit (*tmp_ptr); tmp_ptr++)
149 	cur_fnum = cur_fnum * 10 + *tmp_ptr - '0';
150       if (tmp_ptr != &(dp->d_name[NLENGTH(dp) - 1]) || cur_fnum < max_fnum)
151 	continue;
152       max_fnum = cur_fnum;
153     }
154   closedir (dir);
155   max_fnum++;
156   return_value = (char *) malloc (strlen (dir_name) + len + 12);
157   if (!return_value)
158     return (char *) 0;
159   sprintf (return_value, "%s/%s.~%d~", dir_name, file_name, max_fnum);
160   if (dir_end)
161     *dir_end = '/';
162   return return_value;
163 }
164 
165 
166 char *
__fp_name(fp)167 __fp_name (fp)
168      FILE *fp;
169 {
170   int n;
171 
172   for (n = 0; n < __id_n; n++)
173     {
174       if (__id_s[n].fp == fp)
175 	return __id_s[n].name;
176     }
177   return "{Unknown file pointer}";
178 }
179 
180 void
__set_fp(fp,name,flag)181 __set_fp (fp, name, flag)
182      FILE *fp;
183      const char *name;
184      int flag;
185 {
186   if (__id_s == 0)
187     {
188       __id_s = ck_malloc (20 * sizeof (struct id));
189       __id_n = 0;
190       __id_f = 20;
191     }
192   else
193     {
194       int n;
195 
196       for (n = 0; n < __id_n; n++)
197 	if (__id_s[n].fp == fp)
198 	  {
199 	    free (__id_s[n].name);
200 	    __id_s[n] = __id_s[--__id_n];
201 	    __id_f++;
202 	    break;
203 	  }
204     }
205   if (__id_f == 0)
206     {
207       __id_f = 20;
208       __id_s = ck_realloc (__id_s, (__id_f + __id_n) * sizeof (struct id));
209     }
210   __id_s[__id_n].flag = flag;
211   __id_s[__id_n].name = strdup (name);
212   __id_s[__id_n].fp = fp;
213   __id_n++;
214   __id_f--;
215 }
216 
217 /* Open a file or a pipe */
218 FILE *
xopen(name,mode)219 xopen (name, mode)
220      const char *name;
221      const char *mode;
222 {
223   int flag = 0;
224   FILE *ret;
225 
226   while (*name == ' ')
227     name++;
228   if (*name == '!')
229     {
230       name++;
231       ret = popen (name, mode);
232       flag = 1;
233     }
234   else
235     ret = fopen (name, mode);
236   if (ret == 0)
237     return ret;
238   __set_fp (ret, name, flag);
239   return ret;
240 }
241 
242 /* Open a file, creating a backup file if needed. . . */
243 FILE *
fopen_with_backup(name,mode)244 fopen_with_backup (name, mode)
245      char *name;
246      const char *mode;
247 {
248   char *newname;
249   struct stat stat_buf;
250   int old_file = 0;
251 
252   old_file = (stat (name, &stat_buf) >= 0);
253 
254   if (__make_backups && *mode == 'w' && access (name, F_OK) == 0)
255     {
256       newname = backup_file_name (name);
257       if (!newname)
258 	return (FILE *) 0;
259       if (__backup_by_copying)
260 	{
261 	  FILE *c_in, *c_out;
262 	  int n_read;
263 	  char buf[4096];
264 	  c_in = fopen (name, "r");
265 	  if (!c_in)
266 	    return 0;
267 	  c_out = fopen (newname, "w");
268 	  {
269 	    int fd;
270 	    if (!old_file || (stat (name, &stat_buf) < 0))
271 	      {
272 		fclose (c_in);
273 		return 0;
274 	      }
275 	    fd = creat (newname, stat_buf.st_mode);
276 	    if (fd < 0)
277 	      {
278 		fclose (c_in);
279 		return 0;
280 	      }
281 	    chmod (newname, stat_buf.st_mode);
282 	    c_out = fdopen (fd, "w");
283 	    if (!c_out)
284 	      {
285 		fclose (c_in);
286 		close (fd);
287 		return 0;
288 	      }
289 	  }
290 	  while ((n_read = fread (buf, 1, sizeof (buf), c_in)) > 0)
291 	    if (fwrite (buf, 1, n_read, c_out) != n_read)
292 	      {
293 		fclose (c_in);
294 		fclose (c_out);
295 		return (FILE *) 0;
296 	      }
297 	  if (fclose (c_in) == EOF || fclose (c_out) == EOF)
298 	    return (FILE *) 0;
299 	}
300       else
301 #if defined(HAVE_RENAME)
302       if (rename (name, newname) < 0)
303 #else
304       if (link (name, newname) || unlink (name))
305 #endif
306 	return (FILE *) 0;
307       free (newname);
308     }
309 
310   {
311     FILE * ret = fopen (name, mode);
312     if (ret && old_file)
313       chmod (name, stat_buf.st_mode);
314     return ret;
315   }
316 }
317 
318 /* Open a file or a pipe, creating a backup file if it's a file */
319 FILE *
xopen_with_backup(name,mode)320 xopen_with_backup (name, mode)
321      const char *name;
322      const char *mode;
323 {
324   int flag;
325   FILE *ret;
326 
327   while (*name == ' ')
328     name++;
329   if (*name == '|')
330     {
331       ret = popen (name + 1, mode);
332       flag = 1;
333     }
334   else
335     {
336       ret = fopen_with_backup (name, mode);
337       flag = 0;
338     }
339   if (ret == 0)
340     return ret;
341   __set_fp (ret, name, flag);
342   return ret;
343 }
344 
345 /* Close something opened with xopen. . . */
346 int
xclose(fp)347 xclose (fp)
348      FILE *fp;
349 {
350   int ret;
351   int n;
352 
353   for (n = 0; n < __id_n; n++)
354     {
355       if (__id_s[n].fp == fp)
356 	break;
357     }
358   if (n == __id_n)
359     panic ("Unknown file pointer %p given to xclose", fp);
360   if (__id_s[n].flag)
361     ret = pclose (fp);
362   else
363     ret = fclose (fp);
364   return ret;
365 }
366 
367 /* Fclose or panic */
368 void
ck_fclose(stream)369 ck_fclose (stream)
370      FILE *stream;
371 {
372   if (fclose (stream) == EOF)
373     panic ("Couldn't close %s", __fp_name (stream));
374 }
375 
376 /* fopen or panic */
377 void *
ck_malloc(size)378 ck_malloc (size)
379      size_t size;
380 {
381   void *ret;
382 
383   ret = malloc (size);
384   if (ret == (void *) 0)
385     panic ("Couldn't allocate %u bytes", size);
386   return ret;
387 }
388 
389 
390 void
ck_free(void * mem)391 ck_free (void * mem)
392 {
393   if (mem) free (mem);
394 }
395 
396 char *
ck_savestr(char * str)397 ck_savestr (char *str)
398 {
399   char *newstr = 0;
400   if (str)
401     {
402       int len = strlen (str) + 1;
403       newstr = (char *) ck_malloc (len);
404       bcopy (str, newstr, len);
405     }
406   return newstr;
407 }
408 
409 
410 char *
ck_savestrn(char * str,int n)411 ck_savestrn (char *str, int n)
412 {
413   char *newstr = 0;
414   if (str)
415     {
416       newstr = (char *) ck_malloc (n + 1);
417       if (n)
418 	bcopy (str, newstr, n);
419       newstr[n] = '\0';
420     }
421   return newstr;
422 }
423 
424 void *
ck_calloc(size_t size)425 ck_calloc (size_t size)
426 {
427   void *ret;
428 
429   ret = calloc (size, 1);
430   if (ret == (void *) 0)
431     panic ("Couldn't allocate %u bytes", size);
432   return ret;
433 }
434 
435 /* Realloc or panic */
436 void *
ck_realloc(void * ptr,size_t size)437 ck_realloc (void *ptr, size_t size)
438 {
439   void *ret;
440 
441   if (!ptr)
442     ret = malloc (size);
443   else
444     ret = realloc (ptr, size);
445   if (ret == (void *) 0)
446     panic ("Couldn't re-allocate %u bytes from %p", size, ptr);
447   return ret;
448 }
449 
450 /* Do a sprintf into an allocated buffer. */
451 char *
mk_sprintf(char * str,...)452 mk_sprintf (char *str,...)
453 {
454   va_list iggy;
455   char tmpbuf[1024 * 8];
456   char *ret;
457 
458   va_start (iggy, str);
459   vsprintf (tmpbuf, str, iggy);
460   va_end (iggy);
461   ret = (char *) ck_malloc (strlen (tmpbuf) + 1);
462   strcpy (ret, tmpbuf);
463   return ret;
464 }
465 
466 
467 /* Implement a variable sized LIFO stack of pointers to void */
468 
469 struct stack
470   {
471     int allocated;
472     int used;
473     void **buf;
474   };
475 
476 #define MIN_STACK 20
477 
478 void *
init_stack()479 init_stack ()
480 {
481   struct stack *b;
482 
483   b = (struct stack *) ck_malloc (sizeof (struct stack));
484   b->allocated = MIN_STACK;
485   b->used = 0;
486   b->buf = (void **) ck_malloc (MIN_STACK * sizeof (void *));
487   return (void *) b;
488 }
489 
490 void
flush_stack(bb)491 flush_stack (bb)
492      void *bb;
493 {
494   struct stack *b;
495 
496   b = (struct stack *) bb;
497   free (b->buf);
498   b->buf = 0;
499   b->allocated = 0;
500   b->used = 0;
501   free (b);
502 }
503 
504 void
push_stack(bb,add)505 push_stack (bb, add)
506      void *bb;
507      void *add;
508 {
509   struct stack *b;
510 
511   b = (struct stack *) bb;
512   if (b->allocated == b->used)
513     {
514       b->allocated *= 2;
515       b->buf = (void **) ck_realloc (b->buf, b->allocated * sizeof (void *));
516     }
517   b->buf[(b->used)++] = add;
518 }
519 
520 void *
pop_stack(bb)521 pop_stack (bb)
522      void *bb;
523 {
524   struct stack *b;
525 
526   b = (struct stack *) bb;
527   if (b->used == 0)
528     return (void *) 0;
529   return b->buf[--(b->used)];
530 }
531 
532 int
size_stack(bb)533 size_stack (bb)
534      void *bb;
535 {
536   struct stack *b;
537 
538   b = (struct stack *) bb;
539   return b->used;
540 }
541 
542 #ifndef HAVE_STRDUP
543 char *
strdup(str)544 strdup (str)
545      const char *str;
546 {
547   char *ret;
548 
549   ret = (char *) ck_malloc (strlen (str) + 2);
550   strcpy (ret, str);
551   return ret;
552 }
553 #endif
554 
555 #ifndef _DEBUG_MALLOC_INC
556 #ifndef HAVE_STRICMP
557 /*
558  * stricmp - compare string s1 to s2, ignoring case
559  */
560 
561 int
stricmp(const char * s1,const char * s2)562 stricmp (const char * s1, const char * s2)
563 {
564   register const char *scan1;
565   register const char *scan2;
566   register char chr1, chr2;
567 
568   scan1 = s1;
569   scan2 = s2;
570   do
571     {
572       chr1 = isupper (*scan1) ? tolower (*scan1) : *scan1;
573       chr2 = isupper (*scan2) ? tolower (*scan2) : *scan2;
574       scan1++;
575       scan2++;
576     }
577   while (chr1 && chr1 == chr2);
578 
579   /*
580 	 * The following case analysis is necessary so that characters
581 	 * which look negative collate low against normal characters but
582 	 * high against the end-of-string NUL.
583 	 */
584   if (chr1 == '\0' && chr2 == '\0')
585     return 0;
586   else if (chr1 == '\0')
587     return -1;
588   else if (chr2 == '\0')
589     return 1;
590   else
591     return chr1 - chr2;
592 }
593 #endif
594 #endif /* ndef _DEBUG_MALLOC_INC */
595 
596 #ifndef _DEBUG_MALLOC_INC
597 #ifndef HAVE_STRINCMP
598 /* strincmp - compare first N chars of strings S1 and S2 */
599 int
strincmp(const char * s1,const char * s2,size_t n)600 strincmp (const char * s1, const char * s2, size_t n)
601 {
602   register const char *scan1;
603   register const char *scan2;
604   register size_t count;
605   register char chr1, chr2;
606 
607   scan1 = s1;
608   scan2 = s2;
609   count = n;
610   do
611     {
612       chr1 = isupper (*scan1) ? tolower (*scan1) : *scan1;
613       chr2 = isupper (*scan2) ? tolower (*scan2) : *scan2;
614       scan1++;
615       scan2++;
616     }
617   while (--count != 0 && chr1 && chr1 == chr2);
618 
619   /* if (count == (size_t)-1)
620 		return 0; */
621 
622   /*
623 	 * The following case analysis is necessary so that characters
624 	 * which look negative collate low against normal characters but
625 	 * high against the end-of-string NUL.
626 	 */
627   if (chr1 == '\0' && chr2 == '\0')
628     return 0;
629   else if (chr1 == '\0')
630     return -1;
631   else if (chr2 == '\0')
632     return 1;
633   else
634     return chr1 - chr2;
635 }
636 #endif
637 #endif /* ndef _DEBUG_MALLOC_INC */
638 
639 #ifndef _DEBUG_MALLOC_INC
640 #ifndef HAVE_STRSTR
641 char *
strstr(const char * s,const char * wanted)642 strstr (const char *s, const char *wanted)
643 {
644   register const char *scan;
645   register size_t len;
646   register char firstc;
647 
648   /*
649 	 * The odd placement of the two tests is so "" is findable.
650 	 * Also, we inline the first char for speed.
651 	 * The ++ on scan has been moved down for optimization.
652 	 */
653   firstc = *wanted;
654   len = strlen (wanted);
655   for (scan = s; *scan != firstc || strncmp (scan, wanted, len) != 0;)
656     if (*scan++ == '\0')
657       return (char *) 0;
658   return scan;
659 }
660 #endif
661 #endif /* ndef _DEBUG_MALLOC_INC */
662 
663 char *
err_msg(void)664 err_msg (void)
665 {
666   int n;
667   static char buf[80];	/* Static to be able to return its address */
668   char *p;
669 
670   n = errno;
671 
672 #ifdef	HAVE_STRERROR
673   p = strerror(n);
674   if (p)
675 	strcpy(buf, p);
676   else
677 	sprintf (buf, "Unknown error code %d (%#x)", n, n);
678 #else
679 #if HAVE_SYS_ERRLIST
680   /* This was #if-fed away. Why ? */
681   if (n < sys_nerr);
682      return sys_errlist[n];
683 #endif
684 
685   sprintf (buf, "Unknown error code %d (%#x)", n, n);
686 #endif	/* HAVE_STRERROR */
687 
688   return buf;
689 }
690 
691 
692 /* Take a quoted string and return the character it represents */
693 int
string_to_char(char ** ptr)694 string_to_char (char ** ptr)
695 {
696   char *str;
697   int i;
698   char c1, c2;
699 
700   str = *ptr;
701   if (str[0] == '\\')
702     {
703       switch (str[1])
704 	{
705 	case ' ':
706 	  i = ' ';
707 	  break;
708 	case '\\':
709 	  i = '\\';
710 	  break;
711 	case 'b':
712 	  i = '\b';
713 	  break;
714 	case 'f':
715 	  i = '\f';
716 	  break;
717 	case 'n':
718 	  i = '\n';
719 	  break;
720 	case 'r':
721 	  i = '\r';
722 	  break;
723 	case 't':
724 	  i = '\t';
725 	  break;
726 	case 'x':
727 	  c1 = str[2];
728 	  c2 = str[3];
729 	  if (isxdigit (c1))
730 	    {
731 	      if (isdigit (c1))
732 		c1 -= '0';
733 	      else if (isupper (c1))
734 		c1 -= 'A';
735 	      else
736 		c1 -= 'a';
737 	      if (isxdigit (c2))
738 		{
739 		  if (isdigit (c2))
740 		    c2 -= '0';
741 		  else if (isupper (c2))
742 		    c2 -= 'A';
743 		  else
744 		    c2 -= 'a';
745 		  i = c1 * 0x10 + c2;
746 		  str++;
747 		}
748 	      else
749 		i = c1;
750 	    }
751 	  else
752 	    i = 'x';
753 	  break;
754 
755 	case '0':
756 	case '1':
757 	case '2':
758 	case '3':
759 	case '4':
760 	case '5':
761 	case '6':
762 	case '7':
763 	  if (str[2] >= '0' && str[2] <= '7')
764 	    {
765 	      if (str[3] >= '0' && str[3] <= '7')
766 		{
767 		  i = (str[1] - '0') * 0100 + (str[2] - '0') * 010 + (str[3] - '0');
768 		  str += 2;
769 		}
770 	      else
771 		{
772 		  i = (str[1] - '0') * 010 + (str[2] - '0');
773 		  str++;
774 		}
775 	    }
776 	  else
777 	    i = str[1] - '0';
778 	  break;
779 	default:
780 	  i = str[0];
781 	  --str;
782 	  break;
783 	}
784       str += 2;
785       *ptr = str;
786       return i;
787     }
788 
789   if (str[0] == 'M' && str[1] == '-')
790     {
791       i = META_BIT;
792       str += 2;
793     }
794   else
795     i = 0;
796 
797   if (str[0] == '^')
798     {
799       if (str[1] == '\\')
800 	++str;
801       if (str[1] == '?')
802 	i += BACKSPACE;
803       else if (str[1] >= '@' && str[1] <= '_')
804 	i |= str[1] - '@';
805       else if (str[1] >= 'a' && str[1] <= 'z')
806 	i = str[1] - 'a' + 1;
807       else if (str[1] == '\0' || isspace (str[1]))
808 	i = '^';
809       else
810 	return -1;
811       str += 2;
812     }
813   else
814     {
815       i |= str[0];
816       str++;
817     }
818   *ptr = str;
819 /* Hack */
820   if (i < 0)
821      i += 256;
822   return i;
823 }
824 
825 /* Take a char and turn it into a readable string */
826 char *
char_to_string(int ch)827 char_to_string (int ch)
828 {
829   static char buf[] = "M-\0\0\0\0";
830 
831   if (ch == '\\')
832     return "\\\\";
833 
834   if (isprint(ch)) {
835       buf[3] = ch;
836       buf[4] = 0;
837       return &buf[3];
838   }
839 
840   if (ch & META_BIT)
841     {
842       ch &= MASK_META_BIT;
843       if (ch == BACKSPACE || ch < ' ')
844 	{
845 	  buf[2] = '^';
846 	  buf[3] = (ch == BACKSPACE ? '?' : ch + '@');
847 	  if (buf[3] == '\\')
848 	    {
849 	      buf[4] = '\\';
850 	      buf[5] = 0;
851 	    }
852 	  else
853 	    buf[4] = 0;
854 	}
855       else
856 	{
857 	  buf[2] = ch;
858 	  if (buf[2] == '\\')
859 	    {
860 	      buf[3] = '\\';
861 	      buf[4] = 0;
862 	    }
863 	  else
864 	    buf[3] = '\0';
865 	}
866       return buf;
867     }
868   if (ch == BACKSPACE || ch < ' ')
869     {
870       buf[2] = '^';
871       buf[3] = (ch == BACKSPACE ? '?' : ch + '@');
872       if (buf[3] == '\\')
873 	{
874 	  buf[4] = '\\';
875 	  buf[5] = 0;
876 	}
877       else
878 	buf[4] = 0;
879       return &buf[2];
880     }
881   return "huh";
882 }
883 
884 
885 long
astol(char ** ptr)886 astol (char **ptr)
887 {
888   register long i = 0;
889   register int c;
890   int sign = 1;
891   char *s;
892 
893   s = *ptr;
894   /* Skip whitespace */
895   while (isspace (*s))
896     if (*s++ == '\0')
897       {
898 	*ptr = s;
899 	return (0);
900       }
901   /* Check for - or + */
902   if (*s == '-')
903     {
904       s++;
905       sign = -1;
906     }
907   else if (*s == '+')
908     s++;
909 
910   /* Read in the digits */
911   for (; (c = *s); s++)
912     {
913       if (!isdigit (c) || i > 214748364 || (i == 214748364 && c > (sign > 0 ? '7' : '8')))
914 	break;
915       i = i * 10 + c - '0';
916     }
917   *ptr = s;
918   return i * sign;
919 }
920 
921 /*
922  *	astof - accept a number of the form:
923  *		(and ignores leading white space)
924  *
925  *	null	::=
926  *	digit	::= 0|1|2|3|4|5|6|7|8|9
927  *	digits	::= <digit>*
928  *	DIGITS	::= <digit>+
929  *	sign	::= <null> | + | -
930  *	-------------------------------
931  *		accepted:
932  *	-------------------------------
933  *	integer	::= <sign><DIGITS>
934  *	real	::= <integer> . <digits> | <null> . <DIGITS>
935  *	epart	::= e <integer> | E <integer>
936  *	float	::= <integer> <epart> | <real> <epart>
937  *
938  *	Always returned as a double
939  *
940  *	There is no particular attempt to reduce mpys/divs
941  *	those machines that are still out there (eg. PDP11/Small)
942  *	that shun floating point arithmetic might rethink this routine.
943  */
944 
945 static double exps0[10] =
946 {1E0, 1E1, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7, 1E8, 1E9};
947 static double exps1[10] =
948 {1E00, 1E10, 1E20, 1E30
949 #ifndef vax
950  ,1E40, 1E50, 1E60, 1E70, 1E80, 1E90
951 #endif
952 };
953 
954 #define REGISTER register
955 
956 double
astof(char ** sp)957 astof (char **sp)
958 {
959   REGISTER char *s;
960   REGISTER char *cp;
961   long ipart, epart;
962   int neg = 0;
963   double res;
964   int n;
965 
966   s = *sp;
967   while (isspace (*s))
968     {
969       s++;
970       if (*s == '\0')
971 	{
972 	  *sp = s;
973 	  return (0.0);
974 	}
975     }
976   /*
977 	 *	Need to handle sign here due to '-.3' or '-0.3'
978 	 */
979   if (*s == '-')
980     {
981       ++neg;
982       ++s;
983     }
984   else if (*s == '+')
985     ++s;
986   cp = s;
987   /*
988 	 *	get ipart handling '.n' case
989 	 */
990   res = 0.0;
991   while (isdigit (*s))
992     {
993       for (n = 0, ipart = 0; n < 6 && isdigit (*s); n++)
994 	ipart = ipart * 10 + *s++ - '0';
995       res = res * exps0[n] + (double) ipart;
996     }
997   if (s == cp)
998     {
999       if (*s == '.')
1000 	ipart = 0;
1001       else
1002 	{
1003 	  *sp = s;
1004 	  return (0.0);
1005 	}
1006     }
1007   /*
1008 	 *	either we find a '.' or e|E or done
1009 	 */
1010   if (*s == '.')
1011     {
1012       int m;
1013       ++s;
1014 
1015       m = 0;
1016       while (isdigit (*s))
1017 	{
1018 	  for (n = 0, ipart = 0; n < 6 && isdigit (*s); n++)
1019 	    ipart = ipart * 10 + *s++ - '0';
1020 	  m += n;
1021 	  if (m >= 100)
1022 	    continue;
1023 	  if (m >= 10)
1024 	    res += ((double) ipart) / (exps1[m / 10] * exps0[m % 10]);
1025 	  else
1026 	    res += ((double) ipart) / exps0[m];
1027 	}
1028     }
1029   /*
1030 	 *	In either case (.) handle E part
1031 	 */
1032   if (*s == 'e' || *s == 'E')
1033     {
1034       int eneg;
1035 
1036       ++s;
1037       epart = 0;
1038       eneg = 0;
1039       if (*s == '-')
1040 	{
1041 	  eneg++;
1042 	  s++;
1043 	}
1044       else if (*s == '+')
1045 	s++;
1046       while (isdigit (*s))
1047 	epart = epart * 10 + *s++ - '0';
1048       if (eneg)
1049 	{
1050 #ifndef vax
1051 	  while (epart >= 100)
1052 	    {
1053 	      res /= 1E100;
1054 	      epart -= 100;
1055 	    }
1056 #endif
1057 	  if (epart > 9)
1058 	    {
1059 	      res /= exps1[epart / 10];
1060 	      epart %= 10;
1061 	    }
1062 	  if (epart)
1063 	    res /= exps0[epart];
1064 	}
1065       else
1066 	{
1067 #ifndef vax
1068 	  while (epart >= 100)
1069 	    {
1070 	      res *= 1E100;
1071 	      epart -= 100;
1072 	    }
1073 #endif
1074 	  if (epart > 9)
1075 	    {
1076 	      res *= exps1[epart / 10];
1077 	      epart %= 10;
1078 	    }
1079 	  if (epart)
1080 	    res *= exps0[epart];
1081 	}
1082     }
1083   /*
1084 	 *	fix sign
1085 	 */
1086   if (neg)
1087     res = -res;
1088   *sp = s;
1089   return (res);
1090 }
1091 
1092 #ifdef TEST_ASTOF
main()1093 main ()
1094 {
1095   char buf[80];
1096   char *ptr;
1097   double at, ast;
1098   double atof ();
1099 
1100   while (gets (buf))
1101     {
1102       at = atof (buf);
1103       ptr = buf;
1104       ast = astof (&ptr);
1105       printf ("%15.6f %15.6f %s ", at, ast, at == ast ? "eq" : "NEQ");
1106       if (*ptr)
1107 	printf ("%s->'%s'\n", buf, ptr);
1108       else
1109 	printf ("%s\n", buf);
1110     }
1111 }
1112 char *
ck_savestr(str)1113 ck_savestr (str)
1114      char *str;
1115 {
1116   char *newstr = 0;
1117   if (str)
1118     {
1119       int len = strlen (str) + 1;
1120       newstr = (char *) ck_malloc (len);
1121       bcopy (str, newstr, len);
1122     }
1123   return newstr;
1124 }
1125 
1126 #endif
1127