/*
** GuideTool V1.0 (C) 1995 by Reinhard Katzmann. All Rights Reserved       
** The source code may not be altered or changed! No other similar programs
** like GuideTool may be published with the help of the source code. For
** other purposes parts of it may be used if _CLEARLY_ stated that your
** program uses them. Commercial programs need _written_ permission!
**
** The information contained herein is subject to change without notice,
**
** The source is provided "as is" without warranty of any kind, either ex-
** pressed or implied. The entire risk as to the use of this information is
** assumed by the user.
*/

#include <exec/types.h>
#include <exec/memory.h>
#include <workbench/startup.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <dos/var.h>
#include <dos/dos.h>

#include <clib/alib_protos.h>

#include <proto/exec.h>
#include <proto/dos.h>

#include "GuideTool_rev.h"

UBYTE versiontag[] = VERSTAG;
UBYTE Copyright[] = VERS " Copyright (C) 1995 by Reinhard Katzmann. All Rights Reserved";

#define TEMPLATE "FROM/A,TO/A,C=CONTENTFILE/K,X=XREFFILE/K,CONTENTS/S,INDEX/S,XREF/S,MAKECONT/S,MAKEXREF/S,L=LABELSIZE/K/N,R=XREFSIZE/K/N,ILN=INNERLINKS/S"
#define OPT_FROM    		0
#define OPT_TO      		1
#define OPT_CONTFILE    2
#define OPT_XREFFILE    3
#define OPT_CONTENTS		4
#define OPT_INDEX   		5
#define OPT_XREF    		6
#define OPT_MAKECONT		7
#define OPT_MAKEXREF		8
#define OPT_LABELSIZE   9
#define OPT_XREFSIZE   10
#define OPT_INNERLINKS 11
#define OPT_COUNT   	  12
#define DATABASE		"@database" /* AmigaGuide keywords */
#define NODE			"@node"
#define ENDNODE		"@endnode"
#define TOC				"@toc"
#define REM				"@rem"
#define REMARK			"@remark"
#define IGNORENODE   "@rem ignorenode"
#define STARTLINK		"@{"
#define LINK			" link "
#define LINK2			"link "
#define ENDLINK		"0}"        /* Only used for writing Content links */
#define STOPLINK     "}"
#define INDEX			"@index "
#define INDEXNAME    "Index"

#define DBUG 0

struct RDArgs *myrda=NULL;

char   *nodep[256]={0},*linksp[2048]={0},*linkdp[2048]={0},*endnodep[256]={0};
char	 *infile=NULL,*outfile=NULL,*xreffile=NULL,*contfile=NULL,*linkline[2048];
LONG 	 result[OPT_COUNT]={NULL,NULL};
BOOL   ilnk=FALSE;
ULONG  n=0,l=1;
UBYTE  min=255,lsize,rsize,smax=0,dmax=0,lsmax=0,ldmax=0;

void CheckStop(void);

void CloseAll(BOOL ec)
/* Gibt Speicher wieder frei */
{
	ULONG co;
	for (co=0;co<n;co++) {
		if (nodep[co]) FreeVec(nodep[co]);
	   if (endnodep[co]) FreeVec(endnodep[co]);
	}
	for (co=1;co<l;co++) {
		if (linksp[co]) FreeVec(linksp[co]);
		if (linkdp[co]) FreeVec(linkdp[co]);
		if (linkline[co]) FreeVec(linkline[co]);
	}
	if (myrda) FreeArgs(myrda);
	if (!result[OPT_CONTFILE] && contfile) FreeVec(contfile);
	if (!result[OPT_XREFFILE] && xreffile) FreeVec(xreffile);
	if (ec) exit(0);
	else exit(EXIT_FAILURE);
}

void CheckStop(void)
/* CTRL-C Checking */
{
  puts("Break charakter received. Aborting...");
  CloseAll(TRUE);
}

