util.c


DEFINITIONS

This source file includes following functions.
  1. scan_oct
  2. scan_hex
  3. S_ISDIR
  4. ruby_add_suffix
  5. valid_filename
  6. make_dbcs_table
  7. mblen
  8. push_element
  9. __crt0_glob_function
  10. mmswap_
  11. mmrot3_
  12. ruby_qsort
  13. ruby_strdup
  14. ruby_getcwd
  15. ruby_strtod


   1  /**********************************************************************
   2  
   3    util.c -
   4  
   5    $Author: michal $
   6    $Date: 2002/08/28 08:05:23 $
   7    created at: Fri Mar 10 17:22:34 JST 1995
   8  
   9    Copyright (C) 1993-2002 Yukihiro Matsumoto
  10  
  11  **********************************************************************/
  12  
  13  #include "ruby.h"
  14  
  15  #include <ctype.h>
  16  #include <stdio.h>
  17  #include <errno.h>
  18  
  19  #ifdef NT
  20  #include "missing/file.h"
  21  #endif
  22  
  23  #include "util.h"
  24  #ifndef HAVE_STRING_H
  25  char *strchr _((char*,char));
  26  #endif
  27  
  28  unsigned long
  29  scan_oct(start, len, retlen)
  30      const char *start;
  31      int len;
  32      int *retlen;
  33  {
  34      register const char *s = start;
  35      register unsigned long retval = 0;
  36  
  37      while (len-- && *s >= '0' && *s <= '7') {
  38          retval <<= 3;
  39          retval |= *s++ - '0';
  40      }
  41      *retlen = s - start;
  42      return retval;
  43  }
  44  
  45  unsigned long
  46  scan_hex(start, len, retlen)
  47      const char *start;
  48      int len;
  49      int *retlen;
  50  {
  51      static char hexdigit[] = "0123456789abcdef0123456789ABCDEF";
  52      register const char *s = start;
  53      register unsigned long retval = 0;
  54      char *tmp;
  55  
  56      while (len-- && *s && (tmp = strchr(hexdigit, *s))) {
  57          retval <<= 4;
  58          retval |= (tmp - hexdigit) & 15;
  59          s++;
  60      }
  61      *retlen = s - start;
  62      return retval;
  63  }
  64  
  65  #include <sys/types.h>
  66  #include <sys/stat.h>
  67  #ifdef HAVE_UNISTD_H
  68  #include <unistd.h>
  69  #endif
  70  #if defined(HAVE_FCNTL_H)
  71  #include <fcntl.h>
  72  #endif
  73  
  74  #ifndef S_ISDIR
  75  #   define S_ISDIR(m) ((m & S_IFMT) == S_IFDIR)
  76  #endif
  77  
  78  #ifdef NT
  79  #include "missing/file.h"
  80  #endif
  81  
  82  #if defined(MSDOS) || defined(__CYGWIN32__) || defined(NT)
  83  /*
  84   *  Copyright (c) 1993, Intergraph Corporation
  85   *
  86   *  You may distribute under the terms of either the GNU General Public
  87   *  License or the Artistic License, as specified in the perl README file.
  88   *
  89   *  Various Unix compatibility functions and NT specific functions.
  90   *
  91   *  Some of this code was derived from the MSDOS port(s) and the OS/2 port.
  92   *
  93   */
  94  
  95  
  96  /*
  97   * Suffix appending for in-place editing under MS-DOS and OS/2 (and now NT!).
  98   *
  99   * Here are the rules:
 100   *
 101   * Style 0:  Append the suffix exactly as standard perl would do it.
 102   *           If the filesystem groks it, use it.  (HPFS will always
 103   *           grok it.  So will NTFS. FAT will rarely accept it.)
 104   *
 105   * Style 1:  The suffix begins with a '.'.  The extension is replaced.
 106   *           If the name matches the original name, use the fallback method.
 107   *
 108   * Style 2:  The suffix is a single character, not a '.'.  Try to add the 
 109   *           suffix to the following places, using the first one that works.
 110   *               [1] Append to extension.  
 111   *               [2] Append to filename, 
 112   *               [3] Replace end of extension, 
 113   *               [4] Replace end of filename.
 114   *           If the name matches the original name, use the fallback method.
 115   *
 116   * Style 3:  Any other case:  Ignore the suffix completely and use the
 117   *           fallback method.
 118   *
 119   * Fallback method:  Change the extension to ".$$$".  If that matches the
 120   *           original name, then change the extension to ".~~~".
 121   *
 122   * If filename is more than 1000 characters long, we die a horrible
 123   * death.  Sorry.
 124   *
 125   * The filename restriction is a cheat so that we can use buf[] to store
 126   * assorted temporary goo.
 127   *
 128   * Examples, assuming style 0 failed.
 129   *
 130   * suffix = ".bak" (style 1)
 131   *                foo.bar => foo.bak
 132   *                foo.bak => foo.$$$    (fallback)
 133   *                foo.$$$ => foo.~~~    (fallback)
 134   *                makefile => makefile.bak
 135   *
 136   * suffix = "~" (style 2)
 137   *                foo.c => foo.c~
 138   *                foo.c~ => foo.c~~
 139   *                foo.c~~ => foo~.c~~
 140   *                foo~.c~~ => foo~~.c~~
 141   *                foo~~~~~.c~~ => foo~~~~~.$$$ (fallback)
 142   *
 143   *                foo.pas => foo~.pas
 144   *                makefile => makefile.~
 145   *                longname.fil => longname.fi~
 146   *                longname.fi~ => longnam~.fi~
 147   *                longnam~.fi~ => longnam~.$$$
 148   *                
 149   */
 150  
 151  
 152  static int valid_filename(char *s);
 153  
 154  static char suffix1[] = ".$$$";
 155  static char suffix2[] = ".~~~";
 156  
 157  #define ext (&buf[1000])
 158  
 159  #define strEQ(s1,s2) (strcmp(s1,s2) == 0)
 160  
 161  void
 162  ruby_add_suffix(str, suffix)
 163      VALUE str;
 164      char *suffix;
 165  {
 166      int baselen;
 167      int extlen = strlen(suffix);
 168      char *s, *t, *p;
 169      long slen;
 170      char buf[1024];
 171  
 172      if (RSTRING(str)->len > 1000)
 173          rb_fatal("Cannot do inplace edit on long filename (%ld characters)",
 174                   RSTRING(str)->len);
 175  
 176  #if defined(DJGPP) || defined(__CYGWIN32__) || defined(NT)
 177      /* Style 0 */
 178      slen = RSTRING(str)->len;
 179      rb_str_cat(str, suffix, extlen);
 180  #if defined(DJGPP)
 181      if (_USE_LFN) return;
 182  #else
 183      if (valid_filename(RSTRING(str)->ptr)) return;
 184  #endif
 185  
 186      /* Fooey, style 0 failed.  Fix str before continuing. */
 187      RSTRING(str)->ptr[RSTRING(str)->len = slen] = '\0';
 188  #endif
 189  
 190      slen = extlen;
 191      t = buf; baselen = 0; s = RSTRING(str)->ptr;
 192      while ((*t = *s) && *s != '.') {
 193          baselen++;
 194          if (*s == '\\' || *s == '/') baselen = 0;
 195          s++; t++;
 196      }
 197      p = t;
 198  
 199      t = ext; extlen = 0;
 200      while (*t++ = *s++) extlen++;
 201      if (extlen == 0) { ext[0] = '.'; ext[1] = 0; extlen++; }
 202  
 203      if (*suffix == '.') {        /* Style 1 */
 204          if (strEQ(ext, suffix)) goto fallback;
 205          strcpy(p, suffix);
 206      }
 207      else if (suffix[1] == '\0') {  /* Style 2 */
 208          if (extlen < 4) { 
 209              ext[extlen] = *suffix;
 210              ext[++extlen] = '\0';
 211          }
 212          else if (baselen < 8) {
 213              *p++ = *suffix;
 214          }
 215          else if (ext[3] != *suffix) {
 216              ext[3] = *suffix;
 217          }
 218          else if (buf[7] != *suffix) {
 219              buf[7] = *suffix;
 220          }
 221          else goto fallback;
 222          strcpy(p, ext);
 223      }
 224      else { /* Style 3:  Panic */
 225  fallback:
 226          (void)memcpy(p, strEQ(ext, suffix1) ? suffix2 : suffix1, 5);
 227      }
 228      rb_str_resize(str, strlen(buf));
 229      memcpy(RSTRING(str)->ptr, buf, RSTRING(str)->len);
 230  }
 231  
 232  #if defined(__CYGWIN32__) || defined(NT)
 233  static int 
 234  valid_filename(char *s)
 235  {
 236      int fd;
 237  
 238      /*
 239      // if the file exists, then it's a valid filename!
 240      */
 241  
 242      if (_access(s, 0) == 0) {
 243          return 1;
 244      }
 245  
 246      /*
 247      // It doesn't exist, so see if we can open it.
 248      */
 249      
 250      if ((fd = _open(s, O_CREAT, 0666)) >= 0) {
 251          _close(fd);
 252          _unlink (s);    /* don't leave it laying around */
 253          return 1;
 254      }
 255      return 0;
 256  }
 257  #endif
 258  #endif
 259  
 260  #if defined __DJGPP__
 261  
 262  #include <dpmi.h>
 263  
 264  static char dbcs_table[256];
 265  
 266  int
 267  make_dbcs_table()
 268  {
 269      __dpmi_regs r;
 270      struct {
 271          unsigned char start;
 272          unsigned char end;
 273      } vec;
 274      int offset;
 275  
 276      memset(&r, 0, sizeof(r));
 277      r.x.ax = 0x6300;
 278      __dpmi_int(0x21, &r);
 279      offset = r.x.ds * 16 + r.x.si;
 280  
 281      for (;;) {
 282          int i;
 283          dosmemget(offset, sizeof vec, &vec);
 284          if (!vec.start && !vec.end)
 285              break;
 286          for (i = vec.start; i <= vec.end; i++)
 287              dbcs_table[i] = 1;
 288          offset += 2;
 289      }
 290  }
 291  
 292  int
 293  mblen(const char *s, size_t n)
 294  {
 295      static int need_init = 1;
 296      if (need_init) {
 297          make_dbcs_table();
 298          need_init = 0;
 299      }
 300      if (s) {
 301          if (n == 0 || *s == 0)
 302              return 0;
 303          return dbcs_table[(unsigned char)*s] + 1;
 304      }
 305      else
 306          return 1;
 307  }
 308  
 309  struct PathList {
 310      struct PathList *next;
 311      char *path;
 312  };
 313  
 314  struct PathInfo {
 315      struct PathList *head;
 316      int count;
 317  };
 318  
 319  static void
 320  push_element(const char *path, VALUE vinfo)
 321  {
 322      struct PathList *p;
 323      struct PathInfo *info = (struct PathInfo *)vinfo;
 324  
 325      p = ALLOC(struct PathList);
 326      MEMZERO(p, struct PathList, 1);
 327      p->path = ruby_strdup(path);
 328      p->next = info->head;
 329      info->head = p;
 330      info->count++;
 331  }
 332  
 333  #include <dirent.h>
 334  int __opendir_flags = __OPENDIR_PRESERVE_CASE;
 335  
 336  char **
 337  __crt0_glob_function(char *path)
 338  {
 339      int len = strlen(path);
 340      int i;
 341      char **rv;
 342      char path_buffer[PATH_MAX];
 343      char *buf = path_buffer;
 344      char *p;
 345      struct PathInfo info;
 346      struct PathList *plist;
 347  
 348      if (PATH_MAX <= len)
 349          buf = ruby_xmalloc(len + 1);
 350  
 351      strncpy(buf, path, len);
 352      buf[len] = '\0';
 353  
 354      for (p = buf; *p; p += mblen(p, MB_CUR_MAX))
 355          if (*p == '\\')
 356              *p = '/';
 357  
 358      info.count = 0;
 359      info.head = 0;
 360  
 361      rb_globi(buf, push_element, (VALUE)&info);
 362  
 363      if (buf != path_buffer)
 364          ruby_xfree(buf);
 365  
 366      if (info.count == 0)
 367          return 0;
 368  
 369      rv = ruby_xmalloc((info.count + 1) * sizeof (char *));
 370  
 371      plist = info.head;
 372      i = 0;
 373      while (plist) {
 374          struct PathList *cur;
 375          rv[i] = plist->path;
 376          cur = plist;
 377          plist = plist->next;
 378          ruby_xfree(cur);
 379          i++;
 380      }
 381      rv[i] = 0;
 382      return rv;
 383  }
 384  
 385  #endif
 386  
 387  /* mm.c */
 388  
 389  #define A ((int*)a)
 390  #define B ((int*)b)
 391  #define C ((int*)c)
 392  #define D ((int*)d)
 393  
 394  #define mmprepare(base, size) do {\
 395   if (((long)base & (0x3)) == 0)\
 396     if (size >= 16) mmkind = 1;\
 397     else            mmkind = 0;\
 398   else              mmkind = -1;\
 399   high = (size & (~0xf));\
 400   low  = (size &  0x0c);\
 401  } while (0)\
 402  
 403  #define mmarg mmkind, size, high, low
 404  
 405  static void mmswap_(a, b, mmarg)
 406      register char *a, *b;
 407      int mmarg;
 408  {
 409   register int s;
 410   if (a == b) return;
 411   if (mmkind >= 0) {
 412     if (mmkind > 0) {
 413       register char *t = a + high;
 414       do {
 415         s = A[0]; A[0] = B[0]; B[0] = s;
 416         s = A[1]; A[1] = B[1]; B[1] = s;
 417         s = A[2]; A[2] = B[2]; B[2] = s;
 418         s = A[3]; A[3] = B[3]; B[3] = s;  a += 16; b += 16;
 419       } while (a < t);
 420     }
 421     if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = s;
 422       if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = s;
 423         if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = s;}}}
 424   }
 425   else {
 426     register char *t = a + size;
 427     do {s = *a; *a++ = *b; *b++ = s;} while (a < t);
 428   }
 429  }
 430  #define mmswap(a,b) mmswap_((a),(b),mmarg)
 431  
 432  static void mmrot3_(a, b, c, mmarg)
 433      register char *a, *b, *c;
 434      int mmarg;
 435  {
 436   register int s;
 437   if (mmkind >= 0) {
 438     if (mmkind > 0) {
 439       register char *t = a + high;
 440       do {
 441         s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
 442         s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
 443         s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;
 444         s = A[3]; A[3] = B[3]; B[3] = C[3]; C[3] = s; a += 16; b += 16; c += 16;
 445       } while (a < t);
 446     }
 447     if (low != 0) { s = A[0]; A[0] = B[0]; B[0] = C[0]; C[0] = s;
 448       if (low >= 8) { s = A[1]; A[1] = B[1]; B[1] = C[1]; C[1] = s;
 449         if (low == 12) {s = A[2]; A[2] = B[2]; B[2] = C[2]; C[2] = s;}}}
 450   }
 451   else {
 452     register char *t = a + size;
 453     do {s = *a; *a++ = *b; *b++ = *c; *c++ = s;} while (a < t);
 454   }
 455  }
 456  #define mmrot3(a,b,c) mmrot3_((a),(b),(c),mmarg)
 457  
 458  /* qs6.c */
 459  /*****************************************************/
 460  /*                                                   */
 461  /*          qs6   (Quick sort function)              */
 462  /*                                                   */
 463  /* by  Tomoyuki Kawamura              1995.4.21      */
 464  /* kawamura@tokuyama.ac.jp                           */
 465  /*****************************************************/
 466  
 467  typedef struct { char *LL, *RR; } stack_node; /* Stack structure for L,l,R,r */
 468  #define PUSH(ll,rr) do { top->LL = (ll); top->RR = (rr); ++top; } while (0)  /* Push L,l,R,r */
 469  #define POP(ll,rr)  do { --top; ll = top->LL; rr = top->RR; } while (0)      /* Pop L,l,R,r */
 470  
 471  #define med3(a,b,c) ((*cmp)(a,b)<0 ?                                   \
 472                         ((*cmp)(b,c)<0 ? b : ((*cmp)(a,c)<0 ? c : a)) : \
 473                         ((*cmp)(b,c)>0 ? b : ((*cmp)(a,c)<0 ? a : c)))
 474  
 475  void ruby_qsort (base, nel, size, cmp)
 476       void* base;
 477       const int nel;
 478       const int size;
 479       int (*cmp)();
 480  {
 481    register char *l, *r, *m;             /* l,r:left,right group   m:median point */
 482    register int  t, eq_l, eq_r;          /* eq_l: all items in left group are equal to S */
 483    char *L = base;                       /* left end of curren region */
 484    char *R = (char*)base + size*(nel-1); /* right end of current region */
 485    int  chklim = 63;                     /* threshold of ordering element check */
 486    stack_node stack[32], *top = stack;   /* 32 is enough for 32bit CPU */
 487    int mmkind, high, low;
 488  
 489    if (nel <= 1) return;        /* need not to sort */
 490    mmprepare(base, size);
 491    goto start;
 492  
 493    nxt:
 494    if (stack == top) return;    /* return if stack is empty */
 495    POP(L,R);
 496  
 497    for (;;) {
 498      start:
 499      if (L + size == R) {       /* 2 elements */
 500        if ((*cmp)(L,R) > 0) mmswap(L,R); goto nxt;
 501      }
 502  
 503      l = L; r = R;
 504      t = (r - l + size) / size;  /* number of elements */
 505      m = l + size * (t >> 1);    /* calculate median value */
 506  
 507      if (t >= 60) {
 508        register char *m1;
 509        register char *m3;
 510        if (t >= 200) {
 511          t = size*(t>>3); /* number of bytes in splitting 8 */
 512          {
 513            register char *p1 = l  + t;
 514            register char *p2 = p1 + t;
 515            register char *p3 = p2 + t;
 516            m1 = med3(p1, p2, p3);
 517            p1 = m  + t;
 518            p2 = p1 + t;
 519            p3 = p2 + t;
 520            m3 = med3(p1, p2, p3);
 521          }
 522        }
 523        else {
 524          t = size*(t>>2); /* number of bytes in splitting 4 */
 525          m1 = l + t;
 526          m3 = m + t;
 527        }
 528        m = med3(m1, m, m3);
 529      }
 530  
 531      if ((t = (*cmp)(l,m)) < 0) {                             /*3-5-?*/
 532        if ((t = (*cmp)(m,r)) < 0) {                           /*3-5-7*/
 533          if (chklim && nel >= chklim) {   /* check if already ascending order */
 534            char *p;
 535            chklim = 0;
 536            for (p=l; p<r; p+=size) if ((*cmp)(p,p+size) > 0) goto fail;
 537            goto nxt;
 538          }
 539          fail: goto loopA;                                    /*3-5-7*/
 540        }
 541        if (t > 0) {
 542          if ((*cmp)(l,r) <= 0) {mmswap(m,r); goto loopA;}     /*3-5-4*/
 543          mmrot3(r,m,l); goto loopA;                           /*3-5-2*/
 544        }
 545        goto loopB;                                            /*3-5-5*/
 546      }
 547  
 548      if (t > 0) {                                             /*7-5-?*/
 549        if ((t = (*cmp)(m,r)) > 0) {                           /*7-5-3*/
 550          if (chklim && nel >= chklim) {   /* check if already ascending order */
 551            char *p;
 552            chklim = 0;
 553            for (p=l; p<r; p+=size) if ((*cmp)(p,p+size) < 0) goto fail2;
 554            while (l<r) {mmswap(l,r); l+=size; r-=size;}  /* reverse region */
 555            goto nxt;
 556          }
 557          fail2: mmswap(l,r); goto loopA;                      /*7-5-3*/
 558        }
 559        if (t < 0) {
 560          if ((*cmp)(l,r) <= 0) {mmswap(l,m); goto loopB;}     /*7-5-8*/
 561          mmrot3(l,m,r); goto loopA;                           /*7-5-6*/
 562        }
 563        mmswap(l,r); goto loopA;                               /*7-5-5*/
 564      }
 565  
 566      if ((t = (*cmp)(m,r)) < 0)  {goto loopA;}                /*5-5-7*/
 567      if (t > 0) {mmswap(l,r); goto loopB;}                    /*5-5-3*/
 568  
 569      /* determining splitting type in case 5-5-5 */           /*5-5-5*/
 570      for (;;) {
 571        if ((l += size) == r)      goto nxt;                   /*5-5-5*/
 572        if (l == m) continue;
 573        if ((t = (*cmp)(l,m)) > 0) {mmswap(l,r); l = L; goto loopA;}  /*575-5*/
 574        if (t < 0)                 {mmswap(L,l); l = L; goto loopB;}  /*535-5*/
 575      }
 576  
 577      loopA: eq_l = 1; eq_r = 1;  /* splitting type A */ /* left <= median < right */
 578      for (;;) {
 579        for (;;) {
 580          if ((l += size) == r)
 581            {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
 582          if (l == m) continue;
 583          if ((t = (*cmp)(l,m)) > 0) {eq_r = 0; break;}
 584          if (t < 0) eq_l = 0;
 585        }
 586        for (;;) {
 587          if (l == (r -= size))
 588            {l -= size; if (l != m) mmswap(m,l); l -= size; goto fin;}
 589          if (r == m) {m = l; break;}
 590          if ((t = (*cmp)(r,m)) < 0) {eq_l = 0; break;}
 591          if (t == 0) break;
 592        }
 593        mmswap(l,r);    /* swap left and right */
 594      }
 595  
 596      loopB: eq_l = 1; eq_r = 1;  /* splitting type B */ /* left < median <= right */
 597      for (;;) {
 598        for (;;) {
 599          if (l == (r -= size))
 600            {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
 601          if (r == m) continue;
 602          if ((t = (*cmp)(r,m)) < 0) {eq_l = 0; break;}
 603          if (t > 0) eq_r = 0;
 604        }
 605        for (;;) {
 606          if ((l += size) == r)
 607            {r += size; if (r != m) mmswap(r,m); r += size; goto fin;}
 608          if (l == m) {m = r; break;}
 609          if ((t = (*cmp)(l,m)) > 0) {eq_r = 0; break;}
 610          if (t == 0) break;
 611        }
 612        mmswap(l,r);    /* swap left and right */
 613      }
 614  
 615      fin:
 616      if (eq_l == 0)                         /* need to sort left side */
 617        if (eq_r == 0)                       /* need to sort right side */
 618          if (l-L < R-r) {PUSH(r,R); R = l;} /* sort left side first */
 619          else           {PUSH(L,l); L = r;} /* sort right side first */
 620        else R = l;                          /* need to sort left side only */
 621      else if (eq_r == 0) L = r;             /* need to sort right side only */
 622      else goto nxt;                         /* need not to sort both sides */
 623    }
 624  }
 625  
 626  char *
 627  ruby_strdup(str)
 628      const char *str;
 629  {
 630      char *tmp;
 631      int len = strlen(str) + 1;
 632  
 633      tmp = xmalloc(len);
 634      if (tmp == NULL) return NULL;
 635      memcpy(tmp, str, len);
 636  
 637      return tmp;
 638  }
 639  
 640  char *
 641  ruby_getcwd()
 642  {
 643      int size = 200;
 644      char *buf = xmalloc(size);
 645  
 646      while (!getcwd(buf, size)) {
 647          if (errno != ERANGE) rb_sys_fail(0);
 648          size *= 2;
 649          buf = xrealloc(buf, size);
 650      }
 651      return buf;
 652  }
 653  
 654  /* copyright notice for strtod implementation --
 655   *
 656   * Copyright (c) 1988-1993 The Regents of the University of California.
 657   * Copyright (c) 1994 Sun Microsystems, Inc.
 658   *
 659   * Permission to use, copy, modify, and distribute this
 660   * software and its documentation for any purpose and without
 661   * fee is hereby granted, provided that the above copyright
 662   * notice appear in all copies.  The University of California
 663   * makes no representations about the suitability of this
 664   * software for any purpose.  It is provided "as is" without
 665   * express or implied warranty.
 666   *
 667   */
 668  
 669  #define TRUE 1
 670  #define FALSE 0
 671  
 672  static int maxExponent = 511;   /* Largest possible base 10 exponent.  Any
 673                                   * exponent larger than this will already
 674                                   * produce underflow or overflow, so there's
 675                                   * no need to worry about additional digits.
 676                                   */
 677  static double powersOf10[] = {  /* Table giving binary powers of 10.  Entry */
 678      10.0,                       /* is 10^2^i.  Used to convert decimal */
 679      100.0,                      /* exponents into floating-point numbers. */
 680      1.0e4,
 681      1.0e8,
 682      1.0e16,
 683      1.0e32,
 684      1.0e64,
 685      1.0e128,
 686      1.0e256
 687  };
 688  
 689  /*
 690   *----------------------------------------------------------------------
 691   *
 692   * strtod --
 693   *
 694   *      This procedure converts a floating-point number from an ASCII
 695   *      decimal representation to internal double-precision format.
 696   *
 697   * Results:
 698   *      The return value is the double-precision floating-point
 699   *      representation of the characters in string.  If endPtr isn't
 700   *      NULL, then *endPtr is filled in with the address of the
 701   *      next character after the last one that was part of the
 702   *      floating-point number.
 703   *
 704   * Side effects:
 705   *      None.
 706   *
 707   *----------------------------------------------------------------------
 708   */
 709  
 710  double
 711  ruby_strtod(string, endPtr)
 712      const char *string;         /* A decimal ASCII floating-point number,
 713                                   * optionally preceded by white space.
 714                                   * Must have form "-I.FE-X", where I is the
 715                                   * integer part of the mantissa, F is the
 716                                   * fractional part of the mantissa, and X
 717                                   * is the exponent.  Either of the signs
 718                                   * may be "+", "-", or omitted.  Either I
 719                                   * or F may be omitted, or both.  The decimal
 720                                   * point isn't necessary unless F is present.
 721                                   * The "E" may actually be an "e".  E and X
 722                                   * may both be omitted (but not just one).
 723                                   */
 724      char **endPtr;              /* If non-NULL, store terminating character's
 725                                   * address here. */
 726  {
 727      int sign, expSign = FALSE;
 728      double fraction, dblExp, *d;
 729      register const char *p;
 730      register int c;
 731      int exp = 0;                /* Exponent read from "EX" field. */
 732      int fracExp = 0;            /* Exponent that derives from the fractional
 733                                   * part.  Under normal circumstatnces, it is
 734                                   * the negative of the number of digits in F.
 735                                   * However, if I is very long, the last digits
 736                                   * of I get dropped (otherwise a long I with a
 737                                   * large negative exponent could cause an
 738                                   * unnecessary overflow on I alone).  In this
 739                                   * case, fracExp is incremented one for each
 740                                   * dropped digit. */
 741      int mantSize;               /* Number of digits in mantissa. */
 742      int decPt;                  /* Number of mantissa digits BEFORE decimal
 743                                   * point. */
 744      const char *pExp;           /* Temporarily holds location of exponent
 745                                   * in string. */
 746  
 747      /*
 748       * Strip off leading blanks and check for a sign.
 749       */
 750  
 751      errno = 0;
 752      p = string;
 753      while (ISSPACE(*p)) {
 754          p += 1;
 755      }
 756      if (*p == '-') {
 757          sign = TRUE;
 758          p += 1;
 759      }
 760      else {
 761          if (*p == '+') {
 762              p += 1;
 763          }
 764          sign = FALSE;
 765      }
 766  
 767      /*
 768       * Count the number of digits in the mantissa (including the decimal
 769       * point), and also locate the decimal point.
 770       */
 771  
 772      decPt = -1;
 773      for (mantSize = 0; ; mantSize += 1) {
 774          c = *p;
 775          if (!ISDIGIT(c)) {
 776              if ((c != '.') || (decPt >= 0)) {
 777                  break;
 778              }
 779              decPt = mantSize;
 780          }
 781          p += 1;
 782      }
 783  
 784      /*
 785       * Now suck up the digits in the mantissa.  Use two integers to
 786       * collect 9 digits each (this is faster than using floating-point).
 787       * If the mantissa has more than 18 digits, ignore the extras, since
 788       * they can't affect the value anyway.
 789       */
 790      
 791      pExp  = p;
 792      p -= mantSize;
 793      if (decPt < 0) {
 794          decPt = mantSize;
 795      }
 796      else {
 797          mantSize -= 1;                  /* One of the digits was the point. */
 798      }
 799      if (mantSize > 18) {
 800          fracExp = decPt - 18;
 801          mantSize = 18;
 802      }
 803      else {
 804          fracExp = decPt - mantSize;
 805      }
 806      if (mantSize == 0) {
 807          fraction = 0.0;
 808          p = string;
 809          goto done;
 810      }
 811      else {
 812          int frac1, frac2;
 813          frac1 = 0;
 814          for ( ; mantSize > 9; mantSize -= 1) {
 815              c = *p;
 816              p += 1;
 817              if (c == '.') {
 818                  c = *p;
 819                  p += 1;
 820              }
 821              frac1 = 10*frac1 + (c - '0');
 822          }
 823          frac2 = 0;
 824          for (; mantSize > 0; mantSize -= 1) {
 825              c = *p;
 826              p += 1;
 827              if (c == '.') {
 828                  c = *p;
 829                  p += 1;
 830              }
 831              frac2 = 10*frac2 + (c - '0');
 832          }
 833          fraction = (1.0e9 * frac1) + frac2;
 834      }
 835  
 836      /*
 837       * Skim off the exponent.
 838       */
 839  
 840      p = pExp;
 841      if ((*p == 'E') || (*p == 'e')) {
 842          p += 1;
 843          if (*p == '-') {
 844              expSign = TRUE;
 845              p += 1;
 846          }
 847          else {
 848              if (*p == '+') {
 849                  p += 1;
 850              }
 851              expSign = FALSE;
 852          }
 853          while (ISDIGIT(*p)) {
 854              exp = exp * 10 + (*p - '0');
 855              p += 1;
 856          }
 857      }
 858      if (expSign) {
 859          exp = fracExp - exp;
 860      }
 861      else {
 862          exp = fracExp + exp;
 863      }
 864  
 865      /*
 866       * Generate a floating-point number that represents the exponent.
 867       * Do this by processing the exponent one bit at a time to combine
 868       * many powers of 2 of 10. Then combine the exponent with the
 869       * fraction.
 870       */
 871      
 872      if (exp < 0) {
 873          expSign = TRUE;
 874          exp = -exp;
 875      }
 876      else {
 877          expSign = FALSE;
 878      }
 879      if (exp > maxExponent) {
 880          exp = maxExponent;
 881          errno = ERANGE;
 882      }
 883      dblExp = 1.0;
 884      for (d = powersOf10; exp != 0; exp >>= 1, d += 1) {
 885          if (exp & 01) {
 886              dblExp *= *d;
 887          }
 888      }
 889      if (expSign) {
 890          fraction /= dblExp;
 891      }
 892      else {
 893          fraction *= dblExp;
 894      }
 895  
 896  done:
 897      if (endPtr != NULL) {
 898          *endPtr = (char *) p;
 899      }
 900  
 901      if (sign) {
 902          return -fraction;
 903      }
 904      return fraction;
 905  }