text: // author BOSS
  
  // Copyright 2006 Google Inc.
  // All Rights Reserved.
  // Author: renn
  //
  // The fscanf, vfscanf and creat functions are implemented so that their
  // functionality is mostly like their stdio counterparts. However, currently
  // these functions do not use any buffering, making them rather slow.
  // File streams are thus processed one character at a time.
  // Although the implementations of the scanf functions do lack a few minor
  // features, they should be sufficient for their use in tesseract.
  //
  // Licensed under the Apache License, Version 2.0 (the "License");
  // you may not use this file except in compliance with the License.
  // You may obtain a copy of the License at
  // http://www.apache.org/licenses/LICENSE-2.0
  // Unless required by applicable law or agreed to in writing, software
  // distributed under the License is distributed on an "AS IS" BASIS,
  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  // See the License for the specific language governing permissions and
  // limitations under the License.
  
  #ifdef HAVE_CONFIG_H
  #include "config_auto.h"
  #endif
  
  #include <ctype.h>
  #include <math.h>
  #include <stdarg.h>
  #include <stddef.h>
  #include <string.h>
  #include <limits.h>
  #include <stdio.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  
  #include BOSS_TESSERACT_U_scanutils_h //original-code:"scanutils.h"
  #include BOSS_TESSERACT_U_tprintf_h //original-code:"tprintf.h"
  
  enum Flags {
    FL_SPLAT  = 0x01,   // Drop the value, do not assign
    FL_INV    = 0x02,   // Character-set with inverse
    FL_WIDTH  = 0x04,   // Field width specified
    FL_MINUS  = 0x08,   // Negative number
  };
  
  enum Ranks {
    RANK_CHAR = -2,
    RANK_SHORT  = -1,
    RANK_INT  = 0,
    RANK_LONG = 1,
    RANK_LONGLONG = 2,
    RANK_PTR      = INT_MAX // Special value used for pointers
  };
  
  const enum Ranks kMinRank = RANK_CHAR;
  const enum Ranks kMaxRank = RANK_LONGLONG;
  
  const enum Ranks kIntMaxRank = RANK_LONGLONG;
  const enum Ranks kSizeTRank = RANK_LONG;
  const enum Ranks kPtrDiffRank = RANK_LONG;
  
  enum Bail {
    BAIL_NONE = 0,    // No error condition
    BAIL_EOF,         // Hit EOF
    BAIL_ERR          // Conversion mismatch
  };
  
  // Helper functions ------------------------------------------------------------
  inline size_t LongBit() {
    return CHAR_BIT * sizeof(long);
  }
  
  static inline int
  SkipSpace(FILE *s) {
    int p;
    while (isspace(p = BOSS_TESSERACT_fgetc(s))); //original-code:fgetc(s)));
    BOSS_TESSERACT_ungetc(p, s);  // Make sure next char is available for reading //original-code:ungetc(p, s);  // Make sure next char is available for reading
    return p;
  }
  
  static inline void
  SetBit(unsigned long *bitmap, unsigned int bit) {
    bitmap[bit/LongBit()] |= 1UL << (bit%LongBit());
  }
  
  static inline int
  TestBit(unsigned long *bitmap, unsigned int bit) {
    return static_cast<int>(bitmap[bit/LongBit()] >> (bit%LongBit())) & 1;
  }
  
  static inline int DigitValue(int ch, int base) {
    if (ch >= '0' && ch <= '9') {
      if (base >= 10 || ch <= '7')
        return ch-'0';
    } else if (ch >= 'A' && ch <= 'Z' && base == 16) {
      return ch-'A'+10;
    } else if (ch >= 'a' && ch <= 'z' && base == 16) {
      return ch-'a'+10;
    }
    return -1;
  }
  
  // IO (re-)implementations -----------------------------------------------------
  uintmax_t streamtoumax(FILE* s, int base) {
    int minus = 0;
    uintmax_t v = 0;
    int d, c = 0;
  
    for (c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
      isspace(static_cast<unsigned char>(c)) && (c != EOF);
      c = BOSS_TESSERACT_fgetc(s)) {} //original-code:fgetc(s)) {}
  
    // Single optional + or -
    if (c == '-' || c == '+') {
      minus = (c == '-');
      c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
    }
  
    // Assign correct base
    if (base == 0) {
      if (c == '0') {
        c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
        if (c == 'x' || c == 'X') {
          base = 16;
          c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
        } else {
          base = 8;
        }
      }
    } else if (base == 16) {
      if (c == '0') {
        c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
        if (c == 'x' || c == 'X') c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
      }
    }
  
    // Actual number parsing
    for (; (c != EOF) && (d = DigitValue(c, base)) >= 0; c = BOSS_TESSERACT_fgetc(s)) //original-code:fgetc(s))
      v = v*base + d;
  
    BOSS_TESSERACT_ungetc(c, s); //original-code:ungetc(c, s);
    return minus ? -v : v;
  }
  
  double streamtofloat(FILE* s) {
    int minus = 0;
    int v = 0;
    int d, c = 0;
    int k = 1;
    int w = 0;
  
    for (c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
      isspace(static_cast<unsigned char>(c)) && (c != EOF);
      c = BOSS_TESSERACT_fgetc(s)); //original-code:fgetc(s));
  
    // Single optional + or -
    if (c == '-' || c == '+') {
      minus = (c == '-');
      c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
    }
  
    // Actual number parsing
    for (; c != EOF && (d = DigitValue(c, 10)) >= 0; c = BOSS_TESSERACT_fgetc(s)) //original-code:fgetc(s))
      v = v*10 + d;
    if (c == '.') {
      for (c = BOSS_TESSERACT_fgetc(s); c != EOF && (d = DigitValue(c, 10)) >= 0; c = BOSS_TESSERACT_fgetc(s)) { //original-code:fgetc(s); c != EOF && (d = DigitValue(c, 10)) >= 0; c = fgetc(s)) {
        w = w*10 + d;
        k *= 10;
      }
    }
    double f  = static_cast<double>(v)
              + static_cast<double>(w) / static_cast<double>(k);
    if (c == 'e' || c == 'E') {
      c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
      int expsign = 1;
      if (c == '-' || c == '+') {
        expsign = (c == '-') ? -1 : 1;
        c = BOSS_TESSERACT_fgetc(s); //original-code:fgetc(s);
      }
      int exponent = 0;
      for (; (c != EOF) && (d = DigitValue(c, 10)) >= 0; c = BOSS_TESSERACT_fgetc(s)) { //original-code:fgetc(s)) {
        exponent = exponent * 10 + d;
      }
      exponent *= expsign;
      f *= pow(10.0, static_cast<double>(exponent));
    }
    BOSS_TESSERACT_ungetc(c, s); //original-code:ungetc(c, s);
  
    return minus ? -f : f;
  }
  
  double strtofloat(const char* s) {
    int minus = 0;
    int v = 0;
    int d;
    int k = 1;
    int w = 0;
  
    while(*s && isspace(static_cast<unsigned char>(*s))) s++;
  
    // Single optional + or -
    if (*s == '-' || *s == '+') {
      minus = (*s == '-');
      s++;
    }
  
    // Actual number parsing
    for (; *s && (d = DigitValue(*s, 10)) >= 0; s++)
      v = v*10 + d;
    if (*s == '.') {
      for (++s; *s && (d = DigitValue(*s, 10)) >= 0; s++) {
        w = w*10 + d;
        k *= 10;
      }
    }
    if (*s == 'e' || *s == 'E')
      tprintf("WARNING: Scientific Notation not supported!");
  
    double f  = static_cast<double>(v)
              + static_cast<double>(w) / static_cast<double>(k);
  
    return minus ? -f : f;
  }
  
  static int tvfscanf(FILE* stream, const char *format, va_list ap);
  
  int tfscanf(FILE* stream, const char *format, ...) {
    va_list ap;
    int rv;
  
    va_start(ap, format);
    rv = tvfscanf(stream, format, ap);
    va_end(ap);
  
    return rv;
  }
  
  #ifdef EMBEDDED
  
  int fscanf(FILE* stream, const char *format, ...) {
    va_list ap;
    int rv;
  
    va_start(ap, format);
    rv = tvfscanf(stream, format, ap);
    va_end(ap);
  
    return rv;
  }
  
  int vfscanf(FILE* stream, const char *format, ...) {
    va_list ap;
    int rv;
  
    va_start(ap, format);
    rv = tvfscanf(stream, format, ap);
    va_end(ap);
  
    return rv;
  }
  #endif
  
  static int tvfscanf(FILE* stream, const char *format, va_list ap) {
    const char *p = format;
    char ch;
    int q = 0;
    uintmax_t val = 0;
    int rank = RANK_INT;    // Default rank
    unsigned int width = UINT_MAX;
    int base;
    int flags = 0;
    enum {
      ST_NORMAL,        // Ground state
      ST_FLAGS,         // Special flags
      ST_WIDTH,         // Field width
      ST_MODIFIERS,     // Length or conversion modifiers
      ST_MATCH_INIT,    // Initial state of %[ sequence
      ST_MATCH,         // Main state of %[ sequence
      ST_MATCH_RANGE,   // After - in a %[ sequence
    } state = ST_NORMAL;
    char *sarg = NULL;    // %s %c or %[ string argument
    enum Bail bail = BAIL_NONE;
    int sign;
    int converted = 0;    // Successful conversions
    unsigned long matchmap[((1 << CHAR_BIT)+(CHAR_BIT * sizeof(long) - 1)) /
        (CHAR_BIT * sizeof(long))];
    int matchinv = 0;   // Is match map inverted?
    unsigned char range_start = 0;
    off_t start_off = BOSS_TESSERACT_ftell(stream); //original-code:ftell(stream);
  
    // Skip leading spaces
    SkipSpace(stream);
  
    while ((ch = *p++) && !bail) {
      switch (state) {
        case ST_NORMAL:
          if (ch == '%') {
            state = ST_FLAGS;
            flags = 0; rank = RANK_INT; width = UINT_MAX;
          } else if (isspace(static_cast<unsigned char>(ch))) {
            SkipSpace(stream);
          } else {
            if (BOSS_TESSERACT_fgetc(stream) != ch) //original-code:fgetc(stream) != ch)
              bail = BAIL_ERR;  // Match failure
          }
          break;
  
        case ST_FLAGS:
          if (ch == '*') {
            flags |= FL_SPLAT;
          } else if ('0' <= ch && ch <= '9') {
            width = (ch-'0');
            state = ST_WIDTH;
            flags |= FL_WIDTH;
          } else {
            state = ST_MODIFIERS;
            p--;      // Process this character again
          }
        break;
  
        case ST_WIDTH:
          if (ch >= '0' && ch <= '9') {
            width = width*10+(ch-'0');
          } else {
            state = ST_MODIFIERS;
            p--;      // Process this character again
          }
        break;
  
        case ST_MODIFIERS:
          switch (ch) {
            // Length modifiers - nonterminal sequences
            case 'h':
              rank--;     // Shorter rank
            break;
            case 'l':
              rank++;     // Longer rank
            break;
            case 'j':
              rank = kIntMaxRank;
            break;
            case 'z':
              rank = kSizeTRank;
            break;
            case 't':
              rank = kPtrDiffRank;
            break;
            case 'L':
            case 'q':
              rank = RANK_LONGLONG; // long double/long long
            break;
  
            default:
              // Output modifiers - terminal sequences
              state = ST_NORMAL;  // Next state will be normal
              if (rank < kMinRank)  // Canonicalize rank
                rank = kMinRank;
              else if (rank > kMaxRank)
                rank = kMaxRank;
  
            switch (ch) {
              case 'P':   // Upper case pointer
              case 'p':   // Pointer
                rank = RANK_PTR;
                base = 0; sign = 0;
              goto scan_int;
  
              case 'i':   // Base-independent integer
                base = 0; sign = 1;
              goto scan_int;
  
              case 'd':   // Decimal integer
                base = 10; sign = 1;
              goto scan_int;
  
              case 'o':   // Octal integer
                base = 8; sign = 0;
              goto scan_int;
  
              case 'u':   // Unsigned decimal integer
                base = 10; sign = 0;
              goto scan_int;
  
              case 'x':   // Hexadecimal integer
              case 'X':
                base = 16; sign = 0;
              goto scan_int;
  
              case 'n':   // Number of characters consumed
                val = BOSS_TESSERACT_ftell(stream) - start_off; //original-code:ftell(stream) - start_off;
              goto set_integer;
  
              scan_int:
                q = SkipSpace(stream);
                if ( q <= 0 ) {
                  bail = BAIL_EOF;
                  break;
                }
                val = streamtoumax(stream, base);
                // fall through
  
              set_integer:
                if (!(flags & FL_SPLAT)) {
                  converted++;
                  switch(rank) {
                    case RANK_CHAR:
                      *va_arg(ap, unsigned char *)
                        = static_cast<unsigned char>(val);
                    break;
                    case RANK_SHORT:
                      *va_arg(ap, unsigned short *)
                        = static_cast<unsigned short>(val);
                    break;
                    case RANK_INT:
                      *va_arg(ap, unsigned int *)
                        = static_cast<unsigned int>(val);
                    break;
                    case RANK_LONG:
                      *va_arg(ap, unsigned long *)
                        = static_cast<unsigned long>(val);
                    break;
                    case RANK_LONGLONG:
                      *va_arg(ap, unsigned long long *)
                        = static_cast<unsigned long long>(val);
                    break;
                    case RANK_PTR:
                      *va_arg(ap, void **)
                        = reinterpret_cast<void *>(static_cast<uintptr_t>(val));
                    break;
                  }
                }
              break;
  
              case 'f':   // Preliminary float value parsing
              case 'g':
              case 'G':
              case 'e':
              case 'E':
                q = SkipSpace(stream);
                if (q <= 0) {
                  bail = BAIL_EOF;
                  break;
                }
  
                {
                double fval = streamtofloat(stream);
                if (!(flags & FL_SPLAT)) {
                  if (rank == RANK_INT)
                    *va_arg(ap, float *) = static_cast<float>(fval);
                  else if (rank == RANK_LONG)
                    *va_arg(ap, double *) = static_cast<double>(fval);
                  converted++;
                }
                }
              break;
  
              case 'c':               // Character
                width = (flags & FL_WIDTH) ? width : 1; // Default width == 1
                sarg = va_arg(ap, char *);
                while (width--) {
                  if ((q = BOSS_TESSERACT_fgetc(stream)) <= 0) { //original-code:fgetc(stream)) <= 0) {
                    bail = BAIL_EOF;
                    break;
                  }
                  if (!(flags & FL_SPLAT)) {
                    *sarg++ = q;
                    converted++;
                  }
                }
              break;
  
              case 's':               // String
              {
                char *sp;
                sp = sarg = va_arg(ap, char *);
                while (width--) {
                  q = BOSS_TESSERACT_fgetc(stream); //original-code:fgetc(stream);
                  if (isspace(static_cast<unsigned char>(q)) || q <= 0) {
                    BOSS_TESSERACT_ungetc(q, stream); //original-code:ungetc(q, stream);
                    break;
                  }
                  if (!(flags & FL_SPLAT)) *sp = q;
                  sp++;
                }
                if (sarg == sp) {
                  bail = BAIL_EOF;
                } else if (!(flags & FL_SPLAT)) {
                  *sp = '\0'; // Terminate output
                  converted++;
                } else {
                }
              }
              break;
  
              case '[':   // Character range
                sarg = va_arg(ap, char *);
                state = ST_MATCH_INIT;
                matchinv = 0;
                memset(matchmap, 0, sizeof matchmap);
              break;
  
              case '%':   // %% sequence
                if (BOSS_TESSERACT_fgetc(stream) != '%' ) //original-code:fgetc(stream) != '%' )
                  bail = BAIL_ERR;
              break;
  
              default:    // Anything else
                bail = BAIL_ERR;  // Unknown sequence
              break;
            }
          }
        break;
  
        case ST_MATCH_INIT:   // Initial state for %[ match
          if (ch == '^' && !(flags & FL_INV)) {
            matchinv = 1;
          } else {
            SetBit(matchmap, static_cast<unsigned char>(ch));
            state = ST_MATCH;
          }
        break;
  
        case ST_MATCH:    // Main state for %[ match
          if (ch == ']') {
            goto match_run;
          } else if (ch == '-') {
            range_start = static_cast<unsigned char>(ch);
            state = ST_MATCH_RANGE;
          } else {
            SetBit(matchmap, static_cast<unsigned char>(ch));
          }
        break;
  
        case ST_MATCH_RANGE:    // %[ match after -
          if (ch == ']') {
            SetBit(matchmap, static_cast<unsigned char>('-'));
            goto match_run;
          } else {
            int i;
            for (i = range_start ; i < (static_cast<unsigned char>(ch)) ; i++)
            SetBit(matchmap, i);
            state = ST_MATCH;
          }
        break;
  
        match_run:      // Match expression finished
          char* oarg = sarg;
          while (width) {
            q = BOSS_TESSERACT_fgetc(stream); //original-code:fgetc(stream);
            unsigned char qc = static_cast<unsigned char>(q);
            if (q <= 0 || !(TestBit(matchmap, qc)^matchinv)) {
              BOSS_TESSERACT_ungetc(q, stream); //original-code:ungetc(q, stream);
              break;
            }
            if (!(flags & FL_SPLAT)) *sarg = q;
            sarg++;
          }
          if (oarg == sarg) {
            bail = (q <= 0) ? BAIL_EOF : BAIL_ERR;
          } else if (!(flags & FL_SPLAT)) {
            *sarg = '\0';
            converted++;
          }
        break;
      }
    }
  
    if (bail == BAIL_EOF && !converted)
      converted = -1;   // Return EOF (-1)
  
    return converted;
  }
  
  #ifdef EMBEDDED
  int creat(const char *pathname, mode_t mode) {
    return open(pathname, O_CREAT | O_TRUNC | O_WRONLY, mode);
  }
  
  #endif  // EMBEDDED
  