/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2006  Gaspar Sinai <gaspar@yudit.org>  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License, version 2,
 *  dated June 1991. See file COPYYING for details.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 

#include "ProcessFile.h"
#include "stoolkit/SBMap.h"
#include "stoolkit/SString.h"
#include "stoolkit/SIOStream.h"
#include "stoolkit/SExcept.h"
#include <string.h>
#include <stdlib.h>

/* sscanf */
#include <stdio.h>

#if HAVE_STRTOL==0
#error This module needs strtol.
#endif


// We will process lines with this length
#define LINE_LENGTH 1024
#define TOK_NAME   "NAME="
#define TOK_COMM   "COMM="
#define TOK_TYPE   "TYPE="
#define TOK_ENCODE   "ENCODE="
#define TOK_KEYW   "KEY_WIDTH="
#define TOK_VALUEW   "VALUE_WIDTH="
#define TOK_KEYL   "KEY_LENGTH="
#define TOK_VALUEL   "VALUE_LENGTH="
#define TOK_SECTION   "SECTION="

#define TOK_MAX_SEGMENTS  5

// The maximum length of key and value.
#define TOK_STATE_SIZE    32

/**
 * Prorcess a UNI file. Columns are separated by tabs or white spaces.
 * @param filename is the name of the iput file.
 * @param file is the input stream. It should be open, and it will not
 *        be closed at the end.
 * @param keysize is 0 for 8bit and 1 for 16 bit keys
 * @param hilo is 1 if we need to or local codes  with 0x8080 
 *        -1 if we need to and local codes with 0x7f7f.
 * @param from is the column from which coding happen.
 * @parem to is the column where the coded value is
 * @param reverse is nonzero to show that a reverse map should be built
 *        If reverse has been specified the role changes, so the same 
 *        from and to parameters result in a reverse (encoding)  map.
 * @param comment will hold the comments if any.
 * @param name will be the name (if parsed)
 * @param type will be the type (if parsed)
 */
