home *** CD-ROM | disk | FTP | other *** search
/ ARM Club 3 / TheARMClub_PDCD3.iso / hensa / help / strongtest_1 / c_strongtest < prev    next >
Encoding:
Text File  |  1996-02-27  |  32.6 KB  |  1,380 lines

  1. /*
  2.    Strongtest.c
  3.    Tests StrongHelp manuals for 'dangling' links, etc.
  4.  
  5.    v0.00  Dec '95    Incomplete.
  6.    v1.00  960113     Rewrite becuase I 'accidentally' deleted v0.00
  7.    v1.10  960213     Partial Links implemented
  8.    v1.20  960226     Simple links with Postfix & Prefix implemented
  9.    v1.30  960227/00  Subpage testing implemented
  10.    v1.31  960227/01  added -w option, tidied messages
  11.    v1.32  960227/09  added activity messages
  12.                      tidied up messages even more
  13.    v1.33  960227/10  Fixed bug in partial link testing that caused bogus
  14.                          successes for partial links.
  15.    v1.40  960227/11  Added test for bad <> (missing \)
  16.                      Niceified pluralisation in messages
  17.    v1.41  960227/12  Expanded bad character error message to include code
  18.                      Made \r a valid character
  19.    v1.42  960227/13  Expanded -w to include pages of the form xx>yy
  20.  
  21.    © Musus Umbra, 1996.
  22.  
  23.    Sorry about the state of this, C isn't my first language.
  24.    Also, much mucking around has been done, so there are lots of
  25.    commented bits of line here and there - sorry, I guess I'm just
  26.    too paranoid to delete them :-)
  27. */
  28.  
  29. #define VERSION "1.42 (27 Feb 1996)"
  30.  
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #include <string.h>
  34. #include <ctype.h>
  35. #include "kernel.h"
  36. /*#include "swis.h"*/
  37.  
  38. #define OS_File 0x08
  39. #define OS_GBPB 0x0c
  40. #define DDEUtils_ThrowbackStart 0x42587
  41. #define DDEUtils_ThrowbackSend 0x42588
  42. #define DDEUtils_ThrowbackEnd 0x42589
  43.  
  44.  
  45. static char *identity = "StrongTest " VERSION
  46.                         " [compiled "__DATE__"]\n"
  47.                         "© Musus Umbra, 1996\n\n";
  48.  
  49.  
  50. typedef struct listnode {
  51.           struct listnode* next;
  52.           char*     pagename;
  53.           char*     path;
  54.           int       referenced;
  55.         } node;
  56.  
  57. char  block[1024];         /* general workspace */
  58. char  manual[256];         /* the manual name we're working on */
  59. node* head;                /* global to save passing it around everywhere */
  60. _kernel_swi_regs r;        /* global to save time */
  61. _kernel_oserror *e;        /* ... */
  62. int  dangling = 0;         /* dangling link flag/counter */
  63. int  unaccessible = 0;     /* unaccessible page flag/counter */
  64. int  fserror = 0;          /* filing system error flag/counter */
  65. int  badchars = 0;         /* non-ASCII chars flag/counter */
  66. int  missingslash = 0;     /* \ missing in \<> */
  67. int  warnings = 0;         /* warnings flag/counter */
  68. int  throwback = 0;        /* throwback flag */
  69. int  debugmd = 0;          /* debugmdness flag :-) */
  70. int  verbose = 0;          /* verbosity flag */
  71. int  warnext = 0;          /* whether to raise warnings for external links */
  72. int  throwbackstarted = 0;
  73.  
  74.  
  75.  
  76. char *plural(int n, char* pe)
  77. {
  78.    return n==1 ? "" : pe;
  79. }
  80.  
  81.  
  82. char* leafname(char* source)     /* Function to find leafname of object */
  83. {
  84.   char* ret;
  85.   char* search;
  86.   for (search=source; *search!=0; search++) { /* find terminator */ };
  87.   for (ret=search; *ret!='.' && ret>=source; ret--) { /* find leaf */ };
  88.   return(ret+1);
  89. }
  90.  
  91.  
  92. int isconversion(char *p)
  93. {
  94.    while (*p++)
  95.    {
  96.       if ( *p=='>' ) { return 1; }
  97.    }
  98.    return 0;
  99. }
  100.  
  101.  
  102. /*----------------------------------------------------------------------------
  103.    throwback_start
  104.    => n/a
  105.    <= n/a
  106. */
  107. void throwback_start(void)
  108. {
  109.    e=_kernel_swi(DDEUtils_ThrowbackStart,&r,&r);
  110.    if (e)
  111.    {
  112.        fprintf(stderr,"WARNING - %s\n",e->errmess);
  113.        return;
  114.    }
  115.    r.r[0]=0;
  116.    r.r[2]=(int) manual;
  117.    e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
  118.    if (e)
  119.    {
  120.       if (debugmd)
  121.       {
  122.          fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
  123.       }
  124.       return;
  125.    }
  126. }
  127.  
  128.  
  129.  
  130. /*----------------------------------------------------------------------------
  131.    throwback_error(file, line, m, x)
  132.    => x = flag: 1=>already done an error in this file, 0=>first error ...
  133.       file = ->filename
  134.       line = line
  135.       m = ->error message
  136.    <= n/a
  137. */
  138. void throwback_error(char* file, int line, char *m, int x)
  139. {
  140.    if (throwbackstarted==0)
  141.    {
  142.       throwback_start();
  143.       throwbackstarted=1;
  144.    }
  145.    r.r[0]=1;
  146.    r.r[2]=(int) file;
  147.    r.r[3]=line;
  148.    r.r[4]=1;
  149.    r.r[5]=(int) m;
  150.    e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
  151.    if (e)
  152.    {
  153.       if (debugmd)
  154.       {
  155.          fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
  156.       }
  157.    }
  158. }
  159.  
  160.  
  161.  
  162. /*----------------------------------------------------------------------------
  163.    throwback_warning(file, line, m, x)
  164.    => x = flag: 1=>already done an error in this file, 0=>first error ...
  165.       file = ->filename
  166.       line = line
  167.       m = ->error message
  168.    <= n/a
  169. */
  170. void throwback_warning(char* file, int line, char *m, int x)
  171. {
  172.    if (throwbackstarted==0)
  173.    {
  174.       throwback_start();
  175.       throwbackstarted=1;
  176.    }
  177.    r.r[0]=1;
  178.    r.r[2]=(int) file;
  179.    r.r[3]=line;
  180.    r.r[4]=0;
  181.    r.r[5]=(int) m;
  182.    e=_kernel_swi(DDEUtils_ThrowbackSend,&r,&r);
  183.    if (e)
  184.    {
  185.       if (debugmd)
  186.       {
  187.          fprintf(stderr,"DEBUG: throwback failed - %s\n",e->errmess);
  188.       }
  189.    }
  190. }
  191.  
  192.  
  193.  
  194.  
  195.  
  196. /*---------------------------------------------------------------------------
  197.    throwback_end
  198.    => n/a
  199.    <= n/a
  200. */
  201. void throwback_end(void)
  202. {
  203.    e=_kernel_swi(DDEUtils_ThrowbackEnd,&r,&r);
  204.    if (e)
  205.    {
  206.       if (debugmd)
  207.       {
  208.          fprintf(stderr,"DEBUG: end throwback failed - %s\n",e->errmess);
  209.       }
  210.    }
  211. }
  212.  
  213.  
  214.  
  215.  
  216. /*----------------------------------------------------------------------------
  217.    oof(x)
  218.    => x
  219.    <= ->"On" | "Off"
  220.    .
  221. */
  222. char* oof(int x)
  223. {
  224.    return x ? "On" : "Off";
  225. }
  226.  
  227.  
  228. /*----------------------------------------------------------------------------
  229.    unpad(s)
  230.    => s = ->string
  231.    <= s modified so that there are no preceding or trailing spaces.
  232. */
  233. void unpad(char *s)
  234. {
  235.    char *c;
  236.  
  237.    c=s+strlen(s);
  238.    while (*--c==' ')       /* scan from end for non-space */
  239.       ;
  240.    *++c=0;                 /* terminate string over first space */
  241.  
  242.    c=s;
  243.    while (*c++==' ')
  244.       ;
  245.    c--;
  246.    if (c==s) { return; }
  247.    while ( (*s++=*c++)!=0 )
  248.       ;
  249.    *s=0;
  250. }
  251.  
  252.  
  253.  
  254.  
  255. /*----------------------------------------------------------------------------
  256.    printlist(head)
  257.    => head = ->head node of list
  258.    <= n/a
  259.    debugging function to print out the full contents of the list
  260. */
  261. void printlist(node *head)
  262. {
  263.   if ( !(head->next) )
  264.   {
  265.     printf("---the list is empty\n");
  266.     return;
  267.   }
  268.  
  269.   while (head->next)
  270.   {
  271.     head=head->next;
  272.     printf("   %s - %s\n",head->pagename,head->path);
  273.   }
  274. }
  275.  
  276.  
  277. /*----------------------------------------------------------------------------
  278.    lowerise(text)
  279.    => text = ->string
  280.    <= n/a
  281.    Converts the string to lower case.
  282. */
  283. void lowerise(char *text)
  284. {
  285.    while (*text)
  286.    {
  287.      *text=tolower(*text);
  288.      text++;
  289.    }
  290. }
  291.  
  292.  
  293.  
  294. /*----------------------------------------------------------------------------
  295.    lowcpy(d,s)
  296.    => d = ->destination string
  297.       s = ->source string
  298.    <= n/a (d updated)
  299.    copies s into d, ensuring that d is all lower case.
  300. */
  301. void lowcpy(char *d, char *s)
  302. {
  303.    while (*s)
  304.    {
  305.       *d++=tolower(*s++);
  306.    }
  307.    *d=0;
  308. }
  309.  
  310.  
  311.  
  312. /*----------------------------------------------------------------------------
  313.    sinsert(d,i)
  314.    => d= ->destination string (not empty)
  315.       i= ->string to insert
  316.    <= i is inserted at the start of s
  317. */
  318. void sinsert(char *d, char *i)
  319. {
  320.    int l,m,k;
  321.  
  322.    l=strlen(i);
  323.    m=strlen(d);
  324.  
  325.    for (k=m; k>=0; k--)
  326.    {
  327.       d[k+l]=d[k];
  328.    }
  329.    for (k=0; k<l; k++)
  330.    {
  331.       d[k]=i[k];
  332.    }
  333. }
  334.  
  335.  
  336.  
  337.  
  338.  
  339.  
  340.  
  341.  
  342. /*----------------------------------------------------------------------------
  343.    dnamecpy(s,d)
  344.    => s= ->destination string
  345.       d= ->directory name
  346.    <= s modified to hold the dir name, with '.'s missing
  347. */
  348. void dnamecpy(char *s, char *d)
  349. {
  350.    while (*d)
  351.    {
  352.       if (*d!='.') { *s++=*d; }
  353.       d++;
  354.    }
  355.    *s=0;
  356. }
  357.  
  358.  
  359.  
  360.  
  361.  
  362. /*----------------------------------------------------------------------------
  363.    makenameubar(s,d,o)
  364.    => s= ->destination string
  365.       d= ->full path for object (without trainling '.')
  366.       o= ->objectname
  367.    <= s modified, d corrupted
  368. */
  369. void makenameubar(char *s, char *d, char *o)
  370. {
  371.    char *p;
  372.    char *dd;
  373.    char *dirstart;
  374.  
  375.    dd=malloc(strlen(d)+1);
  376.    strcpy(dd,d);
  377.    d=dd;
  378.  
  379.    while (*d!=0)
  380.    {
  381.       dirstart=d;
  382.       for (p=d; *p!='.' && *p; p++)
  383.          ;
  384.       if (*(p-1)=='_')
  385.       {
  386.          dnamecpy(s,dirstart);
  387.          strcat(s,o);
  388.          free(dd);
  389.          return;
  390.       }
  391.       if (*p) { d=p+1; } else { d=p; }
  392.    }
  393.    strcpy(s,o);
  394.    free(dd);
  395. }
  396.  
  397.  
  398. /*----------------------------------------------------------------------------
  399.    addnode(tail,dir,objname)
  400.    => tail = ->current tail of list
  401.       dir = ->name of directory 'page' is in
  402.       objname = ->leaf name of object
  403.    <= new tail of list
  404. */
  405. node* addnode(node *tail, char* dir, char* objname /*, int ubar*/)
  406. {
  407.    int i;
  408.    node *newnode;
  409.    char *tptr;
  410.    char *pptr;
  411.  
  412.    newnode = malloc(sizeof(node));
  413.    if ( !newnode )
  414.    {
  415.       fprintf(stderr,"insufficient memory (case 2)\n");
  416.       exit(4);
  417.    }
  418.    tail->next = newnode;
  419.    newnode->next = 0;
  420.    i=strlen(dir)+strlen(objname)+2;
  421.    tptr = malloc(i*sizeof(char));
  422.    pptr = malloc((strlen(objname)+30)*sizeof(char));
  423.    if (!(tptr && pptr))
  424.    {
  425.       fprintf(stderr,"insufficient memory (case 2a)\n");
  426.       exit(4);
  427.    }
  428.    newnode->path = tptr;
  429.    newnode->pagename = pptr;
  430.    strcpy(tptr,dir);
  431.    strcat(tptr,".");
  432.    strcat(tptr,objname);
  433.  
  434. /*
  435.  *   if (ubar && 0)             was || 1
  436.  *  {
  437.  *     makenameubar(pptr,dir,objname);
  438.  *  } else {
  439.  *     strcpy(pptr,objname);
  440.  *  }
  441.  * REPLACED BY NEXT LINE */
  442.    strcpy(pptr,objname);
  443.  
  444.    newnode->referenced=0;
  445.  
  446.    lowerise(tptr);
  447.    lowerise(pptr);
  448.  
  449. /*
  450.    if (debugmd)
  451.    {
  452.       printf("   added page '%s', path '%s'\n",pptr,tptr);
  453.    }
  454. */
  455.    return newnode;
  456. }
  457.  
  458.  
  459. /*----------------------------------------------------------------------------
  460.    endinubar(s)
  461.    => s = ->directory name
  462.    <= 1 => s ends in a _, 0 otherwise
  463. */
  464. int endinubar(char *s)
  465. {
  466.    while(*++s)
  467.       ;
  468.    return (*--s=='_');
  469. }
  470.  
  471.  
  472.  
  473.  
  474. /*----------------------------------------------------------------------------
  475.    buildlist(dir,tail,ubar)
  476.    => dir->manual name or subdirectory
  477.       *tail is the node to add the list on to (ie. the tail)
  478.       ubar = 1 => a directory with an _ ending leads to this dir
  479.    <= ->new tail of list
  480.    builds the linked list of page information for directory 'name'.
  481.    NB: RECURSIVE
  482. */
  483. node *buildlist(char* dir,node *tail /*, int ubar*/ )
  484. {
  485.    char pathname[256];
  486.    int  offset;
  487.    int  read;
  488.    int *filetype = (int*) (block);
  489.    int *objtype = (int*) (block+16);
  490.    char *objname = (block+20);
  491.  
  492.    offset = 0;
  493.    do
  494.    {
  495.       r.r[0]=10;              /* OS_GBPB 10 */
  496.       r.r[1]=(int) dir;       /* ->directory name */
  497.       r.r[2]=(int) block;     /* ->buffer */
  498.       r.r[3]=1;               /* entries to read */
  499.       r.r[4]=offset;          /* offset to start from */
  500.       r.r[5]=1024;            /* size of buffer */
  501.       r.r[6]=(int) "*";       /* filename to match */
  502.       e=_kernel_swi(OS_GBPB,&r,&r);
  503.       if (e)
  504.       {
  505.         if (debugmd)
  506.         {
  507.           fprintf(stderr,"FS error: %s\n",e->errmess);
  508.         }
  509.         fserror++;
  510.         return tail;
  511.       }
  512.       read = r.r[3];
  513.       offset = r.r[4];
  514.  
  515.       if (read>0)
  516.       {
  517.          switch ( *objtype )
  518.          {
  519.             case 1 : switch ( (*filetype) & 0xffffff00 )
  520.                      {
  521.                         case 0xffffff00 : /* text file */
  522.                         case 0xfffffd00 : /* data file */
  523.                                           tail=addnode(tail,dir,
  524.                                                        objname /*,ubar*/);
  525.                                           break;
  526.                         default : ; /* skip anything else */
  527.                      }
  528.                      break;
  529.             case 2 : strcpy(pathname,dir);
  530.                      strcat(pathname,".");
  531.                      strcat(pathname,objname);
  532.                      tail=buildlist(pathname,tail /*,endinubar(objname)*/);
  533.                      break;
  534.             default: ; /* Huh? what the hell is *this* doing here?!?! */
  535.          }
  536.       }
  537.    } while (offset!=-1);
  538.  
  539.    return tail;
  540. }
  541.  
  542.  
  543.  
  544.  
  545. /*----------------------------------------------------------------------------
  546.    isexternal(s)
  547.    => s = ->string (manual page name)
  548.    <= 1 is page is *probably* external, 0 otherwise
  549.    checks page name for ':' or '.'
  550. */
  551. int isexternal(char* s)
  552. {
  553.    int e = 0;
  554.  
  555.    while(*s && !e)
  556.    {
  557.       if (*s==':' || *s=='.') { e=1; }
  558.       s++;
  559.    }
  560.    return e;
  561. }
  562.  
  563.  
  564.  
  565. /*-----------------------------------------------------------------------------
  566.   testpartial(pagename,path,link)
  567.   => pagename ->pagename to test link against
  568.      path = ->pathname of page
  569.      link ->link to test
  570.   <= !0 if link fails, 0 if link succedes.
  571. */
  572.  
  573. int testpartial(char *pagename, char *path, char *link)
  574. {
  575.    char temp[256];
  576.    char *t = path;
  577.    char *u = temp+255;
  578.    int  linklen;
  579.    int  templen;
  580.    int  dotflag = 0;
  581.  
  582.    *u-- = 0;                  /* terminate temp string */
  583.    templen = 0;               /* length of temp string */
  584.    linklen = strlen(link);    /* length of link */
  585.  
  586.    while ( *++t )
  587.       ;                       /* find last char of pathname */
  588.    t--;
  589.  
  590.    while (templen<linklen && *t)
  591.    {
  592.       if ( *t !='.' )
  593.       {
  594.          *u-- = /*tolower*/(*t);
  595.          templen++;
  596.       }
  597.       else
  598.       {
  599.          dotflag = 1;
  600.       }
  601.       t--;
  602.    }
  603.  
  604. /*
  605.    if (debugmd)
  606.    {
  607.       printf("+++testing partial %s=>%s ??? ",link,u+1);
  608.       printf("[%s,%s]\n",link,u+1);
  609.    }
  610. */
  611.  
  612.    if ( (templen!=linklen) || !dotflag )
  613.    {
  614.       return 1;               /* no need for string cmp */
  615.    }
  616.  
  617.    return strcmp(link,u+1);
  618. }
  619.  
  620.  
  621.  
  622. /*----------------------------------------------------------------------------
  623.  tonlcpy(d,s)
  624.  => d = ->dest string
  625.     s = ->source string
  626.  <= d updated with s upto \n or 0. Leading whitespace in s is skipped.
  627. */
  628. void tonlcpy(char *d, char *s)
  629. {
  630.     while (isspace(*s) && *s!='\n' && *s!=0)
  631.     {
  632.         s++;
  633.     }
  634.     while (*s!='\n' && *s!=0)
  635.     {
  636.         *d++ = *s++;
  637.     }
  638.     *d = 0;    /* terminate dest. */
  639. }
  640.  
  641.  
  642. /*----------------------------------------------------------------------------
  643.  lowmatch(t,a)
  644.  => t = ->test string
  645.     a = ->wanted string
  646.  <= ->char after wanted string in test string or NULL if no match
  647. */
  648. char *lowmatch(char *t, char *a)
  649. {
  650.     while ( (*a == tolower(*t)) && *a!=0)
  651.     {
  652.         a++;
  653.         t++;
  654.     }
  655.     return (*a==0 ? t : NULL);
  656. }
  657.  
  658.  
  659.  
  660. /*---------------------------------------------------------------------------
  661.  testsubpage(lab,page)
  662.  => lab   = ->search link (incl '.')
  663.     page  = ->pagetext (null terminated)
  664.  <= -1 if no subpage match, 1 if a match
  665. */
  666. int testsubpage(char *lab, char *page)
  667. {
  668.     char temp[256];
  669.     int state = 0;
  670.     char *t;
  671.     if (debugmd)
  672.     {
  673.         printf("@@@testing subpage %s ???\n",lab+1);
  674.     }
  675.  
  676.     lab++;             /* skip over leading '.' */
  677.     while (*page && (state != 100) )
  678.     {
  679.         switch(state)
  680.         {
  681.             case 0 :    if (*page == '#')
  682.                         {
  683.                             t=lowmatch(page+1,"subpage");
  684.                             if (t)
  685.                             {
  686.                                 tonlcpy(temp,t);
  687.                                 if (strcmp(lab,temp)==0)
  688.                                 {
  689.                                     state = 100;
  690.                                 }
  691.                             }
  692.                         }
  693.                         state = 1;
  694.                         break;
  695.             case 1 :    if (*page=='\n')
  696.                         {
  697.                             state = 0;
  698.                         }
  699.                         break;
  700.         }
  701.         page++;
  702.     }
  703.     return (state==100 ? 1 : -1);
  704. }
  705.  
  706.  
  707.  
  708.  
  709. /*----------------------------------------------------------------------------
  710.    testlink(name,dest,page,line,thrown,pagetext)
  711.    => name = ->name of link (for messages, etc)
  712.       dest = ->link destination (pagename)
  713.    <= n/a (global dangling updated if necessary, referenced element of
  714.             list node of destination page updated as necessary).
  715. */
  716. void testlink(char *name, char *dest, node *page,int line,int *thrown,char *p)
  717. {
  718.    node *scan;
  719.    int  succeded = 0;
  720.    char search[256];
  721.    char search2[256];
  722.  
  723.    lowcpy(search,dest);
  724.    unpad(search); /* moved from below */
  725.    if (1 || endinubar(search))   /* || 1 is to force test <x=>x.!Root>*/
  726.    {
  727.       strcpy(search2,search);
  728.       strcat(search2,"!root");
  729.    } else
  730.    {
  731.       *search2=65;
  732.       *(search2+1)=0;
  733.    }
  734.    /* unpad was here */
  735.    if (debugmd)
  736.    {
  737.      printf("***testing link <%s=>%s> [%s,%s]\n",name,dest,search,search2);
  738.    }
  739.  
  740.    if (*search=='*') { return; }       /* don't test OS commands! */
  741.    if (*search=='.')
  742.    {
  743.       succeded = testsubpage(search,p);  /* either -1 or 1 */
  744.    }
  745.  
  746.    if (isexternal(search) && succeded==0)  /* don't test external links */
  747.    {
  748.       if (warnext)
  749.       {
  750.          warnings++;
  751.          if (strcmp(name,dest)==0)
  752.          {
  753.             if (verbose)
  754.             {
  755.               fprintf(stderr,"Warning: page '%s' line %d - link <%s> "
  756.                    "is external (not tested)\n", page->pagename,line,name);
  757.             }
  758.             sprintf(search,"link <%s> is external (not tested)",name);
  759.             if (throwback)
  760.             {
  761.                throwback_warning(page->path,line,search,*thrown);
  762.                *thrown=1;
  763.             }
  764.          } else {
  765.             if (verbose)
  766.             {
  767.                fprintf(stderr,"Warning: page '%s' line %d - link "
  768.                               "<%s=>%s> is external (not tested)\n",
  769.                               page->pagename,line,name,dest);
  770.             }
  771.             sprintf(search,"link <%s=>%s> is external (not tested)",name,dest);
  772.             if (throwback)
  773.             {
  774.                throwback_warning(page->path,line,search,*thrown);
  775.                *thrown=1;
  776.             }
  777.          }
  778.       }
  779.       return;
  780.    }
  781.  
  782.    scan=head->next;
  783.    while (scan && succeded==0)
  784.    {
  785.       if ( strcmp(scan->pagename,search)==0
  786.                       /* || strcmp(scan->pagename,search2)==0*/ )
  787.  
  788.       {
  789.          if (debugmd)
  790.          {
  791.             printf("  succedes (normal): ->page '%s'\n",scan->pagename);
  792.          }
  793.          succeded=1;
  794.          (scan->referenced)++;
  795.       }
  796.       else
  797.       {
  798.          if ( testpartial(scan->pagename,scan->path,search)==0 )
  799.          {
  800.             if (debugmd)
  801.             {
  802.                printf("  succedes (partial): ->page '%s'\n",scan->pagename);
  803.             }
  804.             succeded=1;
  805.             (scan->referenced)++;
  806.          }
  807.          else
  808.          {
  809.             if ( testpartial(scan->pagename,scan->path,search2)==0 )
  810.             {
  811.                if (debugmd)
  812.                {
  813.                   printf("  succedes (partial.!Root): ->page '%s'\n",scan->pagename);
  814.                }
  815.                succeded=1;
  816.                (scan->referenced)++;
  817.             }
  818.          }
  819.       }
  820.       scan=scan->next;
  821.    }
  822.  
  823.    if (succeded==0)
  824.    {
  825.       if (strcmp(name,dest)==0)
  826.       {
  827.          if (strlen(name)==0)
  828.          {
  829.             if (verbose)
  830.             {
  831.                fprintf(stderr,"page '%s', line %d - Missing '\\' in \"\\<>\"\n",
  832.                               page->pagename,line);
  833.             }
  834.             sprintf(search,"Missing \\ in \"\\<>\"");
  835.             if (throwback)
  836.             {
  837.                throwback_error(page->path,line,search,*thrown);
  838.                *thrown=1;
  839.             }
  840.             dangling--;   /* because it'll be incremented after this block */
  841.             missingslash++;
  842.          }
  843.          else
  844.          {
  845.             if (verbose)
  846.             {
  847.                fprintf(stderr,"page '%s', line %d - link <%s> fails\n",
  848.                               page->pagename,line,name);
  849.             }
  850.             sprintf(search,"link <%s> fails",name);
  851.             if (throwback)
  852.             {
  853.                throwback_error(page->path,line,search,*thrown);
  854.                *thrown=1;
  855.             }
  856.          }
  857.       } else {
  858.          if (verbose)
  859.          {
  860.             fprintf(stderr,"page '%s', line %d - link <%s=>%s> "
  861.                            "fails\n",page->pagename,line,name,dest);
  862.          }
  863.          sprintf(search,"link <%s=>%s> fails",name,dest);
  864.          if (throwback)
  865.          {
  866.             throwback_error(page->path,line,search,*thrown);
  867.             *thrown=1;
  868.          }
  869.       }
  870.       dangling++;
  871.    }
  872. }
  873.  
  874.  
  875. /*----------------------------------------------------------------------------
  876.   prepost(ptr,*pre,*post)
  877.   => ptr = ->page text, null terminated
  878.   <= *pre & *post = -> prefix & postfix for simple links
  879. */
  880. void prepost(char *page, char* *pre, char* *post)
  881. {
  882.     static char prefix[128];
  883.     static char postfix[128];
  884.     int    state = 0;
  885.     char   *t;
  886.  
  887.     *prefix = *postfix = 0;
  888.  
  889.     while (*page)
  890.     {
  891.         switch(state)
  892.         {
  893.             case 0 :    if ( *page == '#' )
  894.                         {
  895.                             t = lowmatch(page+1,"prefix");
  896.                             if (t)
  897.                             {
  898.                                 tonlcpy(prefix,t);
  899.                             }
  900.                             else
  901.                             {
  902.                                 t = lowmatch(page+1,"postfix");
  903.                                 if (t)
  904.                                 {
  905.                                     tonlcpy(postfix,t);
  906.                                 }
  907.                             }
  908.                         }
  909.                         state = 1;
  910.                         break;
  911.             case 1 :    if (*page == '\n')
  912.                         {
  913.                             state = 0;
  914.                         }
  915.                         break;
  916.         }
  917.         page++;
  918.     }
  919.     *pre = prefix;
  920.     *post = postfix;
  921. }
  922.  
  923.  
  924.  
  925.  
  926.  
  927.  
  928. /*----------------------------------------------------------------------------
  929.    testpage(ptr)
  930.    => ptr = ->node containg page information
  931.    <= n/a (dangling, fserror, badchars updated)
  932.    tests the page for bad characters & dangling links.
  933. */
  934. void testpage(node* ptr)
  935. {
  936.    int size;
  937.    char *pagetext;
  938.    char linkname[256];
  939.    char linkdest[256];
  940.    char temp[128];
  941.    char at,prev,next;
  942.    int  p;
  943.    int  status;
  944.    int  lnp;
  945.    int  ldp;
  946.    int  line;
  947.    int  thrown = 0;
  948.    char *prefix, *postfix;
  949.  
  950.    if (debugmd)
  951.    {
  952.      printf("\n---testing page '%s'\n",ptr->pagename);
  953.    }
  954.    r.r[0]=17;
  955.    r.r[1]=(int) (ptr->path);
  956.    e=_kernel_swi(OS_File,&r,&r);       /* get size of page */
  957.    if (e)
  958.    {
  959.      fserror++;
  960.      if (debugmd)
  961.      {
  962.        fprintf(stderr,"FS error: %s\n",e->errmess);
  963.      }
  964.      return;
  965.    }
  966.    size=r.r[4];
  967.    pagetext=malloc(size+1);            /* allocate memory for page */
  968.    if (!pagetext)
  969.    {
  970.       fprintf(stderr,"insufficient memory (case 3)\n");
  971.       exit(4);
  972.    }
  973.    r.r[0]=16;
  974.    r.r[1]=(int) (ptr->path);
  975.    r.r[2]=(int) pagetext;
  976.    r.r[3]=0;
  977.    e=_kernel_swi(OS_File,&r,&r);       /* load page */
  978.    if (e)
  979.    {
  980.      fserror++;
  981.      if (debugmd)
  982.      {
  983.        fprintf(stderr,"FS error: %s\n",e->errmess);
  984.      }
  985.      return;
  986.    }
  987.    if (debugmd)
  988.    {
  989.      printf("   page loaded OK.\n");
  990.    }
  991.    pagetext[size]=0;       /* terminate page so we can treat it as a string */
  992.  
  993.    prepost(pagetext,&prefix,&postfix); /* Get prefix & postfix (if any) */
  994.  
  995.    for (p=0; pagetext[p]!='\n' && pagetext[p]!=0; p++)
  996.       ;     /* Skip over first (page title) line */
  997.    p++;
  998.  
  999.    status=0; at='!'; line = 2;
  1000.    for ( /*p=0*/ ; p<size; p++)
  1001.    {
  1002.       prev=at;
  1003.       at=pagetext[p];
  1004.       next=pagetext[p+1];
  1005.       if (at=='\n') { line++; }
  1006.       if (at<32 && at!='\n' && at!='\t' && at!='\r')
  1007.       {
  1008.          badchars++;
  1009.          if (verbose)
  1010.          {
  1011.             fprintf(stderr,"'%s', line %d - bad character %x\n",
  1012.                     ptr->pagename,line,at);
  1013.          }
  1014.          if (throwback)
  1015.          {
  1016.             sprintf(temp,"Bad character &%x (%d)",at,at);
  1017.             throwback_error(ptr->path, line, temp, thrown);
  1018.             thrown=1;
  1019.          }
  1020.       }
  1021.       switch (status)
  1022.       {
  1023.          case 0 : if (at=='<' && prev!='\\' && prev!='<' && next!='<'
  1024.                       && next!='-' && next!='=')
  1025.                   {
  1026.                      status=1; lnp=0;
  1027.                   }
  1028.                   if (at=='#' && prev=='\n')
  1029.                   {
  1030.                      status=3;
  1031.                   }
  1032.                   break;
  1033.          case 1 : if (at=='=' && prev!='\\')
  1034.                   {
  1035.                      if (next=='>')
  1036.                      {
  1037.                         linkname[lnp]=0;
  1038.                         status=2; ldp=0;
  1039.                         p++;
  1040.                      }
  1041.                   } else {
  1042.                      if (at=='>' && prev!='\\')
  1043.                      {
  1044.                            linkname[lnp]=0;
  1045.                            strcpy(linkdest,prefix);
  1046.                            strcat(linkdest,linkname);
  1047.                            strcat(linkdest,postfix);
  1048.                           testlink(linkname,linkdest,ptr,line,&thrown,pagetext);
  1049.                            status=0;
  1050.                      } else {
  1051.                         linkname[lnp++]=at;
  1052.                         if (lnp>255)
  1053.                         {
  1054.                            warnings++;
  1055.                            if (verbose)
  1056.                            {
  1057.                               fprintf(stderr,"Can't check page '%s' as it "
  1058.                               "contains a link name more than 255 chars "
  1059.                               "long.\n",ptr->pagename);
  1060.                            }
  1061.                            if (throwback)
  1062.                            {
  1063.                               throwback_warning(ptr->path,line,"Link "
  1064.                                                 "name >255 chars",thrown);
  1065.                               thrown=1;
  1066.                            }
  1067.                            free(pagetext);
  1068.                            return;
  1069.                         }
  1070.                      }
  1071.                   }
  1072.                   break;
  1073.          case 2 : if (at=='>' && prev!='\\')
  1074.                   {
  1075.                      linkdest[ldp]=0;
  1076.                      testlink(linkname,linkdest,ptr,line,&thrown,pagetext);
  1077.                      status=0;
  1078.                   } else {
  1079.                      linkdest[ldp++]=at;
  1080.                      if (ldp>255)
  1081.                      {
  1082.                         warnings++;
  1083.                         if (verbose)
  1084.                         {
  1085.                            fprintf(stderr,"Can't check page '%s' as it contains"
  1086.                                    " a link destination  more than 255 chars "
  1087.                                    "long.\n", ptr->pagename);
  1088.                         }
  1089.                         if (throwback)
  1090.                         {
  1091.                            throwback_warning(ptr->path,line,"Link dest >255 "
  1092.                               "chars",thrown);
  1093.                            thrown=1;
  1094.                         }
  1095.                         free(pagetext);
  1096.                         return;
  1097.                      }
  1098.                   }
  1099.                   break;
  1100.          case 3 : if (at=='\n')
  1101.                   {
  1102.                      status=0;
  1103.                   }
  1104.                   break;
  1105.          default: fprintf(stderr,"Page '%s' testing abandonned: "
  1106.                            "status has escaped!\n", ptr->pagename);
  1107.                   free(pagetext);
  1108.                   return;
  1109.       }
  1110.    }
  1111.  
  1112.    free(pagetext);         /* free the memory we've claimed */
  1113. }
  1114.  
  1115.  
  1116.  
  1117.  
  1118.  
  1119.  
  1120.  
  1121.  
  1122. /*----------------------------------------------------------------------------
  1123.    testmanual(name)
  1124.    => name ->manual name to test (full filename)
  1125.    <= returns 0 if manual is good, code otherwise:
  1126.                                     bit   meaning
  1127.                                     3     dangling link(s)
  1128.                                     4     unaccessible pages(s)
  1129.                                     5     filing system error(s)
  1130.                                     6     non ASCII chars in page(s)
  1131.                                     7     missing \ in \<>
  1132. */
  1133. int testmanual(char *manual)
  1134. {
  1135.    node *tail;
  1136.    node *scan;
  1137.    char temp[256];
  1138.    char temp2[256];
  1139.  
  1140.    printf("Scanning manual...\n");
  1141.  
  1142.    tail=buildlist(manual,head /*,0*/ );
  1143.  
  1144. /*
  1145.    if (debugmd)
  1146.    {
  1147.       printf("List contents:\n");
  1148.       printlist(head);
  1149.       printf("\n\n");
  1150.    }
  1151. */
  1152.  
  1153.    printf("Testing links...\n");
  1154.  
  1155.    throwbackstarted=0;
  1156.    scan=head->next;
  1157.    while (scan)
  1158.    {
  1159.       testpage(scan);
  1160.       scan=scan->next;
  1161.    }
  1162.  
  1163.    /* if (throwback && throwbackstarted)... moved to below */
  1164.  
  1165.    if (debugmd) { printf("\n-------------------------\n"); }
  1166.  
  1167.    printf("Testing accessibility...\n");
  1168.  
  1169.    scan=head->next;
  1170.  
  1171.    lowcpy(temp2,manual);
  1172.    strcat(temp2,".!root");
  1173.  
  1174.    while (scan)
  1175.    {
  1176.       if ( (scan->referenced)==0 )
  1177.       {
  1178.          if (strcmp(scan->path,temp2)!=0
  1179.                 && strcmp(scan->pagename,"!configure")!=0)
  1180.          {
  1181.             if (isconversion(scan->pagename))
  1182.             {
  1183.                if (warnext)
  1184.                {
  1185.                   if (verbose)
  1186.                   {
  1187.                      fprintf(stderr,"page '%s' is an external conversion\n",
  1188.                              scan->pagename);
  1189.                   }
  1190.                   if (throwback)
  1191.                   {
  1192.                      sprintf(temp,"Page '%s' is an external conversion",scan->pagename);
  1193.                      throwback_warning(scan->path,0,temp,0);
  1194.                   }
  1195.                   warnings++;
  1196.                }
  1197.                else
  1198.                {
  1199.                   unaccessible--;  /* cuz we'll increment it after this block */
  1200.                }
  1201.             }
  1202.             else
  1203.             {
  1204.                if (verbose)
  1205.                {
  1206.                   fprintf(stderr,"page '%s' is inaccessible\n",
  1207.                          scan->pagename);
  1208.                }
  1209.                if (throwback)
  1210.                {
  1211.                   sprintf(temp,"Page '%s' is inaccessible",scan->pagename);
  1212.                   throwback_error(scan->path,0,temp,0);
  1213.                }
  1214.             }
  1215.             unaccessible++;
  1216.          }
  1217.       }
  1218.       scan=scan->next;
  1219.    }
  1220.  
  1221.    printf("\nTesting complete:\n\n");
  1222.  
  1223.    if (throwback && throwbackstarted)
  1224.    {
  1225.       throwback_end();
  1226.    }
  1227.  
  1228.    if (dangling)
  1229.    {
  1230.       printf("found %d dangling link%s\n",dangling,plural(dangling,"s"));
  1231.    }
  1232.    if (unaccessible)
  1233.    {
  1234.       printf("found %d inaccessible page%s\n",unaccessible,plural(unaccessible,"s"));
  1235.    }
  1236.    if (missingslash)
  1237.    {
  1238.       printf("found %d missing \\%s in \\<>\n",missingslash,plural(missingslash,"s"));
  1239.    }
  1240.    if (fserror)
  1241.    {
  1242.       printf("%d FS error%s occurred during testing\n",fserror,plural(fserror,"s"));
  1243.    }
  1244.    if (badchars)
  1245.    {
  1246.       printf("%d illegal character%s found\n",badchars,plural(badchars,"s"));
  1247.    }
  1248.  
  1249.    return ( (dangling ? 8 : 0) | (unaccessible ? 16 : 0)
  1250.                  | (fserror ? 32 : 0) | (badchars ? 64 : 0)
  1251.                  | (missingslash ? 128 : 0) );
  1252. }
  1253.  
  1254.  
  1255.  
  1256.  
  1257.  
  1258.  
  1259. /*-----------------------------------------------------------------------------
  1260.    main.
  1261.    tests args, makes sure that the manual to be tested actually is a manual
  1262.    (ie. is an image filetype 3d6). NB: returns an error if StrongHelp isn't
  1263.    running - this is because the image wouldn't be accessible.
  1264.    => command line args
  1265.    <= result of testmanual(<manual>) q.v.
  1266. */
  1267. int main(int argc, char * argv[])
  1268. {
  1269.    int goodbad;
  1270.    int gotname = 0;
  1271.    int i;
  1272.  
  1273.    if (argc<2 || argc>5)
  1274.    {
  1275.       fprintf(stderr,"syntax: strongtest [-t] [-v] [-w] <manual>\n");
  1276.       exit(1);
  1277.    }
  1278.  
  1279.    for (i=1; i<argc; i++)
  1280.    {
  1281.       switch (*argv[i])
  1282.       {
  1283.          case '-' :  switch (*(argv[i]+1))
  1284.                      {
  1285.                         case 't' : case 'T' : throwback=1; break;
  1286.                         case 'v' : case 'V' : verbose=1; break;
  1287.                         case 'd' : case 'D' : debugmd=1; break;
  1288.                         case 'w' : case 'W' : warnext=1; break;
  1289.                         default: fprintf(stderr,"bad switch -%c\n",
  1290.                                          *(argv[i]+1));
  1291.                                  exit(1);
  1292.                      }
  1293.                      break;
  1294.          default  :  if (gotname)
  1295.                      {
  1296.                         fprintf(stderr,"syntax: strongtest [-t] [-v] "
  1297.                                        "<manual>\n");
  1298.                         exit(1);
  1299.                      }
  1300.                      gotname=1;
  1301.                      strcpy(manual,argv[i]);
  1302.       }
  1303.    }
  1304.  
  1305.    if (!gotname)
  1306.    {
  1307.       fprintf(stderr,"syntax: strongtest [-t] [-v] <manual>\n");
  1308.       exit(1);
  1309.    }
  1310.  
  1311.    printf(identity); /* Output name, version, compilation date & © */
  1312.  
  1313.    if (debugmd)
  1314.    {
  1315.       printf("DEBUG: testing '%s', throwback %s\n",
  1316.              manual, oof(throwback));
  1317.    }
  1318.  
  1319.    r.r[0]=17;
  1320.    r.r[1]=(int) manual;
  1321.    _kernel_swi(OS_File,&r,&r);      /* read cat. info for 'manual' */
  1322.  
  1323.    switch (r.r[0])                  /* r0 = object type */
  1324.    {
  1325.       case 0 : fprintf(stderr,"'%s' not found\n",manual);
  1326.                exit(2);
  1327.                break;
  1328.       case 1 : fprintf(stderr,"'%s' is a file "
  1329.                        "(is Stronghelp running?)\n",manual);
  1330.                exit(2);
  1331.                break;
  1332.       case 2 : fprintf(stderr,"'%s' is a directory\n",manual);
  1333.                exit(2);
  1334.                break;
  1335.       case 3 : if ( (r.r[2] & 0xffffff00)!=0xfff3d600 )
  1336.                {
  1337.                   fprintf(stderr,"'%s' is not a manual\n",manual);
  1338.                   exit(2);
  1339.                }
  1340.                break;
  1341.       default: fprintf(stderr,"unrecognised object type"
  1342.                               " (%d) for '%s'\n",r.r[0],manual);
  1343.                exit(2);
  1344.    }
  1345.  
  1346.    head=malloc(sizeof(node));
  1347.    if (head==0)
  1348.    {
  1349.      fprintf(stderr,"nowhere near enough memory to run!\n");
  1350.      exit(4);
  1351.    }
  1352.  
  1353.    goodbad = testmanual(manual);
  1354.    i = dangling+unaccessible+badchars+fserror+missingslash;
  1355.    if (goodbad)
  1356.    {
  1357.       fprintf(stderr,"Manual '%s' contains %d error%s\n",
  1358.                leafname(manual),i,plural(i,"s"));
  1359.       if (warnings)
  1360.       {
  1361.          fprintf(stderr,"and raised %d warning%s\n",warnings,plural(warnings,"s"));
  1362.       }
  1363.    } else {
  1364.       printf("Manual '%s' checks out OK.\n",leafname(manual));
  1365.       if (warnings)
  1366.       {
  1367.          printf("(with %d warning%s)\n",warnings,plural(warnings,"s"));
  1368.       }
  1369.    }
  1370.  
  1371.    return goodbad;
  1372. }
  1373.  
  1374. /* Phew! */
  1375.  
  1376. /*
  1377.         © Musus Umbra, 1996
  1378.         See Help file for Licence
  1379. */
  1380.