/*
** Read the nodes from an Amigaguide file
** Does not write anything but closes all files if not an AG Database
*/
void AGLoopNodes(FILE *rp,FILE *wp)
{
	BOOL adb=FALSE;
	char	zeile[256],hilfzeile[256];
	ULONG keyword,hp=0,zz=0;

	while ( (fgets(zeile,256,rp)) || !(feof(rp)) ) { /* Read all nodes */
		if (!strcmp(zeile,"\n")) continue;
		strcpy(hilfzeile,zeile);

		if (!adb) { /* Make sure we have an Amigaguide file */
			keyword=(ULONG)strstr(strlwr(hilfzeile),DATABASE);
			if (keyword) {
				adb=TRUE;
				continue;
			} else {
				if (wp) fclose(wp);
				if (rp) fclose(rp);
				printf("Not an Amigaguide Database file.\n");
				CloseAll(FALSE);
			}
		}

		keyword=(ULONG)strstr(strlwr(hilfzeile),NODE);
		if (keyword) { /* Add node to node array */
			if (n>255) {
				zz++;
				continue;
			}
			nodep[n]=AllocVec(rsize,MEMF_PUBLIC|MEMF_CLEAR);
			if (nodep[n]) { /* Read node name */
				hp=(ULONG)stptok(zeile,nodep[n],rsize," ");
				hp=(ULONG)stpblk((char *)++hp);
				if (stricmp(nodep[n],NODE)) { /* @node not at beginning of line */
					FreeVec(nodep[n]);
					nodep[n]=NULL;
					continue;
				}
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hilfzeile,rsize-2,"\""); /* Get node */
					strcpy(nodep[n],"\"");
					strcat(nodep[n],hilfzeile);
					strcat(nodep[n],"\"");
				}
				else hp=(ULONG)stptok((char *)hp,nodep[n],rsize," ");
				strcpy(hilfzeile,nodep[n]);
				strlwr(hilfzeile);
				if (!(stricmp(hilfzeile,"main")) || !(stricmp(hilfzeile,"main\n")) || !(stricmp(hilfzeile,"\"main\"")) ) { /* Ignore main node */
					FreeVec(nodep[n]);
					nodep[n]=NULL;
					continue;
				}
				if (strlen(nodep[n])>smax) smax=strlen(nodep[n]);
				if (DBUG) printf("nodep[%d]=%s,hp=%s\n",n,nodep[n],hp);
			}
			endnodep[n]=AllocVec(lsize,MEMF_PUBLIC|MEMF_CLEAR);
			if (endnodep[n]) { /* Read node label */
				strcat(endnodep[n]," ");
				hp=(ULONG)stpblk((char *)++hp);
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hilfzeile,lsize-2,"\""); /* Get node */
					strcat(endnodep[n],"\"");
					strcat(endnodep[n],hilfzeile);
					strcat(endnodep[n],"\"");
				} else {
					hp=(ULONG)stptok((char *)hp,hilfzeile,lsize," ");
					strcat(endnodep[n],hilfzeile);
				}
				if (strlen(endnodep[n])>dmax) dmax=strlen(endnodep[n]);
			}
			n++;
			continue;
		}

		keyword=(ULONG)strstr(strlwr(hilfzeile),IGNORENODE);
		if (keyword) { /* Ignore last node */
			hp=(ULONG)stptok(zeile,hilfzeile,256," ");
			hp=(ULONG)stpblk((char *)++hp);
			if (stricmp(hilfzeile,IGNORENODE)) continue; /* IGNORENODE not at beginning of line */
			if (nodep[n]) {
				FreeVec(nodep[n]);
				nodep[n]=NULL;
			}
			if (endnodep[n]) {
				FreeVec(endnodep[n]);
				endnodep[n]=NULL;
			}
			n--;
		}
		
	}
	if (zz) printf("Link number overflow: %d more links than program supports.\n",zz);
}

/*
** Read all links (Currently only LINK command supported)
** Does not write anything but closes all files if not an AG Database
*/
void AGLoopLinks(FILE *rp, FILE *wp)
{
	BOOL adb=FALSE,del=FALSE;
	char	zeile[256],hilfzeile[256];
	ULONG keyword,hp=0,hp03=0,co,zz=0;

	while ( (fgets(zeile,256,rp)) || !(feof(rp)) ) { /* Read all links */
		if (!strcmp(zeile,"\n")) continue;
		strcpy(hilfzeile,zeile);

		if (!adb) { /* Make sure we have an Amigaguide file */
			keyword=(ULONG)strstr(strlwr(hilfzeile),DATABASE);
			if (keyword) {
				adb=TRUE;
				continue;
			} else {
				if (wp) fclose(wp);
				if (rp) fclose(rp);
				printf("Not an Amigaguide Database file.\n");
				CloseAll(FALSE);
			}
		}

		keyword=(ULONG)zeile;
		while (keyword=(ULONG)strstr((char *)keyword,STARTLINK)) { /* Add Label to label array */
			if (l>2047) {
				zz++;
				keyword+=2;
				continue;
			}
			if ((*(char *)(keyword-1))=='\\') {
			  keyword+=2;
           continue;
         }
			linksp[l]=AllocVec(lsize,MEMF_PUBLIC|MEMF_CLEAR);
			if (linksp[l]) { /* Read link label name */
				hp=keyword+sizeof(STARTLINK)-1;
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hilfzeile,lsize-2,"\""); /* Get node */
					strcpy(linksp[l],"\"");
					hp03=(ULONG)stpblk(hilfzeile);
					hp03=(ULONG)strrev((char *)hp03);
					hp03=(ULONG)stpblk((char *)hp03);
					hp03=(ULONG)strrev((char *)hp03);
					strcat(linksp[l],(char *)hp03);
					strcat(linksp[l],"\"");
				} else { /* Original AmigaGuide has problems here, too, so I simply skip it */
					keyword+=2;
					continue; 
				}
				/* hp=(ULONG)stptok((char *)hp,linksp[l],lsize," "); */
			} else CloseAll(FALSE);
			strcpy(hilfzeile,(char *)hp);
			strlwr(hilfzeile);
			if (!((ULONG)strstr(hilfzeile,LINK)) && !((ULONG)strstr(hilfzeile,LINK2))) { /* not LINK command */
				FreeVec(linksp[l]);
				linksp[l]=NULL;
				keyword+=2;
				continue;
			}
			linkdp[l]=AllocVec(rsize,MEMF_PUBLIC|MEMF_CLEAR);
			if (linkdp[l]) { /* Read link (path) name */
				hp+=sizeof(LINK);
				if (*(char *)(hp-1)!=' ') hp--;
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hilfzeile,rsize-2,"\""); /* Get node */
					strcpy(linkdp[l],"\"");
					strcat(linkdp[l],hilfzeile);
					strcat(linkdp[l],"\"");
				} else {
					hp=(ULONG)stptok((char *)hp,linkdp[l],rsize,STOPLINK);
					hp03=(ULONG)linkdp[l];
					hp03=(ULONG)stptok((char *)hp03,hilfzeile,rsize," ");
					if ((*(char *)hp03)==' ') hp-=sizeof(hp03); else hp--;
					strcpy(linkdp[l],"\"");
					strcat(linkdp[l],hilfzeile);
					strcat(linkdp[l],"\"");
				}
			} else CloseAll(FALSE);
			/* Check if label is already in the array (Slow, but must be) */
			for (co=0;co<l;co++) {
				if ( (!stricmp(linksp[co],linksp[l])) && (!stricmp(linkdp[co],linkdp[l]))) {
				   if (ilnk) {
				   	del=TRUE;
						break; 
					}
               else {
						if (linksp[l]) FreeVec(linksp[l]);
						linksp[l]=NULL;
						if (linkdp[l]) FreeVec(linkdp[l]);
						linkdp[l]=NULL;
						break;
					}
				}
			}
			if (co!=l && !ilnk) {
				keyword+=2;
				continue;
			}
			if (linksp[l] && (strlen(linksp[l])>lsmax)) lsmax=strlen(linksp[l]);
			if (linkdp[l] && (strlen(linkdp[l])>ldmax)) ldmax=strlen(linkdp[l]);
			linkline[l]=AllocVec(10,MEMF_PUBLIC|MEMF_CLEAR);
			if (linkline[l]) {
				hp=(ULONG)stpblk((char *)++hp);
				hp=(ULONG)stptok((char *)hp,linkline[l],10,STOPLINK); /* Get line number */
			} else CloseAll(FALSE);
			if (del)
				if (!stricmp(linkline[l],linkline[co])) {
					if (linksp[l]) FreeVec(linksp[l]);
					linksp[l]=NULL;
					if (linkdp[l]) FreeVec(linkdp[l]);
					linkdp[l]=NULL;
					if (linkline[l]) FreeVec(linkline[l]);
					linkline[l]=NULL;
					keyword+=2;
					continue;
				}
			l++;
			keyword+=2;
		}
	}
	if (zz) printf("Link number overflow: %d more links than program supports.\n",zz);
}

