Subversion Repositories Kolibri OS

Rev

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