SBMapItemVector*
processUNI (const char* filename,  SInputStream& _fl, int nocomment, int keysize, int hilo, int from, int to, int reverse, char* comment, char*name, int* type)
{
  int    commentSize=0;
  int    len;
  SBMapItemVector*  retVle = new SBMapItemVector();
  int    i;
  int    empty;
  int    section=-1;
  char    line [LINE_LENGTH+1];

  char*    chr;
  int    lineNo =0;
  int    keyw=keysize;
  int    valuew=1;
  int    keyl=0;
  int    valuel=0;
  char    sectName [LINE_LENGTH+1];
  char    newLine [LINE_LENGTH+24];
  int    sectCommSize;
  int    posKey=0;
  int    posVle=0;
  int    parsedKey;
  int    parsedVle;
  int    lend;
  int    status;

  strncpy (name, filename, 32);
  int  count = 0;
  int  pos;
  *type = 1;

  strcpy (sectName, name);
  sectCommSize =0;

        SStringVector ssl("\r\n,\n,\r");
        SReader r(_fl, ssl);
  
  while (true)
  {
    SString ss;
                if (!r.read(&ss)) break;
    if (ss.size() <= 0) break;
                ssl.trim (&ss);
                char* cstring = ss.cString();
    strncpy (line, cstring, LINE_LENGTH);

    lineNo++;
    line[LINE_LENGTH] = 0;
    len = strlen (line);
    empty = 1;
    pos = 0;
    posKey=-1;
    posVle=-1;
    for (i=0; i<len; i++)
    {
      if (line[i] == '\n' || line[i] == '#') break;
      if (line[i] == ' ' || line[i] == '\t') continue;
      empty = 0;
      if (i==0  || line[i-1] == ' ' || line[i-1] == '\t')
      {
        if (pos == from) posKey = i;
        if (pos == to) posVle = i;
        pos++;
      }
    }
    lend=i;

    // Get the header comment.
    if (line[0]=='#')
    {
      len = strlen (line);
      if (section==-1)
      {
        if (nocomment || len + commentSize +1 >= MAX_COMMENT_SIZE)
        {
          continue;
        }
        if (commentSize==0)
        {
          strcpy (comment, &line[1]);
        }
        else
        {
          strcat (comment, "\n");
          strcat (comment, &line[1]);
        }
        commentSize += len;
      }
      continue;
    }

    if (empty) continue;

    if (section == -1)
    {
      section =0;
      if (reverse)
      {
        retVle->maps[section] = new SBMapItem (
          reverse,
          (const unsigned char*) sectName, 
          (const unsigned char*) newLine,
          sectCommSize, valuew, keyw, valuel, keyl);
        
      }
      else
      {
        retVle->maps[section] = new SBMapItem (
          reverse,
          (const unsigned char*) sectName, 
          (const unsigned char*) newLine,
          sectCommSize, keyw, valuew, keyl, valuel);
        
      }
      retVle->count++;
    }
    count++;

    if (posKey==-1 || posVle==-1)
    {
      fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
        filename, lineNo);
      continue;
    }
    if (line[posKey] == '0' && 
      (line[posKey+1] == 'x' || line[posKey+1] == 'X'))
    {
      posKey++; posKey++;
    }
    if (line[posVle] == '0' && 
      (line[posVle+1] == 'x' || line[posVle+1] == 'X'))
    {
      posVle++; posVle++;
    }
    parsedKey = strtol (&line[posKey], &chr, 16);
    if (hilo==1)
    {
      parsedKey |= ((keysize==0) ? 0x80 : 0x8080);
    }
    if (hilo==-1)
    {
      parsedKey &= 0x7f7f;
    }

    parsedVle = strtol (&line[posVle], &chr, 16);

    if ((keysize==0 && parsedKey > 255) || parsedKey > 0xffff ||
      parsedKey < 0 || parsedVle > 0xffff || parsedVle < 0)
    {
      fprintf (stderr, "mytool: file %s at line %d  has some errors - check -8 option!. key=%d, vle=%d\n",
      filename, lineNo, parsedKey, parsedVle);
      continue;
    }

    // Compose a new line - parsedKey parsedVle
    if (keysize==0)
    {
      if (reverse)
      {
        sprintf (newLine, "%04X -> %02X ", parsedVle, parsedKey);
      }
      else
      {
        sprintf (newLine, "%02X -> %04X ", parsedKey, parsedVle);
      }  
    }
    else
    {
      if (reverse)
      {
        sprintf (newLine, "%04X -> %04X ", parsedVle, parsedKey);
      }
      else
      {
        sprintf (newLine, "%04X -> %04X ", parsedKey, parsedVle);
      }  
    }
    if (!reverse && line[lend]=='#' && !nocomment)
    {
      strcat (newLine, &line[lend]);
    }

//cerr << "ADD: " << newLine << "\n";
    status = retVle->maps[section]->addLine (
      (const unsigned char*) newLine, strlen (newLine),
      0);

    if (status<0)
    {
      fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
        filename, lineNo);
      continue;
    }
  }
  return retVle;
}

/**
 * Prorcess a MYS  file. "local code -> unicode"
 * @param filename is the name of the iput file.
 * @param file is the input stream. It should be open, and it will not
 *        be closed at the end.
 * @param reverse is nonzero to show that a reverse map should be built
 *        If reverse has been specified the role changes, so the same 
 *        from and to parameters result in a reverse (encoding)  map.
 * @param comment will hold the comments if any.
 * @param name will be the name (if parsed)
 * @param type will be the type (if parsed)
 */
