#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
#include <sys/uio.h>


#define MAXSYM (1<<11)
#define MAXSYMCHAR 128

#define out(string, size)	write(1, string, size)
#define out1(string)	out(string, 1)
#define outs(string)	out(string, strlen(string))

int warns=0;

void outh(h)
{ outs("#|WARN");
  outs(h);
  outs("|# ");
  warns++; }

int syntax_errors=0;

void oute()
{ outs("#<OLD-SYNTAX>");
  syntax_errors++; }

/* translation table management */

typedef struct { int size;
		 char entries[2*MAXSYM*MAXSYMCHAR]; } trans_s;

#define TRANS_ENTRY(t,e) ((t->entries)+((e)*2*MAXSYMCHAR))

trans_s *fun_trans;
trans_s *car_trans;
trans_s *dyn_trans;

trans_s *trans_new()
{ trans_s *trans;
  trans = (trans_s *)malloc(sizeof(trans_s));
  memset(trans, 0, sizeof(trans_s));
  trans->size = 0;
  return trans; }

void trans_insert(trans, string1, string2)
trans_s *trans;
char *string1;
char *string2;
{ int slen1 = strlen(string1);
  if (trans->size == MAXSYM) {
    printf("** increase MAXSYM\n");
    exit(-1); }
  strcat(TRANS_ENTRY(trans,trans->size), string1);
  strcat(TRANS_ENTRY(trans,trans->size), ".");
  strcat(TRANS_ENTRY(trans,trans->size), string2);
  TRANS_ENTRY(trans,trans->size)[slen1] = '\0';
  trans->size++; }

int trans_compare(s1, s2)
char *s1;
char *s2;
{ int result;
  /* printf("Comparing 0x%lx and 0x%lx...\n", s1, s2); */
  result = strcmp(s1, s2);
  /* printf("...done %d\n", result); */
  return result; }

void trans_sort(trans)
trans_s *trans;
{ qsort(trans->entries, MAXSYM, 2*MAXSYMCHAR, trans_compare); }

char *trans_seek(trans,string)
trans_s *trans;
char *string;
{ int i, j;
  i = 0;
  j = MAXSYM;
  while (i+1 != j) {
    int res;
    int k = ( i + j ) / 2;
    /* printf("%d\n %lx<?>%lx\n",k, string, TRANS_ENTRY(trans,k)); */
    res = strcmp(string, TRANS_ENTRY(trans,k));
    if (!res) {
      /* printf("<found %s -> %s>\n", string,
	     TRANS_ENTRY(trans,k)+strlen(TRANS_ENTRY(trans,k))+1); */
      return TRANS_ENTRY(trans,k)+strlen(TRANS_ENTRY(trans,k))+1; }
    if (res>0)
      i=k;
    else
      j=k; }
  return 0; }

char *module;

trans_s *trans_file(file)
{ FILE *fs;
  char s2[MAXSYMCHAR];
  char s3[MAXSYMCHAR];
  trans_s *trans;
  char name[100];
  name[0]='\0';
  strcat(name,CDIR);
  strcat(name,file);
  trans = trans_new();
  fs = fopen(name, "r");
  while (EOF != fscanf(fs, "%s %s ", s2, s3)) {
    /* printf("%s -> %s\n", s2, s3); */
    trans_insert(trans, s2, s3); }
  fclose(fs);
  trans_sort(trans);
  return trans;
  }

/* help table management */

#define MAXHELPCHAR 100

typedef struct { int size;
		 char entries[MAXSYM*(MAXSYMCHAR+MAXHELPCHAR)]; } help_s;

#define HELP_ENTRY(t,e) ((t->entries)+((e)*(MAXSYMCHAR+MAXHELPCHAR)))

help_s *fun_help;
help_s *car_help;
help_s *dyn_help;

help_s *help_new()
{ help_s *help;
  help = (help_s *)malloc(sizeof(help_s));
  memset(help, 0, sizeof(help_s));
  help->size = 0;
  return help; }

void help_insert(help, string1, string2)
help_s *help;
char *string1;
char *string2;
{ int slen1 = strlen(string1);
  if (help->size == MAXSYM) {
    printf("** increase MAXSYM\n");
    exit(-1); }
  strcat(HELP_ENTRY(help,help->size), string1);
  strcat(HELP_ENTRY(help,help->size), ". ");
  strcat(HELP_ENTRY(help,help->size), string2);
  HELP_ENTRY(help,help->size)[slen1] = '\0';
  help->size++; }

