/*
* File:        ElftosbLexer.cpp
*
* Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
* See included license file for license details.
*/
#include "ElftosbLexer.h"
#include <algorithm>
#include "HexValues.h"

using namespace elftosb;

ElftosbLexer::ElftosbLexer(istream & inputStream)
:       yyFlexLexer(&inputStream), m_line(1), m_blob(0), m_blobFirstLine(0)
{
}

void ElftosbLexer::getSymbolValue(YYSTYPE * value)
{
       if (!value)
       {
               return;
       }
       *value = m_symbolValue;
}

void ElftosbLexer::addSourceName(std::string * ident)
{
       m_sources.push_back(*ident);
}

bool ElftosbLexer::isSourceName(std::string * ident)
{
       string_vector_t::iterator it = find(m_sources.begin(), m_sources.end(), *ident);
       return it != m_sources.end();
}

void ElftosbLexer::LexerError(const char * msg)
{
       throw elftosb::lexical_error(msg);
}

//! Reads the \a in string and writes to the \a out string. These strings can be the same
//! string since the read head is always in front of the write head.
//!
//! \param[in] in Input string containing C-style escape sequences.
//! \param[out] out Output string. All escape sequences in the input string have been converted
//!             to the actual characters. May point to the same string as \a in.
//! \return The length of the resulting \a out string. This length is necessary because
//!             the string may have contained escape sequences that inserted null characters.
int ElftosbLexer::processStringEscapes(const char * in, char * out)
{
       int count = 0;
       while (*in)
       {
               switch (*in)
               {
                       case '\\':
                       {
                               // start of an escape sequence
                               char c = *++in;
                               switch (c)
                               {
                                       case 0: // end of the string, bail
                                               break;
                                       case 'x':
                                       {
                                               // start of a hex char escape sequence

                                               // read high and low nibbles, checking for end of string
                                               char hi = *++in;
                                               if (hi == 0) break;
                                               char lo = *++in;
                                               if (lo == 0) break;

                                               if (isHexDigit(hi) && isHexDigit(lo))
                                               {
                                                       if (hi >= '0' && hi <= '9')
                                                               c = (hi - '0') << 4;
                                                       else if (hi >= 'A' && hi <= 'F')
                                                               c = (hi - 'A' + 10) << 4;
                                                       else if (hi >= 'a' && hi <= 'f')
                                                               c = (hi - 'a' + 10) << 4;

                                                       if (lo >= '0' && lo <= '9')
                                                               c |= lo - '0';
                                                       else if (lo >= 'A' && lo <= 'F')
                                                               c |= lo - 'A' + 10;
                                                       else if (lo >= 'a' && lo <= 'f')
                                                               c |= lo - 'a' + 10;

                                                       *out++ = c;
                                                       count++;
                                               }
                                               else
                                               {
                                                       // not hex digits, the \x must have wanted an 'x' char
                                                       *out++ = 'x';
                                                       *out++ = hi;
                                                       *out++ = lo;
                                                       count += 3;
                                               }
                                               break;
                                       }
                                       case 'n':
                                               *out++ = '\n';
                                               count++;
                                               break;
                                       case 't':
                                               *out++ = '\t';
                                               count++;
                                               break;
                                       case 'r':
                                               *out++ = '\r';
                                               count++;
                                               break;
                                       case 'b':
                                               *out++ = '\b';
                                               count++;
                                               break;
                                       case 'f':
                                               *out++ = '\f';
                                               count++;
                                               break;
                                       case '0':
                                               *out++ = '\0';
                                               count++;
                                               break;
                                       default:
                                               *out++ = c;
                                               count++;
                                               break;
                               }
                               break;
                       }

                       default:
                               // copy all other chars directly
                               *out++ = *in++;
                               count++;
               }
       }

       // place terminating null char on output
       *out = 0;
       return count;
}