/*
 * Translit.c - Remap Characters
 *
 * Adapted from Kernighan and Plauger's "Software Tools"
 *         into "C" by Jeff Lydiatt
 *         22 Dec 1985
*/

#include <stdio.h>
#include <ctype.h>

#define MAXSET   81
#define MAXARR   81
#define ESCAPE   '@'
#define NOT      '!'
#define DASH     '-'
#define EOS      '\0'
#define YES      1
#define NO       0
#define TRUE	 1
#define FALSE	 0

main (argc, argv)
int argc;
char *argv[];
{

char arg[MAXARR];
register char c;
char from[MAXSET], to[MAXSET];
int makeset();
int allbut, collapse;
register int i, lastto;
int xindex();
extern int strlen();
extern int Enable_Abort;

Enable_Abort = YES;
if (setcmd(argc, argv, 1, arg, MAXARR) == FALSE)
   error("Usage: Translit <from >to FromSet ToSet.");
else if (arg[0] == NOT)
   {
    allbut = YES;
    if (makeset(arg, 1, from, MAXSET) == NO)
       error("FromSet: too large.");
   }
else
   {
    allbut = NO;
    if (makeset(arg, 0, from, MAXSET) == NO)
      error("FromSet: too large");
   }

if (setcmd(argc, argv, 2, arg, MAXARR) == FALSE)
  to[0] = EOS;
else if (makeset(arg, 0, to, MAXSET) == NO)
  error("ToSet: too large.");

lastto = strlen(to) - 1;
if (strlen(from) >= lastto || allbut == YES)
   collapse = YES;
else
   collapse = NO;
do
  {
   i = xindex(from, c = getchar(), allbut, lastto);
   if (feof(stdin))
      break;
   if (collapse == YES && i >= lastto && lastto >= 0)
     {
      putchar(to[lastto]); /* Collapse */
      do
        {
         i = xindex (from, c = getchar(), allbut, lastto);
        }
        while (i >= lastto && !feof(stdin));
     }
   if (feof(stdin))
     break;
   if (i >= 0 && lastto >= 0)
      putchar(to[i]); /* translate */
   else if (i < 0)
      putchar(c);     /* copy */
   else ;          /* Delete */
  }
    while(TRUE);
}

/* makeset: creates the from and to sets by calling fillset
            and addset */

int makeset (str, k, set, size)
   int k, size;
   char str[], set[];
{
   int i, j;
   int addset();

   i = k;
   j = 0;
   fillset(EOS, str, &i, set, &j, size);
   return addset(EOS, set, &j, size);
}

/* addset: - put c in set(j) if it fits, increment j */

int addset (c, set, j, maxsize)
   char c, set[];
   int *j, maxsize;
{
   if (*j > maxsize)
     return NO;
   else
     {
      set[(*j)++] = c;
      return YES;
     }
}

/* fillset: expand set at str(i) into set(j), stop at delim */

   fillset (delim, str, i, set, j, maxset)
   int *i, *j, maxset;
   char str[], delim, set[];
{
   char esc();
   int  junk;
   static char * digits = "0123456789";
   static char * lowalf = "abcdefghijklmnopqrstuvwxyz";
   static char * upalf  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

   for (; str[*i] != delim && str[*i] != EOS; (*i)++)
     {
      if (str[*i] == ESCAPE)
         junk = addset(esc(str, i), set, j, maxset);
      else if (str[*i] != DASH)
         junk = addset(str[*i], set, j, maxset);
      else if (*j <= 0  || str[*i+1] == EOS) /* literal - */
         junk = addset(DASH, set, j, maxset);
      else if (isdigit(set[*j-1]))
         dodash (digits, str, i, set, j, maxset);
      else if (islower(set[*j-1]))
         dodash(lowalf, str, i, set, j, maxset);
      else if (isupper(set[*j-1]))
         dodash(upalf, str, i, set, j, maxset);
      else
          junk = addset(DASH, set, j, maxset);
    }
}

/* esc --- map str(i) into escaped character */

char esc (str, i)
   char str[];
   int *i;
{
   if (str[*i] != ESCAPE)
      return str[*i];
   else if (str[*i+1] == EOS) /* Esc not special at end */
      return ESCAPE;
   else
      switch (str[++(*i)])
        {
         case 'n': return '\n';
         case 'r': return '\r';
         case 't': return '\t';
         default:  return str[*i];
        }
}

/* dodash: expand str(i-1) to str(i+1) into set(j) */

dodash (valid, str, i, set, j, maxset)
char valid[], str[], set[];
int  *i, *j, maxset;
{
   char esc();
   int junk, addset();
   int k, limit;
   int index();
   (*i)++;
   (*j)--;
   limit = index (valid, esc(str, i));
   for (k = index(valid, set[*j]); k <= limit; k++)
      junk = addset (valid[k], set, j, maxset);
}

/* index: find the character c in string str */

int index (str, c)
char str[], c;
{
   register int i;

   for (i = 0; str[i] != EOS; i++)
      {
       if (str[i] == c)
          return i;
      }
   return (int) -1;
}
 
/* xindex: invert the condition returned by index */

int xindex (str, c, allbut, lastto)
char str[], c;
int allbut, lastto;
{
   int index();

   if (c == EOF)
      return (int) -1;
   else if (allbut == NO)
       return index(str, c);
   else if (index(str, c) >= 0)
      return (int) -1;
   else
      return (int) (lastto + 1);
}

/* error: Print error message and quit */

error(msg)
char * msg;
{
   extern void exit();
   fputs(msg, stderr);
   fputs("\n", stderr);
   exit(12);
}

/* setcmd: set the n'th command line parameter into str */

setcmd (Nargs, Arg, n, str, maxsize)
int Nargs, n, maxsize;
char *Arg[], str[];
{
   register char *s1, *s2, *smax;

   if (n >= Nargs)
     return FALSE;
   s1 = Arg[n];
   s2 = str;
   smax = s1 + maxsize - 2;
   if (*s1 == '"')
      {
       for (++s1; *s1 != EOS && *s1 != '"' && s1 < smax;)
          *s2++ = *s1++;
      }
   else
      {
       for (; *s1 != EOS && s1 < smax;)
          *s2++ = *s1++;
      }
   *s2 = EOS;
   return TRUE;
}