int help_compare(s1, s2)
char *s1;
char *s2;
{ int result;
  /* printf("Comparing 0x%lx and 0x%lx...\n", s1, s2); */
  result = strcmp(s1, s2);
  /* printf("...done %d\n", result); */
  return result; }

void help_sort(help)
help_s *help;
{ qsort(help->entries, MAXSYM, MAXSYMCHAR+MAXHELPCHAR, help_compare); }

char *help_seek(help,string)
help_s *help;
char *string;
{ int i, j;
  i = 0;
  j = MAXSYM;
  while (i+1 != j) {
    int res;
    int k = ( i + j ) / 2;
    /* printf("%d\n %lx<?>%lx\n",k, string, HELP_ENTRY(help,k)); */
    res = strcmp(string, HELP_ENTRY(help,k));
    if (!res) {
      /* printf("<found %s -> %s>\n", string,
	     HELP_ENTRY(help,k)+strlen(HELP_ENTRY(help,k))+1); */
      return HELP_ENTRY(help,k)+strlen(HELP_ENTRY(help,k))+1; }
    if (res>0)
      i=k;
    else
      j=k; }
  return 0; }

help_s *help_file(file)
{ FILE *fs;
  char s2[MAXSYMCHAR];
  char s3[MAXHELPCHAR];
  char tabs[MAXHELPCHAR];
  help_s *help;
  char name[100];
  name[0]='\0';
  strcat(name,CDIR);
  strcat(name,file);
  help = help_new();
  fs = fopen(name, "r");
  while (EOF != fscanf(fs, "%s %[ -~]\n", s2, (s3[0]='\0', s3))) {
    /* printf("%s -> \"%s\"\n", s2, s3); */
    help_insert(help, s2, s3); }
  fclose(fs);
  help_sort(help);
  return help;
  }


/* global, in case it gets needed */
int file_size;

char *c;
char *file_contents;
int car, fun, dyn, def;

#define na_c "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-.@$%^&*_=~<>/!?:"

static long is_name_char[256];

static void init_is_name_char()
{ int i;
  for(i=0; i<256; i++)
    is_name_char[i] = 0;
  for(i=strlen(na_c)-1; i>=0; i--)
    is_name_char[na_c[i]] = 1; }

void in_file(file_name)
char *file_name;
{ int file_desc;
  struct stat file_stat;
  caddr_t l_file_contents;
  stat(file_name, &file_stat);
  file_size = file_stat.st_size;
  file_desc = open(file_name, O_RDONLY);
  file_contents=mmap(0, file_size, PROT_READ, MAP_SHARED, file_desc, 0); }

void translate_car_name(symbol2,symbol2_size,symbol3_p,symbol3_size_p)
char *symbol2;
int symbol2_size;
char **symbol3_p;
int *symbol3_size_p;
{ /* printf("<car: %s>", symbol2); */
  *symbol3_p = trans_seek(car_trans, symbol2);
  if (!*symbol3_p)
    *symbol3_p = trans_seek(fun_trans, symbol2);
  if (!*symbol3_p)
    *symbol3_p = symbol2;
  if (*symbol3_p == symbol2) {
    char *help;
    if (help = help_seek(fun_help,symbol2)) outh(help);
    if (help = help_seek(car_help,symbol2)) outh(help); }
  *symbol3_size_p = strlen(*symbol3_p); }

void translate_dyn_name(symbol2,symbol2_size,symbol3_p,symbol3_size_p)
char *symbol2;
int symbol2_size;
char **symbol3_p;
int *symbol3_size_p;
{ /* printf("<dyn: %s>", symbol2); */
  *symbol3_p = trans_seek(dyn_trans, symbol2);
  if (!*symbol3_p)
    *symbol3_p = symbol2;
  if (*symbol3_p == symbol2) {
    char *help;
    if (help = help_seek(dyn_help,symbol2)) outh(help); }
  *symbol3_size_p = strlen(*symbol3_p); }

void translate_fun_name(symbol2,symbol2_size,symbol3_p,symbol3_size_p)
char *symbol2;
int symbol2_size;
char **symbol3_p;
int *symbol3_size_p;
{ /* printf("<fun: %s>", symbol2); */
  *symbol3_p = trans_seek(fun_trans, symbol2);
  if (!*symbol3_p)
    *symbol3_p = symbol2;
  if (*symbol3_p == symbol2) {
    char *help;
    if (help = help_seek(fun_help,symbol2)) outh(help); }
  *symbol3_size_p = strlen(*symbol3_p); }

