Subversion Repositories Kolibri OS

Rev

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