/*  Copyright (C) 2004 Garrett A. Kajmowicz
 *
 * This file is part of the uClibc++ Library.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <ios>
#include <cctype>
#include <ctype.h>

#include <string>

#ifndef __STD_HEADER_ISTREAM_HELPERS
#define __STD_HEADER_ISTREAM_HELPERS 1

#pragma GCC visibility push(default)

extern "C++"
{
namespace std
{
  /* We are making the following template class for serveral reasons.  Firstly,
   * we want to keep the main istream code neat and tidy.  Secondly, we want it
   * to be easy to do partial specialization of the istream code so that it can
   * be expanded and put into the library.  This will allow us to make application
   * code smaller at the expense of increased library size.  This is a fair
   * trade-off when there are multiple applications being compiled.  Also, this
   * feature will be used optionally via configuration options.  It will also
   * allow us to keep the code bases in sync, dramatically simplifying the
   * maintenance required.  We specialized for char because wchar and others
   * require different scanf functions
   */

  template <class C, class traits> _UCXXEXPORT
    basic_string<C, traits> _readToken(basic_istream<C, traits>& stream)
  {
    basic_string<C, traits> temp;
    typename traits::int_type c;
    while (true)
    {
      c = stream.rdbuf()->sgetc();
      if (c != traits::eof() && isspace(c) == false)
      {
        stream.rdbuf()->sbumpc();
        temp.append(1, traits::to_char_type(c));
      }
      else
      {
        break;
      }
    }

    if (temp.size() == 0)
    {
      stream.setstate(ios_base::eofbit|ios_base::failbit);
    }

    return temp;
  }

  template <class C, class traits> _UCXXEXPORT
    basic_string<C, traits> _readTokenDecimal(basic_istream<C, traits>& stream)
  {
    basic_string<C, traits> temp;
    typename traits::int_type c;
    while (true)
    {
      c = stream.rdbuf()->sgetc();
      if (c != traits::eof() && isspace(c) == false && (
        isdigit(c) ||
        c == '.' ||
        c == ',' ||
        ((c == '-' || c == '+') && temp.size() == 0)))
      {
        stream.rdbuf()->sbumpc();
        temp.append(1, traits::to_char_type(c));
      }
      else
      {
        break;
      }
    }

    if (temp.size() == 0)
    {
      stream.setstate(ios_base::eofbit|ios_base::failbit);
    }

    return temp;
  }

#ifdef __UCLIBCXX_EXPAND_ISTREAM_CHAR__
  template <> _UCXXEXPORT string _readToken<char, char_traits<char> >(istream & stream);
