/*
   Strongtest.c
   Tests StrongHelp manuals for 'dangling' links, etc.

   v0.00  Dec '95    Incomplete.
   v1.00  960113     Rewrite becuase I 'accidentally' deleted v0.00
   v1.10  960213     Partial Links implemented
   v1.20  960226     Simple links with Postfix & Prefix implemented
   v1.30  960227/00  Subpage testing implemented
   v1.31  960227/01  added -w option, tidied messages
   v1.32  960227/09  added activity messages
                     tidied up messages even more
   v1.33  960227/10  Fixed bug in partial link testing that caused bogus
                         successes for partial links.
   v1.40  960227/11  Added test for bad <> (missing \)
                     Niceified pluralisation in messages
   v1.41  960227/12  Expanded bad character error message to include code
                     Made \r a valid character
   v1.42  960227/13  Expanded -w to include pages of the form xx>yy

    Musus Umbra, 1996.

   Sorry about the state of this, C isn't my first language.
   Also, much mucking around has been done, so there are lots of
   commented bits of line here and there - sorry, I guess I'm just
   too paranoid to delete them :-)
*/

#define VERSION "1.42 (27 Feb 1996)"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "kernel.h"
/*#include "swis.h"*/

#define OS_File 0x08
#define OS_GBPB 0x0c
#define DDEUtils_ThrowbackStart 0x42587
#define DDEUtils_ThrowbackSend 0x42588
#define DDEUtils_ThrowbackEnd 0x42589


static char *identity = "StrongTest " VERSION
						" [compiled "__DATE__"]\n"
						" Musus Umbra, 1996\n\n";


typedef struct listnode {
          struct listnode* next;
          char*     pagename;
          char*     path;
          int       referenced;
        } node;

char  block[1024];         /* general workspace */
char  manual[256];         /* the manual name we're working on */
node* head;                /* global to save passing it around everywhere */
_kernel_swi_regs r;        /* global to save time */
_kernel_oserror *e;        /* ... */
int  dangling = 0;         /* dangling link flag/counter */
int  unaccessible = 0;     /* unaccessible page flag/counter */
int  fserror = 0;          /* filing system error flag/counter */
int  badchars = 0;         /* non-ASCII chars flag/counter */
int  missingslash = 0;     /* \ missing in \<> */
int  warnings = 0;         /* warnings flag/counter */
int  throwback = 0;        /* throwback flag */
int  debugmd = 0;          /* debugmdness flag :-) */
int  verbose = 0;          /* verbosity flag */
int  warnext = 0;          /* whether to raise warnings for external links */
int  throwbackstarted = 0;



char *plural(int n, char* pe)
{
   return n==1 ? "" : pe;
}


char* leafname(char* source)     /* Function to find leafname of object */
{
  char* ret;
  char* search;
  for (search=source; *search!=0; search++) { /* find terminator */ };
  for (ret=search; *ret!='.' && ret>=source; ret--) { /* find leaf */ };
  return(ret+1);
}


int isconversion(char *p)
{
   while (*p++)
   {
      if ( *p=='>' ) { return 1; }
   }
   return 0;
}


/*----------------------------------------------------------------------------
   throwback_start
   => n/a
   <= n/a
*/
void throwback_start(void)
{
   e=_kernel_swi(DDEUtils_ThrowbackStart,&r,&r);
   if (e)
   {
       fprintf(stderr,"WARNING - %s\n",e->errmess);
       return;
   }
   r.r[0]=0;
   r.r[2]=(int) manual;
   e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
   if (e)
   {
      if (debugmd)
      {
         fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
      }
      return;
   }
}



/*----------------------------------------------------------------------------
   throwback_error(file, line, m, x)
   => x = flag: 1=>already done an error in this file, 0=>first error ...
      file = ->filename
      line = line
      m = ->error message
   <= n/a
*/
void throwback_error(char* file, int line, char *m, int x)
{
   if (throwbackstarted==0)
   {
      throwback_start();
      throwbackstarted=1;
   }
   r.r[0]=1;
   r.r[2]=(int) file;
   r.r[3]=line;
   r.r[4]=1;
   r.r[5]=(int) m;
   e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
   if (e)
   {
      if (debugmd)
      {
         fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
      }
   }
}



