Subversion Repositories Kolibri OS

Rev

Rev 9861 | 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.         ch = vgetc(save, src);
  175.         if (ch == 'o')
  176.             base = 8;
  177.         else if (ch == 'x' || ch == 'X')
  178.             base = 16;
  179.         if (base == 10)
  180.             have_digits++;
  181.         else
  182.         {
  183.             char tch = vgetc(save, src);
  184.             if ((base == 8 && isdigit(tch) && tch < '8') ||
  185.                 (base == 16 && isxdigit(tch)))
  186.                 ch = tch;
  187.             else
  188.             {
  189.                 have_digits++;
  190.                 //base = 10; // not required: zero is zero with any (base > 1)
  191.                 vungetc(save, tch, src);
  192.             }
  193.         }
  194.     }
  195.     *digit = 0;
  196.     for (;;)
  197.     {
  198.         // test ch is valid
  199.         if ((isdigit(ch) && base == 10) ||
  200.             (isdigit(ch) && base == 8 && ch < '8') ||
  201.             (isxdigit(ch) && base == 16))
  202.         {
  203.             if (base == 16)
  204.             {
  205.                 if (ch <= '9') ch -= '0';
  206.                 else
  207.                 if (ch <= 'F') ch = 10 + ch - 'A';
  208.                 else
  209.                     ch = 10 + ch - 'a';
  210.             }
  211.             else
  212.                 ch -= '0';
  213.             *digit = *digit * base + ch;
  214.             have_digits++;
  215.             ch = vgetc(save, src);
  216.             if (ch == EOF || isspace(ch)) break; // ok, just finish num
  217.         }
  218.         else if (ch == EOF || isspace(ch))
  219.             break;
  220.         else
  221.         {
  222.             vungetc(save, ch, src);
  223.             break;
  224.         }
  225.     }
  226.     *digit *= sign;
  227.     if (have_digits)
  228.         return 1;
  229.     else
  230.         return -2;
  231. }
  232.  
  233.  
  234.  
  235. int format_scan(const void *src, const char *fmt, va_list argp, virtual_getc vgetc, virtual_ungetc vungetc)
  236. {
  237.     int                     i;
  238.     int                     length;
  239.     int                     fmt1, fmt2;  // width, precision
  240.     size_t                  pos, posc;
  241.     const char            *fmtc;  // first point to %, fmtc points to specifier
  242.     int                    ch;
  243.     int                     format_flag;
  244.     int                     flag_long;        // 2 = long double or long long int or wchar
  245.     int                    *point_to_n = NULL, nread = 0;
  246.     int                     flags;  // parsed flags
  247.     int                     save = 0;
  248.     char                *arg_str;
  249.     int         *arg_int;
  250.     long        *arg_long;
  251.     long long   *arg_longlong;
  252.     float       *arg_float;
  253.     double      *arg_double;
  254.     long double *arg_longdouble;
  255.     long long  digit;
  256.     long double real;
  257.     int skip_next; // skip arguments with * char format
  258.  
  259.     pos = 0;
  260.     while(*fmt)
  261.     {
  262.         while (*fmt && isspace(*fmt)) fmt++; // skip spaces in format str
  263.  
  264.         if (*fmt != '%')  // usual char
  265.         {
  266.             ch = vgetc(&save, src);
  267.             if (ch != *fmt++) // char not match format
  268.             {
  269.                 vungetc(&save, ch, src);
  270.                 break;
  271.             }
  272.             pos++;
  273.             continue;
  274.         }
  275.  
  276.         if (*(fmt + 1) == '%') // %%
  277.         {
  278.             ch = vgetc(&save, src);
  279.             if (ch != '%') // char not match format
  280.             {
  281.                 vungetc(&save, ch, src);
  282.                 break;
  283.             }
  284.             pos++;
  285.             fmt += 2;
  286.             continue;
  287.         }
  288.         //checking to containg format in the string
  289.         fmtc = fmt;
  290.         posc = pos;
  291.  
  292.         skip_next = 0;
  293.         flags = 0;
  294.         format_flag = 0;
  295.         flag_long = 0;  // 2 = long double or long long int or wchar
  296.  
  297.         while(*fmtc != '\0' && !format_flag)    // searching end of format
  298.         {
  299.                 fmtc++; posc++;
  300.                 switch( *fmtc )
  301.                 {
  302.                 case 'a':
  303.                     format_flag = 1;
  304.                     flags |= flag_unsigned;
  305.                     break;
  306.                 case 'c':   case 'd':   case 'i':   case 'e':   case 'f':   case 'g':   case 's':   case 'n':
  307.                     format_flag = 1;
  308.                     break;
  309.                 case 'l':
  310.                     flag_long  = flag_long ? 2 : 1;  // ll.eq.L
  311.                     break;
  312.                 case 'L':
  313.                     flag_long = 2;
  314.                     break;
  315.                 case 'o':   case 'u':   case 'x':   case 'p':
  316.                     format_flag = 1;
  317.                     flags |= flag_unsigned;
  318.                     break;
  319.                 case '*':
  320.                     skip_next = 1;
  321.                     break;
  322.                 case '.':  // just skip
  323.                     break;
  324.                 default:
  325.                     if(isdigit(*fmtc))  break;
  326.                     goto exit_me;  // non format char found - user error
  327.                 }
  328.         }
  329.  
  330.         if (format_flag == 0)
  331.         {
  332.             goto exit_me;  // format char not found - user error
  333.         }
  334.  
  335.         fmt1 = 0;
  336.         fmt2 = 0;
  337.         if (posc - pos > 1)  // try to read width, precision
  338.         {
  339.             fmt++;
  340.             for(i = pos + 1; i < posc; i++)
  341.             {
  342.                 switch(*fmt)
  343.                 {
  344.                 case '0':
  345.                     if(fmt1 == 0 && (flags & flag_point) == 0)    flags |= flag_lead_zeros;
  346.                 case '1':   case '2':   case '3':   case '4':
  347.                 case '5':   case '6':   case '7':   case '8':   case '9':
  348.                     if ((flags & flag_point) == 0)
  349.                         fmt1 = fmt1 * 10 + (*fmt -'0');
  350.                     else
  351.                         fmt2 = fmt2 * 10 + (*fmt -'0');
  352.                     break;
  353.                 case '*': // ignoring
  354.                     break;
  355.                 case '.':
  356.                     flags |= flag_point;
  357.                     break;
  358.                 case 'l':   case 'L':      // valid chars - skip
  359.                     break;
  360.                 default: // must be error
  361.                     goto exit_me;  // format char not found - user error
  362.                 }
  363.                 fmt++;
  364.             }
  365.         }
  366.  
  367.         // do real work - format arguments values
  368.         // skip input spaces
  369.         do {
  370.             ch = vgetc(&save, src);
  371.             if (ch == EOF) goto exit_me;
  372.         } while (isspace(ch));
  373.  
  374.         switch(*fmtc)
  375.         {
  376.         case 'n':
  377.             point_to_n = va_arg(argp, int*);
  378.             vungetc(&save, ch, src);
  379.             break;
  380.         case 'c':  // read width chars, ever spaces
  381.             if (!skip_next) arg_str = va_arg(argp, char*);
  382.             if (fmt1 == 0) length = 1;
  383.             else length = fmt1;
  384.             for (i = 0; i < length;)
  385.             {
  386.                 if (!skip_next) *arg_str++ = ch;
  387.                 i++;
  388.                 ch = vgetc(&save, src);
  389.                 if (ch == EOF) break;
  390.             }
  391.             if (i < length) goto exit_me; // not enough chars
  392.             break;
  393.         case 's':
  394.             if (!skip_next) arg_str = va_arg(argp, char*);
  395.             if (fmt1 == 0) length = 4095;   // max string scan 4096
  396.             else length = fmt1;
  397.             for (i = 0; i < length; i++)
  398.             {
  399.                 if (!skip_next) *arg_str++ = ch;
  400.  
  401.                 ch = vgetc(&save, src);
  402.                 if (ch == EOF || isspace(ch)) break; // ok, just finish string
  403.             }
  404.             if (!skip_next) *arg_str++ = '\0';
  405.             break;
  406.         case 'd':   case 'i':   case 'u':
  407.         case 'o':   case 'p':   case 'x':
  408.             i = try_parse_int(&digit, ch, src, &save, vgetc, vungetc);
  409.             if (i < 0) goto exit_me;
  410.  
  411.             if (!skip_next)
  412.             if (flag_long == 0) { arg_int = va_arg(argp, int*); *arg_int = (int)digit; } else
  413.             if (flag_long == 1) { arg_long = va_arg(argp, long*); *arg_long = (long)digit; } else
  414.             if (flag_long == 2) { arg_longlong = va_arg(argp, long long*); *arg_longlong = digit; }
  415.             break;
  416.         case 'a':   case 'A':   case 'f':   case 'F':
  417.         case 'e':   case 'E':
  418.         case 'g':   case 'G':
  419.             i = try_parse_real(&real, ch, src, &save, vgetc, vungetc);
  420.             if (i < 0) goto exit_me;
  421.  
  422.             if (!skip_next)
  423.             if (flag_long == 0) { arg_float = va_arg(argp, float*); *arg_float = (float)real; } else
  424.             if (flag_long == 1) { arg_double = va_arg(argp, double*); *arg_double = (double)real; } else
  425.             if (flag_long == 2) { arg_longdouble = va_arg(argp, long double*); *arg_longdouble = real; }
  426.             break;
  427.         }
  428.  
  429.         fmt = fmtc + 1;
  430.         if (!skip_next) nread++;
  431.     }
  432. exit_me:
  433.     if (point_to_n) *point_to_n = nread;
  434.     return  nread;
  435. }
  436.