Subversion Repositories Kolibri OS

Rev

Rev 6425 | Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.         function for format output to the string
  3.  
  4. Siemargl update formats as http://www.cplusplus.com/reference/cstdio/printf/, no wchar though
  5. http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html  is used too
  6. %g explain https://support.microsoft.com/en-us/kb/43392
  7.  
  8. todo:
  9. -fix precision in %g
  10. -%u printed as signed, %x, %o also is promoted to long long
  11. // FAIL 0x0FFFF7A7E as %x - signed long promotes sign, need %llx or %Lx and type conversion
  12. -%a
  13. -%n nothing printed
  14. -%17.18f digits maximum format
  15. -use %C as w_char L'x' (non standard extension)
  16. -radix point always '.', no LOCALEs
  17. */
  18.  
  19.  
  20. #include <string.h>
  21. #include <stdio.h>
  22. #include <ctype.h>
  23. #include <math.h>
  24. #include <stdarg.h>
  25.  
  26. enum flags_t
  27. {
  28.         flag_unsigned   = 0x02,
  29.         flag_register   = 0x04,
  30.         flag_plus       = 0x08,
  31.         flag_left_just  = 0x10,
  32.         flag_lead_zeros = 0x20,
  33.         flag_space_plus = 0x40,
  34.         flag_hash_sign  = 0x80,
  35.         flag_point      = 0x100
  36. };
  37. int formatted_double_to_string(long double number, int format1, int format2, char *s, int flags);
  38. int formatted_double_to_string_scientific(long double number, int format1, int format2, char *s, int flags);
  39. int formatted_long_to_string(long long number, int format1, int prec, char *s, int flags);
  40. int formatted_hex_to_string(unsigned long long number, int fmt1, int prec, char *s, int flags);
  41. int formatted_octa_to_string(unsigned long long number, int fmt1, int prec, char *s, int flags);
  42.  
  43.  
  44. int formatted_double_special(long double number, char *s)
  45. // return 0 if no special values: NAN, INF. -0.0 ignored
  46. // http://steve.hollasch.net/cgindex/coding/ieeefloat.html
  47. {
  48.     struct IEEExp {
  49.         unsigned manl:32;
  50.         unsigned manh:32;
  51.         unsigned exp:15;
  52.         unsigned sign:1;
  53.     } *ip = (struct IEEExp *)&number;
  54.  
  55.     if (ip->exp != 0x7fff) return 0;
  56.  
  57.     if (ip->manh == 0x80000000 && ip->manl == 0) // Inf
  58.     {
  59.         if(ip->sign)
  60.             strcpy(s, "-INF");
  61.         else
  62.             strcpy(s, "+INF");
  63.     } else
  64.     if (ip->manh & ~0x7fffffff)
  65.         strcpy(s, "QNaN");
  66.     else
  67.         strcpy(s, "SNaN");
  68.  
  69.     return 4;
  70. }
  71.  
  72. int copy_and_align(char *dest, int width, char *src, int src_len, char sign, int flags)
  73. // alingn number in buffer, put sign and fills additional places
  74. // flags used only flag_left_just and flag_lead_zeros
  75. // sign can be zero, 0, x, X, space, +, -
  76. {
  77.     int     rc = 0, sign_len;
  78.     char    fill;
  79.  
  80.     fill = (flags & flag_lead_zeros)&&((flags & flag_left_just)==0) ? '0' : ' ';
  81.     if(sign == 'x' || sign == 'X')
  82.     {
  83.         sign_len = 2;
  84.     } else
  85.     if (sign)
  86.         sign_len = 1;
  87.     else
  88.         sign_len = 0;
  89.  
  90.     if ((flags & flag_left_just) || (src_len + sign_len >= width))   // left justify or no room
  91.     {
  92.         if (sign)
  93.         {
  94.             if(sign == 'x' || sign == 'X')
  95.             {
  96.                 dest[0] = '0';
  97.                 dest[1] = sign;
  98.                 memcpy(dest + 2, src, src_len);
  99.                 rc = src_len + 2;
  100.             } else
  101.             { // single sign
  102.                 dest[0] = sign;
  103.                 memcpy(dest + 1, src, src_len);
  104.                 rc = src_len + 1;
  105.             }
  106.         } else
  107.         {
  108.             memcpy(dest, src, src_len);
  109.             rc = src_len;
  110.         }
  111.         if (rc < width)
  112.         {
  113.             memset(dest + rc, fill, width - rc);
  114.             rc = width;
  115.         }
  116.     } else // right justify and fill
  117.     {
  118.         rc = width;
  119.         memcpy(dest + width - src_len, src, src_len);
  120.         memset(dest, fill, width - src_len);
  121.         if (flags & flag_lead_zeros)
  122.         {
  123.             if(sign == 'x' || sign == 'X')
  124.             {
  125.                 dest[0] = '0';
  126.                 dest[1] = sign;
  127.             } else
  128.             if (sign) dest[0] = sign;
  129.         } else
  130.         {
  131.             if(sign == 'x' || sign == 'X')
  132.             {
  133.                 dest[width - src_len - 2] = '0';
  134.                 dest[width - src_len - 1] = sign;
  135.             } else
  136.             if (sign) dest[width - src_len - 1] = sign;
  137.         }
  138.     }
  139.     return rc;
  140. }
  141.  
  142. int formatted_double_to_string_scientific(long double number, int format1, int format2, char *s, int flags)
  143. {
  144.     long double     norm_digit;
  145.     long            mul = 0;
  146.     char            sign = 0;
  147.     char            buf[50];
  148.     int     len;
  149.  
  150.     if((flags & flag_point) == 0) format2 = 6;  // default prec if no point spec
  151.  
  152.     len = formatted_double_special(number, buf);
  153.     if (len == 0)
  154.     {
  155.         if (number < 0) { sign = '-'; norm_digit = -number; }
  156.         else
  157.         {
  158.             norm_digit = number;
  159.             if (flags & flag_plus) sign = '+';  else
  160.             if (flags & flag_space_plus) sign = ' ';
  161.         }
  162.         // normalize
  163.         while (norm_digit < 1.0 && norm_digit > 0) { norm_digit *= 10; mul--; }
  164.         while (norm_digit >= 10.0) { norm_digit /= 10; mul++; }
  165.  
  166.         len = formatted_double_to_string(norm_digit, 0, format2, buf, flags & ~(flag_plus | flag_space_plus));
  167.  
  168.         if (flags & flag_register)
  169.             buf[len++] = 'E';
  170.         else
  171.             buf[len++] = 'e';
  172.  
  173.         len += formatted_long_to_string(mul, 0, 3, buf + len, flag_plus | flag_lead_zeros);
  174.     }
  175.     else
  176.         flags &= ~flag_lead_zeros; // no need for INF, NAN
  177.  
  178.     len = copy_and_align(s, format1, buf, len, sign, flags);
  179.  
  180.     return len;
  181. }
  182.  
  183. int formatted_double_to_string(long double number, int format1, int format2, char *s, int flags)
  184. {
  185.     long double nafter, beforpointdigit;
  186.     long long   intdigit, mul;
  187.     int     div;
  188.     int     i;
  189.     char    sign = 0;
  190.     int     fmt1;
  191.     int     fmt2;
  192.     char    buf[100], *pbuf = buf;
  193.     char    buf_low[50], *pbuf_lo = buf_low;
  194.  
  195.     if((flags & flag_point) == 0) format2 = 6;  // default prec if no point spec
  196.  
  197.     i = formatted_double_special(number, buf);
  198.     if (i == 0)
  199.     {
  200.         if (number < 0) {sign = '-'; number = -number; }
  201.         else
  202.         {
  203.             if (flags & flag_plus) sign = '+';  else
  204.             if (flags & flag_space_plus) sign = ' ';
  205.         }
  206.  
  207.         fmt1 = 1;
  208.         fmt2 = format2;
  209.         if (fmt2 > 18) fmt2 = 18; //maximum size of long long type
  210.  
  211.         beforpointdigit = floor(number + 0.00000000000001);
  212.         nafter = number - beforpointdigit;
  213.  
  214.         //print part of number before point
  215.         mul = 1;
  216.         for(i = 0; i < sizeof buf - 1; i++)
  217.         {
  218.             mul *= 10;
  219.             if ((beforpointdigit/mul) < 1.0) { fmt1 = i + 1; break; }
  220.         }
  221.         if (i == sizeof buf - 1 || fmt1 > 17)
  222.         {
  223.             strcpy(s, "[too big number for %f, %a]");
  224.             return strlen(s);
  225.         }
  226.  
  227.         mul /= 10;
  228.         while(mul > 1)
  229.         {
  230.             div = beforpointdigit / mul;
  231.             *pbuf++ = (char)div + '0';
  232.             beforpointdigit = beforpointdigit - div * mul;
  233.             mul /= 10;
  234.         }
  235.         *pbuf++=(char)beforpointdigit + '0';
  236.  
  237.         //print part of number after point
  238.         mul = 1;
  239.         for(i = 0; i < fmt2; i++)
  240.         {
  241.             nafter = nafter*10;
  242.             mul *= 10;
  243.         }
  244.  
  245.         intdigit = roundl(nafter);
  246.  
  247.         mul /= 10;
  248.         for(i = 0; i < fmt2 - 1; i++)
  249.         {
  250.                 div = intdigit / mul;
  251.                 *pbuf_lo++=(char)div + '0';
  252.                 intdigit = intdigit - div * mul;
  253.                 mul /= 10;
  254.                 if (mul == 1) break;
  255.         }
  256.         *pbuf_lo++ = (char)intdigit + '0';
  257.  
  258.         // form full number
  259.         if (roundl(nafter) != 0 || fmt2 != 0)
  260.         {
  261.             *pbuf++ = '.';
  262.             memcpy(pbuf, buf_low, pbuf_lo - buf_low);  pbuf += pbuf_lo - buf_low;
  263.         } else if (flags & flag_hash_sign)
  264.             *pbuf++ = '.';
  265.     }
  266.     else
  267.     {
  268.         flags &= ~flag_lead_zeros; // no need for INF, NAN
  269.         pbuf += i;
  270.     }
  271.  
  272.     return copy_and_align(s, format1, buf, pbuf - buf, sign, flags);
  273. }
  274.  
  275. int formatted_long_to_string(long long number, int format1, int prec, char *s, int flags)
  276. {
  277.         int         i;
  278.         int         fmt;
  279.         char        sign = 0;
  280.         long long   digit;
  281.         long long   mul;
  282.         int         div;
  283.         char        buf[100], *pbuf = buf;
  284.  
  285.         if (number == -9223372036854775807LL - 1)  // overflow all our math, cant minus this
  286.         {
  287.             strcpy(s, "-9223372036854775808");
  288.             return strlen(s);
  289.         }
  290.  
  291.         if (flags & flag_point) flags &= ~flag_lead_zeros;  // conflicting flags
  292.  
  293.         if (number < 0) {sign = '-'; number = -number; }
  294.         else
  295.         {
  296.             if (flags & flag_plus) sign = '+';  else
  297.             if (flags & flag_space_plus) sign = ' ';
  298.         }
  299.  
  300.         digit = number;
  301.  
  302.         mul = (digit < 0) ? -1 : 1;
  303.  
  304.         for(i = 0; i < sizeof buf - 2; i++)
  305.         {
  306.             if (digit / mul < 10) { fmt = i + 1; break; }
  307.             mul *= 10;
  308.         }
  309.  
  310.         // add leading zeros by prec
  311.         for(i = 0; i < prec - fmt; i++) *pbuf++ = '0';
  312.  
  313.         for(i = 0; i < fmt - 1; i++)
  314.         {
  315.             div = digit / mul;
  316.             *pbuf++ = (char)div + '0';
  317.             digit = digit - div * mul;
  318.             mul /= 10;
  319.             if (mul == 1 || mul == -1) break;
  320.         }
  321.         *pbuf++ = (char)digit + '0';
  322.  
  323.         return copy_and_align(s, format1, buf, pbuf - buf, sign, flags);
  324. }
  325.  
  326. int formatted_hex_to_string(unsigned long long number, int fmt1, int prec, char *s, int flags)
  327. {
  328.     unsigned long long digit, mul;
  329.     int             i, div, fmt;
  330.     char            xdigs_lower[16]="0123456789abcdef";
  331.     char            xdigs_upper[16]="0123456789ABCDEF";
  332.     char            buf[50], *pbuf = buf, sign;
  333.  
  334.     if (number == -9223372036854775807LL - 1)  // overflow all our math, cant minus this
  335.     {
  336.         strcpy(buf, "FFFFFFFFFFFFFFFF");
  337.         pbuf += strlen(buf);
  338.     }
  339.     else
  340.     {
  341.         if (flags & flag_point) flags &= ~flag_lead_zeros;  // conflicting flags
  342.  
  343.         digit = number;
  344.  
  345.         mul = (digit < 0) ? -1 : 1;
  346.  
  347.         for(i = 0; i < sizeof buf - 2; i++)
  348.         {
  349.             if (digit / mul < 16) { fmt = i + 1; break; }
  350.             mul <<= 4;
  351.         }
  352.  
  353.         // add leading zeros by prec
  354.         for(i = 0; i < prec - fmt; i++) *pbuf++ = '0';
  355.  
  356.         for(i = 0; i < fmt - 1; i++)
  357.         {
  358.             div = digit / mul;
  359.             *pbuf++ = (flags & flag_register) ? xdigs_upper[div] : xdigs_lower[div];
  360.             digit = digit - div * mul;
  361.             mul >>= 4;
  362.             if (mul == 1 || mul == -1) break;
  363.         }
  364.         *pbuf++ = (flags & flag_register) ? xdigs_upper[digit] : xdigs_lower[digit];
  365.     }
  366.  
  367.     sign = 0;
  368.     if(flags & flag_hash_sign)
  369.         sign = (flags & flag_register) ? 'X' : 'x';
  370.  
  371.     return copy_and_align(s, fmt1, buf, pbuf - buf, sign, flags);
  372. }
  373.  
  374. int formatted_octa_to_string(unsigned long long number, int fmt1, int prec, char *s, int flags)
  375. {
  376.     unsigned long long digit, mul;
  377.     int             i, div, fmt;
  378.     char            xdigs_lower[16]="01234567";
  379.     char            buf[50], *pbuf = buf;
  380.  
  381.     if (number == -9223372036854775807LL - 1)  // overflow all our math, cant minus this
  382.     {
  383.         strcpy(buf, "1777777777777777777777");
  384.         pbuf += strlen(buf);
  385.     }
  386.     else
  387.     {
  388.         if (flags & flag_point) flags &= ~flag_lead_zeros;  // conflicting flags
  389.  
  390.         digit = number;
  391.  
  392.         mul = (digit < 0) ? -1 : 1;
  393.  
  394.         for(i = 0; i < sizeof buf - 2; i++)
  395.         {
  396.             if (digit / mul < 8) { fmt = i + 1; break; }
  397.             mul <<= 3;
  398.         }
  399.  
  400.         // add leading zeros by prec
  401.         for(i = 0; i < prec - fmt; i++) *pbuf++ = '0';
  402.  
  403.         for(i = 0; i < fmt - 1; i++)
  404.         {
  405.             div = digit / mul;
  406.             *pbuf++ = xdigs_lower[div & 0x7];
  407.             digit = digit - div * mul;
  408.             mul >>= 3;
  409.             if (mul == 1 || mul == -1) break;
  410.         }
  411.         *pbuf++ = xdigs_lower[digit];
  412.     }
  413.  
  414.     return copy_and_align(s, fmt1, buf, pbuf - buf, (flags & flag_hash_sign) ? '0' : 0, flags);
  415. }
  416.  
  417. //int vsnprintf (char * s, size_t n, const char * format, va_list arg );
  418. int format_print(char *dest, size_t maxlen, const char *fmt0, va_list argp)
  419. {
  420.     int                     i;
  421.     int                     length;
  422.     int                     fmt1, fmt2;  // width, precision
  423.     size_t                  pos, posc;
  424.     long        long            intdigit;
  425.     long        double          doubledigit;
  426.     const   char            *fmt, *fmtc;  // first point to %, fmtc points to specifier
  427.     char                    *s; // pointer to current dest char
  428.     char                    *str;
  429.     char                    buf[200];  // buffer for current argument value print representation
  430.     int                     format_flag;
  431.     int                     flag_long;        // 2 = long double or long long int or wchar
  432.     int                    *point_to_n = NULL;
  433.     int                     flags;  // parsed flags
  434.  
  435.     fmt = fmt0;
  436.     s = dest;
  437.     pos = 0;
  438.     while(pos < maxlen)
  439.     {
  440.         if (*fmt != '%')  // usual char
  441.         {
  442.             if ('\0' == (*s++ = *fmt++)) break;
  443.             pos++;
  444.             continue;
  445.         }
  446.  
  447.         if (*(fmt + 1) == '%') // %%
  448.         {
  449.                 *s++ = '%'; pos++;
  450.                 fmt += 2;
  451.                 continue;
  452.         }
  453.         //checking to containg format in the string
  454.         fmtc = fmt;
  455.         posc = pos;
  456.  
  457.         flags = 0;
  458.         format_flag = 0;
  459.         flag_long = 0;  // 2 = long double or long long int or wchar
  460.  
  461.         while(*fmtc != '\0' && !format_flag)    // searching end of format
  462.         {
  463.                 fmtc++; posc++;
  464.                 switch( *fmtc )
  465.                 {
  466.                 case 'a':
  467.                     format_flag = 1;
  468.                     flags |= flag_unsigned;
  469.                     break;
  470.                 case 'A':
  471.                     format_flag = 1;
  472.                     flags |= flag_unsigned | flag_register;
  473.                     break;
  474.                 case 'c':   case 'd':   case 'i':   case 'e':   case 'f':   case 'g':   case 's':   case 'n':
  475.                     format_flag = 1;
  476.                     break;
  477.                 case 'E':   case 'F':   case 'G':
  478.                     format_flag = 1;
  479.                     flags |= flag_register;
  480.                     break;
  481.                 case 'l':
  482.                     flag_long  = flag_long ? 2 : 1;  // ll.eq.L
  483.                     break;
  484.                 case 'L':
  485.                     flag_long = 2;
  486.                     break;
  487.                 case 'o':   case 'u':   case 'x':   case 'p':
  488.                     format_flag = 1;
  489.                     flags |= flag_unsigned;
  490.                     break;
  491.                 case 'X':   case 'P':
  492.                     format_flag = 1;
  493.                     flags |= flag_unsigned | flag_register;
  494.                     break;
  495.                 case '+':
  496.                     flags |= flag_plus;
  497.                     break;
  498.                 case '-':
  499.                     flags |= flag_left_just;
  500.                     break;
  501.                 case ' ': // space
  502.                     flags |= flag_space_plus;
  503.                     break;
  504.                 case '#':
  505.                     flags |= flag_hash_sign;
  506.                     break;
  507.                 case '*':   case '.':  // just skip
  508.                     break;
  509.                 default:
  510.                     if(isdigit(*fmtc))  break;
  511.                     strncpy(dest, "print format error - in % invalid char found", maxlen);
  512.                     return -1;  // non format char found - user error
  513.                 }
  514.         }
  515.  
  516.         if (format_flag == 0)
  517.         {
  518.             strncpy(dest, "print format error - % without format specifier", maxlen);
  519.             return -1;  // format char not found - user error
  520.         }
  521.  
  522.         fmt1 = 0;
  523.         fmt2 = 0;
  524.         if (posc - pos > 1)  // try to read width, precision
  525.         {
  526.             fmt++;
  527.             for(i = pos + 1; i < posc; i++)
  528.             {
  529.                 switch(*fmt)
  530.                 {
  531.                 case '0':
  532.                     if(fmt1 == 0 && (flags & flag_point) == 0)    flags |= flag_lead_zeros;
  533.                 case '1':   case '2':   case '3':   case '4':
  534.                 case '5':   case '6':   case '7':   case '8':   case '9':
  535.                     if ((flags & flag_point) == 0)
  536.                         fmt1 = fmt1 * 10 + (*fmt -'0');
  537.                     else
  538.                         fmt2 = fmt2 * 10 + (*fmt -'0');
  539.                     break;
  540.                 case '*':
  541.                     if (flag_point == 0)
  542.                         fmt1 = va_arg(argp, int);
  543.                     else
  544.                         fmt2 = va_arg(argp, int);
  545.                     break;
  546.                 case '.':
  547.                     flags |= flag_point;
  548.                     break;
  549.                 case 'l':   case 'L':   case '+':   // valid chars - skip
  550.                 case '-':   case ' ':   case '#':
  551.                     break;
  552.                 default: // must be error
  553.                     strncpy(dest, "print format error - %width.precision", maxlen);
  554.                     return -1;  // format char not found - user error
  555.                 }
  556.                 fmt++;
  557.             }
  558.         }
  559.  
  560.         // do real work - format arguments values
  561.         length = 0;
  562.         switch(*fmtc)
  563.         {
  564.         case 'n':
  565.             point_to_n = va_arg(argp, int*);
  566.             break;
  567.         case 'c':
  568.             if (pos + 1 <= maxlen)
  569.             {
  570.                 buf[0] = (char)va_arg(argp, int);
  571.                 length = 1;
  572.             }
  573.             break;
  574.         case 's':   // special case - without buf
  575.             str = va_arg(argp, char*);
  576.             length = strlen(str);
  577.             if ((flags & flag_point) && (length > fmt2)) length = fmt2;  // cut by precision
  578.             if (pos + length > maxlen) length = maxlen - pos;
  579.             memcpy(s, str ,length);
  580.             s += length;
  581.             pos += length;
  582.             break;
  583.         case 'd':   case 'i':   case 'u':   case 'U':
  584.             if (flag_long == 0) intdigit = va_arg(argp, int); else
  585.             if (flag_long == 1) intdigit = va_arg(argp, long); else
  586.             if (flag_long == 2) intdigit = va_arg(argp, long long);
  587.             length = formatted_long_to_string(intdigit, fmt1, fmt2, buf, flags);
  588.             break;
  589.         case 'o':
  590.             if (flag_long == 0) intdigit = va_arg(argp, int); else
  591.             if (flag_long == 1) intdigit = va_arg(argp, long); else
  592.             if (flag_long == 2) intdigit = va_arg(argp, long long);
  593.             length = formatted_octa_to_string(intdigit, fmt1, fmt2, buf, flags);
  594.             break;
  595.         case 'p':   case 'P':   case 'x':   case 'X':
  596.             if (flag_long == 0) intdigit = va_arg(argp, int); else
  597.             if (flag_long == 1) intdigit = va_arg(argp, long); else
  598.             if (flag_long == 2) intdigit = va_arg(argp, long long);
  599.             length=formatted_hex_to_string(intdigit, fmt1, fmt2, buf, flags);
  600.             break;
  601.         case 'a':   case 'A':   case 'f':   case 'F':
  602.             if (flag_long <= 1) doubledigit = va_arg(argp, double); else
  603.             if (flag_long == 2) doubledigit = va_arg(argp, long double);
  604.             length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags);
  605.             break;
  606.         case 'e':   case 'E':
  607.             if (flag_long <= 1) doubledigit = va_arg(argp, double); else
  608.             if (flag_long == 2) doubledigit = va_arg(argp, long double);
  609.             length = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf, flags);
  610.             break;
  611.         case 'g':   case 'G':
  612.  //prec special case, this is just workaround
  613.             if (flag_long <= 1) doubledigit = va_arg(argp, double); else
  614.             if (flag_long == 2) doubledigit = va_arg(argp, long double);
  615.             if (flags & flag_point)
  616.                 length = formatted_double_to_string(doubledigit, fmt1, fmt2, buf, flags);
  617.             else
  618.                 length = formatted_double_to_string(doubledigit, fmt1, 1, buf, flags | flag_point);
  619.             i = formatted_double_to_string_scientific(doubledigit, fmt1, fmt2, buf + sizeof buf / 2, flags);
  620.             if(length > i)
  621.             {
  622.                 memcpy(buf, buf + sizeof buf / 2, i);
  623.                 length = i;
  624.             }
  625.             break;
  626.         }
  627.         if (*fmtc != 's' && length > 0) // skip multiple string copying
  628.         {
  629.             if (pos + length > maxlen)  length = maxlen - pos;
  630.             memcpy(s, buf, length);
  631.             s += length;
  632.             pos += length;
  633.         }
  634.         fmt = fmtc + 1;
  635.     }
  636.  
  637.     if (point_to_n) *point_to_n = pos;
  638.     return(pos);
  639. }
  640.