/* wrapag.c v1 By Bill the Hamster, godfrey2@coventry.ac.uk *
 * Copyright, Bill Godfrey, UK, 1995. See file COPYING for Licence.
 * Written for DICE C on the Amiga 1200.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define TRUE 1
#define FALSE 0
#define DEFAULTLINEWIDTH 75

int linewidth;			/* Line width, set by parameters in function main. */

struct linenode {		/* A line of text. */
	char text[255];			/* The line. */
	struct linenode *next;		/* List link. */
};

struct paragraph {		/* A paragraph of text, made up of lines. */
	struct linenode *head;		/* Head of list of lines. */
	struct linenode *last;		/* Last item in list. */
};

void newparagraph(struct paragraph *p)	/* Init new paragraph. */
{
	p->head=NULL;
	p->last=NULL;
}

void emptyparagraph(struct paragraph *p) /* Clear old paragraph. */ 
{
	struct linenode *curr;
	struct linenode *link;

	curr=p->head;
	while (curr != NULL) {
		link=curr->next;
		free(curr);
		curr=link;
	}
	newparagraph(p);
}

int addlinetop(struct paragraph *p, char *s)	/* Add line to paragraph. */
{
	struct linenode *nl;

	nl=malloc(sizeof(struct linenode));
	
	if (nl == NULL) {		/* Not enough memory? */
		return(FALSE);
	}
	else {
		strcpy(nl->text,s);	
		nl->next=NULL;			/* Setup new node. */
		if (p->head == NULL) {
			p->head=nl;
			p->last=nl;		/* Start empty list. */
		}
		else {
			p->last->next=nl;
			p->last=nl;		/* Continue existing list. */
		}
		
		return(TRUE);
	}
}

void getline(FILE *inf, char *s, int maxline)	/* Get a line of text from a file. */
{
	char inc;	/* Charcter to be inputted. */
	int charin;	/* Length of s. */

	s[0]='\0';
	charin=0;

	inc=fgetc(inf);
	while ((!feof(inf)) && (inc != '\n') && (charin<maxline)) {
		s[charin]=inc;
		charin++;
		
		inc=fgetc(inf);
	}
	s[charin]='\0';
}

struct wordnode {		/* A word. */
	char word[255];			/* The word. */
	int alen;			/* Actual length of word. */
	int vlen;			/* Virtual length. */
	int ns;				/* No. of spaces. */
	struct wordnode *next;		/* List link. */
};

/* Paragraphs which begin with a space will have the first
word as a space or tab only, with alen, vlen and ns all equal. */

struct wordlist {
	struct wordnode *head;	/* Head of the word list. */
};

void newwordlist(struct wordlist *wl) /* Start a new word list. */
{
	wl->head=NULL;
}

void emptywordlist(struct wordlist *wl)	/* Empty a word list. */
{
	struct wordnode *curr;
	struct wordnode *link;

	curr=wl->head;
	while (curr != NULL) {
		link=curr->next;
		free(curr);
		curr=link;
	}
	newwordlist(wl);
}

void remtrail(char *s)		/* Remove all trailing whitespace. */
{
	int lastchar;
	int c;

	lastchar=0;
	c=0;

	while (s[c] != '\0') {
		if ((s[c] != ' ') && (s[c] != '\t') && (s[c] != '\n') && (s[c] != '\0')) {
			lastchar=c;
		}
		c++;
	}
	s[lastchar+1]='\0';
}

void addaspace(char *s)
{
	int sl;

	sl=strlen(s);
	s[sl]=' ';
	s[sl+1]='\0';
}

char nextchar(struct linenode *curr, int cinl)	/* Look ahead to see what the next char is. */
{
	char nc;

	nc=curr->text[cinl];
	if (nc == '\0') {
		if (curr->next != NULL) {
			nc=curr->next->text[0];
		}
	}
	return(nc);
}

char loadchar(struct linenode **curr, int *cinl)
{
	char res;

	res=nextchar(*curr,*cinl);
	if (res == '\0') {
		(*curr)=NULL;
		cinl=0;
	}
	else {
		(*cinl)++;
		if ((*curr)->text[*cinl] == '\0') {
			if ((*curr)->next != NULL) {
				*curr=(*curr)->next;
				*cinl=0;
			}
		}
	}
	return(res);
}

void setns(struct wordnode *w)
{
	int c;
	int res;
	
	c=w->alen-1;	
	res=0;

	while (((w->word[c] == ' ') || (w->word[c] == '\t')) && (c != 0)) {
		c--;
		res++;
	}
		
	if (c == 0) {		/* If we fall out of the loop, c==0, add one to res. */
		res++;
	}
	
	w->ns=res;
}