/*
** Read Xref File
*/
void ParseXref(FILE *rp)
{
	ULONG hp=0,zz=0;
	char zeile[256],hzeile[256];

	while ( (fgets(zeile,256,rp)) || !(feof(rp)) ) {
		if (!strcmp(zeile,"\n")) continue;
		if (l>2047) {
			zz++;
			continue;
		}
		linksp[l]=AllocVec(lsize,MEMF_PUBLIC|MEMF_CLEAR);
		if (linksp[l]) { /* Read link label name */
			hp=(ULONG)zeile;
			hp=(ULONG)stpblk((char *)hp);
			if (*(char *)hp==';') continue;  /* ; = comment */
			if (*(char *)hp=='\"') {
				hp=(ULONG)stptok((char *)(hp+1),hzeile,lsize-2,"\""); /* Get node */
				strcpy(linksp[l],"\"");
				strcat(linksp[l],hzeile);
				strcat(linksp[l],"\"");
			} else hp=(ULONG)stptok((char *)hp,linksp[l],lsize," ");
			if (strlen(linksp[l])<min) min=strlen(linksp[l])-2;
		}
		linkdp[l]=AllocVec(rsize,MEMF_PUBLIC|MEMF_CLEAR);
		if (linkdp[l]) { /* Read link (path) name */
			hp=(ULONG)stpblk((char *)++hp);
			if (*(char *)hp=='\"') {
				hp=(ULONG)stptok((char *)(hp+1),hzeile,rsize-2,"\""); /* Get node */
				strcpy(linkdp[l],"\"");
				strcat(linkdp[l],hzeile);
				strcat(linkdp[l],"\"");
			}
		}		
		linkline[l]=AllocVec(10,MEMF_PUBLIC|MEMF_CLEAR);
		if (linkline[l]) { /* Read link (path) name */
			hp=(ULONG)stpblk((char *)++hp);
			hp=(ULONG)stptok((char *)hp,linkline[l],10,"\n");
		}
		l++;
	}
	if (zz) printf("Link number overflow: %d more links than program supports.\n",zz);
}

