File indexing completed on 2024-05-12 17:22:34

0001 /* floatio.c: low level conversion, based on floatnum. */
0002 /*
0003     Copyright (C) 2007, 2008 Wolf Lammen.
0004 
0005     This program is free software; you can redistribute it and/or modify
0006     it under the terms of the GNU General Public License as published by
0007     the Free Software Foundation; either version 2 of the License , or
0008     (at your option) any later version.
0009 
0010     This program is distributed in the hope that it will be useful,
0011     but WITHOUT ANY WARRANTY; without even the implied warranty of
0012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0013     GNU General Public License for more details.
0014 
0015     You should have received a copy of the GNU General Public License
0016     along with this program; see the file COPYING.  If not, write to:
0017 
0018       The Free Software Foundation, Inc.
0019       59 Temple Place, Suite 330
0020       Boston, MA 02111-1307 USA.
0021 
0022 
0023     You may contact the author by:
0024        e-mail:  ookami1 <at> gmx <dot> de
0025        mail:  Wolf Lammen
0026               Oertzweg 45
0027               22307 Hamburg
0028               Germany
0029 
0030 *************************************************************************/
0031 
0032 #include "floatio.h"
0033 #include "floatlong.h"
0034 #include <string.h>
0035 
0036 typedef enum { NmbNormal, NmbSpecial, NmbBufferOverflow } NmbType;
0037 
0038 /* Besides the regular bases 2, 8, 10 and 16, there are
0039    three others (IO_BASE_DEFAULT, IO_BASE_NAN and IO_BASE_ZERO)
0040    used internally to mark special situations. They are called
0041    pseudo-bases, because you cannot use them as parameter to
0042    floatnum functions */
0043 static char
0044 _isspecial(
0045   signed char base)
0046 {
0047   switch (base)
0048   {
0049   case IO_BASE_NAN:
0050   case IO_BASE_ZERO:
0051   case IO_BASE_DEFAULT:
0052     return 1;
0053   }
0054   return 0;
0055 }
0056 
0057 /*--------------   ASCII based functions   -------------*/
0058 
0059 static char emptystr = '\0';
0060 
0061 /* sort of alias for (strlen == 0). I don't have access to a
0062    POSIX manual, so I don't know how strlen reacts on NULL.*/
0063 static char
0064 _isempty(
0065   const char* p)
0066 {
0067   return p == NULL || *p == '\0';
0068 }
0069 
0070 /* copies the null-terminated ASCIIZ string src into the
0071    buffer dest. If the buffer is too small to hold src,
0072    0 is returned */
0073 static char
0074 _setstr(
0075   p_buffer dest,
0076   const char* src)
0077 {
0078   if (dest->sz > 0)
0079   {
0080     if (src == NULL)
0081       *(dest->buf) = '\0';
0082     else if (dest->sz < (int)strlen(src) + 1)
0083       return 0;
0084     else
0085       strcpy(dest->buf, src);
0086   }
0087   return 1;
0088 }
0089 
0090 /* looks, whether dest begins with the substring in
0091    pattern */
0092 static int
0093 _match(
0094   const char* dest,
0095   const char* pattern)
0096 {
0097   int lg;
0098 
0099   if (_isempty(dest) || _isempty(pattern))
0100     return 0;
0101   lg = strlen(pattern);
0102   return strncmp(dest, pattern, lg) == 0? lg : 0;
0103 }
0104 
0105 /* converts an ASCII encoded hexagesimal digit a
0106    corresponding value < 16 */
0107 char
0108 _ascii2digit(
0109   char c)
0110 {
0111   if (c >= '0' && c <= '9')
0112     return c - '0';
0113   if (c >= 'A' && c <= 'F')
0114     return (c - 'A') + 10;
0115   if (c >= 'a' && c <= 'f')
0116     return (c - 'a') + 10;
0117   return NO_DIGIT;
0118 }
0119 
0120 static char hexdigits[] = "0123456789ABCDEF";
0121 
0122 /* converts a value 0 <=x < 16 into a hexdigit */
0123 char
0124 _digit2ascii(
0125   int value)
0126 {
0127   return hexdigits[value];
0128 }
0129 
0130 /* advances the pointer buf until it points to the
0131    first character not being a valid digit of base
0132    base. The return pointer is the initial value
0133    of buf, or NULL if no digit was found */
0134 static const char*
0135 _scandigits(
0136   const char** buf,
0137   char base)
0138 {
0139   const char* p;
0140   const char* result;
0141 
0142   result = *buf;
0143   p = result - 1;
0144   while (_ascii2digit(*++p) < base);
0145   *buf = p;
0146   return p == result? NULL : result;
0147 }
0148 
0149 /*--------------------  t_seq_desc   ---------------*/
0150 
0151 /* returns the offset6 of the last significant digit */
0152 static int
0153 _ofslastnz(
0154   p_seq_desc n)
0155 {
0156   return n->digits - n->trailing0 - 1;
0157 }
0158 
0159 /* number of significant digits in a number sequence */
0160 int
0161 _significantdigits(
0162   p_seq_desc n)
0163 {
0164   return n->digits - n->leadingSignDigits - n->trailing0;
0165 }
0166 
0167 /* number of digits without leading zeros in a sequence.
0168   If the sequence describes an integer, these are the digits
0169   of this integer */
0170 static int
0171 _intdigits(
0172   p_seq_desc n)
0173 {
0174   return n->digits - n->leadingSignDigits;
0175 }
0176 
0177 /* has a sequence significant digits? */
0178 static char
0179 _iszero(
0180   p_seq_desc n)
0181 {
0182   return _significantdigits(n) == 0;
0183 }
0184 
0185 /* returns a digit from an ASCIIZ string containing
0186    digits only */
0187 static char
0188 _getseqdigit(
0189   int ofs,
0190   p_seq_desc n)
0191 {
0192   if (ofs < 0 || ofs > _ofslastnz(n))
0193     return 0;
0194   return _ascii2digit(*((char*)(n->param) + ofs));
0195 }
0196 
0197 /* returns a digit from an ASCIIZ string containing
0198    digits only, but complements them  */
0199 static char
0200 _getcmpldigit(
0201   int ofs,
0202   p_seq_desc n)
0203 {
0204   int lastnz;
0205   char c;
0206 
0207   lastnz = _ofslastnz(n);
0208   if (ofs > lastnz)
0209     return 0;
0210   c = n->base - _getseqdigit(ofs, n) - (ofs == lastnz? 0 : 1);
0211   return c == n->base? 1 : c;
0212 }
0213 
0214 /* initializes a structure used for copying digit
0215    sequences */
0216 static void
0217 _clearint(
0218   p_ext_seq_desc n)
0219 {
0220   n->seq.leadingSignDigits = 0;
0221   n->seq.digits = 0;
0222   n->seq.trailing0 = 0;
0223   n->seq.base = IO_BASE_ZERO;
0224   n->seq.param = NULL;
0225   n->getdigit = _getseqdigit;
0226 }
0227 
0228 /*----------------------   t_[io]token   ------------------*/
0229 
0230 /* initializes a structure used for describing a
0231    floating point number */
0232 void
0233 _clearnumber(
0234   p_number_desc n)
0235 {
0236   n->prefix.sign = IO_SIGN_NONE;
0237   n->prefix.base = IO_BASE_NAN;
0238   _clearint(&n->intpart);
0239   _clearint(&n->fracpart);
0240   n->exp = 0;
0241 }
0242 
0243 /* creates a sequence descriptor from an ASCII digit
0244    sequence, not necessarily terminated by \0.
0245    After maxdigits digits, all following digits are
0246    assumed to be zero, regardless of their true
0247    value. */
0248 static void
0249 _str2seq(
0250   p_ext_seq_desc n,
0251   const char* digits,
0252   int maxdigits,
0253   signed char base,
0254   char complement)
0255 {
0256   /* pre: n has to be initialized to describe a zero */
0257   const char* p;
0258   const char* pz;
0259   char leadingdigit;
0260   unsigned char c;
0261 
0262   leadingdigit = 0;
0263   if (complement)
0264   {
0265     leadingdigit = base - 1;
0266     n->getdigit = _getcmpldigit;
0267   }
0268   p = digits;
0269   /* skip sign digits (usually 0, in complement mode
0270      F, 7 or 1 */
0271   for (; _ascii2digit(*p) == leadingdigit; ++p);
0272   n->seq.leadingSignDigits = p - digits;
0273   /* pz is pointer to first trailing zero */
0274   pz = p;
0275   for (; (c = _ascii2digit(*(p++))) < base;)
0276   {
0277     if (--maxdigits >= 0 && c != 0)
0278       pz = p;
0279   }
0280   n->seq.trailing0 = p - pz - 1;
0281   n->seq.digits = p - digits - 1;
0282   n->seq.param = (void*)digits;
0283   if (complement || _significantdigits(&n->seq) != 0)
0284     n->seq.base = base;
0285 }
0286 
0287 /* copy count digits (leading zeros included) from the
0288    digit sequence described by n to an ASCII buffer */
0289 static Error
0290 _seq2str(
0291   p_buffer dest,
0292   int count,
0293   p_ext_seq_desc n)
0294 {
0295   int ofs;
0296   char* buf;
0297 
0298   ofs = 0;
0299   if (count >= dest->sz)
0300     return IOBufferOverflow;
0301   buf = dest->buf;
0302   for (; ofs < count; ++ofs)
0303     *(buf++) = _digit2ascii(n->getdigit(ofs, &n->seq));
0304   *(buf) = '\0';
0305   return Success;
0306 }
0307 
0308 /* copy all digits (leading zeros included) from the
0309    digit sequence described by n to an ASCII buffer, but
0310    complement the sequence before writing to the buffer */
0311 static Error
0312 _cmplseq2str(
0313   p_buffer dest,
0314   p_ext_seq_desc n)
0315 {
0316   int bound;
0317   int ofs;
0318   int lastnz;
0319   char* buf;
0320   char c;
0321 
0322   buf = dest->buf;
0323   bound = _intdigits(&n->seq);
0324   if (bound + 1 > dest->sz)
0325     return IOBufferOverflow;
0326   lastnz = _ofslastnz(&n->seq);
0327   for (ofs = -1; ++ofs < lastnz;)
0328   {
0329     c = n->seq.base - n->getdigit(ofs, &n->seq) - 1;
0330     *(buf++) = _digit2ascii(c);
0331   }
0332   c = n->getdigit(ofs, &n->seq);
0333   if (c != 1 || _significantdigits(&n->seq) != 1)
0334     *(buf++) = _digit2ascii(n->seq.base - c);
0335   else
0336   {
0337     --bound;
0338     --ofs;
0339   }
0340   for (; ++ofs < bound;)
0341     *(buf++) = _digit2ascii(0);
0342   *(buf) = '\0';
0343   return Success;
0344 }
0345 
0346 /* create a descriptor from a sequence of digits,
0347    assuming the sequence is an integer */
0348 static Error
0349 str2int(
0350   p_ext_seq_desc n,
0351   const char* value,
0352   p_prefix prefix,
0353   int maxdigits)
0354 {
0355   char complement;
0356 
0357   if (prefix->base == IO_BASE_NAN)
0358     return IONoBase;
0359   complement = prefix->sign == IO_SIGN_COMPLEMENT;
0360   if (complement)
0361   {
0362     n->getdigit = _getcmpldigit;
0363     /* necessary, because when value is empty, base = IO_BASE_ZERO */
0364     n->seq.base = prefix->base;
0365   }
0366   if (value)
0367     _str2seq(n, value, maxdigits, prefix->base, complement);
0368   return Success;
0369 }
0370 
0371 /* if base describes a special value (0 or NaN), the normal
0372    conversion routines fail. This routine creates special output
0373    values for these bases, and return
0374    nmbBufferOverflow: if the buffer is too small
0375    nmbSpecial: if it created output
0376    nmbNormal: if base stands for a usual number that the
0377                normal routines should deal with */
0378 static NmbType
0379 _special2str(
0380   p_otokens tokens,
0381   signed char base)
0382 {
0383   const char* p;
0384   switch (base)
0385   {
0386   case IO_BASE_ZERO:
0387     p = "0";
0388     break;
0389   case IO_BASE_NAN:
0390     p = "NaN";
0391     break;
0392   default:
0393     return NmbNormal;
0394   }
0395   return _setstr(&tokens->intpart, p)? NmbSpecial : NmbBufferOverflow;
0396 }
0397 
0398 /* create an ASCIIZ sequence of the integer part,
0399    set sign and base. */
0400 static Error
0401 int2str(
0402   p_otokens tokens,
0403   p_number_desc n,
0404   char complement)
0405 {
0406   tokens->sign = n->prefix.sign;
0407   tokens->base = n->prefix.base;
0408   switch (_special2str(tokens, n->prefix.base))
0409   {
0410     case NmbSpecial: return Success;
0411     case NmbBufferOverflow: return IOBufferOverflow;
0412     default: break; /* NmbNormal */
0413   }
0414   /* no special encodings */
0415   if (complement)
0416     return _cmplseq2str(&tokens->intpart, &n->intpart);
0417   return _seq2str(&tokens->intpart, _intdigits(&n->intpart.seq),
0418                         &n->intpart);
0419 }
0420 
0421 /* do some sanity checks, create descriptors of integer and fraction
0422    part in tokens, set sign and base */
0423 static Error
0424 str2fixp(
0425   p_number_desc n,
0426   p_itokens tokens)
0427 {
0428   Error result;
0429   int maxdigits;
0430 
0431   maxdigits = tokens->maxdigits;
0432   n->prefix.base = tokens->base;
0433   n->prefix.sign = tokens->sign;
0434   if (tokens->sign == IO_SIGN_COMPLEMENT
0435       && (!_isempty(tokens->fracpart) || tokens->exp))
0436     return IOInvalidComplement;
0437   result = str2int(&n->intpart, tokens->intpart, &n->prefix, maxdigits);
0438   if (_isspecial(n->prefix.base))
0439     return result;
0440   if (!_isempty(tokens->fracpart))
0441     _str2seq(&n->fracpart, tokens->fracpart,
0442              maxdigits - _intdigits(&n->intpart.seq),
0443              n->prefix.base, 0);
0444   if (n->prefix.sign != IO_SIGN_COMPLEMENT
0445       && n->intpart.seq.digits + n->fracpart.seq.digits == 0)
0446     return IONoSignificand;
0447   if (n->prefix.sign != IO_SIGN_COMPLEMENT
0448       && _iszero(&n->intpart.seq) && _iszero(&n->fracpart.seq))
0449     n->prefix.base = IO_BASE_ZERO;
0450   return Success;
0451 }
0452 
0453 /* convert integer and fraction part into ASCIIZ sequences */
0454 static Error
0455 fixp2str(
0456   p_otokens tokens,
0457   p_number_desc n,
0458   int scale)
0459 {
0460   Error result;
0461 
0462   result = int2str(tokens, n, n->prefix.sign == IO_SIGN_COMPLEMENT);
0463   if (result != Success || _isspecial(n->prefix.base))
0464     return result;
0465   return _seq2str(&tokens->fracpart, scale, &n->fracpart);
0466 }
0467 
0468 /* create a descriptor from the digit sequence of the exponent */
0469 static Error
0470 _exp2desc(
0471   p_number_desc n,
0472   p_itokens tokens)
0473 {
0474   if (tokens->expsign != IO_SIGN_NONE || tokens->exp)
0475   {
0476     unsigned upperLimit;
0477     signed char sign;
0478     switch (tokens->base)
0479     {
0480       case 2 : upperLimit = BITS_IN_BINEXP - 1; break;
0481       case 8 : upperLimit = BITS_IN_OCTEXP - 1; break;
0482       case 16: upperLimit = BITS_IN_HEXEXP - 1; break;
0483       default: upperLimit = BITS_IN_EXP - 1; break;
0484     }
0485     upperLimit = (1 << (upperLimit)) - 1;
0486     sign = tokens->expsign;
0487     switch (sign)
0488     {
0489       case IO_SIGN_COMPLEMENT:
0490         return IOBadExp;
0491       case IO_SIGN_NONE:
0492         sign = IO_SIGN_PLUS; break;
0493       case IO_SIGN_MINUS:
0494         ++upperLimit; break;
0495       default:;
0496     }
0497     if (tokens->exp > upperLimit)
0498       return IOExpOverflow;
0499     if (sign < 0)
0500       n->exp = -(int)(tokens->exp);
0501     else
0502       n->exp = tokens->exp;
0503   }
0504   return Success;
0505 }
0506 
0507 /* create a descriptor from the floating point number given in
0508    tokens */
0509 Error
0510 str2desc(
0511   p_number_desc n,
0512   p_itokens tokens)
0513 {
0514   Error result;
0515 
0516   _clearnumber(n);
0517   result = str2fixp(n, tokens);
0518   if (result == Success)
0519     result = _exp2desc(n, tokens);
0520   if (result != Success)
0521     n->prefix.base = IO_BASE_NAN;
0522   return result;
0523 }
0524 
0525 Error
0526 desc2str(
0527   p_otokens tokens,
0528   p_number_desc n,
0529   int scale)
0530 {
0531   Error result;
0532 
0533   result = fixp2str(tokens, n, scale);
0534   if (result != Success || _isspecial(n->prefix.base))
0535     return result;
0536   tokens->exp = n->exp;
0537   return Success;
0538 }
0539 
0540 Error
0541 exp2str(
0542   p_buffer dest,
0543   int exp,
0544   char base)
0545 {
0546   char tmp[BITS_IN_EXP + 3];
0547   int idx = 0;
0548   int di = 0;
0549   if (exp < 0)
0550     exp = -exp;
0551   while (exp != 0)
0552   {
0553     tmp[idx++] = hexdigits[exp % base];
0554     exp /= base;
0555   }
0556   if (idx == 0)
0557     tmp[idx++] = hexdigits[0];
0558   if (dest->sz <= idx)
0559     return IOBufferOverflow;
0560   for (; --idx >= 0;)
0561     dest->buf[di++] = tmp[idx];
0562   dest->buf[di] = 0;
0563   return Success;
0564 }
0565 
0566 
0567 /* *****************   additional stuff, just to get started   *************/
0568 
0569 static t_ioparams stdioparams[4] =
0570 {
0571   {10, 10, '.', "0d", "eE(", "  )", "", DECPRECISION},
0572   {16, 10, '.', "0x", "hH(", "  )", "sF", HEXPRECISION},
0573   {2, 10, '.', "0b", "bB(", "  )", "s1", BINPRECISION},
0574   {8, 10, '.', "0o", "oOC(", "   )", "s7", OCTPRECISION}
0575 };
0576 
0577 enum {idzero, idx10, idx16, idx2, idx8, idxcount};
0578 
0579 static t_ioparams ioparams[idxcount] = {
0580   {IO_BASE_ZERO, IO_BASE_ZERO, '\0', "", "", "", "", 0x7FFFFFFF},
0581   {IO_BASE_DEFAULT, IO_BASE_DEFAULT, '\0', "", "", "", "", 0},
0582   {IO_BASE_DEFAULT, IO_BASE_DEFAULT, '\0', "", "", "", "", 0},
0583   {IO_BASE_DEFAULT, IO_BASE_DEFAULT, '\0', "", "", "", "", 0},
0584   {IO_BASE_DEFAULT, IO_BASE_DEFAULT, '\0', "", "", "", "", 0}
0585 };
0586 
0587 static p_ioparams _defaultbase = NULL;
0588 
0589 static char
0590 _isvalidioparams(
0591   p_ioparams param)
0592 {
0593   return param != NULL && param->base != IO_BASE_DEFAULT;
0594 }
0595 
0596 static void
0597 _invalidateioparams(
0598   p_ioparams param)
0599 {
0600   if (param)
0601     param->base = IO_BASE_DEFAULT;
0602 }
0603 
0604 p_ioparams
0605 _base2ioparams(
0606   signed char base)
0607 {
0608   int idx;
0609 
0610   switch (base)
0611   {
0612   case 10:
0613     idx = idx10;
0614     break;
0615   case 16:
0616     idx = idx16;
0617     break;
0618   case 2:
0619     idx = idx2;
0620     break;
0621   case 8:
0622     idx = idx8;
0623     break;
0624   case IO_BASE_ZERO:
0625     idx = idzero;
0626     break;
0627   default:
0628     return NULL;
0629   }
0630   return &ioparams[idx];
0631 }
0632 
0633 char
0634 setioparams(
0635   p_ioparams params)
0636 {
0637   p_ioparams dest;
0638 
0639   if (!_isvalidioparams(params))
0640     return 0;
0641   dest = _base2ioparams(params->base);
0642   if (dest == NULL)
0643     return 0;
0644   *dest = *params;
0645   return 1;
0646 }
0647 
0648 char
0649 delioparams(
0650   signed char base)
0651 {
0652   p_ioparams dest;
0653 
0654   dest = _base2ioparams(base);
0655   _invalidateioparams(dest);
0656   return dest != NULL;
0657 }
0658 
0659 p_ioparams
0660 getioparams(
0661   signed char base)
0662 {
0663   p_ioparams result;
0664 
0665   if (base == IO_BASE_DEFAULT)
0666     return _defaultbase;
0667   result = _base2ioparams(base);
0668   return _isvalidioparams(result)? result : NULL;
0669 }
0670 
0671 static signed char
0672 _getdefaultbase()
0673 {
0674   p_ioparams param;
0675 
0676   param = getioparams(IO_BASE_DEFAULT);
0677   return param == NULL? IO_BASE_DEFAULT : param->base;
0678 }
0679 
0680 signed char
0681 setdefaultbase(
0682   signed char base)
0683 {
0684   char result;
0685 
0686   result = _getdefaultbase();
0687   _defaultbase = _base2ioparams(base);
0688   return result;
0689 }
0690 
0691 void
0692 float_stdconvert()
0693 {
0694   int i;
0695 
0696   for (i = 0; i < 4; ++i)
0697     setioparams(&stdioparams[i]);
0698   setdefaultbase(10);
0699 }
0700 
0701 const char* basePrefix(char base)
0702 {
0703   return getioparams(base)->basetag;
0704 }
0705 
0706 static signed char
0707 _parsesign(
0708   const char** buf)
0709 {
0710   signed char result;
0711 
0712   result = IO_SIGN_NONE;
0713   if (!_isempty(*buf))
0714     switch (**buf)
0715     {
0716     case '-':
0717       result = IO_SIGN_MINUS;
0718       break;
0719     case '+':
0720       result = IO_SIGN_PLUS;
0721     }
0722   if (result != IO_SIGN_NONE)
0723     (*buf)++;
0724   return result;
0725 }
0726 
0727 static signed char
0728 _parsebase(
0729   const char** buf,
0730   char defaultbase)
0731 {
0732   signed char base;
0733   int lg, i;
0734 
0735   lg = 0;
0736   base = IO_BASE_DEFAULT;
0737   if (!_isempty(*buf))
0738     for (i = 0; i < idxcount; ++i)
0739     {
0740       lg = _match(*buf, ioparams[i].basetag);
0741       if (lg > 0)
0742       {
0743         base = ioparams[i].base;
0744         break;
0745       }
0746     }
0747   *buf += lg;
0748   return base == IO_BASE_DEFAULT? defaultbase : base;
0749 }
0750 
0751 static char
0752 _parsecmpl(
0753   const char** buf,
0754   char base)
0755 {
0756   int lg;
0757   p_ioparams param;
0758 
0759   param = getioparams(base);
0760   lg = 0;
0761   if (_isvalidioparams(param))
0762     lg = _match(*buf, param->cmpltag);
0763   *buf += lg;
0764   return lg > 0;
0765 }
0766 
0767 Error
0768 parse(
0769   p_itokens tokens,
0770   const char** buffer)
0771 {
0772   p_ioparams params;
0773   const char* p;
0774   char* expchar;
0775   signed char base;
0776   int idx;
0777   char dot;
0778   char* expbegin;
0779   char* expend;
0780 
0781   tokens->fracpart = NULL;
0782   tokens->exp = 0;
0783   tokens->expsign = IO_SIGN_NONE;
0784   tokens->maxdigits = 0;
0785   dot = '.';
0786   expbegin = "(";
0787   expend = ")";
0788   p = *buffer;
0789   tokens->sign = _parsesign(&p);
0790   base = _parsebase(&p, _getdefaultbase());
0791   params = getioparams(base);
0792   if (params != NULL)
0793   {
0794     dot = params->dot;
0795     if (params->expbegin != NULL)
0796       expbegin = params->expbegin;
0797     if (params->expend != NULL)
0798       expend = params->expend;
0799     tokens->maxdigits = params->maxdigits;
0800   }
0801   else
0802     base = 10;
0803   tokens->base = base;
0804   if (tokens->maxdigits <= 0)
0805     tokens->maxdigits = DECPRECISION;
0806   if (_parsecmpl(&p, tokens->base))
0807   {
0808     if (tokens->sign != IO_SIGN_NONE)
0809       return IOInvalidComplement;
0810     tokens->sign = IO_SIGN_COMPLEMENT;
0811   }
0812   tokens->intpart = _scandigits(&p, base);
0813   if (*p == dot)
0814   {
0815     ++p;
0816     tokens->fracpart = _scandigits(&p, base);
0817   }
0818   if (!tokens->intpart && !tokens->fracpart
0819       && tokens->sign != IO_SIGN_COMPLEMENT)
0820     return IONoSignificand;
0821   expchar = strchr(expbegin, *p);
0822   if (!_isempty(expchar))
0823   {
0824     const char* expptr;
0825     int i;
0826     int e = 0;
0827     int expbase;
0828     ++p;
0829     idx = expchar - expbegin;
0830     tokens->expsign = _parsesign(&p);
0831     expbase = _parsebase(&p, base);
0832     expptr = _scandigits(&p, expbase);
0833     if (!expptr || (*(expend + idx) != ' ' && *(expend + idx) != *p))
0834       return IOBadExp;
0835     for (i = 0; i < p-expptr; ++i)
0836     {
0837       if (!_checkmul(&e, expbase)
0838            || !_checkadd(&e, _ascii2digit(*(expptr+i))))
0839         return IOExpOverflow;
0840     }
0841     tokens->exp = e;
0842   }
0843   *buffer = p;
0844   return Success;
0845 }
0846 
0847 static char
0848 _decodesign(
0849   signed char s)
0850 {
0851   switch (s)
0852   {
0853   case IO_SIGN_PLUS:
0854     return '+';
0855   case IO_SIGN_MINUS:
0856     return '-';
0857   }
0858   return '\0';
0859 }
0860 
0861 static char*
0862 _decodebase(
0863   signed char base)
0864 {
0865   p_ioparams param;
0866 
0867   param = getioparams(base);
0868   if (!_isvalidioparams(param) || _isempty(param->basetag))
0869     return &emptystr;
0870   return param->basetag;
0871 }
0872 
0873 static char*
0874 _decodecomplement(
0875   signed char sign,
0876   signed char base)
0877 {
0878   p_ioparams param;
0879 
0880   param = getioparams(base);
0881   if (sign != IO_SIGN_COMPLEMENT || !_isvalidioparams(param)
0882       || _isempty(param->cmpltag))
0883     return &emptystr;
0884   return param->cmpltag;
0885 }
0886 
0887 static void
0888 _cattoken(
0889   char* buf,
0890   char* token,
0891   char enable)
0892 {
0893   if (enable && !_isempty(token))
0894     strcat(buf, token);
0895 }
0896 
0897 int
0898 cattokens(
0899   char* buf,
0900   int bufsz,
0901   p_otokens tokens,
0902   signed char expbase,
0903   unsigned flags)
0904 {
0905   int sz;
0906   int fraclg;
0907   p_ioparams ioparams;
0908   char* expbegin;
0909   char* expend;
0910   char* cmpltag;
0911   char* basetag;
0912   char* expbasetag;
0913   signed char base;
0914   char dot;
0915   char cbuf[2];
0916   char printsign;
0917   char printbasetag;
0918   char printcmpl;
0919   char printleading0;
0920   char printdot;
0921   char printexp;
0922   char printexpsign = 0;
0923   char printexpbase = 0;
0924   char printexpbegin;
0925   char printexpend;
0926   char exp[BITS_IN_BINEXP+2];
0927   t_buffer expBuf;
0928 
0929   expBuf.sz = sizeof(exp);
0930   expBuf.buf = exp;
0931   cbuf[1] = '\0';
0932   fraclg = 0;
0933   if (!_isempty(tokens->fracpart.buf))
0934   {
0935     fraclg = strlen(tokens->fracpart.buf) - 1;
0936     if ((flags & IO_FLAG_SUPPRESS_TRL_ZERO) != 0)
0937       while (fraclg >= 0 && tokens->fracpart.buf[fraclg] == '0')
0938         --fraclg;
0939     ++fraclg;
0940   }
0941   ioparams = getioparams(IO_BASE_DEFAULT);
0942   base = tokens->base;
0943   printbasetag = !_isspecial(base)
0944                  && (flags & IO_FLAG_SUPPRESS_BASETAG) == 0
0945                  && (ioparams == NULL || ioparams->base != base);
0946   ioparams = getioparams(base);
0947   basetag = _decodebase(base);
0948   cmpltag = _decodecomplement(tokens->sign, base);
0949   expbasetag = NULL;
0950   if (base == IO_BASE_DEFAULT)
0951     flags |= IO_FLAG_SUPPRESS_DOT | IO_FLAG_SUPPRESS_LDG_ZERO;
0952   if ((flags & IO_FLAG_SHOW_BASE) != 0)
0953     printbasetag = 1;
0954   printcmpl = tokens->sign == IO_SIGN_COMPLEMENT
0955                   && (flags & IO_FLAG_SUPPRESS_CMPL) == 0;
0956   printsign = !printcmpl
0957               && tokens->sign != IO_SIGN_NONE
0958               && (tokens->sign != IO_SIGN_PLUS
0959                   || (flags & IO_FLAG_SUPPRESS_PLUS) == 0);
0960   printleading0 = _isempty(tokens->intpart.buf)
0961                   && (flags & IO_FLAG_SUPPRESS_LDG_ZERO) == 0;
0962   printdot = fraclg > 0 || (flags & IO_FLAG_SUPPRESS_DOT) == 0;
0963   printexp = base != IO_BASE_NAN && base != IO_BASE_ZERO
0964              && ((flags & IO_FLAG_SUPPRESS_EXPZERO) == 0
0965                   || tokens->exp != 0);
0966   if (printexp)
0967   {
0968     if (expbase < 2)
0969       expbase = ioparams->expbase;
0970     expbasetag = _decodebase(expbase);
0971     printexpsign = tokens->exp < 0
0972                    || (flags & IO_FLAG_SUPPRESS_EXPPLUS) == 0;
0973     printexpbase = expbasetag != NULL
0974                    && (flags & IO_FLAG_SUPPRESS_EXPBASE) == 0
0975                    && (_isempty(basetag)
0976                        || strcmp(basetag, expbasetag) != 0);
0977     if ((flags & IO_FLAG_SHOW_EXPBASE) != 0)
0978       printexpbase = 1;
0979   }
0980   dot = '.';
0981   expbegin = "(";
0982   expend = ")";
0983   if (ioparams != NULL)
0984   {
0985     dot = ioparams->dot;
0986     expbegin = ioparams->expbegin;
0987     expend = ioparams->expend;
0988   }
0989   printexpbegin = *expbegin != '\0';
0990   printexpend = *expend != '\0' && *expend != ' ';
0991   sz = 1;
0992   if (printsign)
0993     sz += 1;
0994   if (printbasetag)
0995     sz += strlen(basetag);
0996   if (printcmpl)
0997     sz += strlen(cmpltag);
0998   if (printleading0)
0999     ++sz;
1000   if (!_isempty(tokens->intpart.buf))
1001     sz += strlen(tokens->intpart.buf);
1002   if (printdot)
1003     sz += 1;
1004   sz += fraclg;
1005   if (printexp)
1006   {
1007     exp2str(&expBuf, tokens->exp, expbase);
1008     if (printexpbegin)
1009       ++sz;
1010     if (printexpsign)
1011       sz += 1;
1012     if (printexpbase)
1013       sz += strlen(expbasetag);
1014     sz += strlen(expBuf.buf);
1015     if (printexpend)
1016       ++sz;
1017   }
1018   if (sz <= bufsz)
1019   {
1020     *buf = '\0';
1021     cbuf[0] = _decodesign(tokens->sign);
1022     _cattoken(buf, cbuf, printsign);
1023     _cattoken(buf, basetag, printbasetag);
1024     _cattoken(buf, cmpltag, printcmpl);
1025     _cattoken(buf, "0", printleading0);
1026     _cattoken(buf, tokens->intpart.buf, 1);
1027     cbuf[0] = dot;
1028     _cattoken(buf, cbuf, printdot);
1029     if (fraclg > 0)
1030       strncat(buf, tokens->fracpart.buf, fraclg);
1031     if (printexp)
1032     {
1033       cbuf[0] = *expbegin;
1034       _cattoken(buf, cbuf, printexpbegin);
1035       cbuf[0] = _decodesign(tokens->exp < 0? -1:1);
1036       _cattoken(buf, cbuf, printexpsign);
1037       _cattoken(buf, expbasetag, printexpbase);
1038       strcat(buf, expBuf.buf);
1039       cbuf[0] = *expend;
1040       _cattoken(buf, cbuf, printexpend);
1041     }
1042   }
1043   return sz;
1044 }