void translate_sharp()
{ if (((c[1]=='m')||
       (c[1]=='m'))&&
      (c[2]=='\'')) {
  out1(c++); out1(c++); out1(" "); }
  
  else switch (c[1]) {
  
  case 'm':
  case 'M':
    out1(c++); out1(c++);
    break;
  
  case '$':
    outs("0x"); c+=2;
    while (  ((c[0]>='0')&&(c[0]<='9'))
	   ||((c[0]>='a')&&(c[0]<='f'))
	   ||((c[0]>='A')&&(c[0]<='F')))
      out1(c++);
    break;

  case '%':
    outs("0x"); c+=2;
    { long val;
      char hex[20];
      strtol(c, &val, 2);
      sprintf(hex, "%lx", val);
      outs(hex); }
    break;

  case '"':
    outs("( "); c+=2;
    while (1) {
      char cur;
      char printout[10];
      if ((c[0]=='"')&&(c[1]=='"')) {
	/* " */
	cur = '"'; c+=2; }
    else if (c[0]=='"') {
      break; }
    else { 
      cur = c[0]; c++; }
      if((cur>' ')&&(cur<='~')&&(cur!='\\'))
	sprintf(printout, "#\\%c ", cur);
      else
	sprintf(printout, "#\\\\x%02x ", cur);
      outs(printout); }
    outs(")"); c++;
    break; 

  case 'p':
  case 'u':
    outs("#f"); c+=2;
    break;

  case '[':
    outs("#("); c+=2;
    break;
    
  case '\\':
    /* do this by hand */
    /* NYI */
    oute();
    out1(c++); out1(c++);
    while (is_name_char[c[0]]) {
      out1(c++); }
    break;
    
  case '/':
    /* do this by hand */
    outs("#\\"); c+=2;
    out1(c++);
    break;

  default:
    /* can't translate */
    oute();
    out1(c++); out1(c++);
    break; }}

char *package;
int package_size;

void translate_symbol()
{ char symbol2[MAXSYMCHAR];
  int symbol2_size=0;
  int i;
  int translated;
  char *symbol3;
  int symbol3_size=0;
  if ((c[0]=='#')&&(c[1]==':')) {
    /* drop #: */
    c+=2; }
  /* clear symbol2 */
  for (i=0; i<MAXSYMCHAR; i++)
    symbol2[i]='\0';
  if ((c[0]==':')) {
    /* default package in symbol2 */
    strcat(symbol2, package);
    symbol2_size=package_size; }

  /* locate the whole symbol */
  while (is_name_char[c[0]]) {
    symbol2[symbol2_size]=c[0];
    c++;
    symbol2_size++; }

  /* special case for nil */
  if (!strcmp(symbol2,"nil")) {
    outs("()");
    return; }
      
  symbol3 = &symbol2[0];
  symbol3_size = symbol2_size;

  if (def) {
    /* translate lambda-list key-words */
    if (!strcmp(symbol2,"&optional"))     { outs("optional:");     return; }
    if (!strcmp(symbol2,"internal:&key")) { outs("internal.key:"); return; }
    if (!strcmp(symbol2,"&rest"))         { outs("rest:");         return; }
    if (!strcmp(symbol2,"&multiple"))     { outs("multiple:");     return; } }
  else if (car) {
    translate_car_name(symbol2,symbol2_size,&symbol3,&symbol3_size);
    dyn = (!strcmp(symbol2,"dynamic") || !strcmp(symbol2,"dynamic-let")); }
  else if (fun)
    translate_fun_name(symbol2,symbol2_size,&symbol3,&symbol3_size);
  else if (dyn) {
    translate_dyn_name(symbol2,symbol2_size,&symbol3,&symbol3_size);
    dyn=0; }
  
  /* if package is current package, */
  if (symbol3[package_size]==':') {
    symbol3[package_size]='\0';
    if (!strcmp(symbol3, package)) {
      /* drop explicit package */
      symbol3[package_size]=':';
      symbol3+=package_size;
      symbol3_size-=package_size; }
    else
      symbol3[package_size]=':'; }

  /* replace package separator `:' with `.' (not the trailing :, if any!) */
  /* known bug: replaces : inside ppipes as well */
  for (i=0; i<symbol3_size-1; i++)
    if (symbol3[i]==':')
      symbol3[i]='.';
    
  out(symbol3, symbol3_size); }

