/*
 * TRANS -- translate input to output as specified
 *
 * USAGE
 * trans how [ file ... ]
 *	how 	describes how to translate the input; see namemap[]
 *		for defined strings; to do multiple translations,
 *		separate them by commas and NO spaces.  For example,
 *			id,rev
 *		prints each input line as given and reversed.  You can
 *		use the shortest string uniquely matching any of the
 *		strings, or exactly matching any of the strings
 *	file ... list of input files; if none are given, use standard
 *		input
 */	
/****************************************************************
 * Copyright notice.						*
 * This software is copyrighted (c) 1991 by Matt Bishop and the *
 * Trustees of Dartmouth College.  All rights reserved.		*
 *								*
 * Author:	Matt Bishop					*
 * Address:	Department of Mathematics and Computer Science	*
 *		Dartmouth College				*
 *		Hanover, NH  03755-1831				*
 *		USA						*
 * telephone:	+1 603 646 3267					*
 * fax:		+1 603 646 1312					*
 * internet:	Matt.Bishop@dartmouth.edu			*
 * usenet:	...!decvax!dartvax!Matt.Bishop			*
 ****************************************************************/
#ifndef lint
static char *version = "Version GAMMA 6/31/91 Matt.Bishop@dartmouth.edu";
#endif

#include <stdio.h>
#include <ctype.h>
#ifdef __STDC__
#	include <stdlib.h>
#	include <string.h>
#else
#	include "stdlib.h"
#	include "string.h"
#endif

/*
 * type for lookup table
 */
struct nm {				/* used to hold keys, transforms */
	char *name;				/* key string */
	void (*map)();				/* the function */
};

/*
 * forward function declarations
 */
void trinit();		/* initialize and sort things */
void tropt();		/* process "how" option */
void settrans();	/* process each part of the "how" option */
void dofile();		/* routine to handle the files */
void dotrans();		/* routine to call the translation routine */
void ident();		/* transformation -- identity */
void tolow();		/* transformation -- all letters to lower case */
void toupp();		/* transformation -- all letters to upper case */
void tocap();		/* transformation -- first letter capital */
void dbl();		/* transformation -- repeat the line */
void reverse();		/* transformation -- reverse */
void revtolow();	/* transformation -- reverse, all to lower case */
void revtoupp();	/* transformation -- reverse, all to upper case */
void revtocap();	/* transformation -- reverse, first letter capital */
void revdbl();		/* transformation -- reverse, repeat */
void n0();		/* transformation -- identity, append 0 */
void n1();		/* transformation -- identity, append 1 */
void n2();		/* transformation -- identity, append 2 */
void n3();		/* transformation -- identity, append 3 */
void n4();		/* transformation -- identity, append 4 */
void n5();		/* transformation -- identity, append 5 */
void n6();		/* transformation -- identity, append 6 */
void n7();		/* transformation -- identity, append 7 */
void n8();		/* transformation -- identity, append 8 */
void n9();		/* transformation -- identity, append 9 */
void r0();		/* transformation -- reverse, append 0 */
void r1();		/* transformation -- reverse, append 1 */
void r2();		/* transformation -- reverse, append 2 */
void r3();		/* transformation -- reverse, append 3 */
void r4();		/* transformation -- reverse, append 4 */
void r5();		/* transformation -- reverse, append 5 */
void r6();		/* transformation -- reverse, append 6 */
void r7();		/* transformation -- reverse, append 7 */
void r8();		/* transformation -- reverse, append 8 */
void r9();		/* transformation -- reverse, append 9 */

/*
 * globals
 */