SBMapItemVector*
processMYS (const char* filename, SInputStream& _fl, int nocomment, int reverse, char* comment, char*name, int* type)
{
  int    commentSize=0;
  int    len=0;
  SBMapItemVector*  retVle = new SBMapItemVector();
  int    i=0;
  int    empty;
  int    section=-1;
  int     keyw=0;
  int     valuew=1;
  int     keyl=0;
  int     valuel=0;
  int    count=0;
  char*    chr=0;
  int    lineNo=0;
  int    status=0;
  int    encode=0;

  char    line [LINE_LENGTH+1];
  char    sectName [LINE_LENGTH+1];
  char    sectComm [LINE_LENGTH+1];
  int    sectCommSize=0;

  SStringVector ssl("\r\n,\n,\r");
  SReader r(_fl, ssl);
  
  while (true)
  {
    SString ss;
                if (!r.read(&ss)) break;
    if (ss.size() <= 0) break;
                ssl.trim (&ss);
                char* cstring = ss.cString();
    strncpy (line, cstring, LINE_LENGTH);

    lineNo++;
    line[LINE_LENGTH] = 0;
    len = strlen (line);
    empty = 1;
    for (i=0; i<len; i++)
    {
      if (line[i] == '\n' || line[i] == '#') break;
      if (line[i] != ' ' && line[i] != '\t') empty = 0;
    }
    if (empty) continue;

    //
    // Process the header
    //
    if (section == -1 && strncmp (TOK_NAME, line, strlen(TOK_NAME)) == 0)
    {
      strncpy (name, &line[strlen(TOK_NAME)], 32);
      continue;
    }
    if (section == -1 && strncmp (TOK_COMM, line, strlen(TOK_COMM)) == 0)
    {
      if (nocomment || len + commentSize +1  >= MAX_COMMENT_SIZE)
      {
        continue;
      }
      if (commentSize==0)
      {
        strcpy (comment, &line[strlen(TOK_COMM)]);
      }
      else
      {
        strcat (comment, "\n");
        strcat (comment, &line[strlen(TOK_COMM)]);
      }
      commentSize+=len-strlen(TOK_COMM) +1;
      continue;
    }
    if (section == -1 && strncmp (TOK_TYPE, line, strlen(TOK_TYPE)) == 0)
    {
      *type = strtol (&line[strlen(TOK_TYPE)], &chr, 16);
      continue;
    }


    //
    // Process the MapItems here.
    //
    if (strncmp (TOK_SECTION, line, strlen(TOK_SECTION)) == 0)
    {
      strcpy (sectName, &line[strlen(TOK_SECTION)]);
      keyw=0;
      valuew=1;
      keyl=0;
      valuel=0;
      encode = 0;
      section++;
      count=0;
      sectCommSize=0;
      continue;
    }

    if (section == -1)
    {
      fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
        filename, lineNo);
      continue;
    }

    if (strncmp (TOK_COMM, line, strlen(TOK_COMM)) == 0)
    {
      if (nocomment || len + sectCommSize + 1 >= MAX_COMMENT_SIZE)
      {
        continue;
      }
      if (sectCommSize==0)
      {
        strcpy (sectComm, &line[strlen(TOK_COMM)]);
      }
      else
      {
        strcat (sectComm, "\n");
        strcat (sectComm, &line[strlen(TOK_COMM)]);
      }
      sectCommSize+=len-strlen(TOK_COMM) +1;
      continue;
    }
    if (strncmp (TOK_KEYW, line, strlen(TOK_KEYW)) == 0)
    {
      keyw = strtol (&line[strlen(TOK_KEYW)], &chr, 16);
      if (count != 0 || keyw<0 || keyw>3)
      {
        fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
          filename, lineNo);
        continue;
      }
      continue;
    }
    if (strncmp (TOK_VALUEW, line, strlen(TOK_VALUEW)) == 0)
    {
      valuew = strtol (&line[strlen(TOK_VALUEW)], &chr, 16);
      if (count != 0 || valuew<0 || valuew>3)
      {
        fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
         filename, lineNo);
        continue;
      }
      continue;
    }
    if (strncmp (TOK_KEYL, line, strlen(TOK_KEYL)) == 0)
    {
      keyl = strtol (&line[strlen(TOK_KEYL)], &chr, 16);
      if (count != 0 || keyl<0 || keyl>3)
      {
        fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
          filename, lineNo);
        continue;
      }
      continue;
    }
    if (strncmp (TOK_VALUEL, line, strlen(TOK_VALUEL)) == 0)
    {
      valuel = strtol (&line[strlen(TOK_VALUEL)], &chr, 16);
      if (count != 0 || valuel<0 || valuel>3)
      {
        fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
         filename, lineNo);
        continue;
      }
      continue;
    }
    if (strncmp (TOK_ENCODE, line, strlen(TOK_ENCODE)) == 0)
    {
      encode = strtol (&line[strlen(TOK_ENCODE)], &chr, 16);
      if (count != 0 || encode<0 || encode>3)
      {
        fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
        filename, lineNo);
        continue;
      }
      continue;
    }

    if (retVle->count >= MAX_NMAPS)
    {
      fprintf (stderr, "mytool: file %s has too many sections\n", filename);
      delete retVle;
      return 0;
    }
    // Finally
    if (count == 0)
    {
      if (reverse) encode = ! encode;

      if (reverse)
      {
        retVle->maps[section] = new SBMapItem (
          encode,
          (const unsigned char*) sectName, 
          (const unsigned char*) sectComm,
          sectCommSize, valuew, keyw, valuel, keyl);
        
      }
      else
      {
        retVle->maps[section] = new SBMapItem (
          encode,
          (const unsigned char*) sectName, 
          (const unsigned char*) sectComm,
          sectCommSize, keyw, valuew, keyl, valuel);
        
      }
      retVle->count++;
    }
    count++;

    status = retVle->maps[section]->addLine (
      (const unsigned char*) line, strlen (line),
      reverse);

    if (status<0)
    {
      fprintf (stderr, "mytool: file %s at line %d has some errors.\n", 
        filename, lineNo);
      continue;
    }
  }
  return retVle;
}

