/*------------------------------------------------------------------------- * * numutils.c * utility functions for I/O of built-in numeric types. * * integer: pg_itoa, pg_ltoa * floating point: ftoa, atof1 * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/utils/adt/numutils.c,v 1.42 2000/08/01 18:29:35 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include #include #include "utils/builtins.h" #ifndef INT_MAX #define INT_MAX (0x7FFFFFFFL) #endif #ifndef INT_MIN #define INT_MIN (-INT_MAX-1) #endif #ifndef SHRT_MAX #define SHRT_MAX (0x7FFF) #endif #ifndef SHRT_MIN #define SHRT_MIN (-SHRT_MAX-1) #endif #ifndef SCHAR_MAX #define SCHAR_MAX (0x7F) #endif #ifndef SCHAR_MIN #define SCHAR_MIN (-SCHAR_MAX-1) #endif int32 pg_atoi(char *s, int size, int c) { long l = 0; char *badp = (char *) NULL; Assert(s); errno = 0; /* * Some versions of strtol treat the empty string as an error. This * code will explicitly return 0 for an empty string. */ if (s == (char *) NULL) elog(ERROR, "pg_atoi: NULL pointer!"); else if (*s == 0) l = (long) 0; else l = strtol(s, &badp, 10); if (errno) /* strtol must set ERANGE */ elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); if (badp && *badp && (*badp != c)) elog(ERROR, "pg_atoi: error in \"%s\": can\'t parse \"%s\"", s, badp); switch (size) { case sizeof(int32): #if defined(HAVE_LONG_INT_64) /* won't get ERANGE on these with 64-bit longs... */ if (l < INT_MIN) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } if (l > INT_MAX) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } #endif /* HAVE_LONG_INT_64 */ break; case sizeof(int16): if (l < SHRT_MIN) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } if (l > SHRT_MAX) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } break; case sizeof(int8): if (l < SCHAR_MIN) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } if (l > SCHAR_MAX) { errno = ERANGE; elog(ERROR, "pg_atoi: error reading \"%s\": %m", s); } break; default: elog(ERROR, "pg_atoi: invalid result size: %d", size); } return (int32) l; } /* * pg_itoa - converts a short int to its string represention * * Note: * previously based on ~ingres/source/gutil/atoi.c * now uses vendor's sprintf conversion */ void pg_itoa(int16 i, char *a) { sprintf(a, "%hd", (short) i); } /* * pg_ltoa - converts a long int to its string represention * * Note: * previously based on ~ingres/source/gutil/atoi.c * now uses vendor's sprintf conversion */ void pg_ltoa(int32 l, char *a) { sprintf(a, "%d", l); } /* ** ftoa - FLOATING POINT TO ASCII CONVERSION ** ** CODE derived from ingres, ~ingres/source/gutil/ftoa.c ** ** 'Value' is converted to an ascii character string and stored ** into 'ascii'. Ascii should have room for at least 'width' + 1 ** characters. 'Width' is the width of the output field (max). ** 'Prec' is the number of characters to put after the decimal ** point. The format of the output string is controlled by ** 'format'. ** ** 'Format' can be: ** e or E: "E" format output ** f or F: "F" format output ** g or G: "F" format output if it will fit, otherwise ** use "E" format. ** n or N: same as G, but decimal points will not always ** be aligned. ** ** If 'format' is upper case, the "E" comes out in upper case; ** otherwise it comes out in lower case. ** ** When the field width is not big enough, it fills the field with ** stars ("*****") and returns zero. Normal return is the width ** of the output field (sometimes shorter than 'width'). */ #ifdef NOT_USED int ftoa(double value, char *ascii, int width, int prec1, char format) { #ifndef HAVE_FCVT char out[256]; char fmt[256]; int ret; sprintf(fmt, "%%%d.%d%c", width, prec1, format); sprintf(out, fmt, value); if ((ret = strlen(out)) > width) { MemSet(ascii, '*', width - 2); ascii[width] = 0; return 0; } strcpy(ascii, out); return ret; #else auto int expon; auto int sign; int avail = 0; char *a = NULL; char *p = NULL; char mode; int lowercase; int prec; /* extern char *ecvt(), *fcvt();*/ prec = prec1; mode = format; lowercase = 'a' - 'A'; if (mode >= 'a') mode -= 'a' - 'A'; else lowercase = 0; if (mode != 'E') { /* try 'F' style output */ p = fcvt(value, prec, &expon, &sign); avail = width; a = ascii; /* output sign */ if (sign) { avail--; *a++ = '-'; } /* output '0' before the decimal point */ if (expon <= 0) { *a++ = '0'; avail--; } /* compute space length left after dec pt and fraction */ avail -= prec + 1; if (mode == 'G') avail -= 4; if (avail >= expon) { /* it fits. output */ while (expon > 0) { /* output left of dp */ expon--; if (*p) *a++ = *p++; else *a++ = '0'; } /* output fraction (right of dec pt) */ avail = expon; goto frac_out; } /* won't fit; let's hope for G format */ } if (mode != 'F') { /* try to do E style output */ p = ecvt(value, prec + 1, &expon, &sign); avail = width - 5; a = ascii; /* output the sign */ if (sign) { *a++ = '-'; avail--; } } /* check for field too small */ if (mode == 'F' || avail < prec) { /* sorry joker, you lose */ a = ascii; for (avail = width; avail > 0; avail--) *a++ = '*'; *a = 0; return 0; } /* it fits; output the number */ mode = 'E'; /* output the LHS single digit */ *a++ = *p++; expon--; /* output the rhs */ avail = 1; frac_out: *a++ = '.'; while (prec > 0) { prec--; if (avail < 0) { avail++; *a++ = '0'; } else { if (*p) *a++ = *p++; else *a++ = '0'; } } /* output the exponent */ if (mode == 'E') { *a++ = 'E' + lowercase; if (expon < 0) { *a++ = '-'; expon = -expon; } else *a++ = '+'; *a++ = (expon / 10) % 10 + '0'; *a++ = expon % 10 + '0'; } /* output spaces on the end in G format */ if (mode == 'G') { *a++ = ' '; *a++ = ' '; *a++ = ' '; *a++ = ' '; } /* finally, we can return */ *a = 0; avail = a - ascii; return avail; #endif } #endif /* ** atof1 - ASCII TO FLOATING CONVERSION ** ** CODE derived from ~ingres/source/gutil/atof.c ** ** Converts the string 'str' to floating point and stores the ** result into the cell pointed to by 'val'. ** ** The syntax which it accepts is pretty much what you would ** expect. Basically, it is: ** {} [+|-] {} {} [.{digit}] {} [] ** where is "e" or "E" followed by an integer, is a ** space character, is zero through nine, [] is zero or ** one, and {} is zero or more. ** ** Parameters: ** str -- string to convert. ** val -- pointer to place to put the result (which ** must be type double). ** ** Returns: ** zero -- ok. ** -1 -- syntax error. ** +1 -- overflow (not implemented). ** ** Side Effects: ** clobbers *val. */ #ifdef NOT_USED int atof1(char *str, double *val) { char *p; double v; double fact; int minus; char c; int expon; int gotmant; v = 0.0; p = str; minus = 0; /* skip leading blanks */ while ((c = *p) != '\0') { if (c != ' ') break; p++; } /* handle possible sign */ switch (c) { case '-': minus++; case '+': p++; } /* skip blanks after sign */ while ((c = *p) != '\0') { if (c != ' ') break; p++; } /* start collecting the number to the decimal point */ gotmant = 0; for (;;) { c = *p; if (c < '0' || c > '9') break; v = v * 10.0 + (c - '0'); gotmant++; p++; } /* check for fractional part */ if (c == '.') { fact = 1.0; for (;;) { c = *++p; if (c < '0' || c > '9') break; fact *= 0.1; v += (c - '0') * fact; gotmant++; } } /* skip blanks before possible exponent */ while ((c = *p) != '\0') { if (c != ' ') break; p++; } /* test for exponent */ if (c == 'e' || c == 'E') { p++; expon = pg_atoi(p, sizeof(expon), '\0'); if (!gotmant) v = 1.0; fact = expon; v *= pow(10.0, fact); } else { /* if no exponent, then nothing */ if (c != 0) return -1; } /* store the result and exit */ if (minus) v = -v; *val = v; return 0; } #endif