#endif

  template <class traits, class charT, class dataType> class _UCXXEXPORT __istream_readin
  {
  public:
    static void readin(basic_istream<charT,traits>& stream, dataType & var);
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, bool>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, bool & var)
    {
      /* 22.4.2.1.2.4 */

      basic_string<char, traits > temp;
      temp = _readToken(stream);
      if (stream.flags() & ios_base::boolalpha)
      {
        if (temp == "true") // truename()
        {
          var = true;
        }
        else
        {
          var = false;
          if (temp != "false") // falsename()
          {
            stream.setstate(ios_base::failbit);
          }
        }
      }
      else
      {
        long int i = 0;
        int ret;
        if (stream.flags() & ios_base::dec)
        {
          ret = sscanf(temp.c_str(), "%ld", &i);
        }
        else
        {
          if (stream.flags() & ios_base::oct)
          {
            ret = sscanf(temp.c_str(), "%lo", (unsigned long int *)(&i));
          }
          else if (stream.flags() & ios_base::hex)
          {
            if (stream.flags() & ios_base::uppercase)
            {
              ret = sscanf(temp.c_str(), "%lX", (unsigned long int *)(&i));
            }
            else
            {
              ret = sscanf(temp.c_str(), "%lx", (unsigned long int *)(&i));
            }
          }
          else
          {
            ret = sscanf(temp.c_str(), "%li", &i);
          }
        }

        if (ret != 1 || i >> 1)
        {
          stream.setstate(ios_base::failbit);
        }

        var = ret == 1 && bool(i);
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, short>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, short & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%hd", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%ho", (unsigned short int *)(&var));
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%hX", (unsigned short int *)(&var));
          }
          else
          {
            sscanf(temp.c_str(), "%hx", (unsigned short int *)(&var));
          }
        }
        else
        {
          sscanf(temp.c_str(), "%hi", &var);
        }
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, unsigned short>{
  public:
    inline static void readin(basic_istream<char, traits >& stream, unsigned short & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%hu", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%ho", &var);
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%hX", &var);
          }
          else
          {
            sscanf(temp.c_str(), "%hx", &var);
          }
        }
        else
        {
          sscanf(temp.c_str(), "%hi", (signed short int*)(&var));
        }
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, int>{
  public:
    inline static void readin(basic_istream<char, traits >& stream, int & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%d", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%o", (unsigned int *)(&var));
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%X", (unsigned int *)(&var));
          }
          else
          {
            sscanf(temp.c_str(), "%x", (unsigned int *)(&var));
          }
        }
        else
        {
          sscanf(temp.c_str(), "%i", &var);
        }
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, unsigned int>{
  public:
    inline static void readin(basic_istream<char, traits >& stream, unsigned int & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%u", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%o", (unsigned int *)(&var));
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%X", (unsigned int *)(&var));
          }
          else
          {
            sscanf(temp.c_str(), "%x", (unsigned int *)(&var));
          }
        }
        else
        {
          sscanf(temp.c_str(), "%i", (int *)(&var));
        }
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, long int>{
  public:
    inline static void readin(basic_istream<char, traits >& stream, long int & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%ld", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%lo", (unsigned long int *)(&var));
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%lX", (unsigned long int *)(&var));
          }
          else
          {
            sscanf(temp.c_str(), "%lx", (unsigned long int *)(&var));
          }
        }
        else
        {
          sscanf(temp.c_str(), "%li", (long int *)(&var));
        }
      }
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, unsigned long int>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, unsigned long int & var)
    {
      basic_string<char, traits > temp;

      if (stream.flags() & ios_base::dec)
      {
        temp = _readTokenDecimal(stream);
        sscanf(temp.c_str(), "%lu", &var);
      }
      else
      {
        temp = _readToken(stream);
        if (stream.flags() & ios_base::oct)
        {
          sscanf(temp.c_str(), "%lo", &var);
        }
        else if (stream.flags() & ios_base::hex)
        {
          if (stream.flags() & ios_base::uppercase)
          {
            sscanf(temp.c_str(), "%lX", &var);
          }
          else
          {
            sscanf(temp.c_str(), "%lx", &var);
          }
        }
        else
        {
          sscanf(temp.c_str(), "%li", (long int *)(&var));
        }
      }
    }
  };

#ifdef CONFIG_HAVE_FLOAT

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, float>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, float & var)
    {
      basic_string<char, traits > temp;
      temp = _readTokenDecimal(stream);

      sscanf(temp.c_str(), "%g", &var);
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, double>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, double & var)
    {
      basic_string<char, traits > temp;
      temp = _readTokenDecimal(stream);
      sscanf(temp.c_str(), "%lg", &var);
    }
  };

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, long double>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, long double & var)
    {
      basic_string<char, traits > temp;
      temp = _readTokenDecimal(stream);
      sscanf(temp.c_str(), "%Lg", &var);
    }
  };

#endif  // ifdef CONFIG_HAVE_FLOAT

  template <class traits> class _UCXXEXPORT __istream_readin<traits, char, void*>
  {
  public:
    inline static void readin(basic_istream<char, traits >& stream, void* & var)
    {
      basic_string<char, traits > temp;
      temp = _readToken(stream);
      sscanf(temp.c_str(), "%p", &var);
    }
  };

  template<class charT, class traits> void __skipws(basic_istream<charT,traits>& is)
  {
    const typename basic_istream<charT,traits>::int_type eof = traits::eof();
    typename basic_istream<charT,traits>::int_type c;

    // While the next character normally read doesn't equal eof
    // and that character is a space, advance to the next read position
    // Thus itterating through all whitespace until we get to the meaty stuff

    while (
      !traits::eq_int_type((c = is.rdbuf()->sgetc()),  eof)
      && isspace(c))
    {
      is.rdbuf()->sbumpc();
    }

    if (traits::eq_int_type(c, eof))
    {
      is.setstate(ios_base::eofbit);
    }
  }

} // namespace
} // extern "C++"

#pragma GCC visibility pop

#endif