/*
** Create Contents from Amigaguide file and add it
*/
void ParseWriteContents(void)
{
	ULONG co;
	FILE *fp,*gp,*cp;
	char zeile[256],hilfzeile[256],hzeile[256];
	ULONG hp;

	fp=fopen(infile,"r");
	if (!fp) {
		printf("Could not open Amigaguide file for reading.\n");
		CloseAll(FALSE);
	}

	gp=fopen(outfile,"w");
	if (!gp) {
		printf("Could not open Contents AG file for writing.\n");
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}
	fputs("@node \"main\" \"main\"\n",gp);
	fputs("@toc \"main\"\n",gp);

	cp=fopen(contfile,"r");
	if (!cp) {
		printf("Could not open Contents file for reading.\n");
		if (gp) fclose(gp);
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

	while ( (fgets(zeile,256,cp)) || !(feof(cp)) ) {
		if (!strcmp(zeile,"###\n")) break;
      fputs(zeile,gp);
	}
	fputs("\n\n               Table of Contents\n\n",gp);

	AGLoopNodes(fp,gp);

	if (fp) fclose(fp);

	while ( (fgets(zeile,256,cp)) || !(feof(cp)) ) {
		if (!strcmp(zeile,"\n")) {
			fputs(zeile,gp);
			continue;
		}
		hp=(ULONG)stpblk(zeile); /* Skip leading blanks */
		if (*(char *)hp==';') continue;  /* ; = comment */
		if (*(char *)hp=='\"') {
			hp=(ULONG)stptok((char *)(hp+1),hilfzeile,rsize-2,"\""); /* Get node */
			strcpy(hzeile,"\"");
			strcat(hzeile,hilfzeile);
			strcat(hzeile,"\"");
		}
		else hp=(ULONG)stptok(zeile,hzeile,rsize," "); /* Get node */
		hp=(ULONG)stpblk((char *)++hp);
		
		if (DBUG) printf("hilfzeile=%s,hzeile=%s,hp=%s\n",hilfzeile,hzeile,hp);
      for (co=0;co<n;co++) {
      	if (!(stricmp(hzeile,nodep[co]))) {
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hilfzeile,lsize-2,"\""); /* Get endnode */
					strcpy(hzeile,"\"");
					strcat(hzeile,hilfzeile);
					strcat(hzeile,"\"");
				}
				else hp=(ULONG)stptok((char *)hp,hzeile,lsize," ");
				hp=(ULONG)stpblk((char *)++hp);
				strcpy(hilfzeile,"  ");
      		strcat(hilfzeile,STARTLINK);
      		strcat(hilfzeile,hzeile);
      		strcat(hilfzeile,LINK);
      		strcat(hilfzeile,nodep[co]);
      		strcat(hilfzeile," ");
      		strcat(hilfzeile,ENDLINK);
      		strcat(hilfzeile," ");
      		strcat(hilfzeile,(char *)hp);
      		strcat(hilfzeile,"\n");
      		fputs(hilfzeile,gp);
      		break;
      	}
      }
	}
	
	if(cp) fclose(cp);
	
	fputs("\n\n\n",gp);
	fputs(ENDNODE,gp);
	fputs("\n",gp);
	if (gp) fclose(gp);
	CloseAll(TRUE);
}

/*
** Convert Charakter to string
*/
char *c2str(char c,char *ch)
{
  ch[0]=c;
  ch[1]='\0';
  return ch;
}

/*
** Sort all links
*/
void SortXref(void)
{
	ULONG co,cu,elem,hp,hp03;

	for (co=2;co<l;co++) { /* Sort all links (Insertion Sort) */
		elem=(ULONG)linksp[co];
		hp=(ULONG)linkdp[co];
		hp03=(ULONG)linkline[co];
		cu=co;
		while (cu>=2 && stricmp(linksp[cu-1],(char *)elem)>0) {
			linksp[cu]=linksp[cu-1];
			linkdp[cu]=linkdp[cu-1];
			linkline[cu]=linkline[cu-1];
			cu--;
		}
		linksp[cu]=(char *)elem;
		linkdp[cu]=(char *)hp;
		linkline[cu]=(char *)hp03;
	}
}