void splittowords(struct paragraph *p, struct wordlist *wl)
{
	struct wordnode *tempw;
	struct wordnode *currw;
	struct wordnode *prevw;		/* List points for wordlist. */

	struct linenode *currl;		/* Current line. */
	int cinl;			/* Character in line. */

	char inc;			/* Character input. */

	int inalink;			/* Are we inside a link button text? */

	newwordlist(wl);
	prevw=NULL;
	currw=malloc(sizeof(struct wordnode));
	wl->head=currw;

	currw->word[0]='\0';
	currw->alen=0;
	currw->vlen=0;
	currw->ns=0;
	currw->next=NULL;

	currl=p->head;
	cinl=0;
	inalink=FALSE;


	inc=loadchar(&currl,&cinl);
	while (inc != '\0') {
		if ((inalink == TRUE) && (inc == '\"')) {
			while (inc != '}') {
				currw->word[currw->alen]=inc;
				currw->alen++;
				inc=loadchar(&currl,&cinl);
			}
			currw->word[currw->alen]=inc;
			currw->alen++;
			currw->word[currw->alen]='\0';
			inalink=FALSE;
		}
		else if (inc == '\\') {
			currw->word[currw->alen]=inc;
			currw->alen++;
			currw->word[currw->alen]=loadchar(&currl,&cinl);
			currw->alen++;
			currw->word[currw->alen]='\0';

			currw->vlen++;
		}
		else if ((inc == '@') && (nextchar(currl,cinl) == '{')) {
			inc=loadchar(&currl,&cinl);  /* And ignore it. */
			inc=loadchar(&currl,&cinl);
			if (inc == '\"') {
				inalink=TRUE;
				currw->word[currw->alen]='@';
				currw->alen++;
				currw->word[currw->alen]='{';
				currw->alen++;
				currw->word[currw->alen]='\"';
				currw->alen++;
				currw->word[currw->alen]='\0';
			}
			else {
				currw->word[currw->alen]='@';
				currw->alen++;
				currw->word[currw->alen]='{';
				currw->alen++;
				while (inc != '}') {
					currw->word[currw->alen]=inc;
					currw->alen++;
					inc=loadchar(&currl,&cinl);
				}
				currw->word[currw->alen]='}';
				currw->alen++;
				currw->word[currw->alen]='\0';
			}
		}
		else if (((inc == ' ') || (inc == '\t')) && ((nextchar(currl,cinl) != ' ') && (nextchar(currl,cinl) != '\t'))) {
			currw->word[currw->alen]=inc;
			currw->alen++;
			currw->word[currw->alen]='\0';

			currw->vlen++;

			setns(currw);

			if (wl->head == NULL) {	/* Empty list? */
				wl->head=currw;
			}
			else {			
				prevw->next=currw;
				prevw=currw;

				tempw=malloc(sizeof(struct wordnode));
				tempw->word[0]='\0';
				tempw->alen=0;
				tempw->vlen=0;
				tempw->ns=0;
				tempw->next=NULL;

				currw->next=tempw;
				currw=tempw;
			}
		}
		else {
			currw->word[currw->alen]=inc;
			currw->alen++;
			currw->word[currw->alen]='\0';
			
			currw->vlen++;
		}
		inc=loadchar(&currl,&cinl);		
	}
	

	if (currw->alen != 0) {
		setns(currw);	
		if (wl->head == NULL) {
			wl->head=currw;
		}
		else {
			prevw->next=currw;
			prevw=currw;
		}
	}
}

void testwraptext(struct wordlist *wl, int width, FILE *outf)
{
	struct wordnode *curr;

	printf("-=-=-=-=- New wordlist. -=-=-=-=-\n");
	curr=wl->head;
	while (curr != NULL) {
		printf(">%s< %d %d %d\n",curr->word, curr->alen, curr->vlen, curr->ns);
		curr=curr->next;
	}
	printf("-=-=-=-=-=- End of wordlist. -=-=-=-=-=-\n");
}

void wraptext(struct wordlist *wl, int width, FILE *outf)
{
	struct wordnode *curr;
	char work[255];		/* For modifying words. */
	char line[255];		/* The current line. */
	int alength;		/* Actual length of line. */
	int vlength;		/* Virtual length of line. */

	alength=0;
	vlength=0;
	curr=wl->head;
	line[0]='\0';

	while (curr != NULL) {
		if (vlength + curr->vlen - curr->ns > width) {
			remtrail(line);
			fprintf(outf,"%s\n",line);
			strcpy(line,curr->word);
			alength=curr->alen;
			vlength=curr->vlen;
		}
		else {
			strcat(line,curr->word);
			alength+=curr->alen;
			vlength+=curr->vlen;
		}
		curr=curr->next;
	}
	fprintf(outf,"%s\n",line);
}