/*----------------------------------------------------------------------------
   throwback_warning(file, line, m, x)
   => x = flag: 1=>already done an error in this file, 0=>first error ...
      file = ->filename
      line = line
      m = ->error message
   <= n/a
*/
void throwback_warning(char* file, int line, char *m, int x)
{
   if (throwbackstarted==0)
   {
      throwback_start();
      throwbackstarted=1;
   }
   r.r[0]=1;
   r.r[2]=(int) file;
   r.r[3]=line;
   r.r[4]=0;
   r.r[5]=(int) m;
   e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
   if (e)
   {
      if (debugmd)
      {
         fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
      }
   }
}





/*---------------------------------------------------------------------------
   throwback_end
   => n/a
   <= n/a
*/
void throwback_end(void)
{
   e=_kernel_swi(DDEUtils_ThrowbackEnd,&r,&r);
   if (e)
   {
      if (debugmd)
      {
         fprintf(stderr,"DEBUG: end throwback failed - %s\n",e->errmess);
      }
   }
}




/*----------------------------------------------------------------------------
   oof(x)
   => x
   <= ->"On" | "Off"
   .
*/
char* oof(int x)
{
   return x ? "On" : "Off";
}


/*----------------------------------------------------------------------------
   unpad(s)
   => s = ->string
   <= s modified so that there are no preceding or trailing spaces.
*/
void unpad(char *s)
{
   char *c;

   c=s+strlen(s);
   while (*--c==' ')       /* scan from end for non-space */
      ;
   *++c=0;                 /* terminate string over first space */

   c=s;
   while (*c++==' ')
      ;
   c--;
   if (c==s) { return; }
   while ( (*s++=*c++)!=0 )
      ;
   *s=0;
}




/*----------------------------------------------------------------------------
   printlist(head)
   => head = ->head node of list
   <= n/a
   debugging function to print out the full contents of the list
*/
void printlist(node *head)
{
  if ( !(head->next) )
  {
    printf("---the list is empty\n");
    return;
  }

  while (head->next)
  {
    head=head->next;
    printf("   %s - %s\n",head->pagename,head->path);
  }
}


/*----------------------------------------------------------------------------
   lowerise(text)
   => text = ->string
   <= n/a
   Converts the string to lower case.
*/
void lowerise(char *text)
{
   while (*text)
   {
     *text=tolower(*text);
     text++;
   }
}



/*----------------------------------------------------------------------------
   lowcpy(d,s)
   => d = ->destination string
      s = ->source string
   <= n/a (d updated)
   copies s into d, ensuring that d is all lower case.
*/
void lowcpy(char *d, char *s)
{
   while (*s)
   {
      *d++=tolower(*s++);
   }
   *d=0;
}



/*----------------------------------------------------------------------------
   sinsert(d,i)
   => d= ->destination string (not empty)
      i= ->string to insert
   <= i is inserted at the start of s
*/
void sinsert(char *d, char *i)
{
   int l,m,k;

   l=strlen(i);
   m=strlen(d);

   for (k=m; k>=0; k--)
   {
      d[k+l]=d[k];
   }
   for (k=0; k<l; k++)
   {
      d[k]=i[k];
   }
}








/*----------------------------------------------------------------------------
   dnamecpy(s,d)
   => s= ->destination string
      d= ->directory name
   <= s modified to hold the dir name, with '.'s missing
*/
void dnamecpy(char *s, char *d)
{
   while (*d)
   {
      if (*d!='.') { *s++=*d; }
      d++;
   }
   *s=0;
}





/*----------------------------------------------------------------------------
   makenameubar(s,d,o)
   => s= ->destination string
      d= ->full path for object (without trainling '.')
      o= ->objectname
   <= s modified, d corrupted
*/
void makenameubar(char *s, char *d, char *o)
{
   char *p;
   char *dd;
   char *dirstart;

   dd=malloc(strlen(d)+1);
   strcpy(dd,d);
   d=dd;

   while (*d!=0)
   {
      dirstart=d;
      for (p=d; *p!='.' && *p; p++)
         ;
      if (*(p-1)=='_')
      {
         dnamecpy(s,dirstart);
         strcat(s,o);
         free(dd);
         return;
      }
      if (*p) { d=p+1; } else { d=p; }
   }
   strcpy(s,o);
   free(dd);
}


