text: /*
   * Copyright (c) 2002-2006 The Regents of The University of Michigan
   * All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions are
   * met: redistributions of source code must retain the above copyright
   * notice, this list of conditions and the following disclaimer;
   * redistributions in binary form must reproduce the above copyright
   * notice, this list of conditions and the following disclaimer in the
   * documentation and/or other materials provided with the distribution;
   * neither the name of the copyright holders nor the names of its
   * contributors may be used to endorse or promote products derived from
   * this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
   * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   *
   * Authors: Nathan Binkert
   */
  
  #include <cassert>
  #include <iomanip>
  #include <iostream>
  #include <sstream>
  
  #include "base/cprintf.hh"
  
  using namespace std;
  
  namespace cp {
  
  Print::Print(std::ostream &stream, const std::string &format)
      : stream(stream), format(format.c_str()), ptr(format.c_str()), cont(false)
  {
      saved_flags = stream.flags();
      saved_fill = stream.fill();
      saved_precision = stream.precision();
  }
  
  Print::Print(std::ostream &stream, const char *format)
      : stream(stream), format(format), ptr(format), cont(false)
  {
      saved_flags = stream.flags();
      saved_fill = stream.fill();
      saved_precision = stream.precision();
  }
  
  Print::~Print()
  {
  }
  
  void
  Print::process()
  {
      fmt.clear();
  
      size_t len;
  
      while (*ptr) {
          switch (*ptr) {
            case '%':
              if (ptr[1] != '%') {
                  process_flag();
                  return;
              }
              stream.put('%');
              ptr += 2;
              break;
  
            case '\n':
              stream << endl;
              ++ptr;
              break;
            case '\r':
              ++ptr;
              if (*ptr != '\n')
                  stream << endl;
              break;
  
            default:
              len = strcspn(ptr, "%\n\r\0");
              stream.write(ptr, len);
              ptr += len;
              break;
          }
      }
  }
  
  void
  Print::process_flag()
  {
      bool done = false;
      bool end_number = false;
      bool have_precision = false;
      int number = 0;
  
      stream.fill(' ');
      stream.flags((ios::fmtflags)0);
  
      while (!done) {
          ++ptr;
          if (*ptr >= '0' && *ptr <= '9') {
              if (end_number)
                  continue;
          } else if (number > 0)
              end_number = true;
  
          switch (*ptr) {
            case 's':
              fmt.format = Format::string;
              done = true;
              break;
  
            case 'c':
              fmt.format = Format::character;
              done = true;
              break;
  
            case 'l':
              continue;
  
            case 'p':
              fmt.format = Format::integer;
              fmt.base = Format::hex;
              fmt.alternate_form = true;
              done = true;
              break;
  
            case 'X':
              fmt.uppercase = true;
            case 'x':
              fmt.base = Format::hex;
              fmt.format = Format::integer;
              done = true;
              break;
  
            case 'o':
              fmt.base = Format::oct;
              fmt.format = Format::integer;
              done = true;
              break;
  
            case 'd':
            case 'i':
            case 'u':
              fmt.format = Format::integer;
              done = true;
              break;
  
            case 'G':
              fmt.uppercase = true;
            case 'g':
              fmt.format = Format::floating;
              fmt.float_format = Format::best;
              done = true;
              break;
  
            case 'E':
              fmt.uppercase = true;
            case 'e':
              fmt.format = Format::floating;
              fmt.float_format = Format::scientific;
              done = true;
              break;
  
            case 'f':
              fmt.format = Format::floating;
              fmt.float_format = Format::fixed;
              done = true;
              break;
  
            case 'n':
              stream << "we don't do %n!!!\n";
              done = true;
              break;
  
            case '#':
              fmt.alternate_form = true;
              break;
  
            case '-':
              fmt.flush_left = true;
              break;
  
            case '+':
              fmt.print_sign = true;
              break;
  
            case ' ':
              fmt.blank_space = true;
              break;
  
            case '.':
              fmt.width = number;
              fmt.precision = 0;
              have_precision = true;
              number = 0;
              end_number = false;
              break;
  
            case '0':
              if (number == 0) {
                  fmt.fill_zero = true;
                  break;
              }
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
              number = number * 10 + (*ptr - '0');
              break;
  
            case '*':
              if (have_precision)
                  fmt.get_precision = true;
              else
                  fmt.get_width = true;
              break;
  
            case '%':
              assert(false && "we shouldn't get here");
              break;
  
            default:
              done = true;
              break;
          }
  
          if (end_number) {
              if (have_precision)
                  fmt.precision = number;
              else
                  fmt.width = number;
  
              end_number = false;
              number = 0;
          }
  
          if (done) {
              if ((fmt.format == Format::integer) && have_precision) {
                  // specified a . but not a float, set width
                  fmt.width = fmt.precision;
                  // precision requries digits for width, must fill with 0
                  fmt.fill_zero = true;
              } else if ((fmt.format == Format::floating) && !have_precision &&
                          fmt.fill_zero) {
                  // ambiguous case, matching printf
                  fmt.precision = fmt.width;
              }
          }
      } // end while
  
      ++ptr;
  }
  
  void
  Print::end_args()
  {
      size_t len;
  
      while (*ptr) {
          switch (*ptr) {
            case '%':
              if (ptr[1] != '%')
                  stream << "<extra arg>";
  
              stream.put('%');
              ptr += 2;
              break;
  
            case '\n':
              stream << endl;
              ++ptr;
              break;
            case '\r':
              ++ptr;
              if (*ptr != '\n')
                  stream << endl;
              break;
  
            default:
              len = strcspn(ptr, "%\n\r\0");
              stream.write(ptr, len);
              ptr += len;
              break;
          }
      }
  
      stream.flags(saved_flags);
      stream.fill(saved_fill);
      stream.precision(saved_precision);
  }
  
  } // namespace cp
  