void (*ftrans[BUFSIZ])() = { NULL };	/* list of transformations */
int nftrans = 0;			/* number of transformations */
char tolowcase[128];			/* letter --> lower case (or as is) */
char toupcase[128];			/* letter --> upper case (or as is) */
struct nm namemap[] = {			/* table of keys to maps */
	{ "identity",		ident		},
	{ "capitalize",		tocap		},
	{ "fc",			tocap		},
	{ "fcapitalize",	tocap		},
	{ "lc",			tolow		},
	{ "lowercase",		tolow		},
	{ "uc",			toupp		},
	{ "uppercase",		toupp		},
	{ "r9",			r9		},
	{ "reverse",		reverse		},
	{ "rcapitalize",	revtocap	},
	{ "rfc",		revtocap	},
	{ "rfcapitalize",	revtocap	},
	{ "rlc",		revtolow	},
	{ "ruc",		revtoupp	},
	{ "double",		dbl		},
	{ "rdouble",		revdbl		},
	{ "0",			n0		},
	{ "1",			n1		},
	{ "2",			n2		},
	{ "3",			n3		},
	{ "4",			n4		},
	{ "5",			n5		},
	{ "6",			n6		},
	{ "7",			n7		},
	{ "8",			n8		},
	{ "9",			n9		},
	{ "r0",			r0		},
	{ "r1",			r1		},
	{ "r2",			r2		},
	{ "r3",			r3		},
	{ "r4",			r4		},
	{ "r5",			r5		},
	{ "r6",			r6		},
	{ "r7",			r7		},
	{ "r8",			r8		},
	{ NULL,			NULL		}
};

/*
 * main routine
 */
void main(argc, argv)
int argc;		/* arg count */
char *argv[];		/* arg vector */
{
	register int i;		/* counter in a for loop */
	register FILE *fp;	/* pointer to input file */

	/*
	 * initialize everything
	 * and process the argument list
	 */
	trinit();
	if (argc == 1)
		tropt("identity");
	else
		tropt(argv[1]);

	/*
	 * now do the transformations
	 * if no files given, read from stdin
	 */
	if (argc == 2)
		dofile(stdin);
	else{
		/*
		 * process each file
		 */
		for(i = 2; i < argc; i++){
			/* open it; on failure, give error message */
			if ((fp = fopen(argv[i], "r")) == NULL)
				perror(argv[i]);
			else{
				/* success! transform, then close */
				dofile(fp);
				fclose(fp);
			}
		}
	}
	/*
	 * bye!
	 */
	exit(0);
}

/*
 * do the transformations for each file
 */
void dofile(fp)
FILE *fp;		/* pointer to the file */
{
	register int j;		/* counter in a for loop */
	register int len;	/* length of input line */
	char buf[BUFSIZ];	/* input buffer */

	/*
	 * do the input a line at a time
	 */
	while(fgets(buf, BUFSIZ, fp) != NULL){
		/* get length, clobber trailing newline */
		if ((len = strlen(buf)) > 0 && buf[len-1] == '\n')
			buf[--len] = '\0';
		/* do the transformations */
		for(j = 0; j < nftrans; j++){
			(*ftrans[j])(buf, len);
			putchar('\n');
		}
	}
}

/* comparison function for transformation array */
int mapsort(x, y) struct nm *x, *y; { return(strcmp(x->name, y->name)); }

/*
 * initialize and sort arrays
 */
void trinit()
{
	register int i;		/* counter in a for loop */

	/*
	 * set up the arrays
	 */
	for(i = 0; i < 128; i++){
		tolowcase[i] = isupper(i) ? tolower(i) : i;
		toupcase[i] = islower(i) ? toupper(i) : i;
	}
	/*
	 * sort the transformation keys
	 */
	qsort((char *) namemap, sizeof(namemap)/sizeof(struct nm) - 1,
						sizeof(struct nm), mapsort);
}

/*
 * given the "how" string, parse it, and pass each word
 * (which are comma-separated) down to settrans
 */
void tropt(str)
char *str;		/* comma-seoparated list of keys */
{
	register char *p;		/* used to walk the list */

	/*
	 * do the loop
	 */
	while(str != NULL && *str){
		/* get the next key */
		for(p = str; *p && *p != ','; p++)
			;
		if (*p)
			*p++ = '\0';
		/* pass it down to settrans */
		settrans(str);
		/* advance the pointer */
		str = p;
	}
}