/*----------------------------------------------------------------------------
   addnode(tail,dir,objname)
   => tail = ->current tail of list
      dir = ->name of directory 'page' is in
      objname = ->leaf name of object
   <= new tail of list
*/
node* addnode(node *tail, char* dir, char* objname /*, int ubar*/)
{
   int i;
   node *newnode;
   char *tptr;
   char *pptr;

   newnode = malloc(sizeof(node));
   if ( !newnode )
   {
      fprintf(stderr,"insufficient memory (case 2)\n");
      exit(4);
   }
   tail->next = newnode;
   newnode->next = 0;
   i=strlen(dir)+strlen(objname)+2;
   tptr = malloc(i*sizeof(char));
   pptr = malloc((strlen(objname)+30)*sizeof(char));
   if (!(tptr && pptr))
   {
      fprintf(stderr,"insufficient memory (case 2a)\n");
      exit(4);
   }
   newnode->path = tptr;
   newnode->pagename = pptr;
   strcpy(tptr,dir);
   strcat(tptr,".");
   strcat(tptr,objname);

/*
 *   if (ubar && 0)             was || 1
 *  {
 *     makenameubar(pptr,dir,objname);
 *  } else {
 *     strcpy(pptr,objname);
 *  }
 * REPLACED BY NEXT LINE */
   strcpy(pptr,objname);

   newnode->referenced=0;

   lowerise(tptr);
   lowerise(pptr);

/*
   if (debugmd)
   {
      printf("   added page '%s', path '%s'\n",pptr,tptr);
   }
*/
   return newnode;
}


/*----------------------------------------------------------------------------
   endinubar(s)
   => s = ->directory name
   <= 1 => s ends in a _, 0 otherwise
*/
int endinubar(char *s)
{
   while(*++s)
      ;
   return (*--s=='_');
}




/*----------------------------------------------------------------------------
   buildlist(dir,tail,ubar)
   => dir->manual name or subdirectory
      *tail is the node to add the list on to (ie. the tail)
      ubar = 1 => a directory with an _ ending leads to this dir
   <= ->new tail of list
   builds the linked list of page information for directory 'name'.
   NB: RECURSIVE
*/
node *buildlist(char* dir,node *tail /*, int ubar*/ )
{
   char pathname[256];
   int  offset;
   int  read;
   int *filetype = (int*) (block);
   int *objtype = (int*) (block+16);
   char *objname = (block+20);

   offset = 0;
   do
   {
      r.r[0]=10;              /* OS_GBPB 10 */
      r.r[1]=(int) dir;       /* ->directory name */
      r.r[2]=(int) block;     /* ->buffer */
      r.r[3]=1;               /* entries to read */
      r.r[4]=offset;          /* offset to start from */
      r.r[5]=1024;            /* size of buffer */
      r.r[6]=(int) "*";       /* filename to match */
      e=_kernel_swi(OS_GBPB,&r,&r);
      if (e)
      {
        if (debugmd)
        {
          fprintf(stderr,"FS error: %s\n",e->errmess);
        }
        fserror++;
        return tail;
      }
      read = r.r[3];
      offset = r.r[4];

      if (read>0)
      {
         switch ( *objtype )
         {
            case 1 : switch ( (*filetype) & 0xffffff00 )
                     {
                        case 0xffffff00 : /* text file */
                        case 0xfffffd00 : /* data file */
                                          tail=addnode(tail,dir,
                                                       objname /*,ubar*/);
                                          break;
                        default : ; /* skip anything else */
                     }
                     break;
            case 2 : strcpy(pathname,dir);
                     strcat(pathname,".");
                     strcat(pathname,objname);
                     tail=buildlist(pathname,tail /*,endinubar(objname)*/);
                     break;
            default: ; /* Huh? what the hell is *this* doing here?!?! */
         }
      }
   } while (offset!=-1);

   return tail;
}




