#include <ascii.h>
#include <fido.h>
#include <xfbuf.h>

/*
	T. Jennings
	15 May 87

 Perform a telex type function by printing all unread messages
to the printer or diskfile. Because of the funky scheduler in
Fido, where this might get invoked a number of times, dont creat
the output file unless there really are message to be printed. 

 This is a hack, and no apologies are made for the ugly code 
contained herein. So there.
*/

int outfile;			/* output file handle */

struct _msg msg;		/* message header structure */
struct _fido fido;		/* big system file */
struct _area msgarea;
struct _xfbuf xfbuf;		/* file search first/next struct */
struct _nmap nmap;
struct _ndat ndat;
struct _ntc ntc;

int nodefile;			/* node file handles */
int ntcfile;
int ndatfile;
int idxfile;
int ndxfile;
int zdxfile;

int nodetotal;			/* satisfy link */
int connect_tries,dial_tries;	/* satisfy link */

char nodepath[80] = "";		/* current directory */

struct _node id,altid;

/* Crude telex type function; prints out messages from the mail
directory. It prints only messages that have not been read yet; it then
increments the 'times' counter in the message. */

unsigned _stack = 20000;	/* be overly generous (paranoid) */

main(argc,argv)
int argc;
char **argv;
{
char msgname[SS],outname[SS],buff[SS],*p;
int i,f,v,n;
struct _xfbuf xfbuf;
int doff,doall;


	printf("TWIX (16 Jun 91) FidoNet Message Printer for ");
	copr();
	strcpy(outname,"PRN");			/* default output device */

	allmem();
	fastfile(8);				/* enable buffered I/O */

	n= open("fido.sys",0);				/* load system stuff */
	if (n == -1) {
		clprintf("The Fido system file \"FIDO.SYS\" is missing!\r\n");
		exit(1);
	}
	read(n,&fido,sizeof(fido));
	close(n);
	if (fido.fido_version != FIDOVER) {
		clprintf("Wrong Fido/FidoNet or TWIX version!\r\n");
		exit(1);
	}
	id.zone= fido.zone;			/* node ID */
	id.net= fido.net;
	id.number= fido.number;
	id.point= fido.point;

	altid.zone= fido.altzone;		/* alternate ID */
	altid.net= fido.altnet;
	altid.number= fido.altnumber;
	altid.point= fido.altpoint;

	open_node();				/* open the node files */
	open_nmap(fido.event);			/* specific nodemap */
	if (node_files()) {
		printf("Missing or wrong version nodelist -- run \"MAKELIST.EXE\"\r\n");
		exit(1);
	}
	doff= 1;				/* do formfeeds */
	doall= 0;				/* not all, only new ones */

	while (--argc) {
		++argv;
		cpyarg(buff,*argv);		/* a copy of any filename */
		if (*buff) strcpy(outname,buff);/* output name, if any */

		strip_switch(msgname,*argv);	/* a copy of the switches, */
		switch (*msgname) {
			case 'A': doall= 1; break;
			case 'V': doff= 0; break;
		}
	}
	printf("- Sending output to %s\r\n",outname);

	outfile= -1;					/* mark not open yet */

	v= open("fido.sys",0);				/* load system stuff */
	if (v == 0) {
		printf("The Fido system file FIDO.SYS is missing!\r\n");
		exit(1);
	}
	read(v,&fido,sizeof(fido));
	close(v);
	if (fido.fido_version != FIDOVER) {
		cprintf("Wrong Fido version!\r\n");
		exit(1);
	}

	id.zone= fido.zone;
	id.net= fido.net;
	id.number= fido.number;
	id.point= fido.point;

	altid.zone= fido.altzone;
	altid.net= fido.altnet;
	altid.number= fido.altnumber;
	altid.point= fido.altpoint;

	getarea(fido.netmarea,&msgarea);		/* select the msg area */

/* Now find all messages that have not been read, and print them. */

	makemname(msgname,"*.msg");
	xfbuf.s_attrib= 0;
	i= 0;
	while (_find(msgname,i++,&xfbuf)) {
		makemname(buff,xfbuf.name);		/* build file name */
		f= open(buff,2);			/* open the message, */
		if (f == -1) continue;
		n= read(f,&msg,sizeof(struct _msg));	/* read the header, */

		if ((n == sizeof(struct _msg)) && 	/* if a good msg file, */
		    ((msg.times == 0) || doall)) {	/* or one we want */

			n= atoi(xfbuf.name);		/* make the msg number */
			if (outfile == -1) {		/* if not open yet */
				outfile= creat(outname,1);
				if (outfile == -1) {	/* open output file */
					printf("Cant create output file %s\r\n",outname);
					exit(2);
				}
			}
			listhdr(&msg,n);		/* print header, */
			dumptext(f);			/* print the text, */
			if (doff) lputs("\014");	/* do FFs */
			++msg.times;			/* mark it read */
			lseek(f,0L,0);			/* seek over header, */
			write(f,&msg,sizeof(struct _msg)); /* write it out, */
		}
		close(f);				/* close the message */
	}
	if (outfile != -1) close(outfile);
	close_node();
	exit(0);
}

