Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

  1. /*
  2.         function for format read from any source
  3.  
  4. Siemargl formats as http://www.cplusplus.com/reference/cstdio/scanf/, no wchar though
  5. http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html  is used too
  6.  
  7. todo:
  8. [characters], [^characters]
  9. -%n nothing scanned, filled only if good result
  10. -%d, i, u, o, x, p read similar - detect base by prefix 0 or 0x
  11. -%a
  12. -can overflow unsigned as signed
  13. -radix point always '.', no LOCALEs
  14. */
  15.  
  16. #include <stdio.h>
  17. #include <ctype.h>
  18. #include <math.h>
  19. #include <stdarg.h>
  20. #include "format_scan.h"
  21.  
  22. static int __try_parse_real(long double *real, int ch, const void *src, void *save, virtual_getc vgetc, virtual_ungetc vungetc)
  23. // returns 1 if OK, -1 == EOF, -2 parse broken
  24. {
  25.     int sign = 1, have_digits = 0;
  26.     long long div;
  27.  
  28.     if (ch == '+')
  29.     {
  30.         ch = vgetc(save, src);
  31.         if (ch == EOF) return EOF;
  32.     } else
  33.     if (ch == '-')
  34.     {
  35.         sign = -1;
  36.         ch = vgetc(save, src);
  37.         if (ch == EOF) return EOF;
  38.     };
  39.     *real = 0.0;
  40.     for (;;) // mantissa before point
  41.     {
  42.         // test ch is valid
  43.         if (isdigit(ch))
  44.         {
  45.             *real = *real * 10 + ch - '0';
  46.             have_digits++;
  47.             ch = vgetc(save, src);
  48.             if (ch == EOF || isspace(ch)) break; // ok, just finish num
  49.         } else
  50.         if (ch == '.' || ch == 'E' || ch == 'e')
  51.         {
  52.             break; // ok
  53.         }
  54.         else
  55.         {
  56.             vungetc(save, ch, src);
  57.             break;
  58.         }
  59.     }
  60.     if (ch != '.' && ch != 'E' && ch != 'e') // ok, just integer part
  61.     {
  62.         *real *= sign;
  63.         if (have_digits)
  64.             return 1;
  65.         else
  66.             return -2;
  67.     }
  68.  
  69.     if(ch == '.')
  70.     {
  71.         ch = vgetc(save, src);
  72.         div = 10; // use as divisor
  73.         for (;;) // mantissa after point
  74.         {
  75.             // test ch is valid
  76.             if (isdigit(ch))
  77.             {
  78.                 *real += (double)(ch - '0') / div;
  79.                 div *= 10;
  80.                 have_digits++;
  81.                 ch = vgetc(save, src);
  82.                 if (ch == EOF || isspace(ch)) break; // ok, just finish num
  83.             } else
  84.             if (ch == 'E' || ch == 'e')
  85.             {
  86.                 break; // ok
  87.             }
  88.             else
  89.             {
  90.                 vungetc(save, ch, src);
  91.                 break;
  92.             }
  93.         }
  94.         if (ch != 'E' && ch != 'e')  // ok, real as XX.YY
  95.         {
  96.             *real *= sign;
  97.             if (have_digits)
  98.                 return 1;
  99.             else
  100.                 return -2;
  101.         }
  102.     }
  103.  
  104.     ch = vgetc(save, src);
  105.     *real *= sign;
  106.     // exponent
  107.     sign = 1;
  108.     if (ch == '+')
  109.     {
  110.         ch = vgetc(save, src);
  111.         if (ch == EOF) return EOF;
  112.     } else
  113.     if (ch == '-')
  114.     {
  115.         sign = -1;
  116.         ch = vgetc(save, src);
  117.         if (ch == EOF) return EOF;
  118.     };
  119.     div = 0;
  120.     for (;;)
  121.     {
  122.         // test ch is valid
  123.         if (isdigit(ch))
  124.         {
  125.             div = div * 10 + ch - '0';
  126.             ch = vgetc(save, src);
  127.             if (ch == EOF || isspace(ch)) break; // ok, just finish num
  128.         }
  129.         else
  130.         {
  131.             vungetc(save, ch, src);
  132.             break;
  133.         }
  134.     }
  135.     div *= sign;
  136.     *real *= pow(10, div);
  137.  
  138.     return 1;
  139. }
  140.  
  141. static int __try_parse_int(long long *digit, int ch, const void *src, void *save, virtual_getc vgetc, virtual_ungetc vungetc)
  142. {
  143.     int sign = 1, base = 10, have_digits = 0;
  144.  
  145.     if (ch == '+')
  146.     {
  147.         ch = vgetc(save, src);
  148.         if (ch == EOF) return EOF;
  149.     } else
  150.     if (ch == '-')
  151.     {
  152.         sign = -1;
  153.         ch = vgetc(save, src);
  154.         if (ch == EOF) return EOF;
  155.     };
  156.  
  157.     if (ch == '0')  // octal or hex, read next
  158.     {
  159.         base = 8;
  160.         ch = vgetc(save, src);
  161.         if (ch == EOF || isspace(ch))
  162.             have_digits++;
  163.         else
  164.         if (ch == 'x' || ch == 'X')
  165.         {
  166.             base = 16;
  167.             ch = vgetc(save, src);
  168.             if (ch == EOF) return EOF;
  169.         }
  170.     }
  171.     *digit = 0;
  172.     for (;;)
  173.     {
  174.         // test ch is valid
  175.         if ((isdigit(ch) && base == 10) ||
  176.             (isdigit(ch) && base == 8 && ch < '8') ||
  177.             (isxdigit(ch) && base == 16))
  178.         {
  179.             if (base == 16)
  180.             {
  181.                 if (ch <= '9') ch-= '0';
  182.                 else
  183.                 if (ch <= 'F') ch = 10 + ch - 'A';
  184.                 else
  185.                     ch = 10 + ch - 'a';
  186.             }
  187.             else
  188.                 ch -= '0';
  189.             *digit = *digit * base + ch;
  190.             have_digits++;
  191.             ch = vgetc(save, src);
  192.             if (ch == EOF || isspace(ch)) break; // ok, just finish num
  193.         }
  194.         else if (ch == EOF || isspace(ch))
  195.             break;
  196.         else
  197.         {
  198.             vungetc(save, ch, src);
  199.             break;
  200.         }
  201.     }
  202.     *digit *= sign;
  203.     if (have_digits)
  204.         return 1;
  205.     else
  206.         return -2;
  207. }
  208.  
  209.  
  210.  
  211. int _format_scan(const void *src, const char *fmt, va_list argp, virtual_getc vgetc, virtual_ungetc vungetc)
  212. {
  213.     int         i;
  214.     int         length;
  215.     int         fmt1, fmt2;  // width, precision
  216.     size_t      pos, posc;
  217.     const char  *fmtc;  // first point to %, fmtc points to specifier
  218.     int         ch;
  219.     int         format_flag;
  220.     int         flag_long;        // 2 = long double or long long int or wchar
  221.     int         *point_to_n = NULL, nread = 0;
  222.     int         flags;  // parsed flags
  223.     int         save = 0;
  224.     char        *arg_str;
  225.     int         *arg_int;
  226.     long        *arg_long;
  227.     long long   *arg_longlong;
  228.     float       *arg_float;
  229.     double      *arg_double;
  230.     long double *arg_longdouble;
  231.     long long  digit;
  232.     long double real;
  233.  
  234.  
  235.     pos = 0;
  236.     while(*fmt)
  237.     {
  238.         while (*fmt && isspace(*fmt)) fmt++; // skip paces in format str
  239.  
  240.         if (*fmt != '%')  // usual char
  241.         {
  242.             ch = vgetc(&save, src);
  243.             if (ch != *fmt++) // char not match format
  244.             {
  245.                 vungetc(&save, ch, src);
  246.                 break;
  247.             }
  248.             pos++;
  249.             continue;
  250.         }
  251.  
  252.         if (*(fmt + 1) == '%') // %%
  253.         {
  254.             ch = vgetc(&save, src);
  255.             if (ch != '%') // char not match format
  256.             {
  257.                 vungetc(&save, ch, src);
  258.                 break;
  259.             }
  260.             pos++;
  261.             fmt += 2;
  262.             continue;
  263.         }
  264.         //checking to containg format in the string
  265.         fmtc = fmt;
  266.         posc = pos;
  267.  
  268.         flags = 0;
  269.         format_flag = 0;
  270.         flag_long = 0;  // 2 = long double or long long int or wchar
  271.  
  272.         while(*fmtc != '\0' && !format_flag)    // searching end of format
  273.         {
  274.                 fmtc++; posc++;
  275.                 switch( *fmtc )
  276.                 {
  277.                 case 'a':
  278.                     format_flag = 1;
  279.                     flags |= flag_unsigned;
  280.                     break;
  281.                 case 'c':   case 'd':   case 'i':   case 'e':   case 'f':   case 'g':   case 's':   case 'n':
  282.                     format_flag = 1;
  283.                     break;
  284.                 case 'l':
  285.                     flag_long  = flag_long ? 2 : 1;  // ll.eq.L
  286.                     break;
  287.                 case 'L':
  288.                     flag_long = 2;
  289.                     break;
  290.                 case 'o':   case 'u':   case 'x':   case 'p':
  291.                     format_flag = 1;
  292.                     flags |= flag_unsigned;
  293.                     break;
  294.                 case '*':   case '.':  // just skip
  295.                     break;
  296.                 default:
  297.                     if(isdigit(*fmtc))  break;
  298.                     goto exit_me;  // non format char found - user error
  299.                 }
  300.         }
  301.  
  302.         if (format_flag == 0)
  303.         {
  304.             goto exit_me;  // format char not found - user error
  305.         }
  306.  
  307.         fmt1 = 0;
  308.         fmt2 = 0;
  309.         if (posc - pos > 1)  // try to read width, precision
  310.         {
  311.             fmt++;
  312.             for(i = pos + 1; i < posc; i++)
  313.             {
  314.                 switch(*fmt)
  315.                 {
  316.                 case '0':
  317.                     if(fmt1 == 0 && (flags & flag_point) == 0)    flags |= flag_lead_zeros;
  318.                 case '1':   case '2':   case '3':   case '4':
  319.                 case '5':   case '6':   case '7':   case '8':   case '9':
  320.                     if ((flags & flag_point) == 0)
  321.                         fmt1 = fmt1 * 10 + (*fmt -'0');
  322.                     else
  323.                         fmt2 = fmt2 * 10 + (*fmt -'0');
  324.                     break;
  325.                 case '*': // ignoring
  326.                     break;
  327.                 case '.':
  328.                     flags |= flag_point;
  329.                     break;
  330.                 case 'l':   case 'L':      // valid chars - skip
  331.                     break;
  332.                 default: // must be error
  333.                     goto exit_me;  // format char not found - user error
  334.                 }
  335.                 fmt++;
  336.             }
  337.         }
  338.  
  339.         // do real work - format arguments values
  340.         // skip input spaces
  341.         do {
  342.             ch = vgetc(&save, src);
  343.             if (ch == EOF) goto exit_me;
  344.         } while (isspace(ch));
  345.  
  346.         switch(*fmtc)
  347.         {
  348.         case 'n':
  349.             point_to_n = va_arg(argp, int*);
  350.             vungetc(&save, ch, src);
  351.             break;
  352.         case 'c':  // read width chars, ever spaces
  353.             arg_str = va_arg(argp, char*);
  354.             if (fmt1 == 0) length = 1;
  355.             else length = fmt1;
  356.             for (i = 0; i < length;)
  357.             {
  358.                 *arg_str++ = ch; i++;
  359.                 ch = vgetc(&save, src);
  360.                 if (ch == EOF) break;
  361.             }
  362.             if (i < length) goto exit_me; // not enough chars
  363.             break;
  364.         case 's':
  365.             arg_str = va_arg(argp, char*);
  366.             if (fmt1 == 0) length = 4095;   // max string scan 4096
  367.             else length = fmt1;
  368.             for (i = 0; i < length; i++)
  369.             {
  370.                 *arg_str++ = ch;
  371.                 ch = vgetc(&save, src);
  372.                 if (ch == EOF || isspace(ch)) break; // ok, just finish string
  373.             }
  374.             *arg_str++ = '\0';
  375.             break;
  376.         case 'd':   case 'i':   case 'u':
  377.         case 'o':   case 'p':   case 'x':
  378.             i = __try_parse_int(&digit, ch, src, &save, vgetc, vungetc);
  379.             if (i < 0) goto exit_me;
  380.  
  381.             if (flag_long == 0) { arg_int = va_arg(argp, int*); *arg_int = (int)digit; } else
  382.             if (flag_long == 1) { arg_long = va_arg(argp, long*); *arg_long = (long)digit; } else
  383.             if (flag_long == 2) { arg_longlong = va_arg(argp, long long*); *arg_longlong = digit; }
  384.             break;
  385.         case 'a':   case 'A':   case 'f':   case 'F':
  386.         case 'e':   case 'E':
  387.         case 'g':   case 'G':
  388.             i = __try_parse_real(&real, ch, src, &save, vgetc, vungetc);
  389.             if (i < 0) goto exit_me;
  390.  
  391.             if (flag_long == 0) { arg_float = va_arg(argp, float*); *arg_float = (float)real; } else
  392.             if (flag_long == 1) { arg_double = va_arg(argp, double*); *arg_double = (double)real; } else
  393.             if (flag_long == 2) { arg_longdouble = va_arg(argp, long double*); *arg_longdouble = real; }
  394.             break;
  395.         }
  396.  
  397.         fmt = fmtc + 1;
  398.         nread++;
  399.     }
  400. exit_me:
  401.     if (point_to_n) *point_to_n = nread;
  402.     return  nread;
  403. }
  404.