Subversion Repositories Kolibri OS

Rev

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