#include "V.h"
/*
*    to [a|b|c|d|i|m|p] path...
*
* This program accepts email paths in several notations, and converts them
* to any of several common formats.  The one-letter first param says which
* format is wanted:
*
* a  "at" notation:   id@m4@m3@m2@m1
*
* b  "bang" notation: m1!m2!m3!m4!id
*
* c  "colon" notation: m1:m2:m3:m4:id
*
* d  "DECnet" notation: m1::m2::m3::m4::id
*
* i  "Internet" notation:   id%m4%m3%m2@m1
* m  "Mixed" notation: same as Internet.
*
* p  "percent" notation:  id%m4%m3%m2%m1
*
*/
#define FLDS 100	/* Max number of fields acceptable */
#define LINE 1000	/* Longest line we can handle */

int count = 0;		/* Number of conversions attempted so far */
int prec = '@';		/* Precedence in mixed-mode paths */
int type = 'b';		/* Which type address to we want? */

main(ac,av)
	int   ac;
	char**av;
{	int   a, i, n, r=0;
	int   c0, c1;
	char  line[LINE+1];
:1	ac = Vinit(ac,av);

	for (a=1; a<ac; a++) {
:3		V3 "Arg %d: \"%s\"",a,av[a] D;
		switch (c0 = av[a][0]) {
		case '-':
		case '+':
:3			V3 "Opt %d: \"%s\"",a,av[a] D;
			switch (c1 = av[a][1]) {
			case 'd': case 'D':
				Vopt(av[a]+2);
				break;
			default:
				V2 "Unknown opt \"%s\" ignored.",a,av[a] D;
				break;
			}
			break;
		default:
			if (!av[a][1]) {
				type = av[a][0];
:3				V3 "Type: '%c'",type D;
				break;
			}
:3			V3 "Arg %d: \"%s\" to be converted.",a,av[a] D;
			convert(av[a]);
			++count;
		}
	}
	if (count <= 0) {
		while (Fgets(line,LINE,stdin)) {
			i = strlen(line);
			while (i > 0 && line[i-1] == '\n')
				line[i-- - 1] = 0;
			convert(line);
		}
	}
	Exit(r);
}
convert(s)
	char* s;
{	int r=0;
	char* p;
	int   c;
	int   f, flds;	/* How many fields so far? */
	char* fld[FLDS];	/* first char of each field */
	int   dlm[FLDS];	/* Delimiter that ended field */
	int   l[FLDS];	/* Length of each field */
	Flag  infld;
	int   na=0, nb=0, nc=0, np=0;	/* Delimiter counters */
:2	Fenter("convert");

	if (BEq(s,"To:",3))
		s+= 3;
	if (BEq(s,"From:",5))
		s+= 5;
	while (isspace(*s))
		++s;
	f = -1;
	infld = 0;
	for (p=s; c = *p; p++) {
:6		V6 "c=%02X='%c'",B8(c),Dsp(c) D;
		switch (c) {
		case '\n':	break;
		case '@':	++na; goto delim;
		case '!':	++nb; goto delim;
		case ':':	++nc; goto delim;
		case '%':	++np; goto delim;
		delim:
			if (f < 0 || l[f] == 0) {
:2				V5 "Skip delimiter '%c'",c D;
				continue;
			}
			*p = 0;		/* Null-terminate the field */
			l[f] = p - fld[f];
			dlm[f] = c;
:2			V3 "Fld %d: l=%d dlm='%c' \"%s\"",f,l[f],dlm[f],fld[f] D;
			infld = 0;
			break;
		default:
			if (!infld) {
				if (++f > FLDS) {
:1					V1 "Too many fields: \"%s\"",p D;
					Fail;
				}
				fld[f] = p;
				l[f] = 1;
:2				V4 "f-- %d: \"%s\"",f,fld[f] D;
				infld = 1;
			}
			++l[f];
		}
	}
	if (infld) {
		l[f] = p - fld[f];
		dlm[f] = 0;
		if (l[f] <= 0) {
			V4 "Final field is null." D;
			--f;
:2		} else {
:2			V3 "Fld %d: l=%d dlm=nil \"%s\"",f,l[f],fld[f] D;
		}
	}
	if (f < 0) {
:1		V2 "### Final field count f=%d.",f D;
		Fail;
	}
	flds = f + 1;
:4	V4 "There were %d fields in the address",flds D;
/*
* Mixed-mode addresses are usually produced  by  doctrinaire  Internet
* mailers  that have no intention of cooperating with the competition.
* So most often the way to parse m1!id@m2  is  (m1!id)@m2.   But  this
* isn't  always correct, and the intended recipient may be m1!(id@m2).
* We can't logically decide, so we do either, and  let  the  user  use
* indicate via command-line delimiter args which is preferred.
*/
	if (na+np > 0 && nb+nc > 0) {
		V3 "Mixed-up address; delimiter counts:" D;
		V3 "\t'!' occurred %d times.",nb D;
		V3 "\t':' occurred %d times.",nc D;
		V3 "\t'@' occurred %d times.",na D;
		V3 "\t'%%' occurred %d times.",np D;
		switch (prec) {
		case '@':
		case '%':
			break;
		case ':':
		case '!':
			break;
		}
	}
	switch (type) {
	case 'A': case 'a': c = '@'; break;
	case 'B': case 'b': c = '!'; break;
	case 'C': case 'c': c = ':'; break;
	case 'D': case 'd': c = ':'; break;
	case 'I': case 'i': c = '%'; break;
	case 'M': case 'm': c = '%'; break;
	case 'P': case 'p': c = '%'; break;
	default:
:1		V2 "Unknown type '%c' treated as 'b'",type D;
		type = 'b';
		c = '!';
	}
	Fputs("To: ",stdout);
	if (nb+nc == 0 && na+np > 0) {
:3		V3 "Input was littlendian." D;
		switch (type) {
		case 'A': case 'a':
		case 'I': case 'i':
		case 'P': case 'p':
:3			V3 "Output is littlendian." D;
			for (f = 0; f < flds; f++) {
				Fputs(fld[f],stdout);
				if (type == 'i' && f == flds-2)
					c = '@';
				if (f < flds-1)
					Fputc(c,stdout);
			}
			break;
		case 'M': case 'm':
:3			V3 "Output is littlendian [mixed]." D;
			for (f = 0; f < flds; f++) {
				Fputs(fld[f],stdout);
				if (f == flds-2) {
					Fputc('@',stdout);
				} else
				if (f < flds-2)  {
					Fputc(c,stdout);
				}
			}
			break;
		case 'B': case 'b':
		case 'C': case 'c':
		case 'D': case 'd':
:3			V3 "Output is bigendian." D;
			for (f = flds-1; f >= 0; f--) {
				Fputs(fld[f],stdout);
				if (f > 0) {
					Fputc(c,stdout);
					if (type == 'd')
						Fputc(c,stdout);
				}
			}
			break;
		}
		Done;
	}
	if (nb+nc > 0 && na+np == 0) {
:3		V3 "Input was bigendian." D;
		switch (type) {
		case 'A': case 'a':
		case 'P': case 'p':
:3			V3 "Output is littlendian." D;
			for (f = flds-1; f >= 0; f--) {
				Fputs(fld[f],stdout);
				if (f > 0) {
					Fputc(c,stdout);
					if (type == 'i')
						c = '%';
				}
			}
			break;
		case 'I': case 'i':
		case 'M': case 'm':
:3			V3 "Output is littlendian." D;
			for (f = flds-1; f >= 0; f--) {
				Fputs(fld[f],stdout);
				if (f > 0) {
					if (f == 1)
						Fputc('@',stdout);
					else
						Fputc(c,stdout);
				}
			}
			break;
		case 'B': case 'b':
		case 'C': case 'c':
		case 'D': case 'd':
:3			V3 "Output is bigendian." D;
			for (f = 0; f < flds; f++) {
				Fputs(fld[f],stdout);
				if (f < flds-1) {
					Fputc(c,stdout);
					if (type == 'd')
						Fputc(c,stdout);
				}
			}
			break;
		}
		Done;
	}
	V1 "\nNote: Mixed-up address; delimiter counts:" D;
	V1 "\t'!' occurred %d times.",nb D;
	V1 "\t':' occurred %d times.",nc D;
	V1 "\t'@' occurred %d times.",na D;
	V1 "\t'%%' occurred %d times.",np D;
fail:
done:
	Fputc('\n',stdout);
:2	Fexit;
	return r;
}