/*----------------------------------------------------------------------------
   isexternal(s)
   => s = ->string (manual page name)
   <= 1 is page is *probably* external, 0 otherwise
   checks page name for ':' or '.'
*/
int isexternal(char* s)
{
   int e = 0;

   while(*s && !e)
   {
      if (*s==':' || *s=='.') { e=1; }
      s++;
   }
   return e;
}



/*-----------------------------------------------------------------------------
  testpartial(pagename,path,link)
  => pagename ->pagename to test link against
     path = ->pathname of page
     link ->link to test
  <= !0 if link fails, 0 if link succedes.
*/

int testpartial(char *pagename, char *path, char *link)
{
   char temp[256];
   char *t = path;
   char *u = temp+255;
   int  linklen;
   int  templen;
   int  dotflag = 0;

   *u-- = 0;                  /* terminate temp string */
   templen = 0;               /* length of temp string */
   linklen = strlen(link);    /* length of link */

   while ( *++t )
      ;                       /* find last char of pathname */
   t--;

   while (templen<linklen && *t)
   {
      if ( *t !='.' )
      {
         *u-- = /*tolower*/(*t);
         templen++;
      }
      else
      {
         dotflag = 1;
      }
      t--;
   }

/*
   if (debugmd)
   {
      printf("+++testing partial %s=>%s ??? ",link,u+1);
      printf("[%s,%s]\n",link,u+1);
   }
*/

   if ( (templen!=linklen) || !dotflag )
   {
      return 1;               /* no need for string cmp */
   }

   return strcmp(link,u+1);
}



/*----------------------------------------------------------------------------
 tonlcpy(d,s)
 => d = ->dest string
    s = ->source string
 <= d updated with s upto \n or 0. Leading whitespace in s is skipped.
*/
void tonlcpy(char *d, char *s)
{
	while (isspace(*s) && *s!='\n' && *s!=0)
	{
		s++;
	}
	while (*s!='\n' && *s!=0)
	{
		*d++ = *s++;
	}
	*d = 0;    /* terminate dest. */
}


/*----------------------------------------------------------------------------
 lowmatch(t,a)
 => t = ->test string
    a = ->wanted string
 <= ->char after wanted string in test string or NULL if no match
*/
char *lowmatch(char *t, char *a)
{
	while ( (*a == tolower(*t)) && *a!=0)
	{
		a++;
		t++;
	}
	return (*a==0 ? t : NULL);
}



/*---------------------------------------------------------------------------
 testsubpage(lab,page)
 => lab   = ->search link (incl '.')
    page  = ->pagetext (null terminated)
 <= -1 if no subpage match, 1 if a match
*/
int testsubpage(char *lab, char *page)
{
	char temp[256];
	int state = 0;
	char *t;
	if (debugmd)
	{
		printf("@@@testing subpage %s ???\n",lab+1);
	}

	lab++;             /* skip over leading '.' */
	while (*page && (state != 100) )
	{
		switch(state)
		{
			case 0 :	if (*page == '#')
						{
							t=lowmatch(page+1,"subpage");
							if (t)
							{
								tonlcpy(temp,t);
								if (strcmp(lab,temp)==0)
								{
									state = 100;
								}
							}
						}
						state = 1;
						break;
			case 1 :	if (*page=='\n')
						{
							state = 0;
						}
						break;
		}
		page++;
	}
	return (state==100 ? 1 : -1);
}