/**
 * Prorcess a KMAP  file. "lcoal code =  unicode"
 * This came from UInput.cpp making that code obselete.
 * @param filename is the name of the iput file.
 * @param file is the input stream. It should be open, and it will not
 *        be closed at the end.
 * @param reverse is nonzero to show that a reverse map should be built
 *        If reverse has been specified the role changes, so the same 
 *        from and to parameters result in a reverse (encoding)  map.
 * @param comment will hold the comments if any.
 * @param name will be the name (if parsed)
 * @param type will be the type (if parsed)
 */
SBMapItemVector*
processKMap (const char* filename, SInputStream& _fl, int nocomment, int reverse, char* comment, char*name, int* type)
{  
  char    line [LINE_LENGTH+1];
  char    quoted [LINE_LENGTH+1];
  int    lineNo;
  int    error;
  char    *from, *to;
  int    iscomment;
  //SGC
  //int    empty;
  int    index;
  int    i;
  int    currentSegment;
  int    segmentCount;
  char    segments[TOK_MAX_SEGMENTS+1][LINE_LENGTH+1];
  char    segmentId[LINE_LENGTH+1];
  int    startingComment;
  int    commentSize;
  int    len;
  char*    lineComment;
  int    lineCommentSize;
  SBMapItemVector*  retVle = 0;
  const char*     segmentArray[TOK_MAX_SEGMENTS];
  int    getNewLine;


  lineNo = 0;
  error = 0;
  iscomment = 0;
  currentSegment = 0;

  segmentCount = 0;

  startingComment = 1;
  commentSize = 0;
  *type = 1;
  strncpy (name, filename, 32);

        SStringVector ssl("\r\n,\n,\r");
        SReader r(_fl, ssl);
  
  while (true)
  {
    SString ss;
                if (!r.read(&ss)) break;
    if (ss.size() <= 0) break;
                ssl.trim (&ss);
                char* cstring = ss.cString();
    strncpy (line, cstring, LINE_LENGTH);
    // stoolkit parser, only '//' comments are OK at the
    // beginning of the line
    line[LINE_LENGTH]=0;
    lineNo++;
    to = line;
    from= line;
    index = 0;
    getNewLine = 0;

    // Rewrite the buffer into the quoted array.
    // All // comments at the beginning are copied to comment.
    while (to[0] != 0)
    {
      if (iscomment)
      {
        if (to[0]=='*' && to[1] == '/')
        {
          to++; 
          iscomment = 0;
        }
        to++; 
        continue;
      }
      if (to[0]=='/' && to[1] == '*')
      {
        to++; to++;
        iscomment = 1;
        continue;
      }
      if (to[0]=='/' && to[1] == '/')
      {
        getNewLine = 1;
        len = strlen (to);
        if (startingComment)
        {
          if (nocomment || len + commentSize +1 > MAX_COMMENT_SIZE)
          {
            break;
          }
          if (commentSize==0)
          {
            strcpy (comment, &to[2]);
          }
          else
          {
            strcat (comment, "\n");
            strcat (comment, &to[2]);
          }
          commentSize += len-2 +1;
        }
        break;
      }

      startingComment = 0;

      if (from == line && to[0] != '"')
      {
        to++; continue;
      }
      if (from == line && to[0] == '"')
      {
        to++;
        from = to;
        continue;
      }
      if (to[0] == '\\')
      {
        switch (to[1])
        {
        case 't':
          quoted[index++] = '\t';
          to++;
          break;
        case '\\':
          quoted[index++] = '\\';
          to++; 
          break;
        case '"':
          quoted[index++] = '"';
          to++; 
          break;
        case '0':
        case '1':
        case '2':
        case '3':
          if (to[2] < '0' || to[2] >'7'
           || to[3] < '0' || to[3] > '7')
          {
            quoted[index++] = '\\';
            break;
          }
          quoted[index++] = ((to[1]-'0') << 6)
                  + ((to[2]-'0') << 3)
            + to[3]-'0';
          to++; to++; to++;
          break;
        }
        to++;
        continue;
      }
      if (to[0] == '"')
      {
        break;
      }
      quoted[index++] = to[0];
      to++;
    }
    if (getNewLine==1) continue;

    if (index == 0)
    {
      if (from != line)
      {
        error=1;
        break;
      }
      continue;
    }
    quoted[index] = 0;

    // Ending comment
    lineComment = 0;
    lineCommentSize = 0;
    while (to[0]!=0)
    {
      if (to[0]=='/' && to[1] == '/')
      {
        lineComment = &to[2];
        lineCommentSize = strlen (lineComment);  
        break;
      }
      to++;
    }

    // Ugly but it works.
    if (TOK_MAX_SEGMENTS != 5)
    {
      error=1 ;
      break;
    }

    if (retVle == 0)
    {
      if (strstr (quoted, "=") == 0)
      {
        segmentCount = sscanf (quoted, 
          "%[^+]+%[^+]+%[^+]+%[^+]+%[^+]+%[^+]",
          segments[0], segments[1],
          segments[2], segments[3],
          segments[4], segments[5]);
        if (segmentCount==0 && segmentCount > 5)
        {
          error=1;
          break;
        }
        segmentArray[0] = segments[0];
        segmentArray[1] = segments[1];
        segmentArray[2] = segments[2];
        segmentArray[3] = segments[3];
        segmentArray[4] = segments[4];
        currentSegment = -1;
        retVle = new SBMapItemVector(
          (int) segmentCount,
          (const char**) &segmentArray[0],
          reverse);

        CHECK_NEW (retVle);
        continue;
      }
      else
      {
        currentSegment = 0;
        segmentCount = 0;
        segmentArray[0] = "Section1";
        retVle = new SBMapItemVector(
          (int)1, (const char**) &segmentArray[0], 
          reverse);
        CHECK_NEW (retVle);
      }
    }

    if (currentSegment==-1)
    {
      for (i=0; i<segmentCount; i++)
      {
        strcpy (segmentId, "begin ");
        strcat (segmentId, segments[i]);

        if (strcmp (quoted, segmentId)==0)
        {
          strcpy (segmentId, "end ");
          strcat (segmentId, segments[i]);
          currentSegment = i;
          break;
        }
      }
      if (currentSegment==-1)
      {
        error=1;
        break;
      }
      continue;
    }
    if (segmentCount != 0)
    {
      if (strcmp (quoted, segmentId) == 0)
      {
        currentSegment=-1;
        continue;
      }
    }
    if (retVle->addKLine (currentSegment, quoted, 
      (const unsigned char*) lineComment, lineCommentSize, reverse)<0)
    {
      error=1;
      break;
    }
  }
  if (error)
  {
    fprintf (stderr, "mytool: error- bad syntax in file '%s' at line %d\n",
      (filename==0?"unkown":filename), lineNo);
    delete retVle;
    return 0;
  }
  // SGC
  // empty = 0;
  for (i=0; i<segmentCount || i==0; i++)
  {
    if (retVle->maps[i]->codeSize==0) 
    {
      if (segmentCount==0)
      {
       fprintf (stderr, "mytool: error- bad syntax in file '%s' at eof\n",
        (filename==0?"unkown":filename));
      }
      else if (currentSegment!=-1)
      {
       fprintf (stderr, "mytool: bad syntax (missing end?) in file '%s' at eof\n",
        (filename==0?"unkown":filename));
      }
      else
      {
       fprintf (stderr, "mytool: error- bad syntax in file '%s' at multipart '%s'\n",
        (filename==0?"unkown":filename), segments[i]);
      }
      delete retVle;
      return 0;
    }
  }
  return retVle;
}