/* Cut bit out of wrap text, inside loop.

		if (curr->vlen+charc <= width) {
			fprintf(outf,"%s",curr->word);
			charc += curr->vlen;
		}
		else if ((curr->vlen - curr->ns) + charc <= width ) {
			strcpy(work,curr->word);
			remtrail(work);
			fprintf(outf,"%s",work);
		}
		else {
			fprintf(outf,"\n%s",curr->word);
			charc=curr->vlen;
		}
		curr=curr->next;

*/

void testprocp(struct paragraph *p)
{
	struct linenode *curr;

	curr=p->head;
	while (curr != NULL) {
		printf(">%s<\n",curr->text);
		curr=curr->next;
	}

}

void procp(struct paragraph *p, FILE *outf) /* Process paragraph and output. */
{
	struct linenode *curr;
	struct wordlist words;

	if (p->head != NULL) {
		curr=p->head;
		while (curr != NULL) { 
			remtrail(curr->text);
			if (curr->next != NULL) {
				addaspace(curr->text);
			}
			curr=curr->next;
		}
		splittowords(p,&words);
		wraptext(&words,linewidth,outf);
		emptywordlist(&words);
	}
	/*testprocp(p);*/
} 



void dowrap(FILE *inf, FILE *outf)
{
	char s[255];
	struct paragraph pgraph;

	newparagraph(&pgraph);
	
	getline(inf,s,254);
	while (! feof(inf)) {
		
		if ((s[0] == '@') && (s[1] != '{')) {	/* Directive? */
			procp(&pgraph,outf);
			emptyparagraph(&pgraph);
			fprintf(outf,"%s\n",s);
		}
		else if (s[0] == '\0') {		/* Blank line. */
			procp(&pgraph,outf);
			emptyparagraph(&pgraph);
			fprintf(outf,"\n",s);
		}
		else if ((s[0] == ' ') || (s[0] == '\t')) {	/* Space/Tab */
			procp(&pgraph,outf);
			emptyparagraph(&pgraph);
			addlinetop(&pgraph, s);
		}
		else {					/* Normal line. */
			addlinetop(&pgraph, s);
		}

		getline(inf,s,254);
	}
	procp(&pgraph,outf);
}

void syntax()
{
	printf("WrapAG. By Bill the Hamster.\n");
	printf("  © Bill Godfrey, UK, 1995.\n\n");
	printf("Syntax;\n");
	printf("  WrapAG [-Wn] file.guide\n    Outputs wrapped file to screen.\n");
	printf("  WrapAG [-Wn] file.guide outfile.guide\n    Outputs wrapped file to outfile.guide\n\n");
	printf("\nInsert -W<number> for a specific line width, EG -W50. 74 by default.\n\n");
	printf("Licensed under the GNU General Public License, Version 2.\nPlease refer to file 'COPYING' for copyright notice.\n");
}

main(int argc, char **argv)
{
	FILE *infile;
	FILE *outfile;

	int p1;

	if ((argc == 1) || (argc >= 5)) {
		syntax();
	}
	else {
		linewidth=74;		/* Set the default width. */
		if ((argv[1][0] == '-') && ((argv[1][1] == 'W') || (argv[1][1] == 'w'))) {
			if ((argv[1][2] < '0') || (argv[1][2] > '9')) {
				printf("Failed maths, did we?\n   N U M B E R !\n");
				syntax();
				exit(28);
			}
			linewidth=atoi(&argv[1][2]);
			if ((linewidth < 1) || (linewidth >250)) {
				printf("A bit out of range, perhaps. Min=1, Max=250\n");
				syntax();
				exit(42);
			}
			p1=2;
		}
		else {
			p1=1;
		}

		infile=fopen(argv[p1],"r");
		if (infile == NULL) {
			printf("Error. Cannout open file, '%s'.\n",argv[1]);
		}
		else {
			if (argc == 1+p1) {
				dowrap(infile,stdout);
			}
			else {
				outfile=fopen(argv[p1+1],"w");
				if (outfile == NULL) {
					printf("Error. Cannot open file. '%s'.\n",argv[2]);
				}
				else {
					dowrap(infile,outfile);
					fclose(outfile);
				}
			}
			fclose(infile);
		}
	}
}

