/*	mark saeger,	msaeger@cse.unl.edu				*/
/*	output.c							*/
/*	Copyright 1995 Mark Saeger.					*/
/*									*/
/*	Permission is granted to any individual or instituition to use,	*/
/*	copy, or redistribute this executable so long as it is not	*/
/*	modified and that it is not sold for profit.			*/
/*									*/
/*	LIKE ANYTHING THAT IS FREE, MORE IS PROVIDED AS IS AND COMES	*/
/*	WITH NO	WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED.	*/
/*	IN NO EVENT WILL THE COPYRIGHT HOLDER BE LIABLE FOR ANY DAMAGES	*/
/*	RESULTING FROM THE USE OF THIS SOFTWARE.			*/

/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/*must use TIOCGWINSZ to get the width/height*/
/*must also initialize termional w/ ti*/
/**************************************/

#include "more.h"

#ifdef MINT
#include <termcap.h>
#endif

#ifdef UNIX
#include <curses.h>
#include <term.h>
#endif  /*have this after for redef of TRUE/FALSE*/

/*********/
int newone;
int globalback;
int testvalue;
int retflag=FALSE;
int test_mlcounter;
/********/

void output(int pipe, FILE *ip, FILE *tty)
{
	extern int optc,optd,optf,optl,optm,optr,opts,optu,optw;
	extern int newone;
	extern int testvalue;
	extern int retflag;
	extern int mcolumns, mlines;
	extern rgx *globrgx;
	extern int test_mlcounter;

	int c;
	int doing;

	extern char	*clearbuf, *bold, *norm, *bright, *nobright,
			*under, *nounder, *under1, *reset, *cleod, *cleol,
			*cls, *cr, *ll, *curson, *cursoff, *homec;

	extern int	optml, optpl;

	int mccounter;		/*my char counter*/
	int mlcounter;		/*my line counter*/
	int qmore, dum, x=0, max_to_read;
	int bs_dum=0;
	int dum2, dumcount, dumvalue=0;
	unsigned char bigbuf[BUFSIZ];	/*our buffer for read's(UNSIGNED)*/
				/*else ascii > 127 ends up signed*/
	char bufline[256];	/*to buffer the line for faster output*/
	int cntline=0;		/*the offset into the 'bufline' buffer*/
	long bigindex;		/*a return value for ftell*/
	long totallines=0;	/*store the total # lines in file*/
	long countlines=0;	/*store the current line out of total #*/
	int showprompt=FALSE;	/*a flag in the 'linefeed' case*/
	int soffset=FALSE;	/*for skip offset*/
	int poffset=TRUE;	/*flag if offset into file by +### */
	int boffset=FALSE;	/*flag if going backwards a screenful*/
	int boffset2=FALSE;	/*flag if still need to go backwards*/
	int xcount=1;		/*so if we say +150 end up at line 150*/
	int countchar=0;	/*used in read_backward for excessive line length*/
	int longlines[1024];
	static int xlonglines;
	static int startover;
	int backwardflag=FALSE;
	node *llist;
	node *bufferstdin;
	int fix_skip=0;
	int savemax_to_read;
	int flagmax=FALSE;
	

	mccounter=0;
	mlcounter=0;
	qmore=TRUE;

	setup_termcap();

	setbuf(ip,bigbuf);	/*set our buf to be BUFSIZ*/
	tputs(cursoff,1,putch);
	if(!pipe)
	{
		printf("\nCalculating line numbers...\n");
		fflush(stdout);
		while(qmore)	/*this gets the total lines in the file*/
		{
			if((bigindex = ftell(ip)) >= 0)
			{
				fseek(ip, bigindex, SEEK_SET);
				max_to_read = read(ip->_file,bigbuf,BUFSIZ);
				x=0;
			}
			if(max_to_read != BUFSIZ)	/******should be 0***/
				qmore = FALSE;
			while(x < max_to_read)
			{
				c=bigbuf[x++];
				if (c == '\n')
					totallines++;
			}
		}
	}
	else
		totallines=0;
	qmore=TRUE;	/*flag for when 'Q/q' pressed*/

	if(!pipe)
	{
		if((dum = fseek(ip,0L,SEEK_SET)) != 0)	/*find position in new file*/
			printf("Error SEEKING\n");	/*shouldn't happen*/
		/*if((max_to_read = fread(&bigbuf, 1, BUFSIZ, ip))==0)*/	/*and read in data*/
		if((max_to_read = read(ip->_file, bigbuf, BUFSIZ))==0)
			printf("ERROR reading\n");
	}
	else
	{
		int xy;

		max_to_read=read(ip->_file, bigbuf, BUFSIZ);

		bufferstdin=newn();
		llist=NULL;
		llist=fill(llist,bufferstdin,max_to_read,bigbuf);
/*		printf("read first chunk...\n");
		fgetc(tty);*/
	}
	if(globrgx!=NULL)
		search_regex(&poffset,0,pipe,llist);

	x=0;	/*clear*/
	if(optc && cls)
		tputs(cls,1,putch);

	while(qmore)			/*no quit? or still more*/
	{
		if(optpl && poffset)	/*want to start on an offset*/
		{
			fix_skip=1;
			if(read_forward(max_to_read,bigbuf,&x,&xcount,&poffset,&countlines))
				goto Mcontinue;
			cntline=0;	/*must reset to 0*/
		}
		if(boffset2)
		{
			boffset=TRUE;
			boffset2=FALSE;
			if(read_backward(bigbuf,&x,&mlcounter,&boffset,&countlines,&countchar))
				goto Bcontinue;
			
			backwardflag=TRUE;
			cntline=0;	/*must reset to 0*/
		}

/*this routine will display the ending chars of a longline when you use return
had to add checks to ensure werent going backwards, also, if on first page and
do a 'l', the 'l' must turn off retflag*/
		if(startover && retflag && !(backwardflag | fix_skip))
		{
			x=startover;
			cntline=0;
		}
/*really should only be uses when the last line is a longline and the user
 presses <cr>*/
		startover=0;	/*clear in all cases*/

		c=bigbuf[x++];	/*get next character*/

/*need to toss the next few characters since we already performed the
bold/underline operation in the previous buffer*/
		if(bs_dum && (bigbuf[x-1]!='\b'))
		{
/*works for s\bs\b]s\b*/
			int savex;

			x--;	/*should make this 0(.e.g. first character*/
			savex=x;	/*should also be 0*/
			while((bigbuf[x]==bs_dum) && (bigbuf[x+1]=='\b'))
				x+=2;	/*keep skipping*/
/*in the following we could not assume to add 1, cuz if there was no match
then we were already at the correct character to print*/
			if(savex!=x)	/*only do if we found a match*/
				x++;		/*around the last match*/
			c=bigbuf[x++];	/*and continue*/
		}
		else if(bs_dum && (bigbuf[x-1]=='\b'))
		{
/*works for s\bs]\bs\bs and s\bs\bs]ta */
			int savex;

			savex=x;
			while((bigbuf[x]==bs_dum) && (bigbuf[x+1]=='\b'))
				x+=2;
			if(savex!=x)
				x++;
			c=bigbuf[x++];
		}
		bs_dum=0;	/*clear*/

		switch (c)
		{
			case '\n':
/*this is necessary because you could have gone backwards a page and the
last line was a longline(so countlines says 80 vs. 81 since only 80 is
actually showing), then if you go back again, countlines is one less than
it is suppossed to be, hence countlines will ALWAYS be one less then*/

				mccounter=0;
				mlcounter++;
				countlines++;
				break;
			default:
				mccounter++;
				testvalue=0;
				if(mccounter > mcolumns-1)/*this is RIGHT*/
				{	/*fold lines*/
					int xxxx;
					int skip=FALSE;

/*if we get here we need to print the line since it will be wrapping*/
					bufline[cntline]='\0';
					cntline=0;
					printf("%s",bufline);

					mlcounter++;

					for(xxxx=0;xxxx<xlonglines;xxxx++)
						if(longlines[xxxx]==countlines+1)
							skip=TRUE;
					if(xxxx==0)
						longlines[xlonglines++]=countlines+1;
					if(xxxx!=0 && !skip)
						longlines[xlonglines++]=countlines+1;

					sort(longlines,xlonglines);

					if(optf)
						printf("\n");
					mccounter=0;

					if(mlcounter==mlines-1)
					{
/*						fix_countlines=1;*/

/*if we skipped into the file and the last line displayed is a longline, then
we need to offset it one*/
						if(retflag || fix_skip)
						{
							startover=x-1;
							testvalue=1;
							goto finish;
						}
					}
				}
				break;
		}
		if ((c >= ' ') && (c <= 127))
			bufline[cntline++]=c;	/*store data*/
		else
		{
			bufline[cntline]='\0';
			cntline=0;	/*reset*/
			printf("%s",bufline);
			switch(c)
			{
				case '\b':
					bs_dum=handle_bs(&x,&mccounter,bigbuf,max_to_read);
					break;
				case '\n':
					tputs(cleol,1,putch);
					printf("\n");
/*					tputs(cleol,1,putch);*/
					break;
				case '\t':
					handle_tab(x,&mccounter);
					break;
				default:  /* \r is sucked into here*/
					if(optr && (c > 127))
						printf("%c",c);
					else
					{
						tputs(norm,1,putch);
						tputs(bold,1,putch);
						mccounter++;
						if(c=='\r' && optm)
				/*abosrt ^M*/		mccounter--;
						else
						{
							if(c < ' ')
								printf("^%c",c+'A'-1);
							else
							{
								printf("\\");
								printf("%o",c);
								mccounter+=3;
							/*a 3 digit number*/
							}
						}
						tputs(norm,1,putch);
						if((c=='\f') && optl)
						{
							showprompt=TRUE;
							tputs(cleol,1,putch);
							printf("\n");
							tputs(cleod,1,putch);
							x++;
/*around the following \n, now have to simulate handling a nl taken from
 case at beginning of function*/
							mccounter=0;
							mlcounter++;
							countlines++;
						}
					}
					/*not outputting any other weird char*/
					break;
			}
		}			
finish:
		if((mlcounter==mlines-1) || showprompt)	/*so we know when at bottom*/
		{
			cntline=0;
			backwardflag=FALSE;
			if(optd)
				do_prompt(tty,&qmore,&mlcounter,&poffset,pipe,
				  countlines,totallines,longlines,xlonglines,
				  PROMPTVERBOSE,TRUE,llist);
			else
				do_prompt(tty, &qmore, &mlcounter, &poffset,pipe,
				  countlines, totallines, longlines,xlonglines,
				  PROMPTNORMAL,TRUE,llist);
			showprompt=FALSE;
			tputs(cleod,1,putch);
			if(optpl)	/*this is for the skip functions*/
			{
				xcount=1;
				mlcounter=0;
				poffset=TRUE;
			}
		}
test_fin:
		if(mlcounter < 0)
		{
			cntline=0;
			boffset=TRUE;
			printf("\n\nSkipping backward %d page(s)...\n\n",-(mlcounter/mlines));
			if(bigbuf[x-1]!='\n')
				test_mlcounter=TRUE;
			else
				test_mlcounter=FALSE;
			if(optc && cls)
				tputs(cls,1,putch);
			if(read_backward(bigbuf,&x,&mlcounter,&boffset,&countlines,&countchar))
				goto Bcontinue;
			backwardflag=TRUE;
		}
Mcontinue:
		savemax_to_read=max_to_read;
		if(x >= max_to_read)	/*to read next chunk of data*/
/*must have >= since x MAY be bigger if read happens in the middle of a
 special effect*/
		{
			if(!pipe)
			{
				if((bigindex = ftell(ip)) >= 0)	/*get offset*/	
				{
					if((dum=fseek(ip, bigindex, SEEK_SET))!=0) /*seek*/
						printf("ERROR seeking2\n");
					max_to_read=read(ip->_file,bigbuf,BUFSIZ);

/*if max_to_read==0 then there is no more data to read to leave x alone(so we
can go backwards)  and then reset max_to_read to the value it was before the
call.  NOTE: flagmax tells the routine at the bottom that it is time to 
display the (END) prompt*/
					if(max_to_read)
						x=0;
					else
					{
						if(optw)
						{
							flagmax=TRUE;
							max_to_read=savemax_to_read;
						}
					}
				}
			}
			else
			{
				node *walk;

				walk = llist;
				while(walk->dirty!=TRUE)
					walk=walk->link;
				if(walk->link==NULL)	/*read more*/
				{
					savemax_to_read=walk->size;
					max_to_read=read(ip->_file, bigbuf,BUFSIZ);
					if(max_to_read!=0)
					{
						x=0;
						bufferstdin=newn();
						llist=fill(llist,bufferstdin,max_to_read,bigbuf);
					}
					else
					{
						if(optw)
						{
							flagmax=TRUE;
							max_to_read=savemax_to_read;
						}
					}
				}
				else
				{
					int dummywalk;

/*					printf("\nat node==%d\n",walk->position);*/
					walk->dirty=FALSE;/*reset flag*/
					walk=walk->link;	/*next*/
/*					printf("now at node==%d\n",walk->position);*/
					max_to_read=walk->size;
					x=0;/*this is OK*/
					for(dummywalk=0;dummywalk<max_to_read;dummywalk++)
						bigbuf[dummywalk] = walk->nbuf[dummywalk];
					walk->dirty=TRUE;
				}
			}
		}
Bcontinue:
		if(boffset && x<0)
		{	
			if(!pipe)
			{
				long off=0;
				bigindex = ftell(ip);
				rewind(ip);
				if(bigindex % BUFSIZ)
					off=((bigindex / BUFSIZ)-1)*BUFSIZ;
				else
					off=((bigindex / BUFSIZ)-2)*BUFSIZ;
				fseek(ip,off,SEEK_SET);
	/*******should probably just skip read if seek fails*/
				max_to_read=read(ip->_file,bigbuf,BUFSIZ);
				x=max_to_read-1;
				boffset=FALSE;
				boffset2=TRUE;
			/*this routine will catch any errors that may
			crop up when seeking to the beginning of the file.
			specifically, if have a 1 chunk file, when the offset
			is calculated above, it will be negative...(the else)
			and this will redo the seek correctly*/
				if(off<0)	/*error routine*/
				{
					int dummy;

/*					printf("\nIN ERROR ROUTINE");*/
					rewind(ip);
					x=0;
					countlines=0;
					dummy=mlcounter;
					mlcounter=0;
					boffset2=FALSE;/*no process the data*/
					max_to_read=read(ip->_file,bigbuf,BUFSIZ);
/*	i think this may help		printf("\ndummy=%d**************\n",dummy);
					if(dummy-mlines > 0)
					{
						xcount=dummy-mlines;
						optpl=TRUE;
						poffset=TRUE;
					}*/
				}
			}
			else	/*pipe*/
			{
/*				printf("GOING BACKWARDS");*/
				if(findprev(llist,bigbuf,&max_to_read))
				{
					x=0;
					countlines=0;
					mlcounter=0;
					boffset2=FALSE;
				}
				else
				{
					x=max_to_read-1;
					boffset=FALSE;
					boffset2=TRUE;
				}
			}
		}
		if(flagmax && optw)
		{
			int testn;
			int tn;

			flagmax=FALSE;
			testn=mlcounter;	/*save this*/
/*we pass in testn since we dont actually want mlcounter modified before the
do_prompt, else the number of lines gets off*/
			filler(&testn,mlines,mccounter);
			tn=testn-mlcounter;	/*this is how many ~ printed*/
			testn=do_prompt(tty,&qmore,&mlcounter,&poffset,pipe,
				countlines,totallines,longlines,xlonglines,
				PROMPTEND, TRUE,llist);
/*if we are going backwards we now adjust mlcounter positively because the ~
need to be 'counted' as lines for the purposes of going backwards*/
			mlcounter+=tn;
			switch(testn)
			{
				case '\002':
				case 'b':
				case 'l':
				case '\014':
				case 'v':
				case 'h':
				case '?':
					break;
				default:
					qmore=FALSE;
					break;
			}
			if(qmore)
				goto test_fin;
		}
		if(max_to_read==0)
			qmore=FALSE;
	}
	tputs(curson,1,putch);
	tputs(cleod,1,putch);/*clean screen*/
	tputs(curson,1,putch);
	xlonglines=0;
}
