xref: /openbsd/gnu/usr.bin/cvs/lib/strtoul.c (revision d415bd75)
1 /*
2  * strtol : convert a string to long.
3  *
4  * Andy Wilson, 2-Oct-89.
5  */
6 
7 #include <errno.h>
8 #include <ctype.h>
9 #include <stdio.h>
10 
11 #ifdef HAVE_CONFIG_H
12 #include "config.h"
13 #endif
14 
15 #ifndef ULONG_MAX
16 #define	ULONG_MAX	((unsigned long)(~0L))		/* 0xFFFFFFFF */
17 #endif
18 
19 extern int errno;
20 
21 unsigned long
22 strtoul(s, ptr, base)
23      const char *s; char **ptr; int base;
24 {
25   unsigned long total = 0;
26   unsigned digit;
27   const char *start=s;
28   int did_conversion=0;
29   int overflow = 0;
30   int negate = 0;
31   unsigned long maxdiv, maxrem;
32 
33   if (s==NULL)
34     {
35       errno = ERANGE;
36       if (!ptr)
37 	*ptr = (char *)start;
38       return 0L;
39     }
40 
41   while (isspace(*s))
42     s++;
43   if (*s == '+')
44     s++;
45   else if (*s == '-')
46     s++, negate = 1;
47   if (base==0 || base==16) /*  the 'base==16' is for handling 0x */
48     {
49       int tmp;
50 
51       /*
52        * try to infer base from the string
53        */
54       if (*s != '0')
55         tmp = 10;	/* doesn't start with 0 - assume decimal */
56       else if (s[1] == 'X' || s[1] == 'x')
57 	tmp = 16, s += 2; /* starts with 0x or 0X - hence hex */
58       else
59 	tmp = 8;	/* starts with 0 - hence octal */
60       if (base==0)
61 	base = (int)tmp;
62     }
63 
64   maxdiv = ULONG_MAX / base;
65   maxrem = ULONG_MAX % base;
66 
67   while ((digit = *s) != '\0')
68     {
69       if (digit >= '0' && digit < ('0'+base))
70 	digit -= '0';
71       else
72 	if (base > 10)
73 	  {
74 	    if (digit >= 'a' && digit < ('a'+(base-10)))
75 	      digit = digit - 'a' + 10;
76 	    else if (digit >= 'A' && digit < ('A'+(base-10)))
77 	      digit = digit - 'A' + 10;
78 	    else
79 	      break;
80 	  }
81 	else
82 	  break;
83       did_conversion = 1;
84       if (total > maxdiv
85 	  || (total == maxdiv && digit > maxrem))
86 	overflow = 1;
87       total = (total * base) + digit;
88       s++;
89     }
90   if (overflow)
91     {
92       errno = ERANGE;
93       if (ptr != NULL)
94 	*ptr = (char *)s;
95       return (ULONG_MAX);
96     }
97   if (ptr != NULL)
98     *ptr = (char *) ((did_conversion) ? (char *)s : (char *)start);
99   return negate ? -total : total;
100 }
101