/* Get message area #n; return 0 if not set, illegal number
or other error. */

getarea(n,a)
unsigned n;		/* area number */
struct _area *a;	/* struct to load */
{
int off,f,i;

	if (n < 0) return(0);				/* no such area */
	off= sizeof(struct _fido);			/* offset to beg msg areas */
	if (n >= fido.marea_max) return(0);		/* msg area max */

	f= open("fido.sys",0);
	if (f == -1) return(0);				/* no system file! */

	off += n * sizeof(struct _area);		/* offset plus area # */
	lseek(f,(0L + off),0);				/* seek there, */
	i= read(f,a,sizeof(struct _area));		/* read some, */
	close(f);					/* immediately close it */

	if (i != sizeof(struct _area)) return(0);	/* check read error */
	a-> number= n;					/* set the path number */
	return(*a-> path != NUL);			/* invalid if no path set */
}
/* Assemble a full msg filename */

makemname(d,p)
char *d,*p;
{
	strcpy(d,msgarea.path);		/* put in the path, */
	strcat(d,p);			/* add the filename */
}

/* Display the contents of a text file on the screen, do word wrap
and all the other good stuff. The handle of an open file is passed. 
This does not output lines beginning with ^A. */


dumptext(f)
int f;					/* open file to dump to screen */
{
int screen;				/* current screen column */
int width;				/* callers maximum screen width */
char *cp;
char word[SS];				/* word we build before output */
int count;
int i;					/* index (length) of word */

char c,lastc;				/* current and previous character */
FLAG crsoh;				/* 1 == found embedded ^A line */

	if (f == -1) return;				/* be serious */

	width= 76;
	screen= 0;					/* nothing on screen yet */
	crsoh= 0;					/* start CR/SOH machine */
	c= CR;						/* in case first is SOH */

	while (1) {
		i= 0;					/* local word index */
		word[i]= NUL;				/* word is empty */
		while (i < sizeof(word)) {		/* word at a time */

			lastc= c;			/* remember last character */

			while (1) {
				c= NUL;
				count= read(f,&c,1);	/* get a character, */
				if (! count) break;	/* check EOF */

/* Do some processing to eliminate crap generated by other programs
and older Fidos. */
				switch (c) {
					case CR + 128: if (lastc == ' ') c= NUL;
						else c= ' '; break;
					case LF: c= NUL; break;
				}
				if (c) break;		/* got a real one */
			}
			if (count == 0) break;		/* end of file, word too */

/* The CR/SOH machine detects "IFNA Kludge" route lines and other nonsense
embedded in messages; this detects lines beginning with ^A and does not 
output them, except to sysops. */

			if (crsoh) {			/* if machine is on, */
				if (c == CR) crsoh= 0;	/* CR turns it off, */
				continue;		/* eat characters */
			}
			crsoh= (c == SOH) && (lastc == CR);
			if (crsoh) continue;		/* check for new start */

			if (c == CR) break;		/* end of (the) wor(l)d */

			word[i++]= c;			/* build the word */
			word[i]= NUL;			/* for output */
			if ((c == TAB) || (c == ' ')) break;
		}

		if (i + screen > width) {		/* if combined width */
			lputs("\r\n");			/* too long, wrap */
			screen= 0;			/* before typing word */
		}
		lputs(word);				/* output the word */
		screen += i; 				/* size on screen */

		if (c == CR) {				/* if a hard CR, */
			lputs("\r\n");			/* do a CR LF */
			screen= 0;
		}
		if (count == 0) break;			/* check EOF */
	}
	lputs("\r\n");
}