SBMapItemVector::SBMapItemVector()
{
  int  i;
  for (i=0; i<MAX_NMAPS; i++)
  {
    maps[i] = 0;
  }
  count = 0;
}

SBMapItemVector::SBMapItemVector(int _size, const char* _names[], int _reverse)
{
  int  i;
  for (i=0; i<MAX_NMAPS; i++)
  {
    maps[i] = 0;
  }
  count = _size;
  for (i=0; i<count; i++)
  {
    if (_reverse)
    {
      maps [i] = new SBMapItem (
        _reverse,
        (const unsigned char*) _names[i], 
        (const unsigned char*) "",
        0, 1, 0, 0, 0);
    }
    else
    {
      maps [i] = new SBMapItem (
        _reverse,
        (const unsigned char*) _names[i], 
        (const unsigned char*) "",
        0, 0, 1, 0, 0);
    }
  }
}

SBMapItemVector::~SBMapItemVector()
{
  int  i;
  for (i=0; i<count; i++)
  {
    if (maps[i]) delete maps[i];
  }
}

/**
 * Adds a new line in kline format of InputMap
 * This came from UInput.cpp making that code obselete.
 * @param _index is the section index
 * @param _line is the quoted line
 * @param _comment is the comment.
 * @param _commentSize is the size of comment.
 * @param _reverse is the reverse indicator, indicating encode map.
 * @return negative on fail
 */
