Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / libgmp / mpz / set_str.c
1 /* mpz_set_str(mp_dest, string, base) -- Convert the \0-terminated
2    string STRING in base BASE to multiple precision integer in
3    MP_DEST.  Allow white space in the string.  If BASE == 0 determine
4    the base in the C standard way, i.e.  0xhh...h means base 16,
5    0oo...o means base 8, otherwise assume base 10.
6
7 Copyright (C) 1991, 1993, 1994, Free Software Foundation, Inc.
8
9 This file is part of the GNU MP Library.
10
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of the GNU Library General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
15
16 The GNU MP Library is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
19 License for more details.
20
21 You should have received a copy of the GNU Library General Public License
22 along with the GNU MP Library; see the file COPYING.LIB.  If not, write to
23 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
24 MA 02111-1307, USA. */
25
26 #include <ctype.h>
27 #include "gmp.h"
28 #include "gmp-impl.h"
29 #include "longlong.h"
30
31 static int
32 digit_value_in_base (c, base)
33      int c;
34      int base;
35 {
36   int digit;
37
38   if (isdigit (c))
39     digit = c - '0';
40   else if (islower (c))
41     digit = c - 'a' + 10;
42   else if (isupper (c))
43     digit = c - 'A' + 10;
44   else
45     return -1;
46
47   if (digit < base)
48     return digit;
49   return -1;
50 }
51
52 int
53 #if __STDC__
54 mpz_set_str (mpz_ptr x, const char *str, int base)
55 #else
56 mpz_set_str (x, str, base)
57      mpz_ptr x;
58      const char *str;
59      int base;
60 #endif
61 {
62   size_t str_size;
63   char *s, *begs;
64   size_t i;
65   mp_size_t xsize;
66   int c;
67   int negative;
68   TMP_DECL (marker);
69
70   /* Skip whitespace.  */
71   do
72     c = *str++;
73   while (isspace (c));
74
75   negative = 0;
76   if (c == '-')
77     {
78       negative = 1;
79       c = *str++;
80     }
81
82   if (digit_value_in_base (c, base == 0 ? 10 : base) < 0)
83     return -1;                  /* error if no digits */
84
85   /* If BASE is 0, try to find out the base by looking at the initial
86      characters.  */
87   if (base == 0)
88     {
89       base = 10;
90       if (c == '0')
91         {
92           base = 8;
93           c = *str++;
94           if (c == 'x' || c == 'X')
95             {
96               base = 16;
97               c = *str++;
98             }
99         }
100     }
101
102   TMP_MARK (marker);
103   str_size = strlen (str - 1);
104   s = begs = (char *) TMP_ALLOC (str_size + 1);
105
106   for (i = 0; i < str_size; i++)
107     {
108       if (!isspace (c))
109         {
110           int dig = digit_value_in_base (c, base);
111           if (dig < 0)
112             {
113               TMP_FREE (marker);
114               return -1;
115             }
116           *s++ = dig;
117         }
118       c = *str++;
119     }
120
121   str_size = s - begs;
122
123   xsize = str_size / __mp_bases[base].chars_per_limb + 1;
124   if (x->_mp_alloc < xsize)
125     _mpz_realloc (x, xsize);
126
127   xsize = mpn_set_str (x->_mp_d, (unsigned char *) begs, str_size, base);
128   x->_mp_size = negative ? -xsize : xsize;
129
130   TMP_FREE (marker);
131   return 0;
132 }