/** 
 *  Yudit Unicode Editor Source File
 *
 *  GNU Copyright (C) 1997-2023  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 "stoolkit/SBMap.h"
#include "stoolkit/STypes.h"
#include "stoolkit/SUtil.h"
#include "ProcessFile.h"

#include <string.h>
#include <stdlib.h>

#include <time.h>

/* They'll stone me for this */
#include <stdio.h>

#ifdef USE_WINAPI
#include <windows.h>
#else
#include <unistd.h>
#endif

unsigned int hexVle (char in);
char hexStr (SS_WORD64, int nibble);
int encode(void* buffer, char* input, int bytesize);

void decode(char* buffer, void* input, int bytesize, int len);
int doBenchMark(SBMap* map, int index);

#define ISHEX(a) ((a>='A' && a<='F') || (a>='a' && a<='f') || (a>='0' && a<='9'))

#define MAX_INPUT_FILES 256
typedef enum {KMAP, RKMAP, UNI, RUNI, MYS, RMYS } MapType;

typedef struct
{
  MapType    type;
  int    from;
  int    to;
  const char*  name;
  SBMapItemVector*  item;
} InputFiles;

char    shortComment[MAX_COMMENT_SIZE];

InputFiles  inputFiles[MAX_INPUT_FILES];
int    inputFileCount=0;
char    inputName[32];
int  inputType = -1;

char  nameb[1024];
char  commentb[1024];
char  in[1024];
char  enc[1024];

int  strip=0;
int  info=0;
int  test=0;
int  stateEnc = 0;
int  stateDec = 0;
int  mapType = -1;
char*  mapName = 0;
char*  mapComment = 0;
char*   file=0;
char*   mapFile=0;
int  showKeys=0;
int  benchmark=0;
int  circle=-1;
int  highLow=0;
int  keySize=1;
int  nocomment=0;

SFileFormat  outformat=SS_BINARY;

static void usage();
static void print_version ();

#define BUFFER_SIZE 512
char  readBuffer[BUFFER_SIZE];

/**
 * @author Gaspar Sinai <gsinai@iname.com> 1999-12-18
 * @version 1.0
 * This is a program to create, convert and test character maps.
 */
