static char RcsId[] = "$Header: /user1/will/psrpack/utilsubs/string/RCS/strtox.c,v 1.36 1995/10/16 10:59:29 will Exp $"; #include #include #include #include #include /* If the preprocessor token ALLOW_0B is defined, then EXstrto[lu] * uses prefix 0b or 0B to indicate a base-2 number. Otherwise the * usual conventions for strto[lu] are followed. */ /* External caller can set strtox_allow_arith to permit +-,-*,/,% to be part * of float or int; see long comment below. */ int strtox_allow_arith=0; /* * digit_a2d[c] maps ascii characters 0..9, a-z, A-Z into the corresponding * integer digits. It is NOT ok to give it anything else as an argument. */ /* C std guarantees this is initialized to 0's */ static unsigned long digit_a2d[128]; static long const pows[] = { 10, /* 10^1 */ 100, /* 10^2 */ 10000, /* 10^4 */ 100000000, /* 10^8 */ }; #define NPOWS (sizeof(pows) / sizeof(pows[0])) #define NELEM(arry) (sizeof(arry) / sizeof(*arry)) /* Initialize the character-to-digit mapping. */ static void init_digit(void) { int c; for (c=0; c < NELEM(digit_a2d); c++) digit_a2d[c] = -1; for (c = '0' ; c <= '9'; c++) digit_a2d[c] = c - '0'; /* C standard doesn't guarantee a-z are adjancent & increasing */ digit_a2d['a']=digit_a2d['A'] = 10; digit_a2d['b']=digit_a2d['B'] = 11; digit_a2d['c']=digit_a2d['C'] = 12; digit_a2d['d']=digit_a2d['D'] = 13; digit_a2d['e']=digit_a2d['E'] = 14; digit_a2d['f']=digit_a2d['F'] = 15; digit_a2d['g']=digit_a2d['G'] = 16; digit_a2d['h']=digit_a2d['H'] = 17; digit_a2d['i']=digit_a2d['I'] = 18; digit_a2d['j']=digit_a2d['J'] = 19; digit_a2d['k']=digit_a2d['K'] = 20; digit_a2d['l']=digit_a2d['L'] = 21; digit_a2d['m']=digit_a2d['M'] = 22; digit_a2d['n']=digit_a2d['N'] = 23; digit_a2d['o']=digit_a2d['O'] = 24; digit_a2d['p']=digit_a2d['P'] = 25; digit_a2d['q']=digit_a2d['Q'] = 26; digit_a2d['r']=digit_a2d['R'] = 27; digit_a2d['s']=digit_a2d['S'] = 28; digit_a2d['t']=digit_a2d['T'] = 29; digit_a2d['u']=digit_a2d['U'] = 30; digit_a2d['v']=digit_a2d['V'] = 31; digit_a2d['w']=digit_a2d['W'] = 32; digit_a2d['x']=digit_a2d['X'] = 33; digit_a2d['y']=digit_a2d['Y'] = 34; digit_a2d['z']=digit_a2d['Z'] = 35; } /* EXstrtoul(): is like strtoul, but accepts certain extensions. * We start with a reminder of strtoul(): defined in ANSI C, strtoul() * converts a string representation of a number to an unsigned long. * Leading whitespace is ignored. The string is scanned * up to the first character inconsistent with the base. * Valid numbers have one of the following formats: * 0[xX]ddd -- hex number; ddd are 0-9, a-f, and A-F. Only allowed * if base==0 or base=16. * 0ddd -- octal number; ddd are octal digits. * dddd -- decimal number; ddd are decimal digits * EXstrtoul() is like strtoul, but also accepts * 1: The prefix: * bX% -- (here, b is literal, and X is one of ([2-9a-zA-Z]) gives base. * The comma is required. Example: b9%17 means 17 base 9. * 2: If compiled with -DALLOW_0B, the prefix 0b or 0B means a base-2 number. * 3: the extension (repeated 0 or more times) * (^lll)[,;]([Ee]mmm)?[,;]([Xx]nnn)?([-+/*%]nnn)?[,;][KkMmGg] * where kkk, lll, mmm and nnn are unsigned numbers ([0-9]+); * ^lll means to raise the value left of ^lll to the power lll; * the extension ([Xx]nnn)? means to multiply by nnn; * the extension ([Ee]mmm)? means to multiply by 10^mmm; * the extension [KkMmGg] means to multiply by 2^10 (if K) * or 2^20 (if M) or 2^30 (if G)). * IF extern strtox_allow_arith !=0, THEN the extension * ([-+/*%]nnn)? means to subtract, add, divide, multiply, or * division modulo by nnn. * Note that we define this variable and init it to zero. * !!NO RANGE CHECKING IS DONE!! */ unsigned long EXstrtoul( register char *str, /* string to be converted */ char **ptr, /* if not (char **)NULL, ptr to char terminating scan * is returned in *ptr. */ int base) /* must be one of 0, 2, ..., 36, else function returns * 0. If the base is zero, the correct base is * determined according as the string has one of the * formats given above. */ { unsigned long value=0; unsigned long digit; if (*digit_a2d == 0) init_digit(); /* skip leading whitespace */ while (isspace(*str)) ++str; /* Determine base */ if ((*str == 'b' || *str == 'B') && (isdigit(*(str+1))||isalpha(*(str+1))) && *(str+2) == '%') { base = digit_a2d[*++str]; str += 2; } else if (base == 0) { if (*str != '0') { base = 10; } else if (*++str == 'x' || *str == 'X') { base = 16; str++; #ifdef ALLOW_0B } else if (*str == 'b' || *str == 'B') { base = 2; str++; #endif } else { base = 8; } } /* Skip optional leader in string */ switch (base) { #ifdef ALLOW_0B case 2: /* Skip 0[bB], if present */ if (*str == '0') if (*++str == 'b' || *str == 'B') ++str; break; #endif case 16: /* Skip 0[xX], if present */ if (*str == '0') if (*++str == 'x' || *str == 'X') ++str; break; } /* Convert value */ while ((digit=digit_a2d[*str]) < base && digit != -1) { value = value*base + digit; ++str; } /* NOTE: IF YOU DISABLE THE CODE ENCLOSED IN * #if 1...#endif, THEN YOU GET A PLAIN strtoul * IMPLEMENTATION. */ #if 1 /* Check for exponents, multipliers, scale factors. * Skip optional commas and semicolons */ while (*str && strchr("eExX+-*/kKmMgG^,;", *str)) { switch (*str) { case ',': case ';': ++str; break; case 'e': case 'E': { int i, p, q, exp = 0; ++str; while (isdigit(*str)) exp = exp*10 + digit_a2d[*str++]; for (p = 1, i = 0; 0 < exp && i < NPOWS; exp >>= 1, ++i) if (exp & 1) p *= pows[i]; /* In case exponent too large for POWS array */ if (exp > 0) for (q = pows[NPOWS-1] ; exp > 0; exp >>= 1, q *= q) p *= q; value *= p; } break; case '^': { long power = 0; ++str; while (isdigit(*str)) power = power*10 + digit_a2d[*str++]; value = (unsigned long) pow((double) value, (double) power); } break; case 'x': case 'X': { long mult = 0; ++str; while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= mult; } break; case '-': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value -= addend; } break; case '+': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value += addend; } break; case '*': if (!strtox_allow_arith) goto quitloop; { long mult = 0; ++str; while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= mult; } break; case '/': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; value /= div; } break; case '%': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; value %= div; } break; case 'k': case 'K': value *= 1024; str++; break; case 'M': case 'm': value *= 1024 * 1024; str++; break; case 'G': case 'g': value *= 1024 * 1024 * 1024; str++; break; } } quitloop: #endif if (ptr) *ptr = str; return value; } /***********************************************************************/ /***********************************************************************/ /* Like EXstrtoul, but accepts a leading sign, the multiplier can be * signed, and returns signed value. */ long EXstrtol(register char *str, char **ptr, int base) { long sign=1; long value=0; long digit; #ifdef DEBUG fprintf(stderr, "[[[ *%#lx = %s = ", str, str); #endif if (*digit_a2d == 0) init_digit(); /* skip leading whitespace */ while (isspace(*str)) ++str; /* Check for optional sign */ switch (*str) { case '-': sign = -1; str++; break; case '+': str++; break; } /* Determine base */ if ((*str == 'b' || *str == 'B') && (isdigit(*(str+1))||isalpha(*(str+1))) && *(str+2) == '%') { base = digit_a2d[*++str]; str += 2; } else if (base == 0) { if (*str != '0') { base = 10; } else if (*++str == 'x' || *str == 'X') { base = 16; ++str; #ifdef ALLOW_0B } else if (*str == 'b' || *str == 'B') { base = 2; ++str; #endif } else { base = 8; } } /* Skip optional leader in string */ switch (base) { #ifdef ALLOW_0B case 2: /* Skip 0[bB], if present */ if (*str == '0') if (*++str == 'b' || *str == 'B') ++str; break; #endif case 16: /* Skip 0[xX], if present */ if (*str == '0') if (*++str == 'x' || *str == 'X') ++str; break; } /* Convert value */ while ((digit=digit_a2d[*str]) < base && digit != -1) { value = value*base + digit; #ifdef DEBUG fprintf(stderr, "{%c:%d:%ld}", *str, digit, value); #endif ++str; } /* NOTE: IF YOU DISABLE THE CODE ENCLOSED IN * #if 1...#endif, THEN YOU GET A PLAIN strtoul * IMPLEMENTATION. */ #if 1 /* Check for exponents, multipliers, scale factors. * Skip optional commas and semicolons */ while (*str && strchr("eExX+-*/kKmMgG^,;", *str)) { switch (*str) { case ',': case ';': ++str; break; case 'e': case 'E': { int i, p, q, exp = 0; ++str; while (isdigit(*str)) exp = exp*10 + digit_a2d[*str++]; for (p = 1, i = 0; 0 < exp && i < NPOWS; exp >>= 1, ++i) if (exp & 1) p *= pows[i]; /* In case exponent too large for POWS array */ if (exp > 0) for (q = pows[NPOWS-1] ; exp > 0; exp >>= 1, q *= q) p *= q; value *= p; } break; case 'x': case 'X': { int sign = 1; long mult = 0; if (*++str == '-') { sign = -1; ++str; } while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= (sign*mult); } break; case '-': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value -= addend; } break; case '+': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value += addend; } break; case '*': if (!strtox_allow_arith) goto quitloop; { long mult = 0; ++str; while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= mult; } break; case '/': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; value /= div; } break; case '%': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; value %= div; } break; case '^': { long power = 0; ++str; while (isdigit(*str)) power = power*10 + digit_a2d[*str++]; value = (long) pow((double) value, (double) power); } break; case 'k': case 'K': value *= 1024; str++; break; case 'M': case 'm': value *= 1024 * 1024; str++; break; case 'G': case 'g': value *= 1024 * 1024 * 1024; str++; break; } } quitloop: #endif if (ptr) *ptr = str; #ifdef DEBUG fprintf(stderr, "*ptr = str = %#lx\n", str); fprintf(stderr, "%d*%d = %d ]]]\n", sign, value, sign*value); #endif return sign*value; } /***********************************************************************/ /***********************************************************************/ /* Like strtod, but accepts the extension * (^mmm)?([-+Xx]nnn)?([Ee][-+]?nnn)?[KkMmGg] * where mmm is an integer; nnn is an unsigned numbers ([0-9]+); * the extension ^mmm means to raise the value left of ^mmm to * the power mmm; and the extension Xnnn means to multiply by * nnn * (2^10 (if k) or 2^20 (if m) or 2^30 (if g)). * Also if the caller sets the boolean flag strtox_allow_arith, then * the number can be suffixed with {+-/*} to have the appropriate * operation done on the value to its left. * !!NO RANGE CHECKING IS DONE!! */ double EXstrtod(register char *str, char **ptr) { double value; if (*digit_a2d == 0) init_digit(); value = strtod(str, ptr); if (ptr && *ptr == str) return value; else if (ptr) str = *ptr; /* Check for multipliers and scale factors */ while (*str && strchr("-+*/eExXkKmMgG^", *str)) { switch (*str) { case 'x': case 'X': { int sign = 1; long mult = 0; if (*++str == '-') { sign = -1; ++str; } while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= (double) (sign*mult); } break; case 'e': case 'E': { int i, p, q, exp = 0; char sign = '+'; ++str; if (*str == '-') sign = *str++; while (isdigit(*str)) exp = exp*10 + digit_a2d[*str++]; for (p = 1, i = 0; 0 < exp && i < NPOWS; exp >>= 1, ++i) if (exp & 1) p *= pows[i]; /* In case exponent too large for POWS array */ if (exp > 0) for (q = pows[NPOWS-1] ; exp > 0; exp >>= 1, q *= q) p *= q; value = (sign == '-') ? value / p : value * p; } break; case '^': { int sign = 1; long power = 0; if (*++str == '-') { sign = -1; ++str; } while (isdigit(*str)) power = power*10 + digit_a2d[*str++]; value = pow(value, (double) (sign*power)); } break; case '-': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value -= addend; } break; case '+': if (!strtox_allow_arith) goto quitloop; { long addend = 0; ++str; while (isdigit(*str)) addend = addend*10 + digit_a2d[*str++]; value += addend; } break; case '*': if (!strtox_allow_arith) goto quitloop; { long mult = 0; ++str; while (isdigit(*str)) mult = mult*10 + digit_a2d[*str++]; value *= mult; } break; case '/': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; value /= div; } break; case '%': if (!strtox_allow_arith) goto quitloop; { long div = 0; ++str; while (isdigit(*str)) div = div*10 + digit_a2d[*str++]; { double d = value / (double) div; value -= d * (double) div; } } break; case 'k': case 'K': value *= 1024.; str++; break; case 'M': case 'm': value *= 1024. * 1024.; str++; break; case 'G': case 'g': value *= 1024. * 1024. * 1024.; str++; break; } } quitloop: if (ptr) *ptr = str; return value; } #ifdef TEST int main(int argc, char **argv) { char buf[1000]; char *s, *end; unsigned long ul; long l; double d; strtox_allow_arith=1; while (1) { printf("Enter an EXstrtoX expression: "); fflush(stdout); if (!fgets(buf, sizeof(buf), stdin)) exit(0); if ((s = strchr(buf, '\n'))) *s = '\0'; ul = EXstrtoul(buf, &end, 0); if (!*end) end = ""; printf("EXstrtoul(\"%s\", &end, 0) = %lu; end=%s\n", buf, ul, end); l = EXstrtol(buf, &end, 0); if (!*end) end = ""; printf("EXstrtol(\"%s\", &end, 0) = %ld; end=%s\n", buf, l, end); d = EXstrtod(buf, &end); if (!*end) end = ""; printf("EXstrtod(\"%s\", &end) = %g; end=%s\n", buf, d, end); } } #endif