/*----------------------------------------------------------------------------
   testlink(name,dest,page,line,thrown,pagetext)
   => name = ->name of link (for messages, etc)
      dest = ->link destination (pagename)
   <= n/a (global dangling updated if necessary, referenced element of
            list node of destination page updated as necessary).
*/
void testlink(char *name, char *dest, node *page,int line,int *thrown,char *p)
{
   node *scan;
   int  succeded = 0;
   char search[256];
   char search2[256];

   lowcpy(search,dest);
   unpad(search); /* moved from below */
   if (1 || endinubar(search))   /* || 1 is to force test <x=>x.!Root>*/
   {
      strcpy(search2,search);
      strcat(search2,"!root");
   } else
   {
      *search2=65;
      *(search2+1)=0;
   }
   /* unpad was here */
   if (debugmd)
   {
     printf("***testing link <%s=>%s> [%s,%s]\n",name,dest,search,search2);
   }

   if (*search=='*') { return; }       /* don't test OS commands! */
   if (*search=='.')
   {
      succeded = testsubpage(search,p);  /* either -1 or 1 */
   }

   if (isexternal(search) && succeded==0)  /* don't test external links */
   {
      if (warnext)
      {
         warnings++;
         if (strcmp(name,dest)==0)
         {
            if (verbose)
            {
              fprintf(stderr,"Warning: page '%s' line %d - link <%s> "
                   "is external (not tested)\n", page->pagename,line,name);
            }
            sprintf(search,"link <%s> is external (not tested)",name);
            if (throwback)
            {
               throwback_warning(page->path,line,search,*thrown);
               *thrown=1;
            }
         } else {
            if (verbose)
            {
               fprintf(stderr,"Warning: page '%s' line %d - link "
                              "<%s=>%s> is external (not tested)\n",
                              page->pagename,line,name,dest);
            }
            sprintf(search,"link <%s=>%s> is external (not tested)",name,dest);
            if (throwback)
            {
               throwback_warning(page->path,line,search,*thrown);
               *thrown=1;
            }
         }
      }
      return;
   }

   scan=head->next;
   while (scan && succeded==0)
   {
      if ( strcmp(scan->pagename,search)==0
                      /* || strcmp(scan->pagename,search2)==0*/ )

      {
         if (debugmd)
         {
            printf("  succedes (normal): ->page '%s'\n",scan->pagename);
         }
         succeded=1;
         (scan->referenced)++;
      }
      else
      {
         if ( testpartial(scan->pagename,scan->path,search)==0 )
         {
            if (debugmd)
            {
               printf("  succedes (partial): ->page '%s'\n",scan->pagename);
            }
            succeded=1;
            (scan->referenced)++;
         }
         else
         {
            if ( testpartial(scan->pagename,scan->path,search2)==0 )
            {
               if (debugmd)
               {
                  printf("  succedes (partial.!Root): ->page '%s'\n",scan->pagename);
               }
               succeded=1;
               (scan->referenced)++;
            }
         }
      }
      scan=scan->next;
   }

   if (succeded==0)
   {
      if (strcmp(name,dest)==0)
      {
         if (strlen(name)==0)
         {
            if (verbose)
            {
               fprintf(stderr,"page '%s', line %d - Missing '\\' in \"\\<>\"\n",
                              page->pagename,line);
            }
            sprintf(search,"Missing \\ in \"\\<>\"");
            if (throwback)
            {
               throwback_error(page->path,line,search,*thrown);
               *thrown=1;
            }
            dangling--;   /* because it'll be incremented after this block */
            missingslash++;
         }
         else
         {
            if (verbose)
            {
               fprintf(stderr,"page '%s', line %d - link <%s> fails\n",
                              page->pagename,line,name);
            }
            sprintf(search,"link <%s> fails",name);
            if (throwback)
            {
               throwback_error(page->path,line,search,*thrown);
               *thrown=1;
            }
         }
      } else {
         if (verbose)
         {
            fprintf(stderr,"page '%s', line %d - link <%s=>%s> "
                           "fails\n",page->pagename,line,name,dest);
         }
         sprintf(search,"link <%s=>%s> fails",name,dest);
         if (throwback)
         {
            throwback_error(page->path,line,search,*thrown);
            *thrown=1;
         }
      }
      dangling++;
   }
}