/* 
** Create Index from Amigaguide file and add it
*/
void ParseWriteIndex(void)
{
	FILE *fp,*gp;
	ULONG co,elem;
	char	hzeile[256],old[256];

	fp=fopen(infile,"r");
	if (!fp) {
		printf("Could not open Xref file for reading.\n");
		CloseAll(FALSE);
	}

	gp=fopen(outfile,"w");
	if (!gp) {
		printf("Could not open Index file for writing.\n");
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

   ParseXref(fp);

	if (fp) fclose(fp);

	SortXref();

	strcpy(hzeile,INDEX);
	strcat(hzeile,INDEXNAME);
	strcat(hzeile,"\n\n");
	fputs(hzeile,gp);
	strcpy(hzeile,NODE);
	strcat(hzeile," \"Index\" \"");
	strcat(hzeile,INDEXNAME);
	strcat(hzeile,"\"\n\n   Index\n");
	fputs(hzeile,gp);
	for(co=1;co<l;co++) {
		elem=(ULONG)linksp[co];
		if (*(char *)elem=='\"') stptok((char *)(elem+1),hzeile,lsize-2,"\"");
		else strcpy(hzeile,linksp[co]);
		if (!co || ((UBYTE)tolower(hzeile[0])!=(UBYTE)tolower(old[0]))) {
			strcpy(old,hzeile);
			strcpy(hzeile,"\n   ");
			strcat(hzeile,strupr(c2str(old[0],old)));
			strcat(hzeile,"\n\n");
			fputs(hzeile,gp);
		}
		strcpy(hzeile,"   ");
		strcat(hzeile,STARTLINK);
		strcat(hzeile,linksp[co]);
		strcat(hzeile,LINK);
		strcat(hzeile,linkdp[co]);
		strcat(hzeile," ");
		strcat(hzeile,linkline[co]);
		strcat(hzeile,STOPLINK);
		strcat(hzeile,"\n");
		fputs(hzeile,gp);
	}
	fputs("\n\n\n",gp);
	fputs(ENDNODE,gp);
	fputs("\n",gp);
	if (gp) fclose(gp);
	CloseAll(TRUE);
}


/*
** Converts pnode to AG Command if it is in link array
** but not if it current node equal to link
*/
char *AddAGIfNode(char *pnode,char *hzeile,char *curnode)
{
	ULONG right=l-1,left=1,x;

	strcpy(hzeile,"\""); /* Convert it to a node with quutation marks */
	strcat(hzeile,pnode);
	strcat(hzeile,"\"");
	while(right>=left) {
		x=(left+right)/2;
		if (stricmp(hzeile,linksp[x])<0) right=x-1; else left=x+1;
		if (!(stricmp(hzeile,linksp[x]))) {
			if ( ! stricmp(curnode,linkdp[x]))
				if (!ilnk || linkline[x]==0) return pnode;
			strrev(hzeile);
			strcat(hzeile,"{@\0");
			strrev(hzeile);
			strcat(hzeile,LINK);
			strcat(hzeile,linkdp[x]);
			strcat(hzeile," ");
			strcat(hzeile,linkline[x]);
			strcat(hzeile,STOPLINK);
			return hzeile;
		}
	}
	strcpy(hzeile,pnode);
	return hzeile;
}

/*
** Check if token contains a valid AG command of type STARTLINK
*/
UBYTE GetAGCommand(char *tok,char **newl)
{
	char *newline, *nl;
   ULONG num;
	
	nl=*newl;
	if ( (*nl)=='@' && (*(nl+1))=='{' ) { /* Ignore command, set newline corresponding */
		if ( (*(nl+2))!='\"' ) {
		   stptok(nl,tok,256,STOPLINK); /* Get command */
		   strcat(tok,STOPLINK);
			nl=strstr(nl,STOPLINK);
			*newl=nl;
			return 1;
		} else { /* @{<label> <command>} link command */
			newline=nl; /* Save pointer for later use */
			nl=strstr(nl+1,"\""); /* Skip Label */
			nl=strstr(nl+1,"\"");
			nl=strstr(nl+1," "); /* Skip command */
			nl=strstr(nl+1," ");
			if ( *(nl+1)!='\"') nl=strstr(nl+1,STOPLINK);
			else { /* Seek end of link path name */
				nl=strstr(nl+1,"\"");
				nl=strstr(nl+1,"\"");
				nl=strstr(nl,STOPLINK);
			}
			num=strlen(newline)-strlen(nl);
			strncpy(tok,newline,num);
			tok[num]='\0';
			strcat(tok,STOPLINK);
		}
		*newl=nl;
		return 1;
	}
	return 0;
}

/*
** Checks if the character is in the symbol quantity
*/
BOOL Chk4Sym(char c)
{
	char  sm[]="\t\n !\"#$&\',-./:;=?@`"; /* symbol quantity */
	UBYTE right=strlen(sm),left=0,x;

	if (c<9) if (c=='§') return TRUE; else return FALSE;
	while(right>=left) {
		if (right>strlen(sm)) right=strlen(sm);
		x=(left+right)/2;
		if (c<sm[x]) right=x-1; else left=x+1;
		if (c==sm[x]) return TRUE;
	}
	return FALSE;
}

/*
** Checks string until quantity symbol for match with link labels
** Deletes charakters from behind
*/
BOOL MatchAGLabel(unsigned char *str, unsigned char *curnode)
{
	unsigned char cp[256],hp[256],co,cu,cl,hp01[2],*hp02,hp03[2],slen;

	strcpy(cp,str); /* Make a copy of string (str will be changed!) */
	cl=strlen(cp)-1;
	cu=cl;
	c2str(cp[cu],hp01); /* Backup Endsymbol */
	/* slen=cu */
	strcpy(hp,str); /* Make a copy of the original string */

	cp[cu]='\0'; /* Remove the end symbol */
	AddAGIfNode(cp,str,curnode);
	if (str[0]=='@') { /* It matches */
		strcat(str,hp01); /* Add the end symbol again */
   	return TRUE;
	}

	/* *str='\0'; * No need to strcpy(str,cp), because cp=="\0" */

	for (co=0;co<=cu; co++) {

		slen=co+cl-cu;
		if (Chk4Sym(cp[slen]) || !(cp[slen])) {  /* Found symbol or end */
			if (cp[slen]) { /* Not needed when we reached the end */
				c2str(cp[slen],hp03);
				hp02=strstr(cp,hp03)+1;
				strcpy(cp,hp02); /* Delete first word */
				cl=strlen(cp);
			} 
			if (cp[0]) {
				AddAGIfNode(cp,str,curnode);
				if (str[0]=='@') { /* It matches */
					hp[co+1]='\0'; /* Make a part string out of backup string */
					strcat(hp,str); /* Add the converted string */
					strcpy(str,hp); /* Combine both strings */
					if(hp01[0]) strcat(str,hp01); /* Add the end symbol again */
			   	return TRUE;
			   }
			}
      }
	}
	strcpy(str,hp);
	return FALSE;
}

/*
** Adds the nodes of a Contents file to the XRef list(s)
*/
void AddNode2XRef(FILE *fp)
{
   char hzeile[256],zeile[256];
   ULONG hp,co;

	while ( (fgets(zeile,256,fp)) || !(feof(fp)) ) {
		if (!strcmp(zeile,"###\n")) break;
	}

	while ( (fgets(zeile,256,fp)) || !(feof(fp)) ) {
		if (!strcmp(zeile,"\n")) continue;
		hp=(ULONG)stpblk(zeile); /* Skip leading blanks */
		if (*(char *)hp==';') continue;  /* ; = comment */
      linksp[l]=AllocVec(lsize,MEMF_PUBLIC|MEMF_CLEAR);
      linkdp[l]=AllocVec(rsize,MEMF_PUBLIC|MEMF_CLEAR);
      if (!linksp[l] || !linkdp[l]) return;
		if (*(char *)hp=='\"') {
			hp=(ULONG)stptok((char *)(hp+1),hzeile,rsize-2,"\""); /* Get node */
			strcpy(linkdp[l],"\"");
			strcat(linkdp[l],hzeile);
			strcat(linkdp[l],"\"");
		}
		else hp=(ULONG)stptok(zeile,linkdp[l],rsize," "); /* Get node */
		hp=(ULONG)stpblk((char *)++hp);
		
		if (DBUG) printf("hzeile=%s,hp=%s\n",hzeile,hp);

		if (*(char *)hp=='\"') {
			hp=(ULONG)stptok((char *)(hp+1),hzeile,lsize-2,"\""); /* Get endnode */
			strcpy(linksp[l],"\"");
			strcat(linksp[l],hzeile);
			strcat(linksp[l],"\"");
		}
		else hp=(ULONG)stptok((char *)hp,linksp[l],lsize," ");
		hp=(ULONG)stpblk((char *)++hp);
      
		for (co=0;co<l;co++) { /* Check for double links */
			if ( (!stricmp(linksp[co],linksp[l])) && (!stricmp(linkdp[co],linkdp[l]))) {
				if (linksp[l]) FreeVec(linksp[l]);
				linksp[l]=NULL;
				if (linkdp[l]) FreeVec(linkdp[l]);
				linkdp[l]=NULL;
				break;
			}
		}
		if (co!=l) continue;
		if (linksp[l] && (strlen(linksp[l])>lsmax)) lsmax=strlen(linksp[l]);
		if (linkdp[l] && (strlen(linkdp[l])>ldmax)) ldmax=strlen(linkdp[l]);
		linkline[l]=AllocVec(10,MEMF_PUBLIC|MEMF_CLEAR);
		if (linkline[l]) strcpy(linkline[l],"0"); else return;
		l++;
	}

}

/*
** Create Xrefs from Xreffile into Amigaguide file
*/
void ParseWriteXrefs(void)
{
	FILE *fp,*gp,*xp,*cp;
	BOOL noag=FALSE,adb=FALSE;
	ULONG keyword,hp;
	char	cc,zeile[256],node[256],hzeile[256],final[512],tmp[256],old[256];

	fp=fopen(infile,"r");
	if (!fp) {
		printf("Could not open Amigaguide file for reading.\n");
		CloseAll(FALSE);
	}

	gp=fopen(outfile,"w");
	if (!gp) {
		printf("Could not open Amigaguide file for writing.\n");
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

	xp=fopen(xreffile,"r");
	if (!xp) {
		printf("Could not open Xref file for reading.\n");
		if (gp) fclose(gp);
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

	ParseXref(xp); /* Read XRef file */
	if (xp) fclose(xp);

   if (contfile) { /* Optional Contents file for nodes */
     cp=fopen(contfile,"r");
     if (!cp) {
		printf("Could not open optional Contents file for reading.\n");
		   if (gp) fclose(gp);
		   if (fp) fclose(fp);
		   CloseAll(FALSE);
	   }
	   AddNode2XRef(cp); /* Adds the nodes to the Xrefs list(s) */
	   if (cp) fclose(cp);
   }

	SortXref(); /* sort links for binary search */

	while ( (fgets(zeile,256,fp)) || !(feof(fp)) ) { /* Read all links */
		if (!strcmp(zeile,"\n")) {
			fputs(zeile,gp);
			continue;
		}
		if (strlen(zeile)>256) { zeile[254]='\n'; zeile[255]=0; }
		strcpy(hzeile,zeile);

		if (!adb) { /* Make sure we have an Amigaguide file */
			keyword=(ULONG)strstr(strlwr(hzeile),DATABASE);
			if (keyword) {
				adb=TRUE;
				fputs(zeile,gp);
				continue;
			} else {
				if (gp) fclose(gp);
				if (fp) fclose(fp);
				printf("Not an Amigaguide Database file.\n");
				CloseAll(FALSE);
			}
		}

		if (keyword=(ULONG)strstr(strlwr(hzeile),REM)) {
			fputs(zeile,gp);
			continue;
		}
		if (keyword=(ULONG)strstr(strlwr(hzeile),REMARK)) {
			fputs(zeile,gp);
			continue;
		}

		keyword=(ULONG)strstr(strlwr(hzeile),NODE);
		if (keyword) { /* note node */
			hp=(ULONG)stptok(zeile,hzeile,256," ");
			hp=(ULONG)stpblk((char *)++hp);
			if (!stricmp(hzeile,NODE)) { /* Assure @node at beginning of line */
				strcpy(node,hzeile);
				if (*(char *)hp=='\"') {
					hp=(ULONG)stptok((char *)(hp+1),hzeile,rsize-2,"\""); /* Get node */
					strcpy(node,"\"");
					strcat(node,hzeile);
					strcat(node,"\"");
				}
				else hp=(ULONG)stptok((char *)hp,node,rsize," ");
				strcpy(hzeile,node);
				strlwr(hzeile);
				if (!(stricmp(hzeile,"main")) || !(stricmp(hzeile,"\"main\"")) ) { /* Ignore main node */
					strcpy(node,"\0");
					fputs(zeile,gp);
					while ( (fgets(zeile,256,fp)) || !(feof(fp)) ) { /* Seek ENDNODE */
						fputs(zeile,gp);
						if (!strcmp(zeile,"\n")) continue;
						strcpy(hzeile,zeile);
						keyword=(ULONG)strstr(strlwr(hzeile),ENDNODE);
						if (keyword) break;
					}
					if (feof(fp)) break; /* Amigaguide contains only main node (can this happen ?!?) */
				}
				else fputs(zeile,gp);
				continue;
			}
		}

		if (zeile[0]=='@' && zeile[1]!='{') {            /*  }  */
			fputs(zeile,gp);
			continue; /* Ignore Amigaguide commands */
		}

		keyword=(ULONG)zeile; /* Read Pointer: shows at which point in the line we are */
		strcpy(final,"\0"); 
		strcpy(old,"\0");
		cc=0;
		while ((*(char *)keyword)!='\n') { /* Check Line for match of AG Command, Label Match */
			strcat(old,c2str((*(char *)keyword),tmp)); /* Save old string */
			if (Chk4Sym((*(char *)keyword))) { /* Found Symbol */
				noag=FALSE;
				if (cc && ((*(char *)(keyword-1))=='\\')) noag=TRUE; /* fake command ? */
				if (!noag && GetAGCommand(tmp,(char **)&keyword)) { /* This is a command */
					if (strlen(old)>min) MatchAGLabel(old,node); /* Check if old string matches label links */
					old[strlen(old)-1]='\0';
					strcat(final,old); /* print AG string (old is changed now!) */
					strcpy(old,"\0"); /* Delete old strings */
					strcat(final,tmp);
					cc+=strlen(tmp);
				} else { /* test if string has min size and matches link label */
					if (strlen(old)>=min) {
						if(MatchAGLabel(old,node)) { /* Check if old string matches label links */
							strcat(final,old); /* print link label (old is changed now!) */
							strcpy(old,"\0"); /* Delete old strings */
						}
					}
				}
			}
			keyword++; /* Increase Read Pointer by 1 */
			cc++;
		}
		strcat(old,"\n");
		if (strlen(old)>=min) MatchAGLabel(old,node); /* Check if old string matches label links */
		strcat(final,old); /* print (old) string */
		fputs(final,gp);
		if (DBUG) printf("final=%s",final);

	}

	if (fp) fclose(fp);
	if (gp) fclose(gp);
	CloseAll(TRUE);
}

/*
** Create .cont File from Amigaguide for Cont
*/
void ParseWriteMyCont(void)
{
	FILE *fp,*gp;
	int co,cu,len;
	char	hzeile[256];

	fp=fopen(infile,"r");
	if (!fp) {
		printf("Could not open Amigaguide file for reading.\n");
		CloseAll(FALSE);
	}

	gp=fopen(outfile,"w");
	if (!gp) {
		printf("Could not open contents file for writing.\n");
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

	AGLoopNodes(fp,gp);

	if (fp) fclose(fp);

	fputs("###\n",gp); /* Important: Else the program asumes we have only a title */

	for (co=0;co<n;co++) { /* Write all nodes into outfile */
		strcpy(hzeile,nodep[co]);
	   len=smax-strlen(nodep[co]);
	   for (cu=0;cu<len;cu++) strcat(hzeile," ");
		strcat(hzeile,endnodep[co]);
		hzeile[strlen(hzeile)-1]='\0';
	   len=dmax-strlen(endnodep[co]);
	   for (cu=0;cu<len;cu++) strcat(hzeile," ");
		strcat(hzeile,"\"\n");
		fputs(hzeile,gp);
	}
	fputs("\n",gp);
	if (gp) fclose(gp);
	CloseAll(TRUE);
}

/*
** Create .xref File from Amigaguide for Xrefs
*/
void ParseWriteMakeXrefs(void)
{
	FILE *fp,*gp,*cp;
	int co,len,cu;
	char	hzeile[256];

	fp=fopen(infile,"r");
	if (!fp) {
		printf("Could not open Amigaguide file for reading.\n");
		CloseAll(FALSE);
	}

	gp=fopen(outfile,"w");
	if (!gp) {
		printf("Could not open Xref file for writing.\n");
		if (fp) fclose(fp);
		CloseAll(FALSE);
	}

	AGLoopLinks(fp,gp);

	if (fp) fclose(fp);

   if (contfile) { /* Optional Contents file for nodes */
     cp=fopen(contfile,"r");
     if (!cp) {
		printf("Could not open optional Contents file for reading.\n");
		   if (gp) fclose(gp);
		   if (fp) fclose(fp);
		   CloseAll(FALSE);
	   }
	   AddNode2XRef(cp); /* Adds the nodes to the Xrefs list(s) */
	   if (cp) fclose(cp);
   }

	for (co=1;co<l;co++) { /* Write all links into outfile */
		strcpy(hzeile,linksp[co]);
		strcat(hzeile," ");
	   len=lsmax-strlen(linksp[co]);
	   for (cu=0;cu<len;cu++) strcat(hzeile," ");
		strcat(hzeile,linkdp[co]);
		strcat(hzeile," ");
	   len=ldmax-strlen(linkdp[co]);
	   for (cu=0;cu<len;cu++) strcat(hzeile," ");
		strcat(hzeile,linkline[co]);
		strcat(hzeile,"\n");
		fputs(hzeile,gp);
	}
	fputs("\n",gp);
	if (gp) fclose(gp);
	CloseAll(TRUE);
}

void main(int argc, char **argv)
{
	BOOL cont=FALSE,ind=FALSE,xr=FALSE,myxr=FALSE,mycont=FALSE;
	struct 		 WBStartup *startup=NULL;

   if (argc==0) startup = (struct WBStartup *)argv;
	if (!startup) printf("%s\n",Copyright);
   if (argc==2 && !strcmp(argv[1],"?")) {
      printf("Usage: %s %s\n",argv[0],TEMPLATE);
      exit(TRUE);
   }
	
   if (!(myrda=ReadArgs(TEMPLATE, result, NULL))) {
      if (!startup) puts("Could not parse arguments.");
      CloseAll(FALSE);
   }

	if (!result[OPT_FROM]) {
		printf("Required argument missing.\n");
		CloseAll(FALSE);
	} else infile=(char *)result[OPT_FROM];

	if (!result[OPT_TO]) {
		printf("Required argument missing.\n");
		CloseAll(FALSE);
	} else outfile=(char *)result[OPT_TO];

	if (!stricmp(infile,outfile)) {
		printf("infile and outfile must differ!\n");
		CloseAll(FALSE);
	}

	if (result[OPT_CONTENTS]) cont=TRUE;
	if (result[OPT_INDEX]) ind=TRUE;
	if (result[OPT_XREF]) xr=TRUE;
	if (result[OPT_MAKEXREF]) myxr=TRUE;
	if (result[OPT_MAKECONT]) mycont=TRUE;	
   if (result[OPT_INNERLINKS]) ilnk=TRUE;

   if (result[OPT_LABELSIZE]) lsize=(UBYTE)result[OPT_LABELSIZE];
   else lsize=60;
   
   if (result[OPT_XREFSIZE]) rsize=(UBYTE)result[OPT_LABELSIZE];
   else rsize=40;
   
	if (!cont && !ind && !xr && !mycont && !myxr) cont=TRUE;  /* Contents creating is default */
   else if ( (cont&&ind) || (cont&&xr) || (cont&&myxr) || (cont&&mycont) || (ind&&xr) || (ind&&myxr) || (ind&&mycont) || (xr&&myxr) || (xr&&mycont) || (mycont&&myxr) ) {
   	printf("Specify only one of CONTENTS/S,INDEX/S,XREF/S,MAKECONT/S,MAKEXREF/S Options.\n");
   	CloseAll(FALSE);
   }

   if (cont) {
		contfile=(char *)result[OPT_CONTFILE];
	   if (!contfile) {
	   	contfile=AllocVec(30,MEMF_PUBLIC|MEMF_CLEAR);
			if (contfile) strcpy(contfile,"s:GT.cont");
		}
	}

	if (xr || ind) {
		xreffile=(char *)result[OPT_XREFFILE];
		if (!xreffile) {
			xreffile=AllocVec(30,MEMF_PUBLIC|MEMF_CLEAR);
			if (xreffile) strcpy(xreffile,"s:GT.xref"); /* Default XRef File */
		}
		contfile=(char *)result[OPT_CONTFILE]; /* Optional contfile */
	}

   if (cont) ParseWriteContents();  /* Create Contents from Amigaguide file and add it */
   if (ind) ParseWriteIndex();      /* Create Index from Amigaguide file and add it    */
   if (xr) ParseWriteXrefs();       /* Create Xrefs from Xreffile into Amigaguide file */
   if (mycont) ParseWriteMyCont();  /* Create .cont File from Amigaguide for Cont      */
   if (myxr) {
      contfile=(char *)result[OPT_CONTFILE]; /* Optional contfile */
      ParseWriteMakeXrefs(); /* Create .xref File from Amigaguide for Xrefs     */
   }
}