int
SBMapItemVector::addKLine (int _index, const char* _line, const unsigned char* _comment, int _commentSize, int _reverse)
{
  const char  *from;
  const char  *to;
  char *    toOut;
  int    kcIndex;
  unsigned char  key[TOK_STATE_SIZE+1];
  unsigned char  code[2*TOK_STATE_SIZE+2];
  char    kcSize[2];
  int    value;
  int    base;

  to = _line;
  kcIndex =0;
  kcSize[0] = 0;
  kcSize[1] = 0;

  while (to[0] != 0)
  {
    from = to;
    if (*from==' ')
    {
      to++; continue;
    }
    if (*from=='=')
    {
      to++; kcIndex=1; continue;
    }
    if (kcSize[kcIndex] >= TOK_STATE_SIZE)
    {
      fprintf (stderr, "error: kmap size exceeded %d .\n", TOK_STATE_SIZE);
      return 0;
    }

    //
    // adding number
    //
    if (to[0] >= '0' && to[0] <= '9')
    {
      base = 10;
      if (to[0] == '0')
      {
        to++;
        if (to[0] == 'x' || to[0] =='X')
        {
          base = 16;
          to++;
        }
        else
        {
          base = 8;
        }
      }
      value = (int)  strtol ((const char*) to, &toOut, base);
      to = toOut;
      if (kcIndex==0)
      {
        key[(int)kcSize[0]] = (unsigned char) value;
      }
      else
      {
        code[kcSize[1]*2]  = (((SS_WORD32)value>>8)&0xff);
        code[kcSize[1]*2+1]  = value&0xff;
      }
      kcSize[kcIndex]++;
      continue;
    }

    // straight
    while (to[0] != 0)
    {
      if (to[0]==' ')
      {
        to++; break;
      }
      if (to[0]=='=') break;
      if (kcSize[kcIndex] >= TOK_STATE_SIZE)
      {
        fprintf (stderr, "error: kmap size exceeded %d .\n", TOK_STATE_SIZE);
        return 0;
      }
      value = to[0];
      if (kcIndex==0)
      {
        key[(int)kcSize[0]] = (unsigned char) value;
      }
      else
      {
        code[kcSize[1]*2]  = (((SS_WORD32)value>>8)&0xff);
        code[kcSize[1]*2+1]  = value&0xff;
      }
      to++;
      kcSize[kcIndex]++;
    }
  }

  if (kcSize[1]==0)
  {
    fprintf (stderr, "error: kmap map is empty. \n");
    return -1;
  }
  key[(int)kcSize[0]] = 0;
  code[kcSize[1]*2] = 0;

  // Now you can see the limitation of kmaps. No matchsize!
  if (_reverse)
  {
    // No comment.
    return maps[_index]->add (code, 2 * kcSize[1],2 *  kcSize[1], 
      key, kcSize[0], _comment, 0);
  }
  return maps[_index]->add (key, kcSize[0], kcSize[0], 
      code, 2 * kcSize[1], _comment, _commentSize);

}