int
main (int argc, char* argv[])
{
  int    i, j;

  if (argc < 2)
  {
    usage();
    exit (1);
  }

  if (argc==3 && strcmp ("-installdir", argv[1])==0)
  { 
    SString pref(argv[2]); 
    SDir d (pref);
    bool ret = false;
    if (d.exists() && d.readable())
    {
      ret = setPrefix (pref);
    }
    else
    {
      fprintf (stderr, "Can not read directory [%*.*s]\n", SSARGS(pref));
    }
    return (ret);
  }

  /** 
   * This option puts everything in a tmp file and 
   * calls the program with agruents specified after cmd, 
   * with the filename as the last argument.
   * when program exists remove the tmp file.
  */
  if (argc>=3 && strcmp ("-pipecmd", argv[1])==0)
  { 
    FILE* input=stdin;
    SString tmpfile = getTemporaryFileName ();
    if (tmpfile.size()==0) 
    {
      fprintf (stderr, "Can not create tmp file.\n");
      return (1);
    }
    tmpfile.append ((char)0);
    FILE* output=stdout;
#ifdef USE_WINAPI
    if ((output = fopen (tmpfile.array(), "wb")) == 0)
#else
    if ((output = fopen (tmpfile.array(), "w")) == 0)
#endif
    {
      fprintf (stderr, "mytool: can not open '%s' to write.\n", 
        tmpfile.array());
      return 1;
    }
    int len;
    while (!feof(input) && (len=fread(readBuffer, 1, BUFFER_SIZE, input))>0)
    {
      int wlen = fwrite(readBuffer, 1, len, output);
      if (wlen!= len)
      {
        fprintf (stderr, "mytool: failed to write %*.*s.\n", SSARGS(tmpfile));
        return 1;
      }
    }
    fclose (output);
    /* call argv[2]...argc - with this file */
    SStringVector vargs;
    /* on linux it is foreground by default */
    for (int i=2; i<argc; i++)
    {
       SString arg (argv[i]);
       arg.insert (0, "\"");
       arg.append ("\"");
       vargs.append (arg);
    }

    SString farg (tmpfile.array());
    farg.insert (0, "\"");
    farg.append ("\"");
    vargs.append (farg);

    SPipe pipe (vargs.join (" "));
    {
      /* we have to use input stream if we want to wait. Hack in SIO.cpp */
      SInputStream is = pipe.getInputStream();
      is.close();
    }

    /* wait till it exits */
    int ret = pipe.wait();
    /* remove tmp file after it opened it  */
#ifdef USE_WINAPI
    Sleep(10 * 1000);
#else
    sleep (10);
#endif
    unlink (tmpfile.array());
    return ret;
  }

  SBMap  map;

  for (i=1; i<argc; i++)
  {
    if (inputFileCount >= MAX_INPUT_FILES)
    {
      fprintf (stderr,  "mytool: maximum % input files can be specified.\n",
		MAX_INPUT_FILES);
      exit (1);
    } 
    if (strcmp (argv[i],"-info")==0)
    {
      info=1;
      continue;
    }
    if (strcmp (argv[i],"-test")==0)
    {
      test=1;
      continue;
    }
    if (strcmp (argv[i],"-benchmark")==0)
    {
      benchmark=1;
      continue;
    }
    if (strcmp (argv[i],"-strip")==0)
    {
      strip=1;
      continue;
    }
    if (strcmp (argv[i],"-version")==0)
    {
      print_version ();
      exit (0);
    }
    if (strcmp (argv[i],"-encode")==0)
    {
      stateEnc=1;
      continue;
    }
    if (strcmp (argv[i],"-decode")==0)
    {
      stateDec=1;
      continue;
    }
    if (strcmp (argv[i],"-high")==0)
    {
      highLow=1;
      continue;
    }
    if (strcmp (argv[i],"-8")==0)
    {
      keySize=0;
      continue;
    }
    if (strcmp (argv[i],"-low")==0)
    {
      highLow=-1;
      continue;
    }
    if (strcmp (argv[i],"-showkeys")==0)
    {
      showKeys=1;
      continue;
    }
    if (strcmp (argv[i],"-nocomment")==0)
    {
      nocomment=1;
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-kmap")==0)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = KMAP;
      inputFileCount++;
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-rkmap")==0)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = RKMAP;
      inputFileCount++;
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-mys")==0)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = MYS;
      inputFileCount++;
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-rmys")==0)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = RMYS;
      inputFileCount++;
      continue;
    }
    if (i < argc-1 && sscanf (argv[i],"-uni:%d,%d", 
      &inputFiles[inputFileCount].from, 
      &inputFiles[inputFileCount].to)==2)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = UNI;
      inputFileCount++;
      continue;
    }
    if (i < argc-1 && sscanf (argv[i],"-runi:%d,%d", 
      &inputFiles[inputFileCount].from, 
      &inputFiles[inputFileCount].to)==2)
    {
      inputFiles [inputFileCount].name = argv[++i];
      inputFiles [inputFileCount].type = RUNI;
      inputFileCount++;
      continue;
    }

    if (i < argc-1 && strcmp (argv[i],"-write")==0)
    {
      if (file != 0)
      {
        fprintf (stderr,  "mytool: only one '-write' option can be specified.\n");
        exit (1);
      }
      file = argv[++i];
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-type")==0)
    {
      i++;
      if (strcmp (argv[i], "kmap")==0)
      {
        mapType = 1;
      }
      else if (strcmp (argv[i], "clkmap")==0)
      {
        mapType = 4;
      }
      else if (strcmp (argv[i], "fontmap")==0)
      {
        mapType = 2;
      }
      else
      {
        fprintf (stderr,  "mytool: unknown maptype '%s'\n", argv[i]);
        exit (1);
      }
      
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-name")==0)
    {
      mapName = argv[++i];
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-comment")==0)
    {
      mapComment = argv[++i];
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-my")==0)
    {
      if (mapFile != 0)
      {
        fprintf (stderr,  "mytool: only one '-my' option can be specified.\n");
        exit (1);
      }
      mapFile = argv[++i];
      continue;
    }
    if (i < argc-1 && strcmp (argv[i],"-convert")==0)
    {
      i++;
      if (strcmp (argv[i],"my")==0)
      {
        outformat = SS_BINARY;
      }
      else if (strcmp (argv[i],"mys")==0)
      {
        outformat = SS_TEXT_MAP;
      }
      else if (strcmp (argv[i],"myc")==0)
      {
        outformat = SS_CTEXT;
      }
      else if (strcmp (argv[i],"bumap")==0)
      {
        outformat = SS_BUMAP;
      }
      else if (strcmp (argv[i],"cumap")==0)
      {
        outformat = SS_CUMAP;
      }
      else
      {
        usage ();
        return (1);
      }
      continue;
    }
    usage ();
    exit (1);
  }

  if (mapFile!=0)
  {
    SFile f(mapFile);
    map.setFileImage (f.getFileImage());
    if (f.size() < 0 || !map.getStatus())
    {
      if (mapFile==0)
      {
        usage ();
        return (1);
      }
      fprintf (stderr,  "mytool: can not find valid '%s' map file\n", mapFile);
      return (1);
     }
    if (strip)
    {
      map.strip ();
    }
  }

  // Process input files
  for (i=0; i<inputFileCount; i++)
  {
    inputFiles[i].item = 0;
  }
  shortComment[0] = 0;
  inputName[0] = 0;
  inputType = -1;


  for (i=0; i<inputFileCount; i++)
  {
    SFile in(inputFiles[i].name);
    if (in.size() < 0)
    {
      do {
        if (inputFiles[i].item!=0) 
        {
          for (j=0; j<inputFiles[i].item->count; j++)
          {
            delete inputFiles[i].item->maps[j];
          }
          delete inputFiles[i].item;
          inputFiles[i].item = 0;
        }
      } while (i-->0);
      fprintf (stderr,  "mytool: can not read '%s'.\n", inputFiles[i].name);
      return (1);
    }
    SInputStream is = in.getInputStream();
    switch (inputFiles[i].type)
    {
    case KMAP:
      inputFiles[i].item = processKMap (inputFiles[i].name, is, nocomment, 0, shortComment, inputName, &inputType);
      break;
    case RKMAP:
      inputFiles[i].item = processKMap (inputFiles[i].name, is, nocomment, 1, shortComment, inputName, &inputType);
      break;
    case MYS:
      inputFiles[i].item = processMYS (inputFiles[i].name, is, nocomment, 0, shortComment, inputName, &inputType);
      break;
    case RMYS:
      inputFiles[i].item = processMYS (inputFiles[i].name, is, nocomment, 1, shortComment, inputName, &inputType);
      break;
    case UNI:
      inputFiles[i].item = processUNI (inputFiles[i].name, is, nocomment, keySize, highLow, inputFiles[i].from, inputFiles[i].to, 0, shortComment, inputName, &inputType);
      break;
    case RUNI:
      inputFiles[i].item = processUNI (inputFiles[i].name, is, nocomment, keySize, highLow, inputFiles[i].from, inputFiles[i].to, 1, shortComment, inputName, &inputType);
      break;
    default:
      fprintf (stderr,  "mytool: internal error: %d \n", inputFiles[i].type);
      break;
    }

    if (inputFiles[i].item==0)
    {
      fprintf (stderr,  "mytool: bad source file '%s'.\n", inputFiles[i].name);
      do {
        if (inputFiles[i].item!=0) 
        {
          delete inputFiles[i].item;
          inputFiles[i].item = 0;
        }
      } while (i-->0);
      return (1);
    }
  }
  if (mapComment != 0)
  {
    map.setComment ((const unsigned char*) mapComment, strlen (mapComment));
  }
  else if (shortComment[0] != 0)
  {
    map.setComment ((const unsigned char*) shortComment, strlen (shortComment));
  }
  else if (inputFileCount)
  {
    time_t  tim;
    char *source_date_epoch = getenv ("SOURCE_DATE_EPOCH");
    if (source_date_epoch == NULL || (tim = (time_t)strtol (source_date_epoch, NULL, 10)) <= 0)
      tim = time(0);
    struct tm* ts = localtime (&tim);
    snprintf (shortComment, MAX_COMMENT_SIZE, "created by mytool %04d-%02d-%02d", 
      ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday);
    map.setComment ((const unsigned char*) shortComment,
      strlen (shortComment));
  }

  if (mapType > 0)
  {
    map.setType (mapType);
  }
  else if (inputType > 0)
  {
    map.setType (inputType);
  }

  if (mapName != 0)
  {
    map.setName ((const unsigned char*) mapName);
  }
  else if (inputName[0] != 0)
  {
    map.setName ((const unsigned char*) inputName);
  }

  for (i=0; i<inputFileCount; i++)
  {
    // ownership is passed to map
    for (j=0; j<inputFiles[i].item->count; j++)
    {
      map.add (inputFiles[i].item->maps[j]);
      inputFiles[i].item->maps[j] = 0;
    }
    delete inputFiles[i].item;
    inputFiles[i].item = 0;
  }

  // Build encoding state machine.
  if (stateEnc)
  {
    for (i=0; i<map.getSize(); i++)
    {
      if (map.getType(i)==SBMap::SBMap_DECODE) continue;
      map.buildStateMachine (i);
    }
  }

  if (stateDec)
  {
    for (i=0; i<map.getSize(); i++)
    {
      if (map.getType(i)==SBMap::SBMap_ENCODE) continue;
      map.buildStateMachine (i);
    }
  }
  if (info)
  {
    const char*  type = 0;
    if (map.mapType==1) type="kmap";
    if (map.mapType==4) type="clkmap";
    if (map.mapType==2) type="fontmap";
    if (type==0)
    {
      fprintf (stdout, "type...: undefined (%d)\n", map.mapType);
    }
    else
    {
      fprintf (stdout, "type...: %s\n", type);
    }
    fprintf (stdout, "size...: %d\n", map.getSize());
    map.getName (nameb, 1024);
    fprintf (stdout, "name...: [%s]\n", nameb);
    map.getComment (commentb, 1024);
    fprintf (stdout, "comment: [%s]\n", commentb);
    int   integ;
    for (i=0; i<map.getSize(); i++)
    {
      SBMapItem* mp = map.getItem(i);
      fprintf (stdout, "info:----------------: map[%d]\n", i);
      map.getName (nameb, 1024, i);
      fprintf (stdout, "%d.name...............: [%s]\n", i, nameb);
      map.getComment (commentb, 1024, i);
      fprintf (stdout, "%d.comment............: [%s]\n", i, commentb);
      type = "unknown";
      if (mp->itemType == SBMapItem::SBMapNToN)
        type = "my";
      if (mp->itemType == SBMapItem::SBMapBumap)
        type = "bumap";

      fprintf (stdout, "%d.itemType...........: %s\n", i, type);
      fprintf (stdout, "%d.size...............: %d matches \n", 
		i, map.getSize(i));
      type="unknown";
      if (map.getType(i)==SBMap::SBMap_DECODE) type="decode";
      if (map.getType(i)==SBMap::SBMap_ENCODE) type="encode";
      fprintf (stdout, "%d.type...............: %s\n",i,  type);

      type = (map.getStateMachine(i) ==0) ? "no" : "yes";
      fprintf (stdout, "%d.state machine based: %s\n",i, type);
      integ = 8 << map.getInWordSize(i);
      fprintf (stdout, "%d.input..............: %d bits\n", i, integ);
      integ = 8 << map.getOutWordSize(i);
      fprintf (stdout, "%d.output.............: %d bits\n", i, integ);
      if (showKeys)
      {
        char   buf[256];
        int  k;
        const unsigned char* tmp;
        unsigned int len;
        unsigned int matched;
        for (k=0; k<map.getSize(i); k++)
        {
          tmp = mp->getKey (k, &len, &matched);
          memcpy (buf, tmp, len);
          buf[len] = 0;
          fprintf (stdout, "%d.keys............: [%d] |%s|\n" ,i, k, buf);
        }
      }
      if (benchmark)
      {
        fprintf (stdout,  "%d.benchmark.speed......: %d\n", i, doBenchMark (&map, i));
      }

    }
    if (benchmark)
    {
      fprintf (stdout, "encode.benchmark.speed.: %d\n", 
	doBenchMark (&map, -1-(int)SBMap::SBMap_ENCODE));
      fprintf (stdout, "decode.benchmark.speed.: %d\n", 
	doBenchMark (&map, -1-(int)SBMap::SBMap_DECODE));
    }
  }
  else if (benchmark)
  {
    for (i=0; i<map.getSize(); i++)
    {
      fprintf(stdout, "%d.benchmark.speed......: %d\n", i, 
	doBenchMark (&map, i));
    }
    if (benchmark)
    {
      fprintf (stderr, "encode.benchmark.speed.: %d\n",
	   doBenchMark (&map, -1-(int)SBMap::SBMap_ENCODE));
      fprintf(stderr,  "decode.benchmark.speed.: %d\n",
	   doBenchMark (&map, -1-(int)SBMap::SBMap_DECODE));
    }
  }


  if (test)
  {
    char  ce[256];

    fprintf (stdout, "Enter map-index-or-d-or-e  in-bytesize out-bytesize: ");
    fflush (stdout);;
    
    int index, in_bytesize, out_bytesize;
    int len, in_bytesize_orig, out_bytesize_orig;
    ce[255] = 0;
    if (fscanf (stdin, "%255s", ce) == 0) {
      fprintf (stderr,  "Can not parse line.\n");
      return (1);
    } 
    index = 0;
    if (ce[0] == 'd')
    {
      circle = (int) SBMap::SBMap_DECODE;
      for (i=0; i<map.getSize(); i++)
      {
        if (map.getType(i)==circle)
        {
          index = i;
          break;
        }
      }
    }
    else if (ce[0] == 'e')
    {
      circle = (int) SBMap::SBMap_ENCODE;
      index = 0;
      for (i=0; i<map.getSize(); i++)
      {
        if (map.getType(i)==circle)
        {
          index = i;
          break;
        }
      }
    }
    else
    {
      index = atoi(ce);
    }
    if (fscanf (stdin, "%d", &in_bytesize_orig) == 0) {
      fprintf (stderr,  "Can not parse line.\n");
      return (1);
    }
    if (fscanf (stdin, "%d", &out_bytesize_orig) == 0) {
      fprintf (stderr,  "Can not parse line.\n");
      return (1);
    }

    switch (in_bytesize_orig)
    {
    case 1: in_bytesize=0; break;
    case 8: in_bytesize=0; break;
    case 16: in_bytesize=1; break;
    case 32: in_bytesize=2; break;
    case 64: in_bytesize=3; break;
    default:
      fprintf (stderr,  "Bad argument for in_bytesize. Should be {1|8|16|32|64}\n");
      return (1);
    }
    switch (out_bytesize_orig)
    {
    case 1: out_bytesize=0; break;
    case 8: out_bytesize=0; break;
    case 16: out_bytesize=1; break;
    case 32: out_bytesize=2; break;
    case 64: out_bytesize=3; break;
    default:
      fprintf (stderr,  "Bad argument for out_bytesize. Should be {1|8|16|32|64}\n");
      return (1);
    }
    if (index < 0 || index >= map.getSize())
    {
      fprintf (stderr,  "Bad argument for index. Should be [0..%d]\n", (int) map.getSize()-1); 
      return (1);
    }
  
    SStateModel model (in_bytesize, map.getInWordSize (index),
       map.getOutWordSize (index), out_bytesize);
    while (!feof(stdin))
    {
      if (in_bytesize_orig==1)
      {
        fprintf (stdout,  "Enter bytestring: ");
      }
      else
      {
        fprintf (stdout,  "Enter hex bytestring nibbles: ");
      }
      fflush (stdout);
      in[1023] = 0;
      if (fscanf (stdin, "%1023s", in) == 0) {
        fprintf (stdout,  "Can not parse line");
        return (1);
      }
      if (in_bytesize_orig==1)
      {
        len = strlen (in);
        memcpy (enc, in, len);
      }
      else
      {
        len = encode (enc, in, in_bytesize);
      }
      if (circle>=0)
      {
	fprintf (stderr,  "circle..\n");
        len = map.circle ((SBMap::SBMapType)circle, enc, len, &model, 1);
      }
      else
      {
        len = map.encode (index, enc, len, &model, 1);
      }
      switch (len)
      {
      case SS_REJECT:
        model.reset(); 
        fprintf(stdout, "reposnse: REJECT\n");
        break;
      case SS_ACCEPT:
        fprintf(stdout, "reposnse: ACCEPT\n");
        break;
      default:
	fprintf (stdout, "result:  MATCH length %d -> %d:", 
		len, model.out.length);
        switch (out_bytesize)
        {
        case 0: 
          memcpy (enc, model.out.u.u8, model.out.length);
          break;
        case 1: 
          memcpy (enc, model.out.u.u16, model.out.length*2);
          break;
        case 2: 
          memcpy (enc, model.out.u.u32, model.out.length*4);
          break;
        case 3: 
          memcpy (enc, model.out.u.u64, model.out.length*64);
          break;
        default:
          // Debug it..
          return (1);
        }
        if (out_bytesize_orig==1)
        {
          memcpy (in, enc, model.out.length);
          in[model.out.length] = 0;
        }
        else
        {
          decode (in, enc, out_bytesize, model.out.length);
        }
        fprintf (stdout, "'%s'\n", in);
        model.reset(); 
      }
    }
  }

  if (file != 0)
  {
    SFile f(file);
    SOutputStream os = f.getOutputStream();
    if (!os.isOK())
    {
      fprintf (stderr,  "mytool: can not open file: '%s'\n",file);
      return (1);
    }
    int size = map.serialize (os, outformat);
    if (size <= 0)
    {
      fprintf (stderr,  "mytool: can serialize map into file: '%s'\n", file);
      return (1);
    }
  }

  return (0);
}

int
encode(void* buffer, char* input, int bytesize)
{
  SS_WORD8  o8[1024];
  SS_WORD16  o16[1024];
  SS_WORD32  o32[1024];
  SS_WORD64  o64[1024];
  int    i, index;
  SS_WORD64  result;

  index = 0; i=0; result=0;
  switch (bytesize)
  {
  case 0:
    for (i=0; input[i]!=0; i++)
    {
      if (!ISHEX(input[i])) continue;
      result = (result << 4) + hexVle(input[i]);
      index++;
      if ((index%2)==0)
      {
        o8[index/2-1] = (SS_WORD8) result;
        result = 0;
      }
    }
    memcpy (buffer, o8, index/2);
    return index/2;
  case 1:
    for (i=0; input[i]!=0; i++)
    {
      if (!ISHEX(input[i])) continue;
      result = (result << 4) + hexVle(input[i]);
      index++;
      if ((index%4)==0)
      {
        o16[index/4-1] = (SS_WORD16) result;
        result = 0;
      }
    }
    memcpy (buffer, o16, index/2);
    return index/4;
  case 2:
    for (i=0; input[i]!=0; i++)
    {
      if (!ISHEX(input[i])) continue;
      result = (result << 4) + hexVle(input[i]);
      index++;
      if ((index%8)==0)
      {
        o32[index/8-1] = (SS_WORD32) result;
        result = 0;
      }
    }
    memcpy (buffer, o32, index/2);
    return index/8;
  case 3:
    for (i=0; input[i]!=0; i++)
    {
      if (!ISHEX(input[i])) continue;
      result = (result << 4) + hexVle(input[i]);
      index++;
      if ((index%16)==0)
      {
        o64[index/16-1] = (SS_WORD32) result;
        result = 0;
      }
    }
    memcpy (buffer, o64, index/2);
    return index/16;
  default:
    break;
  }
  return 0;
}

void
decode(char* buffer, void* input, int bytesize, int len)
{
  int  i;
  int  index;
  SS_WORD64  word;
  unsigned char*  in = (unsigned char*) input;
  switch (bytesize)
  {
  case 0:
    index=0;
    for (i=0; i<len; i++)
    {
      word = (SS_WORD64) (((SS_WORD8*)in)[i]);
      buffer[index++] = hexStr(word, 1);
      buffer[index++] = hexStr(word, 0);
      buffer[index++] = ' ';
    }
    buffer[index++] = 0;
    break;
  case 1:
    index=0;
    for (i=0; i<len; i++)
    {
      word = (SS_WORD64) (((SS_WORD16*)in)[i]);
      buffer[index++] = hexStr(word, 3);
      buffer[index++] = hexStr(word, 2);
      buffer[index++] = hexStr(word, 1);
      buffer[index++] = hexStr(word, 0);
      buffer[index++] = ' ';
    }
    buffer[index++] = 0;
    break;
  case 2:
    index=0;
    for (i=0; i<len; i++)
    {
      word = (SS_WORD64) (((SS_WORD32*)in)[i]);
      buffer[index++] = hexStr(word, 7);
      buffer[index++] = hexStr(word, 6);
      buffer[index++] = hexStr(word, 5);
      buffer[index++] = hexStr(word, 4);
      buffer[index++] = hexStr(word, 3);
      buffer[index++] = hexStr(word, 2);
      buffer[index++] = hexStr(word, 1);
      buffer[index++] = hexStr(word, 0);
      buffer[index++] = ' ';
    }
    buffer[index++] = 0;
    break;
  case 3:
    index=0;
    for (i=0; i<len; i++)
    {
      word = (SS_WORD64) (((SS_WORD64*)in)[i]);
      buffer[index++] = hexStr(word, 15);
      buffer[index++] = hexStr(word, 14);
      buffer[index++] = hexStr(word, 13);
      buffer[index++] = hexStr(word, 12);
      buffer[index++] = hexStr(word, 11);
      buffer[index++] = hexStr(word, 10);
      buffer[index++] = hexStr(word, 9);
      buffer[index++] = hexStr(word, 8);
      buffer[index++] = hexStr(word, 7);
      buffer[index++] = hexStr(word, 6);
      buffer[index++] = hexStr(word, 5);
      buffer[index++] = hexStr(word, 4);
      buffer[index++] = hexStr(word, 3);
      buffer[index++] = hexStr(word, 2);
      buffer[index++] = hexStr(word, 1);
      buffer[index++] = hexStr(word, 0);
      buffer[index++] = ' ';
    }
    buffer[index++] = 0;
    break;
  default:
    buffer[0] = 0;
    break;
  }
}

unsigned int
hexVle (char in)
{
  if (in>='0' && in<='9') return in-'0';
  if (in>='a' && in<='z') return in-'a'+10;
  if (in>='A' && in<='Z') return in-'A'+10;
  return 256;
}
char
hexStr (SS_WORD64 in, int nibble)
{
  SS_WORD64 newIn;
  newIn = (in>>(4*nibble))&0x0f;
  if (newIn < 10) return (char) (newIn + '0');
  return (char) (newIn + 'A' - 10);
}

int
doBenchMark(SBMap* map, int index)
{
  SS_WORD16  in;
  SS_WORD16  input[256];
  int    ind;

  int    count = 0;
  time_t    t0, t1;
  int    len;
  t0 = time(0);
  ind=0;
  in = 0;
  int  i;

  int  which=index;
  
  if (which < 0)
  {
    for (i=0; i<map->getSize(); i++)
    {
      if (map->getType(i) == (SBMap::SBMapType)(-index-1))
      {
        which = i;
        break;
      }
    }
  }
  if (which < 0)
  {
    return 0;
  }
  SStateModel model (1, map->getInWordSize (which),
       map->getOutWordSize (which), 1);
  while (1)
  {
    input[ind]++;
    if (input[ind]==0 && ind>0)
    {
      ind--;
    }
    model.reset();
    if (index<0)
    {
      len = map->circle ((SBMap::SBMapType)(-index-1), input, ind+1, &model, 1);
    }
    else
    {
      len = map->encode (index, input, ind+1, &model, 1);
    }
    switch (len)
    {
    case SS_REJECT:
      break;
  
    case 0:
      if (index<0) fprintf (stderr,  "Match 0.\n");
    default:
      if (ind<255)
      {
        ind++;
        input[ind] = 0;
      }
      break;
    }
    in++;
    if ((in&0x0f)==0)
    {
      count++;
      t1 = time(0);
      if (t1-t0 > 5 || t0-t1 > 5)
      {
        break;
      }
    }
  }
  return count;
}

static void
print_version ()
{
  fprintf (stderr, "mytool version %s GNU (C) Gaspar Sinai\n", SD_YUDIT_VERSION);
}

static void
usage ()
{
  fprintf (stderr,  "usage: mytool [-convert {myc|mys|my|bumap|cumap} [-info [-showkeys]] [-test] [-encode] [-decode]");
  fprintf (stderr,  " [-write new-map] [-name map-name]  [-nocomment] [-comment comment] [-type map-type]");
  fprintf (stderr,  " [-my binary-mapfile] [-benchmark] [-high|-low]");
  fprintf (stderr,  " [-uni:1,2 plainfile [-8]] [-runi:1,2 plainfile [-8]]");
  fprintf (stderr,  " [-mys mapfile] [-rmys reverse-map-file]");
  fprintf (stderr,  " [-kmap keymap-file] [-rkmap reverse-keymap-file]\n");
  fprintf (stderr,  " [-installdir \"Install Dir\"] [-pipecmd command [args]]\n");
  fprintf (stderr,  "   -info     print out useful info about the map.\n");
  fprintf (stderr,  "   -test     print out useful info about the map and test it.\n");
  fprintf (stderr,  "   -strip    delete the state machine, before doing anything else.\n");
  fprintf (stderr,  "   -name     Assign a max 32-byte-long name.\n");
  fprintf (stderr,  "   -comment  Assign a comment of arbitrary length.\n");
  fprintf (stderr,  "   -type     Assign a map type. 0=None, 1=Font, 2=kmap - not used.\n");
  fprintf (stderr,  "   -encode   build a state machine for all encoding maps.\n");
  fprintf (stderr,  "   -decode   build a state machine for all decoding maps.\n");
  fprintf (stderr,  "   -write    write out the latest map before exiting.\n");
  fprintf (stderr,  "   -convert  {mys|c|my} convert the file to 'c' or 'my' \n");
  fprintf (stderr,  "   -uni:local,unicode  a 16 bit file format with columns precified.\n");
  fprintf (stderr,  "   -runi:local,unicode  a 16 bit file format in reverse with columns precified.\n");
  fprintf (stderr,  "   -my       assign a fully specified binary mapfile to input.\n");
  fprintf (stderr,  "   -mys      assign a fully specified mapfile source to input.\n");
  fprintf (stderr,  "   -rmys     same as mys but key - value pairs will be reversed.\n");
  fprintf (stderr,  "   -kmap     assign a fully specified keymap file source to input.\n");
  fprintf (stderr,  "   -rkmap    same as kmap but key - value pairs will be reversed.\n");
}
