home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / vc98 / crt / src / strtoq.c < prev    next >
C/C++ Source or Header  |  1998-06-17  |  7KB  |  212 lines

  1. /***
  2. *strtoq.c - Contains C runtimes strtoq and strtouq
  3. *
  4. *       Copyright (c) 1989-1997, Microsoft Corporation. All rights reserved.
  5. *       Copyright (c) 1992, Digital Equipment Corporation.
  6. *
  7. *Purpose:
  8. *       strtoq - convert ascii string to QUAD (signed quad) integer
  9. *       strtouq - convert ascii string to UQUAD (unsigned quad) integer
  10. *
  11. *******************************************************************************/
  12.  
  13. #include <cruntime.h>
  14. #include <stdlib.h>
  15. #include <limits.h>
  16. #include <errno.h>
  17. #include <ctype.h>
  18.  
  19. typedef __int64 quad;
  20. typedef unsigned __int64 uquad;
  21.  
  22. #define QUAD_MIN  (-9223372036854775807i64) /* minimum (signed) quad value */
  23. #define QUAD_MAX  ( 9223372036854775807i64) /* maximum (signed) quad value */
  24. #define UQUAD_MAX ((uquad)0xffffffffffffffffi64) /* maximum unsigned quad value */
  25.  
  26. /***
  27. *strtoq, strtouq(nptr,endptr,ibase) - Convert ascii string to QUAD un/signed
  28. *       int.
  29. *
  30. *Purpose:
  31. *       Convert an ascii string to a 64-bit quad value.  The base
  32. *       used for the caculations is supplied by the caller.  The base
  33. *       must be in the range 0, 2-36.  If a base of 0 is supplied, the
  34. *       ascii string must be examined to determine the base of the
  35. *       number:
  36. *               (a) First char = '0', second char = 'x' or 'X',
  37. *                   use base 16.
  38. *               (b) First char = '0', use base 8
  39. *               (c) First char in range '1' - '9', use base 10.
  40. *
  41. *       If the 'endptr' value is non-NULL, then strtoq/strtouq places
  42. *       a pointer to the terminating character in this value.
  43. *       See ANSI standard for details
  44. *
  45. *Entry:
  46. *       nptr == NEAR/FAR pointer to the start of string.
  47. *       endptr == NEAR/FAR pointer to the end of the string.
  48. *       ibase == integer base to use for the calculations.
  49. *
  50. *       string format: [whitespace] [sign] [0] [x] [digits/letters]
  51. *
  52. *Exit:
  53. *       Good return:
  54. *               result
  55. *
  56. *       Overflow return:
  57. *               strtoq -- QUAD_MAX or QUAD_MIN
  58. *               strtouq -- UQUAD_MAX
  59. *               strtoq/strtouq -- errno == ERANGE
  60. *
  61. *       No digits or bad base return:
  62. *               0
  63. *               endptr = nptr*
  64. *
  65. *Exceptions:
  66. *       None.
  67. *******************************************************************************/
  68.  
  69. /* flag values */
  70. #define FL_UNSIGNED   1       /* strtouq called */
  71. #define FL_NEG        2       /* negative sign found */
  72. #define FL_OVERFLOW   4       /* overflow occured */
  73. #define FL_READDIGIT  8       /* we've read at least one correct digit */
  74.  
  75. static uquad __cdecl strtoxq (
  76.         const char *nptr,
  77.         const char **endptr,
  78.         int ibase,
  79.         int flags
  80.         )
  81. {
  82.         const char *p;
  83.         char c;
  84.         uquad number;
  85.         unsigned digval;
  86.         uquad maxval;
  87.  
  88.         p = nptr;                       /* p is our scanning pointer */
  89.         number = 0;                     /* start with zero */
  90.  
  91.         c = *p++;                       /* read char */
  92.         while ( isspace((int)(unsigned char)c) )
  93.                 c = *p++;               /* skip whitespace */
  94.  
  95.         if (c == '-') {
  96.                 flags |= FL_NEG;        /* remember minus sign */
  97.                 c = *p++;
  98.         }
  99.         else if (c == '+')
  100.                 c = *p++;               /* skip sign */
  101.  
  102.         if (ibase < 0 || ibase == 1 || ibase > 36) {
  103.                 /* bad base! */
  104.                 if (endptr)
  105.                         /* store beginning of string in endptr */
  106.                         *endptr = nptr;
  107.                 return 0L;              /* return 0 */
  108.         }
  109.         else if (ibase == 0) {
  110.                 /* determine base free-lance, based on first two chars of
  111.                    string */
  112.                 if (c != '0')
  113.                         ibase = 10;
  114.                 else if (*p == 'x' || *p == 'X')
  115.                         ibase = 16;
  116.                 else
  117.                         ibase = 8;
  118.         }
  119.  
  120.         if (ibase == 16) {
  121.                 /* we might have 0x in front of number; remove if there */
  122.                 if (c == '0' && (*p == 'x' || *p == 'X')) {
  123.                         ++p;
  124.                         c = *p++;       /* advance past prefix */
  125.                 }
  126.         }
  127.  
  128.         /* if our number exceeds this, we will overflow on multiply */
  129.         maxval = UQUAD_MAX / ibase;
  130.  
  131.  
  132.         for (;;) {      /* exit in middle of loop */
  133.                 /* convert c to value */
  134.                 if ( isdigit((int)(unsigned char)c) )
  135.                         digval = c - '0';
  136.                 else if ( isalpha((int)(unsigned char)c) )
  137.                         digval = toupper(c) - 'A' + 10;
  138.                 else
  139.                         break;
  140.                 if (digval >= (unsigned)ibase)
  141.                         break;          /* exit loop if bad digit found */
  142.  
  143.                 /* record the fact we have read one digit */
  144.                 flags |= FL_READDIGIT;
  145.  
  146.                 /* we now need to compute number = number * base + digval,
  147.                    but we need to know if overflow occured.  This requires
  148.                    a tricky pre-check. */
  149.  
  150.                 if (number < maxval || (number == maxval &&
  151.                 (uquad)digval <= UQUAD_MAX % ibase)) {
  152.                         /* we won't overflow, go ahead and multiply */
  153.                         number = number * ibase + digval;
  154.                 }
  155.                 else {
  156.                         /* we would have overflowed -- set the overflow flag */
  157.                         flags |= FL_OVERFLOW;
  158.                 }
  159.  
  160.                 c = *p++;               /* read next digit */
  161.         }
  162.  
  163.         --p;                            /* point to place that stopped scan */
  164.  
  165.         if (!(flags & FL_READDIGIT)) {
  166.                 /* no number there; return 0 and point to beginning of
  167.                    string */
  168.                 if (endptr)
  169.                         /* store beginning of string in endptr later on */
  170.                         p = nptr;
  171.                 number = 0L;            /* return 0 */
  172.         }
  173.         else if ((flags & FL_OVERFLOW) || (!(flags & FL_UNSIGNED) &&
  174.         (number & ((uquad)QUAD_MAX+1)))) {
  175.                 /* overflow occurred or signed overflow occurred */
  176.                 errno = ERANGE;
  177.                 if (flags & FL_UNSIGNED)
  178.                         number = UQUAD_MAX;
  179.                 else
  180.                         /* set error code, will be negated if necc. */
  181.                         number = QUAD_MAX;
  182.         }
  183.  
  184.         if (endptr != NULL)
  185.                 /* store pointer to char that stopped the scan */
  186.                 *endptr = p;
  187.  
  188.         if (flags & FL_NEG)
  189.                 /* negate result if there was a neg sign */
  190.                 number = (uquad)(-(quad)number);
  191.  
  192.         return number;                  /* done. */
  193. }
  194.  
  195. quad __cdecl strtoq (
  196.         const char *nptr,
  197.         char **endptr,
  198.         int ibase
  199.         )
  200. {
  201.         return (quad) strtoxq(nptr, endptr, ibase, 0);
  202. }
  203.  
  204. uquad __cdecl strtouq (
  205.         const char *nptr,
  206.         char **endptr,
  207.         int ibase
  208.         )
  209. {
  210.         return strtoxq(nptr, endptr, ibase, FL_UNSIGNED);
  211. }
  212.