void translate_delimited(d)
char d;
{ out1(c++);
  while (1) {

    if ((c[0]==d)&&(c[1]==d)) {
      /* escape " */
      out1("\\"); out1(&d); c+=2;}

    else if (c[0]==d) {
      out1(c++);
      return; }

    else if (c[0]=='\n') {
      /* escape \n */
      outs("\\n"); c++;}

    else if (c[0]=='\\') {
      /* escape \\ */
      outs("\\\\"); c++;}

    else if (c[0]=='\r') {
      /* drop \r */
      c++;}

    else if (c[0]=='\t') {
      /* escape \t */
      outs("\\t"); c++; }

    else if ((c[0]=='%') && (d=='"')) {
      /* probably inside a format string */
      outs("%%"); c++; }

    else if ((c[0]=='~') && (d=='"'))
      /* probably inside a format string */
      switch (c[1]) {

      case '~':
	outs("~"); c+=2;
	break;

      case 'A':
	outs("%A"); c+=2;
	break;

      case '%':
      case '\n':
	outs("\\n"); c+=2;
	break;

      default:
	/* not a format, leave it alone */
	out1(c++);
	break; }
	
    else out1(c++); }}

void translate_string()
{ translate_delimited('"'); }

void translate_piped_symbol()
{ translate_delimited('|'); }

void translate_comment_block()
{ while (!((c[0]=='|')&&(c[1]=='#'))) 
    out1(c++);
    out1(c++);
    out1(c++); }

void translate_comment_line()
{ while (c[0]!='\n')
    out1(c++); }

main(argc, argv)
int argc;
char **argv;
{ char *file_end;
  char *file_name;
  if((argc!=3) && (argc!=4)) {
    printf("** Usage: %s <modulename> <source-filename> [<package>]\n", argv[0]);
    return -1; }
  init_is_name_char();
  fun_trans = trans_file("fun-trans");
  car_trans = trans_file("car-trans");
  dyn_trans = trans_file("dyn-trans");
  fun_help = help_file("fun-help");
  car_help = help_file("car-help");
  dyn_help = help_file("dyn-help");
  module=argv[1];
  file_name=argv[2];
  if (argc==4)
    package=argv[3];
  else
    package="user";
  package_size=strlen(package);
  in_file(file_name);
  c=file_contents;
  file_end = file_contents + file_size;

  outs(";;; Talk2 -> Talk3 translation of ");
  outs(file_name);
  outs(", as of ");
  { char current_time_string[100];
    time_t current_time = time(NULL);
    strftime(current_time_string, 100, "%c", localtime(&current_time));
    outs(current_time_string); }
  outs("\n");

  car=0; fun=0; def=0;

  while (c!=file_end) {

    if (is_name_char[c[0]]) {
      if (!strncmp(c,"defun",strlen("defun"))) def=1;
      if (!strncmp(c,"defmacro",strlen("defmacro"))) def=1;
      if (!strncmp(c,"defmethod",strlen("defmethod"))) def=1;
      if (!strncmp(c,"defgeneric",strlen("defgeneric"))) def=1;
      translate_symbol();
      car=0; fun=0; }

    else switch (c[0]) {

    case '#':
      switch (c[1]) {
      
      case ':':
	translate_symbol();
	car=0; fun=0;
	break; 
      
      case '\'':
	/* if #'(lambda...), drop #' */
	if (!strncmp(c, "#\'(lambda", strlen("#\'(lambda")))
	  c+=2;
	else {
	  fun=1;
	  out1(c++);
	  out1(c++); }
	break;
      
      case '|':
	translate_comment_block();
	break;

      default:
	translate_sharp();
	car=0; fun=0;      
	break; }

      break;

    case '|':
      translate_piped_symbol();

      car=0; fun=0;
      break; 

    case '"':
      translate_string();
      car=0; fun=0;
      break; 

    case '(':
      out1(c++);
      car=1; fun=0;
      break; 

    case ')':
      out1(c++);
      def=0;
      break; 

    case ';':
      translate_comment_line();
      break;
    
    default:
      out1(c++);
      break; }}


  { char buff[100];
    sprintf(buff, "\n;;; %s translation completed with %d warnings and %d syntax errors.\n", module, warns, syntax_errors);
    outs(buff); }

  return 0; }