/* List the header of the message. */

listhdr(m,n)
struct _msg *m;
int n;
{
struct _node msg_orig,msg_dest;

	msg_orig.number= m-> orig_node;		/* set the node struct */
	msg_orig.net= m-> orig_net;		/* for internal compatibility */
	msg_orig.zone= id.zone;

	msg_dest.number= m-> dest_node;
	msg_dest.net= m-> dest_net;
	msg_dest.zone= id.zone;

/**/	msg_orig.zone= id.zone;
/**/	msg_dest.zone= id.zone;


	lprintf("#%-3u %s [%u] ",n,m-> date,m-> times);
	if (m-> attr & MSGPRIVATE) lprintf("(PRIVATE) ");
	if (m-> attr & MSGSENT) lprintf("(SENT) ");
	if (m-> attr & MSGREAD) lprintf("(RECV'D) ");
	if (m-> attr & MSGFILE) lprintf("(FILE ATTACHED) ");

	if (!is_us(&msg_orig) && !is_us(&msg_dest))
		lprintf("(IN TRANSIT) ");

	if (m-> attr & MSGORPHAN) lprintf("(ORPHAN) ");
	if (m-> attr & MSGKILL) lprintf("(KILL/SENT) ");
	lputs(bucks_str(m-> cost));
	lputs("\r\n");

	lprintf("From: %s",m-> from);
	lprintf(", %s",str_node(&msg_orig));
	if (! is_us(&msg_orig)) {
		if (find_ndat(&msg_orig) != -1)
			lprintf(", %s, %s",ndat.name,ndat.city);
	}
	lputs("\r\n");

	lprintf("To:   %s",m-> to);
	lprintf(", %s",str_node(&msg_dest));
	if (! is_us(&msg_dest)) {
		if (find_ndat(&msg_dest) != -1)
			lprintf(", %s, %s",ndat.name,ndat.city);
	}
	lputs("\r\n");

	if (m-> reply) lprintf("Reply To #%u   ",m-> reply);
	if (m-> up) lprintf("See Also #%u",m-> up);
	if (m-> reply || m-> up) lputs("\r\n");

	if (m-> attr & MSGFILE) lprintf("File(s): %s\r\n",m-> subj);
	else lprintf("Subj: %s\r\n",m-> subj);
}
/* Put stuff in the output file. */

lprintf(f)
char *f;
{
char buf[500];

	_spr(buf,&f);
	lputs(buf);
}
/* Output a string. */

lputs(s)
char *s;
{
	while (*s) lputc(*s++);
}

/* Output a character. */

lputc(c)
char c;
{
	write(outfile,&c,1);		/* ignore errors */
}
/* Display money. Since this returns a string, we use a static string
array and can make no more than 4 at a time. */

bucks_str(b)
unsigned b;
{
static char s[4][8],i;		/* string "$327.67", 4 each, plus index, */

	if (++i > 3) i= 0;	/* next string ... */

	sprintf(s[i],"%c%d%c%02d",
		fido.bucks,	/* dollar char, */
		b / 100,	/* dollars, */
		fido.dec_char,	/* the separator, */
		b % 100);	/* cents, */
	return(s[i]);
}

/* Report an error */

error(s)
char *s;
{
	puts(s);
	puts("\r\n");
}


clprintf(s) {}
/* Return true if the FBIT is set. */

fbit(bit)
int bit;
{
	return(fido.fbits[bit / sizeof(char)] & (1 << bit % sizeof(char)));
}