/*----------------------------------------------------------------------------
  prepost(ptr,*pre,*post)
  => ptr = ->page text, null terminated
  <= *pre & *post = -> prefix & postfix for simple links
*/
void prepost(char *page, char* *pre, char* *post)
{
	static char prefix[128];
	static char postfix[128];
	int    state = 0;
	char   *t;

	*prefix = *postfix = 0;

	while (*page)
	{
		switch(state)
		{
			case 0 :	if ( *page == '#' )
						{
							t = lowmatch(page+1,"prefix");
							if (t)
							{
								tonlcpy(prefix,t);
							}
							else
							{
								t = lowmatch(page+1,"postfix");
								if (t)
								{
									tonlcpy(postfix,t);
								}
							}
						}
						state = 1;
						break;
			case 1 :	if (*page == '\n')
						{
							state = 0;
						}
						break;
		}
		page++;
	}
	*pre = prefix;
	*post = postfix;
}






/*----------------------------------------------------------------------------
   testpage(ptr)
   => ptr = ->node containg page information
   <= n/a (dangling, fserror, badchars updated)
   tests the page for bad characters & dangling links.
*/
void testpage(node* ptr)
{
   int size;
   char *pagetext;
   char linkname[256];
   char linkdest[256];
   char temp[128];
   char at,prev,next;
   int  p;
   int  status;
   int  lnp;
   int  ldp;
   int  line;
   int  thrown = 0;
   char *prefix, *postfix;

   if (debugmd)
   {
     printf("\n---testing page '%s'\n",ptr->pagename);
   }
   r.r[0]=17;
   r.r[1]=(int) (ptr->path);
   e=_kernel_swi(OS_File,&r,&r);       /* get size of page */
   if (e)
   {
     fserror++;
     if (debugmd)
     {
       fprintf(stderr,"FS error: %s\n",e->errmess);
     }
     return;
   }
   size=r.r[4];
   pagetext=malloc(size+1);            /* allocate memory for page */
   if (!pagetext)
   {
      fprintf(stderr,"insufficient memory (case 3)\n");
      exit(4);
   }
   r.r[0]=16;
   r.r[1]=(int) (ptr->path);
   r.r[2]=(int) pagetext;
   r.r[3]=0;
   e=_kernel_swi(OS_File,&r,&r);       /* load page */
   if (e)
   {
     fserror++;
     if (debugmd)
     {
       fprintf(stderr,"FS error: %s\n",e->errmess);
     }
     return;
   }
   if (debugmd)
   {
     printf("   page loaded OK.\n");
   }
   pagetext[size]=0;       /* terminate page so we can treat it as a string */

   prepost(pagetext,&prefix,&postfix); /* Get prefix & postfix (if any) */

   for (p=0; pagetext[p]!='\n' && pagetext[p]!=0; p++)
      ;     /* Skip over first (page title) line */
   p++;

   status=0; at='!'; line = 2;
   for ( /*p=0*/ ; p<size; p++)
   {
      prev=at;
      at=pagetext[p];
      next=pagetext[p+1];
      if (at=='\n') { line++; }
      if (at<32 && at!='\n' && at!='\t' && at!='\r')
      {
         badchars++;
         if (verbose)
         {
            fprintf(stderr,"'%s', line %d - bad character %x\n",
                    ptr->pagename,line,at);
         }
         if (throwback)
         {
            sprintf(temp,"Bad character &%x (%d)",at,at);
            throwback_error(ptr->path, line, temp, thrown);
            thrown=1;
         }
      }
      switch (status)
      {
         case 0 : if (at=='<' && prev!='\\' && prev!='<' && next!='<'
                      && next!='-' && next!='=')
                  {
                     status=1; lnp=0;
                  }
                  if (at=='#' && prev=='\n')
                  {
                     status=3;
                  }
                  break;
         case 1 : if (at=='=' && prev!='\\')
                  {
                     if (next=='>')
                     {
                        linkname[lnp]=0;
                        status=2; ldp=0;
                        p++;
                     }
                  } else {
                     if (at=='>' && prev!='\\')
                     {
                           linkname[lnp]=0;
                           strcpy(linkdest,prefix);
                           strcat(linkdest,linkname);
                           strcat(linkdest,postfix);
                          testlink(linkname,linkdest,ptr,line,&thrown,pagetext);
                           status=0;
                     } else {
                        linkname[lnp++]=at;
                        if (lnp>255)
                        {
                           warnings++;
                           if (verbose)
                           {
                              fprintf(stderr,"Can't check page '%s' as it "
                              "contains a link name more than 255 chars "
                              "long.\n",ptr->pagename);
                           }
                           if (throwback)
                           {
                              throwback_warning(ptr->path,line,"Link "
                                                "name >255 chars",thrown);
                              thrown=1;
                           }
                           free(pagetext);
                           return;
                        }
                     }
                  }
                  break;
         case 2 : if (at=='>' && prev!='\\')
                  {
                     linkdest[ldp]=0;
                     testlink(linkname,linkdest,ptr,line,&thrown,pagetext);
                     status=0;
                  } else {
                     linkdest[ldp++]=at;
                     if (ldp>255)
                     {
                        warnings++;
                        if (verbose)
                        {
                           fprintf(stderr,"Can't check page '%s' as it contains"
                                   " a link destination  more than 255 chars "
                                   "long.\n", ptr->pagename);
                        }
                        if (throwback)
                        {
                           throwback_warning(ptr->path,line,"Link dest >255 "
                              "chars",thrown);
                           thrown=1;
                        }
                        free(pagetext);
                        return;
                     }
                  }
                  break;
         case 3 : if (at=='\n')
                  {
                     status=0;
                  }
                  break;
         default: fprintf(stderr,"Page '%s' testing abandonned: "
                           "status has escaped!\n", ptr->pagename);
                  free(pagetext);
                  return;
      }
   }

   free(pagetext);         /* free the memory we've claimed */
}