/*
 * given a key s, see if it is associated with a map,
 * and if so, and the map is not yet in the table of
 * transformations to apply, put it in
 */
void settrans(s)
char *s;		/* key to set transform */
{
	register int i, j;	/* counters in for loops */

	/*
	 * if it's all or empty, just map everything
	 */
	if (s == NULL || *s == '\0' ||
			strcmp(s, "all") == 0 || strcmp(s, "ALL") == 0){
		for(i = 0; namemap[i].name != NULL; i++){
			for(j = 0; j < nftrans; j++)
				if (ftrans[j] == namemap[i].map)
					break;
			if (j == nftrans)
				ftrans[nftrans++] = namemap[i].map;
		}
		return;
	}
	/*
	 * walk the table, looking for the best choise
	 */
	for(i = 0; namemap[i].name != NULL; i++){
		if (strncmp(s, namemap[i].name, strlen(s)) != 0)
			continue;
		if (strcmp(s, namemap[i].name) == 0){
			for(j = 0; j < nftrans; j++)
				if (ftrans[j] == namemap[i].map)
					break;
			if (j == nftrans)
				ftrans[nftrans++] = namemap[i].map;
			return;
		}
		if (namemap[i+1].name != NULL && 
				strncmp(s, namemap[i].name, strlen(s)) != 0){
			fprintf(stderr, "ambiguous transformation: %s\n", s);
			exit(1);
		}
		ftrans[nftrans++] = namemap[i].map;
		return;
	}
}
		
/*==================
 * the transformations
 * these should be optimized for speed
 */
#define XFORM(name) void name(buf, len) register char *buf; register int len;

XFORM(ident) { fputs(buf, stdout); }

XFORM(tolow) { while(*buf) putchar(tolowcase[*buf++]); }

XFORM(toupp) { while(*buf) putchar(toupcase[*buf++]); }

XFORM(tocap) { putchar(toupcase[*buf++]); fputs(buf, stdout); }

XFORM(reverse) { while(len-- > 0) putchar(buf[len]); }

XFORM(revtolow) { while(len-- > 0) putchar(tolowcase[buf[len]]); }

XFORM(revtoupp) { while(len-- > 0) putchar(toupcase[buf[len]]); }

XFORM(revtocap) { reverse(buf+1, len-1); putchar(toupcase[*buf]); }

XFORM(dbl) { ident(buf, len); ident(buf, len); }

XFORM(revdbl) { reverse(buf, len); reverse(buf, len); }

XFORM(n0) { fputs(buf, stdout); putchar('0'); }
XFORM(n1) { fputs(buf, stdout); putchar('1'); }
XFORM(n2) { fputs(buf, stdout); putchar('2'); }
XFORM(n3) { fputs(buf, stdout); putchar('3'); }
XFORM(n4) { fputs(buf, stdout); putchar('4'); }
XFORM(n5) { fputs(buf, stdout); putchar('5'); }
XFORM(n6) { fputs(buf, stdout); putchar('6'); }
XFORM(n7) { fputs(buf, stdout); putchar('7'); }
XFORM(n8) { fputs(buf, stdout); putchar('8'); }
XFORM(n9) { fputs(buf, stdout); putchar('9'); }

XFORM(r0) { reverse(buf, len); putchar('0'); }
XFORM(r1) { reverse(buf, len); putchar('1'); }
XFORM(r2) { reverse(buf, len); putchar('2'); }
XFORM(r3) { reverse(buf, len); putchar('3'); }
XFORM(r4) { reverse(buf, len); putchar('4'); }
XFORM(r5) { reverse(buf, len); putchar('5'); }
XFORM(r6) { reverse(buf, len); putchar('6'); }
XFORM(r7) { reverse(buf, len); putchar('7'); }
XFORM(r8) { reverse(buf, len); putchar('8'); }
XFORM(r9) { reverse(buf, len); putchar('9'); }
