[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
[0735]
[0736]
[0737]
[0738]
[0739]
[0740]
[0741]
[0742]
[0743]
[0744]
[0745]
[0746]
[0747]
[0748]
[0749]
[0750]
[0751]
[0752]
[0753]
[0754]
[0755]
[0756]
[0757]
[0758]
[0759]
[0760]
[0761]
[0762]
[0763]
[0764]
[0765]
[0766]
[0767]
[0768]
[0769]
[0770]
[0771]
[0772]
[0773]
[0774]
[0775]
[0776]
[0777]
[0778]
[0779]
[0780]
[0781]
[0782]
[0783]
[0784]
[0785]
[0786]
[0787]
[0788]
[0789]
[0790]
[0791]
[0792]
[0793]
[0794]
[0795]
[0796]
[0797]
[0798]
[0799]
[0800]
[0801]
[0802]
[0803]
[0804]
[0805]
[0806]
[0807]
[0808]
[0809]
[0810]
[0811]
[0812]
[0813]
[0814]
[0815]
[0816]
[0817]
[0818]
[0819]
[0820]
[0821]
[0822]
[0823]
[0824]
[0825]
[0826]
[0827]
[0828]
/* PROGRAM	Handle a VMS Help request from a WWW client	VMSHelpGate.c
**		===========================================
**		PROGRAM FOR CGI/1.0 SCRIPTS TO GENERATE HTML
**		RENDITIONS OF VMS HELP LIBRARY CONTENTS.
**		  Note that in the present absence of table
**		  support, PRE formatting is used to create
**		  multicolumn displays.  The tab expansion
**		  functions cannot take into account any
**		  numbering of anchors performed by the client,
**		  so the displays will not be as intented when
**		  this gateway is used by the WWW LineMode
**		  browser, or by Lynx when set to "Numbered
**		  Links" mode.  The PRE formatting assumes
**		  the client has an 80 column (or greater)
**		  display window in effect.
**			    FOR USE ON VMS.
**
** USAGE (called from a script, e.g., HelpGate.com):
**
**	$ VMSHelpGate :== $device:[directory]VMSHelpGate.exe
**	$ VMSHelpGate 				! cern server
**	$ VMSHelpGate method url protocol	! decthreads server.
**
**		The symbol WWW_PATH_INFO is set by the httpd and holds
**		the argument for the help library and module to be
**		returned to the client as HTML.  It should be of the
**		form:  /HELP[/@library][/topic[/subtopic...]]
**		with the following in the httpd configuration file to
**		set it up:
**
**		map	/help	/htbin/helpgate/HELP
**		map	/help/*	/htbin/helpgate/HELP/*
**		exec	/htbin/*	/HTTPD_Dir/*
**
**		If no "/@library" is specified, "/@SYS$HELP:HELPLIB"
**		is assumed.
**
**		The symbols WWW_HOST_ACRONYM for indicating the
**		host (e.g., WFEB) in the TITLE, and WWW_COPYRIGHT_STRING
**		for showing a copyright notice at the tops of displays,
**		should be set by the script.
**
**		The script should also set the following two symbols 
**		to "TRUE" or "FALSE" as desired. They default to "FALSE" 
**		if undefined.
**		  WWW_CREATE_REAL_LINKS determines if we create real links 
**		                        from links simply listed in HELP text.
**		  WWW_CREATE_HELP_LINKS determines if we create links to other 
**		                        HELP topics that are referenced.
**
**	  	A couple of notes if you use the WWW_CREATE_HELP_LINKS code:
**		  * Just one space is allowed between HELP words.
**		  * Qualifiers are not supported (yet).
**		  * Hyphenated HELP topics (like HELP E-MAIL) where the 
**		    hyphen is the last character on a line are unsupported.
**		And if you use the WWW_CREATE_REAL_LINKS code, note that the
**		URL is assumed to be contained all on one line.
**
** AUTHORS:
**	JFG	Jean-Francois Groff, CERN, Geneva	jfg@info.cern.ch
**	FM	Foteos Macrides				macrides@sci.wfeb.edu
**	DLJ	David L. Jones, Ohio State Univ.	vman+@osu.edu
**	JW	Jim Winkle, Univ of Wisconsin-Madison	jwinkle@doit.wisc.edu
**
** HISTORY:
**		Originally written by JFG as a standalone gateway.
**		Extensively modified by FM for use with HTTP/1.0 and
**		CGI scripts.
**
** 	JFG's HISTORY:
**	--------------
** 1.1  26 Feb 92 (JFG) Enabled strange topics
** 1.0	 7 Oct 91 (JFG) First release
** 0.4	 2 Oct 91 (JFG) Handle help summary. Definitive address format :
**			//node[:port]/HELP[/@library][/topic[/subtopic...]]
** 0.3	27 Sep 91 (JFG)	Created from h2h.c
**
** (c) CERN WorldWideWeb project 1990-1992. See Copyright.html for details
**
**	Current HISTORY:
**	----------------
** 2.0   1 May 94 (FM)	Polishing up my modifications, for general release.
**
**	DLJ mods:
** 	--------------------
**	 5 May 95	Convert to support DECthreads scripting system
**			(cgilib).  Replace printfs with cgi_printf.
**	 7 May 94	Replace WWW_HELP_DIR 'caching' with direct library 
**			access via VMS Librarian utility routines.
**
** 2.1   8 May 94 (FM)	Adopted DLJ's mods for CERN server too, with minor
**			fixes for spacing in PRE formatted sections, and bug
**			fix in lbr_close() of lbrio.c which caused ACCIO due
**			to bad argument in free() call.
**	11 May 94 (FM)  Added fixes from DLJ for Alphas.
**	27 Jun 94 (FM)  Fixes in anchor_strcpy() for current HELP library
**			rules and escaping of any lead colon.  Unescape
**			it in main() if still present.
**	DLJ mods:
** 	--------------------
**	 2 Jul 94	Make qualifers within a level anchors and include
**			in them with hrefs in generated menus.  This more
**			more closely mimics DCL HELP.  Use cleaner algorithm
**			to determine line-breaks in menus.
**
**	 3 Jul 94	Add hack to deal with bugs in mac-mosaicB6 and
**			<PRE> operations.
**
**	 31 Oct 94	Escape '<', '>', and '&' in help text as per HTML spec.
**			(No checks being done within anchors)
**
**	  2 Aug 99	Make finickier check for module boundary.
**
**	JW mods:
** 	--------------------
** 2.2	13 Nov 96	Create real links to links simply listed in HELP text,
**			and create links to other HELP topics referenced.
*/

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

#include "lbrio.h"		/* direct library access  *DLJ* */
#define TAB_SIZE 16		/* 11 is what DCL HELP uses. */
#define LINE_WIDTH 80
/*
 * For DECthreads, include the cgilib.h
 */
#ifdef NOCGILIB
#define cgi_init(a,b)  1
#define cgi_info(a) getenv(strcpy(&cgi_info_buf[4], a)-4)
static char cgi_info_buf[64] = { 'W', 'W', 'W', '_' };
#define cgi_printf printf
#else
#include "cgilib.h"
#endif


/** Headers taken from h2h.c **/

/* Maximum line size for buffers */
#define LSIZE 256

/* Maximum command line size for VMS */
#define CSIZE 256

/* Maximum VMS file name size (note that it's actually 49.49) */
#define FSIZE 80

/* Maximum anchor size */
#define ASIZE 80


/** Function declarations **/

int strcasecomp(char *, char *);
int strncasecomp(char *, char *, int);
char *anchor_strcpy(char *, char *);
void show_copyright(void);
int hlp_to_html(lbr_index, char *);
int lis_to_html(lbr_index, char *);
lbr_index open_hlp(char *, char *);
static char *pre_start;		/* either "/n<PRE>" or "<PRE>/n" (macmosaic) */

/*****************************************************************************

main  : Retrieve information from VMS help libraries or cached modules
	and convert to HTML "on the fly".

Input :
	symbol WWW_PATH_INFO set by httpd : HT address

------------------------------------------------------------------------------*/

int main(int argc, char *argv[])
{
  char *arg;
  lbr_index hlp;		/* lbr control structure *DLJ* */
  char *help_file;
  char *query;
  char *s;
  char *cp;
  char *browser;		/* User-agent field, DLJ */
  int status;

  /* Initialize CGI library. */
  status = cgi_init ( argc, argv );
  if ( (status&1) == 0 ) exit ( status );
  s = cgi_info ( "HTTP_USER_AGENT" );
  /*
   * Hack to accomodate MacMosaic bug.  Mosaic 1.0.3 ignores the first
   * LF it sees after going into <PRE> mode while other browsers need the
   * line break before going into <PRE>.  Check for the faulty browser and
   * do the right thing.
   */
  pre_start = "\n<PRE>";
  if ( s ) {
	browser = malloc ( strlen(s) + 1 );
	strcpy ( browser, s );
	for ( cp = browser; *cp; cp++ ) if (isspace(*cp) ) {
	    *cp = '\0'; break;
	}
	/* fprintf(stderr,"browser: '%s'\n", browser ); */
	if ( strcmp ( browser, "MacMosaicB6" ) == 0 ) pre_start = "<PRE>\n";
  }

  /* output the content type */
  cgi_printf("Content-Type: text/html\n\n<HTML>\n");

  /* address must be of the form /HELP[/@library][/topic[/subtopic...]] */
  if ((arg=cgi_info("PATH_INFO")) == NULL ||
      (s = strtok (arg + 1, "/")) == NULL) {
      cgi_printf("Address should begin with /HELP arg=%d\n</HTML>\n", arg);
      exit(0);
  }
  if (strcasecomp (s, "HELP")) {
      cgi_printf("Address should begin with /HELP : /%s\n</HTML>\n", s ? s : "");
      exit(0);
  }

  help_file = strtok (NULL, "/");
  if (help_file && *help_file == '@'
      && help_file[1] ) {  /* Explicit help library specified */
    help_file++;  /* Skip @ */
    query = strtok (NULL, "/+");
  } else {  /* This was not a help library, but the beginning of the query */
    query = help_file;
    help_file = NULL;
  }
  /* Unescape lead colon, if present in query */
  if (query && strncasecomp(query, "%3a", 3) == 0) {
      query += 2;
      *query = ':';
  }

  /* The first word from the query is the module name (perhaps empty) */
  if (! (hlp = open_hlp (help_file, query))) {
    cgi_printf("Help library or module not found : %s/%s\n",
	       help_file ? help_file : "", query ? query : "");
    cgi_printf("</HTML>");
    return 2160;
  }
  if (query) {  /* Parse rest of address and process help file */
    while (s = strtok (NULL, "/+"))
      *(s - 1) = ' ';
    status = hlp_to_html(hlp, query);
  }
  else  /* Process library directory */
    status = lis_to_html(hlp, help_file);

  cgi_printf("</HTML>");
  exit(0);
  return(status);
}


/*****************************************************************************

strcasecomp  : Case-insensitive comparison for strings of any length.
strncasecomp : Case-insensitive comparison for strings with count limit.

Return -1, 0, 1 equivalently to strcmp and strncmp

-----------------------------------------------------------------------------*/

/*	Strings of any length
**	---------------------
*/
int strcasecomp(char *a, char *b)
{
	char *p = a;
	char *q = b;
	for(p=a, q=b; *p && *q; p++, q++) {
	    int diff = tolower(*p) - tolower(*q);
	    if (diff) return diff;
	}
	if (*p) return 1;	/* p was longer than q */
	if (*q) return -1;	/* p was shorter than q */
	return 0;		/* Exact match */
}

/*	With count limit
**	----------------
*/
int strncasecomp(char *a, char *b, int n)
{
	char *p = a;
	char *q = b;
	
	for(p=a, q=b;; p++, q++) {
	    int diff;
	    if (p == a+n) return 0;	/*   Match up to n characters */
	    if (!(*p && *q)) return *p - *q;
	    diff = tolower(*p) - tolower(*q);
	    if (diff) return diff;
	}
}


/*****************************************************************************

anchor_strcpy : Converts a string to a name suitable for use as an anchor
		and for comparisons, i.e. trim to its first word (consisting
		of alphanumeric characters plus '_', '=', '@', '-', '$', '/'
		and '.'), make the letters uppercase, and convert each '$'
		'/' and '.' to '_'.  If string begins with ':', escape it.

Inputs :
	char *dest : destination string
	char *src  : source string

Returns the end position of the destination string.

-----------------------------------------------------------------------------*/

char *anchor_strcpy(char *dest, char *src)
{
  char *end = dest;
  /* Trim to first word */
  for ( ; *end =
            isalnum (*src) || *src == '_' || *src == '=' || *src == '@' ||
	    *src == '-' ? _toupper(*src)
          : *src == '/' || *src == '$' || *src == '.' ? '_'
          : '\0' ;
       ++end, ++src);
  if (end == dest) {   /* src doesn't begin with a word or _=@-/$. */
    if (*src == ':') { /* If it begins with a colon, escape it     */
        *end++ = '%';
	*end++ = '3';
	*end++ = 'a';
	src++;
    }
    for ( ; *src ; ++src) {
	*end++ = _toupper (*src);
    }
    *end = 0;  /* Terminate the string */
  }
  return end;
}


/****************************************************************************

show_copyright : Outputs a copyright notice if the script has
		 set the symbol WWW_COPYRIGHT_STRING.

----------------------------------------------------------------------------*/

void show_copyright()
{
  char *cp;

  if ((cp=getenv("WWW_COPYRIGHT_STRING")) != NULL)
  	cgi_printf("%s\n", cp);

  return;
}


/*****************************************************************************

hlp_to_html : Reads in help library module and outputs desired info in HTML format.

Inputs :
	lbr_index hlp : pointer to the .HLP file to be scanned
	char *query : data to be found, as blank-separated keywords
	              NOTE : this string is altered (tokenized) by the function

Optional symbols set by script :
	WWW_HOST_ACRONYM : Acronym for host.  Prefixed to HTML title.
	WWW_CREATE_REAL_LINKS : Create real links to links listed in HELP text.
	WWW_CREATE_HELP_LINKS : Create links to other HELP topics referenced.

Status returned :
	 0 : Success
Unused	-1 : Help file not found
	-2 : Query not found
Unused	-3 : Empty query
	-4 : Structure error in help file (may create some text before that)
Unused	-5 : Write error to HTML file (may create some text before that)
Unused	-6 : Can't open HTML output file

-----------------------------------------------------------------------------*/

int hlp_to_html(lbr_index hlp, char *query)
{
  int subqual;	     /* True if level defines qualifiers (DLJ) */
  int dlen;	     /* 'Display' length, chars on current display line */
  char carcon[TAB_SIZE*2];
  char *sep = " ";   /* Authorized keyword separators in the query */
  char line[LSIZE];  /* Input buffer */
  char out[LSIZE];   /* Output buffer */
  /* Possible states of the scanning algorithm */
  enum { PARSE, TEXT, MENU, DONE } state;
  int depth;   /* Current depth in the help tree */
  char *key;   /* Currently searched keyword from the query */
  int prefix;  /* Prefix number on a line of the help file, indicating depth */
  char *title; /* Pointer to the rest of a title line after depth prefix */
  char anchor[ASIZE];  /* Anchor name built from menu title and item */
  char *menu_item;     /* Pointer to menu item within anchor name */
  int multi_line_HELP;	/* Flag indicating "HELP [topic]..." is > 1 line long */
  char help_topic_saved[256];	/* Place to save first line(s) from multi-line HELP */
  char *cp, *s, *t;  /* Temporary string pointers */
  int i = 1, len;
  int create_real_links;	/* Flag equivalent to WWW_CREATE_REAL_LINKS */
  int create_help_links;	/* Flag equivalent to WWW_CREATE_REAL_LINKS */

  /* Initial things to send out */

  state = PARSE;  /* Searching for the query's keywords */
  multi_line_HELP = 0;
  depth = 1;
  subqual = 0;
  key = strtok (query, sep); /* Read first keyword from the query */
  anchor_strcpy (key, key);  /* Normalise key to help matches */

  /* If TRUE, then TRUE. If undefined, FALSE, or other, then FALSE. */
  if (create_real_links = (cp=getenv("WWW_CREATE_REAL_LINKS")) != NULL)
    create_real_links = strcmp (cp, "TRUE") == 0;
  if (create_help_links = (cp=getenv("WWW_CREATE_HELP_LINKS")) != NULL)
    create_help_links = strcmp (cp, "TRUE") == 0;

  while (1==(1&lbr_fgets (line, LSIZE, hlp)) ) {
    if (isdigit (*line) && isspace(line[1]) ) {  /* This is a title line */
      prefix = strtol (line, &title, 10);
      while (isspace (*title))  /* Find the real beginning of the title */
	title++;
      s = title;
      while (*(++s) != '\n');  /* Find the real end of the title */
      while (isspace (*(--s)));
      *(++s) = '\0';  /* Trim title to a clean string */

      switch (state) {
      case PARSE:  /* Check title against query */
	menu_item = anchor_strcpy (anchor, title);
	/* Is this a match ? */
	if (prefix == depth && !strncmp(anchor, key, strlen(key))) {
	  if (depth == 1) {  /* Root match */
	    /* Start the HTML title with the first word of 'title' */
	    cgi_printf ("<TITLE>%s Help %.*s",
	    		    (cp=getenv("WWW_HOST_ACRONYM")) ? cp : "",
	    		    menu_item - anchor, title);
	  } else {  /* Deep match : continue HTML title in the same way */
	    cgi_printf(" %.*s", menu_item - anchor, title);
	  }
	  if (key = strtok (NULL, sep)) {  /* More levels to descend */
	    depth++;
	    anchor_strcpy (key, key); /* Normalise key to help matches */
	  }
	  else {  /* Query fully parsed */
	    cgi_printf("</TITLE>\n");  /* End the HTML title */
	    show_copyright();  /* show copyright notice if set by script */
	    cgi_printf("<H1>%s</H1>", title);  /* Print full heading */
	    *menu_item++ = '/';  /* Append field separator to anchor */
	    *menu_item = '\0';  /* Ready for anchor completion */
	    state = TEXT;
	    cgi_printf(pre_start);  /* Will copy help text verbatim */
	  }
	}
	break;

      case TEXT:
	cgi_printf("\n</PRE>\n");  /* Help text is finished, folks ! */
	if (prefix <= depth) {
	  /* We reached the next item at this depth or it was the last one */
	  /* That's all, folks ! */
	  state = DONE;
	  break;
	} else if (prefix == depth + 1) {  /* This item has a menu */
	  state = MENU;
	  depth++;
	  cgi_printf("<H3>Additional information available:</H3>%s",pre_start);
	  i = 1;
	  dlen = 0;
	} else {  /* The help file skipped the next depth */
	  lbr_close (hlp);
	  cgi_printf("Help file corrupted.\n");
	  return(-4);
	}
	/* No break here : flow through case MENU if state transition */
	/* (Yeah, you may find this ugly...) */

      case MENU:
	if (prefix < depth) {  /* Seen all menu items */
	  cgi_printf("\n</PRE>\n");
	  /* That's all, folks ! */
	  state = DONE;
	} else if (prefix == depth) {  /* Next menu item */
	  anchor_strcpy (menu_item, title);  /* Append item to anchor prefix */
	  len = strlen(title);		/* printable chars */

	  if ( 0 == dlen ) strcpy ( carcon, "" );
	  else {
	     int j = 0;
	     carcon[j++] = ' ';
	     for ( dlen++; dlen%TAB_SIZE; dlen++ ) carcon[j++] = ' ';
	     carcon[j] = '\0';
	     if ( subqual || ( (dlen + len) >= LINE_WIDTH ) ) {
		strcpy ( carcon, "\n" );
		dlen = 0; i = 1;
	     }
	  }
	  dlen += len;
	  subqual = 0;		/* new level started */
	  cgi_printf("%s<A HREF=\"%s\">%s</A\n>", carcon, anchor, title );

	} /* if prefix > depth, there's a submenu : ignore it. */
	break;
	
      case DONE:  /* Shouldn't happen here */
	fprintf (stderr, "Internal error : invalid state DONE.\n");
	break;

      }  /* End of switch(state) */
    }  /* End of title line processing */

    else { /* This is a text line */
      if (state == TEXT && *line != '!') { /* It's not a comment ('!') */
        if (*line == '/') {
	     /* Make qualifier header bold  and make it an anchor.  Anchor
	      * name is only the qualifier name itself. */
	     char tag[80]; int j;
	     strncpy ( tag, line+1, sizeof(tag)-2 ); tag[sizeof(tag)-1] = '\0';
	     for ( j = 0; tag[j]; j++ ) if ( isspace(tag[j]) ||
		( tag[j] == '\n' ) ) { tag[j] = '\0'; break; }
	     for ( j = 0; line[j] && (line[j] != '\n'); j++); line[j] = '\0';
	     cgi_printf("<b><A NAME=\"%s\">%s</A></b>", tag, line);
	} else {
	  /* Copy text line to HTML file: */
	  /*    - escaping '<', '>', and '&' */
	  /*    - creating real links to links simply listed in HELP text */
	  /*    - creating links to other HELP topics referenced */
    	  char *start, *curp, testc;
	  int url_length, token_length;
	  char *help_start;	/* start of the H"ELP [topic...]" string */
	  char url[256];	/* the url we're forming */
	  char curc_saved;	/* we need to overwrite a character to 
				   terminate a string, so save it */

	  for ( start = curp = line; (testc=(*curp)); curp++ ) {
	    if ( (testc == '<') || (testc == '>') || (testc == '&') ) {
	      *curp = '\0';	/* terminate the start string */
	      cgi_printf( (testc=='<') ? "%s&lt;" : 
		         ((testc=='>') ? "%s&gt;" : "%s&amp;"), start );
	      start = curp+1;                 
	    } else if (create_real_links && (
	       (testc == 'h' && strncmp (curp, "http://", strlen ("http://")) == 0) ||
	       (testc == 'g' && strncmp (curp, "gopher://", strlen ("gopher://")) == 0) ||
	       (testc == 't' && strncmp (curp, "telnet://", strlen ("telnet://")) == 0) ||
	       (testc == 'f' && strncmp (curp, "ftp://", strlen ("ftp://")) == 0) ||
	       (testc == 'm' && strncmp (curp, "mailto:", strlen ("mailto:")) == 0) ||
	       (testc == 'n' && strncmp (curp, "news:", strlen ("news:")) == 0) )) {
	      url_length = strcspn (curp, " \t\n,?;!)");
	      strncpy (url, curp, url_length); url[url_length] = '\0';
	      *curp = '\0';	/* terminate the start string */
	      cgi_printf("%s<A HREF=\"%s\">%s</A>", start, url, url);
	      start = curp+url_length;
	    } else if (create_help_links && (
	       (testc == 'H' && strncmp (curp, "HELP", strlen ("HELP")) == 0) &&
	       (curp == line || *(curp-1) == '(' || *(curp-1) == ' ') )) { /* avoid  SYS$HELP, 264-HELP, etc. */
	      int j;
	      char token[64];

	      strcpy (url, "");
	      while (isupper (*curp)) {
	        token_length = strcspn (curp, " \t\n,?;!).");
	        strncpy (token, curp, token_length);
	        token[token_length] = '\0';
	        if ( strcmp (token, "HELP") == 0 ) {
	          *curp = '\0';	/* terminate the start string */
	          help_start = curp+1;
	        } else
	          strcat (url, "/");
	        strcat (url, token);
	        curp = curp + token_length;
	        if (*curp == ' ') curp++;
  	      } 
	      multi_line_HELP = ( *curp == '\n' );
	      if ( *(curp - 1) == ' ' ) --curp; 
	      curc_saved = *curp;
	      *curp = '\0';	/* terminate the help_start string */
	      if (!multi_line_HELP)
	        if (strlen (url) > strlen ("HELP"))
	          cgi_printf("%s<A HREF=\"/%s\">%c%s</A>%c", start, url, testc, help_start, curc_saved);
	        else
	          cgi_printf("%s%c%s%c", start, testc, help_start, curc_saved);
	      else {
		cgi_printf("%s<A HREF=\"/%s", start, url);
	      	/* save the help_topic for later */
		strcpy (help_topic_saved, " ");
		help_topic_saved[0] = testc;
		strcat (help_topic_saved, help_start);
		*(curp + 1) = '\0';	/* terminate the for loop, way above */
	      }
	      start = curp+1;
	    }
	    else if (multi_line_HELP) {
	      int j;
	      char token[64];

	      strcpy (url, "");
	      while (*curp == ' ' || *curp == '\t') curp++; /* skip white space at beginning of line */
	      while (isupper (*curp)) {
	        token_length = strcspn (curp, " \t\n,?;!).");
	        strncpy (token, curp, token_length);
	        token[token_length] = '\0';
	        strcat (url, token);
	        strcat (url, "/");
	        curp = curp + token_length;
	        if (*curp == ' ') curp++;
	      }
	      multi_line_HELP = ( *curp == '\n' && strlen (url) > 0 );
	      if ( *(curp - 1) == ' ' ) --curp; 
	      curc_saved = *curp;
	      *curp = '\0';	/* terminate the start string */
	      if (!multi_line_HELP)
	        if (strlen (url) > 0) {
	          url[strlen (url) - 1] = '\0';	/* delete the trailing '/' */
	          cgi_printf("/%s\">%s\n%s</A>%c", url, help_topic_saved, start, curc_saved);
	        } else
	          if (strcmp (help_topic_saved, "HELP") == 0) /* don't create link for just "HELP" */
	            cgi_printf("\"></A>%s\n%s%c", help_topic_saved, start, curc_saved);
	          else
	            cgi_printf("\">%s\n</A>%s%c", help_topic_saved, start, curc_saved);
	      else {	/* third and subsequent lines */
	        url[strlen (url) - 1] = '\0';	/* delete the trailing '/' */
	        cgi_printf("/%s", url);
	        strcat (help_topic_saved, "\n");
	        strcat (help_topic_saved, start);
	        *(curp + 1) = '\0';	/* terminate the for loop, way above */
	      }
	      start = curp+1;
	    }   
	  }
	  cgi_printf("%s", start);
	}
      }           
	else if ( (state == MENU) && (*line == '/') && (prefix == depth) ) {
	    /*
	     * The help module has qualifier definitions that don't go to the
	     * next level.  Add these to the menu, starting them on a fresh
	     * line.  Reference will be using intra-module syntax.
	     */
	    char tag[80]; int j;
	     strncpy ( tag, line+1, sizeof(tag)-2 ); tag[sizeof(tag)-1] = '\0';
	     for ( j = 0; tag[j]; j++ ) if ( isspace(tag[j]) ||
		( tag[j] == '\n' ) ) { tag[j] = '\0'; break; }
	     for ( j = 0; line[j] && (line[j] != '\n'); j++); line[j] = '\0';

	    len = strlen ( line );
	    if ( 0 == dlen ) strcpy ( carcon, "" );
	    else {
		j = 0;
		carcon[j++] = ' ';
		for ( dlen++; dlen%TAB_SIZE; dlen++ ) carcon[j++] = ' ';
		carcon[j] = '\0';
		if ( (subqual==0) || ( dlen + len >= LINE_WIDTH ) ) {
		    strcpy ( carcon, "\n" );
		    dlen = 0;
		}
	    }
	    subqual = 1;
	    dlen += len;
	    cgi_printf ( "%s<A HREF=\"%s#%s\">%s</A\n>", 
			carcon, anchor, tag, line );
	}
    }  /* End of text line processing */

    if (state == DONE) {  /* Have we finished yet ? */
      lbr_close (hlp);
      cgi_printf("\n");
      return 0;  /* success */
    }

  }  /* EOF reached on help file */
  lbr_close (hlp);
  if (state != PARSE) {  /* EOF reached while outputting HTML */
    cgi_printf("\n</PRE>\n");
    return 0;  /* success */
  } else {  /* key not found */
    if (depth > 1)  /* Partial match : end the title cleanly */
      cgi_printf("</TITLE>\n");
    cgi_printf("\"%s\" not found in help file.\n", key ? key : "");
    return(-2);
  }
}


/*****************************************************************************

lis_to_html : Reads current library module index and outputs menu in HTML format.

Inputs :
	lbr_index dir : pointer to the library to be scanned
	char *lib_name : name of the help library (for HTML title)

Optional symbols set by script :
	WWW_HOST_ACRONYM : Acronym for host.  Prefixed to HTML title.

Status returned :
	 0 : Success
	-2 : Structure error in .LIS file

-----------------------------------------------------------------------------*/

int lis_to_html(lbr_index dir, char *lib_name)
{
  char line[LSIZE];  /* Input buffer */
  char spacing[24];	/* Holds output chars that move cursoro to next col */
  char *entry;  /* Pointer to current directory entry */
  char *cp, *at_prefix;
  int dlen;
  int i = 1, len, j, extra_cols, fill;
  
  if (lib_name)  /* Help library specified */
    at_prefix = "HELP/../@"; /* Use a relative path trick */
  else {  /* Plain HELP request */
    at_prefix = "";
    lib_name = "HELP";  /* Root level help */
  }
    
  /* Initial things to send out */
  cgi_printf("<TITLE>%s Help Library ",
  		 (cp=getenv("WWW_HOST_ACRONYM")) ? cp : "");
  cgi_printf("%s", lib_name);
  cgi_printf(" Contents</TITLE>\n");
  show_copyright();  /* show copyright notice if set by script */
  cgi_printf("<H1>%s</H1>%s", lib_name, pre_start);

  /* Now scan the entries */
  i = 1;			/* Current column, 1..5  */
  entry = line;
  extra_cols = 0;
  dlen = 0;
  fill = 0;
  while ( 1==(1&lbr_read_directory (entry, LSIZE, dir)) ) {
    len = strlen(entry);	/* length of entry */
    if ( 0 == dlen ) strcpy ( spacing, "" );
    else {
	j = 0;
	spacing[j++] = ' ';
	for ( dlen++; dlen%TAB_SIZE; dlen++ ) spacing[j++] = ' ';
	spacing[j] = '\0';
	if ( dlen + len >= LINE_WIDTH ) {
	    strcpy ( spacing, "\n" );
	    dlen = 0;
	}
    }
    dlen += len;
    cgi_printf ( "%s<A HREF=\"%s%s/%s\">%s</A\n>", 
		spacing, at_prefix, lib_name, entry, entry );
  }  /* End of entry processing */

  lbr_close (dir);
  cgi_printf("\n</PRE>\n");  /* End the pseudo-table cleanly */
  return 0;  /* success */
}


/*****************************************************************************

open_hlp : Opens desired module from specified library. Extracts it from
           .HLB help library if necessary. If no module is specified, opens
	   (and perhaps creates) .LIS library directory file.

Inputs :
	char *help_lib : library name
	char *module : name of the module to extract

Returns lbr_index pointing to control stucture.

----------------------------------------------------------------------------*/

lbr_index open_hlp(char *help_lib, char *module)
{
    lbr_index lptr;
    int status, searching;
    char *lib_name;
    char fallback[256];

  /* First, create the relevant (unique) file name */
  if ( help_lib ) {
      lib_name = help_lib;
      strcpy ( fallback, "HELPLIB" );
      if ( strcmp(help_lib,"HELPLIB") == 0 ) strcpy ( fallback, "HLP$LIBRARY");
  } else {
      /* Try helplib first */
      lib_name = "HELPLIB";
      strcpy ( fallback, "HLP$LIBRARY" );
  }

  /*
   * Search specified library plus default libraries.
   */
  for ( searching = 1; searching; ) {
        status = lbr_open ( lib_name, "SYS$HELP:.HLB", &lptr );
	if ( 1 == (status&1) ) {
	    /* See if module in library */
	    if ( module ) {
		status = lbr_set_module ( lptr, module );
		if ( 1 == (status&1) ) searching = 0;	/* Found a match */
		else lbr_close ( lptr );		/* not this library */
	    } 
	    else searching = 0;		/* Assume directory scan */
	    
	}
        if ( searching ) {
	    /*
	     * Library open failed, set up libname for next try.
	     */
	    if ( strcmp(fallback,"HELPLIB") == 0 ) {
		strcpy ( fallback, "HLP$LIBRARY" );
		lib_name = "HELPLIB";
	    } else {
		lib_name = getenv(fallback);
		if ( !lib_name ) break;		/* no more fallbacks */
		sprintf(fallback,"HLP$LIBRARY_%d", searching++ );
	    }
	}
    }
    /*
     * Breaking out of loop (searching non-zero) means module not found.
     */
   return searching ? (lbr_index) 0 : lptr;		/* DLJ */
}