/*----------------------------------------------------------------------------
   testmanual(name)
   => name ->manual name to test (full filename)
   <= returns 0 if manual is good, code otherwise:
                                    bit   meaning
                                    3     dangling link(s)
                                    4     unaccessible pages(s)
                                    5     filing system error(s)
                                    6     non ASCII chars in page(s)
                                    7     missing \ in \<>
*/
int testmanual(char *manual)
{
   node *tail;
   node *scan;
   char temp[256];
   char temp2[256];

   printf("Scanning manual...\n");

   tail=buildlist(manual,head /*,0*/ );

/*
   if (debugmd)
   {
      printf("List contents:\n");
      printlist(head);
      printf("\n\n");
   }
*/

   printf("Testing links...\n");

   throwbackstarted=0;
   scan=head->next;
   while (scan)
   {
      testpage(scan);
      scan=scan->next;
   }

   /* if (throwback && throwbackstarted)... moved to below */

   if (debugmd) { printf("\n-------------------------\n"); }

   printf("Testing accessibility...\n");

   scan=head->next;

   lowcpy(temp2,manual);
   strcat(temp2,".!root");

   while (scan)
   {
      if ( (scan->referenced)==0 )
      {
         if (strcmp(scan->path,temp2)!=0
                && strcmp(scan->pagename,"!configure")!=0)
         {
            if (isconversion(scan->pagename))
            {
               if (warnext)
               {
                  if (verbose)
                  {
                     fprintf(stderr,"page '%s' is an external conversion\n",
                             scan->pagename);
                  }
                  if (throwback)
                  {
                     sprintf(temp,"Page '%s' is an external conversion",scan->pagename);
                     throwback_warning(scan->path,0,temp,0);
                  }
                  warnings++;
               }
               else
               {
                  unaccessible--;  /* cuz we'll increment it after this block */
               }
            }
            else
            {
               if (verbose)
               {
                  fprintf(stderr,"page '%s' is inaccessible\n",
                         scan->pagename);
               }
               if (throwback)
               {
                  sprintf(temp,"Page '%s' is inaccessible",scan->pagename);
                  throwback_error(scan->path,0,temp,0);
               }
            }
            unaccessible++;
         }
      }
      scan=scan->next;
   }

   printf("\nTesting complete:\n\n");

   if (throwback && throwbackstarted)
   {
      throwback_end();
   }

   if (dangling)
   {
      printf("found %d dangling link%s\n",dangling,plural(dangling,"s"));
   }
   if (unaccessible)
   {
      printf("found %d inaccessible page%s\n",unaccessible,plural(unaccessible,"s"));
   }
   if (missingslash)
   {
      printf("found %d missing \\%s in \\<>\n",missingslash,plural(missingslash,"s"));
   }
   if (fserror)
   {
      printf("%d FS error%s occurred during testing\n",fserror,plural(fserror,"s"));
   }
   if (badchars)
   {
      printf("%d illegal character%s found\n",badchars,plural(badchars,"s"));
   }

   return ( (dangling ? 8 : 0) | (unaccessible ? 16 : 0)
                 | (fserror ? 32 : 0) | (badchars ? 64 : 0)
                 | (missingslash ? 128 : 0) );
}






/*-----------------------------------------------------------------------------
   main.
   tests args, makes sure that the manual to be tested actually is a manual
   (ie. is an image filetype 3d6). NB: returns an error if StrongHelp isn't
   running - this is because the image wouldn't be accessible.
   => command line args
   <= result of testmanual(<manual>) q.v.
*/
int main(int argc, char * argv[])
{
   int goodbad;
   int gotname = 0;
   int i;

   if (argc<2 || argc>5)
   {
      fprintf(stderr,"syntax: strongtest [-t] [-v] [-w] <manual>\n");
      exit(1);
   }

   for (i=1; i<argc; i++)
   {
      switch (*argv[i])
      {
         case '-' :  switch (*(argv[i]+1))
                     {
                        case 't' : case 'T' : throwback=1; break;
                        case 'v' : case 'V' : verbose=1; break;
                        case 'd' : case 'D' : debugmd=1; break;
                        case 'w' : case 'W' : warnext=1; break;
                        default: fprintf(stderr,"bad switch -%c\n",
                                         *(argv[i]+1));
                                 exit(1);
                     }
                     break;
         default  :  if (gotname)
                     {
                        fprintf(stderr,"syntax: strongtest [-t] [-v] "
                                       "<manual>\n");
                        exit(1);
                     }
                     gotname=1;
                     strcpy(manual,argv[i]);
      }
   }

   if (!gotname)
   {
      fprintf(stderr,"syntax: strongtest [-t] [-v] <manual>\n");
      exit(1);
   }

   printf(identity); /* Output name, version, compilation date &  */

   if (debugmd)
   {
      printf("DEBUG: testing '%s', throwback %s\n",
             manual, oof(throwback));
   }

   r.r[0]=17;
   r.r[1]=(int) manual;
   _kernel_swi(OS_File,&r,&r);      /* read cat. info for 'manual' */

   switch (r.r[0])                  /* r0 = object type */
   {
      case 0 : fprintf(stderr,"'%s' not found\n",manual);
               exit(2);
               break;
      case 1 : fprintf(stderr,"'%s' is a file "
                       "(is Stronghelp running?)\n",manual);
               exit(2);
               break;
      case 2 : fprintf(stderr,"'%s' is a directory\n",manual);
               exit(2);
               break;
      case 3 : if ( (r.r[2] & 0xffffff00)!=0xfff3d600 )
               {
                  fprintf(stderr,"'%s' is not a manual\n",manual);
                  exit(2);
               }
               break;
      default: fprintf(stderr,"unrecognised object type"
                              " (%d) for '%s'\n",r.r[0],manual);
               exit(2);
   }

   head=malloc(sizeof(node));
   if (head==0)
   {
     fprintf(stderr,"nowhere near enough memory to run!\n");
     exit(4);
   }

   goodbad = testmanual(manual);
   i = dangling+unaccessible+badchars+fserror+missingslash;
   if (goodbad)
   {
      fprintf(stderr,"Manual '%s' contains %d error%s\n",
               leafname(manual),i,plural(i,"s"));
      if (warnings)
      {
         fprintf(stderr,"and raised %d warning%s\n",warnings,plural(warnings,"s"));
      }
   } else {
      printf("Manual '%s' checks out OK.\n",leafname(manual));
      if (warnings)
      {
         printf("(with %d warning%s)\n",warnings,plural(warnings,"s"));
      }
   }

   return goodbad;
}

/* Phew! */

/*
         Musus Umbra, 1996
        See Help file for Licence
*/
