/* Handle a VMS Help request from a WWW client VMSHelpGate.c
** ===========================================
**
** Authors
** JFG Jean-Francois Groff, CERN, Geneva jfg@info.cern.ch
**
** 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 */
/** Headers taken from HTRetrieve.c **/
#define BUFFER_SIZE 4096 /* Arbitrary size for efficiency */
#include "HTUtils.h"
#include "tcp.h"
extern int WWW_TraceFlag; /* Control diagnostic output */
extern FILE * LogFile; /* Log file output */
extern char HTClientHost[16]; /* Client name to be output */
extern int HTWriteASCII(int soc, char * s); /* In HTDaemon.c */
/** 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 (on 5.3, it's really 41, yuk! hope for more) */
#define FSIZE 80
/* Maximum anchor size */
#define ASIZE 80
/* Diagnostics processing utilities */
static const char *module_name = "VMSHelpGate: ";
char diag[LSIZE]; /* Diagnostic string */
/* Horrible kludge, but saves a lot of pain */
#define DIAGNOSE(sprintf_arg_list, error_code) { \
sprintf sprintf_arg_list; \
if (TRACE) fprintf (stderr, "%s%s", module_name, diag); \
HTWriteASCII (soc, diag); \
return error_code; \
}
/*******************************************************************************
anchor_strcpy : Converts a string to a name suitable for use as an anchor and
for comparisons, i.e. trim it to its first word (consisting of
alphanumeric characters plus '_', '-', '$' and '/'),
make it uppercase and convert each '/', '-' and '$' to '_'.
If the string does not begin with a "good" character, escape it.
Inputs :
char *dest : destination string
char *src : source string
Returns the end position of the destination string.
------------------------------------------------------------------------------*/
static char *anchor_strcpy
#ifdef __STDC__
(char *dest, char *src)
#else
(dest, src)
char *dest;
char *src;
#endif
{
char *end = dest;
/* Trim to first word */
for ( ; *end =
isalnum (*src) ? toupper(*src)
: *src == '/' || *src == '_' || *src == '-' || *src == '$' ? '_'
: '\0' ;
++end, ++src);
if (end == dest) { /* src doesn't begin with a word or /_-$ : escape it */
for ( ; *src ; ++src) {
*end++ =
isalnum (*src) ? toupper (*src)
: 'A' + (*src % 26); /* Hash a letter from the bad character */
}
*end = 0; /* Terminate the string */
}
return end;
}
/*******************************************************************************
hlp_to_html : Reads in .HLP help file and outputs desired info in HTML format.
Inputs :
FILE *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
int soc : HTML output socket
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
------------------------------------------------------------------------------*/
static int hlp_to_html
#ifdef __STDC__
(const FILE *hlp, const char *query, const int soc)
#else
(hlp, query, soc)
FILE *hlp;
char *query;
int soc;
#endif
{
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 */
char *s, *t; /* Temporary string pointers */
/* Initial things to send out */
/* HTWriteASCII (soc, "\n"); /* DON'T Enable keyword searches */
state = PARSE; /* Searching for the query's keywords */
depth = 1;
key = strtok (query, sep); /* Read first keyword from the query */
anchor_strcpy (key, key); /* Normalise key to help matches */
while (fgets (line, LSIZE, hlp)) {
if (isdigit (*line)) { /* 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' */
sprintf (out, "
Help %.*s", menu_item - anchor, title);
HTWriteASCII (soc, out);
} else { /* Deep match : continue HTML title in the same way */
sprintf (out, " %.*s", menu_item - anchor, title);
HTWriteASCII (soc, out);
}
if (key = strtok (NULL, sep)) { /* More levels to descend */
depth++;
anchor_strcpy (key, key); /* Normalise key to help matches */
}
else { /* Query fully parsed */
HTWriteASCII (soc, "\n"); /* End the HTML title */
sprintf (out, "%s
\n", title); /* Print full heading */
HTWriteASCII (soc, out);
*menu_item++ = '/'; /* Append field separator to anchor */
*menu_item = '\0'; /* Ready for anchor completion */
state = TEXT;
HTWriteASCII (soc, "\n"); /* Will copy help text verbatim */
}
}
break;
case TEXT:
HTWriteASCII (soc, "\n"); /* Help text is finished, folks ! */
if (prefix <= depth) {
/* We reached the next item at this depth or it was the last one */
state = DONE;
break;
} else if (prefix == depth + 1) { /* This item has a menu */
state = MENU;
depth++;
HTWriteASCII (soc, "Additional information available:
\n\n");
} else { /* The help file skipped the next depth */
fclose (hlp);
DIAGNOSE ((diag, "Help file corrupted.\n"), -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 */
HTWriteASCII (soc, "\n"); /* That's all, folks ! */
state = DONE;
} else if (prefix == depth) { /* Next menu item */
anchor_strcpy (menu_item, title); /* Append item to anchor prefix */
sprintf (out, "%s\n", anchor, title);
HTWriteASCII (soc, out);
} /* 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 != '!')
/* Copy line verbatim to HTML file, except if it's a comment ('!') */
HTWriteASCII (soc, line);
} /* End of text line processing */
if (state == DONE) { /* Have we finished yet ? */
fclose (hlp);
/* FIXME Insert here HTML file termination */
return 0; /* success */
}
} /* EOF reached on help file */
fclose (hlp);
if (state != PARSE) { /* EOF reached while outputting HTML */
if (state == MENU)
HTWriteASCII (soc, "\n"); /* End the menu cleanly */
return 0; /* success */
} else { /* key not found */
if (depth > 1) /* Partial match : end the title cleanly */
HTWriteASCII (soc, "\n");
DIAGNOSE ((diag, "\"%s\" not found in help file.\n", key), -2);
}
}
/*******************************************************************************
lis_to_html : Reads in .LIS library directory and outputs menu in HTML format.
Inputs :
FILE *dir : pointer to the .LIS file to be scanned
char *lib_name : name of the help library (for HTML title)
int soc : HTML output socket
Status returned :
0 : Success
-2 : Structure error in .LIS file
------------------------------------------------------------------------------*/
static int lis_to_html
#ifdef __STDC__
(const FILE *dir, const char *lib_name, const int soc)
#else
(dir, lib_name, soc)
FILE *dir;
char *lib_name;
int soc;
#endif
{
char line[LSIZE]; /* Input buffer */
char out[LSIZE]; /* Output buffer */
char *entry; /* Pointer to current directory entry */
char *at_prefix;
if (lib_name) /* Help library specified */
at_prefix = "@";
else { /* Plain HELP request */
at_prefix = "";
lib_name = "HELP"; /* Root level help */
}
/* Initial things to send out */
/* HTWriteASCII (soc, "\nHelp Library "); */
HTWriteASCII (soc, "Help Library ");
HTWriteASCII (soc, lib_name);
HTWriteASCII (soc, " Contents\n");
HTWriteASCII (soc, lib_name);
HTWriteASCII (soc, "
\n\n");
do { /* Find the first empty line */
if (! fgets (line, LSIZE, dir))
DIAGNOSE ((diag, "Structure error in help library directory.\n"), -2);
} while (*line != '\n');
/* Now scan the entries */
while (fgets (line, LSIZE, dir) && (entry = strtok (line, " \n"))) {
sprintf (out, "%s\n",
at_prefix, lib_name, entry, entry);
HTWriteASCII (soc, out);
} /* End of entry processing */
fclose (dir);
HTWriteASCII (soc, "\n"); /* End the menu cleanly */
return 0; /* success */
}
/*******************************************************************************
open_hlp : Opens desired .HLP 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 FILE * pointing to relevant .HLP or .LIS if success, NULL on error.
------------------------------------------------------------------------------*/
static FILE *open_hlp
#ifdef __STDC__
(const char *help_lib, const char *module, const int soc)
#else
(help_lib, module, soc)
char *help_lib;
char *module;
int soc;
#endif
{
char hlpfile[FSIZE];
char *help_prefix;
char *s, *t;
FILE *hlp;
char command[CSIZE];
/* First, create the relevant (unique) file name */
if (! help_lib) /* Plain HELP request */
help_lib = "HELPLIB"; /* Open default help library */
s = strchr (help_lib, ':');
help_prefix = s ? '\0' : "SYS$HELP:";
t = strchr (help_lib, ']');
s = (t && t > s) ? t + 1 : (s ? s + 1 : help_lib); /* Find beginning */
if (! (t = strrchr (help_lib, '.'))) /* Find end */
t = help_lib + strlen (help_lib);
if (module) {
sprintf (hlpfile, "WWW$TEMP_DIR:%.*s_", t - s, s);
anchor_strcpy (hlpfile + strlen (hlpfile), module); /* Append valid module name */
strcat (hlpfile, ".HLP");
}
else
sprintf (hlpfile, "WWW$TEMP_DIR:%.*s.LIS", t - s, s);
if (hlp = fopen (hlpfile, "r")) /* Does it already exist ? */
return hlp; /* Yes : return it, else create it */
if (module)
sprintf (command, "LIBRARY /HELP %s%s /EXTRACT=\"%s\" /OUTPUT=%s",
help_prefix, help_lib, module, hlpfile);
else
sprintf (command, "LIBRARY /HELP %s%s /LIST=%s",
help_prefix, help_lib, hlpfile);
if (! (system (command)) & 1) { /* VMS error status ? */
if (! module)
return NULL; /* Failure */
/* Try again in default help libraries (FIXME scan a list here) */
sprintf (command, "LIBRARY /HELP SYS$HELP:HELPLIB /EXTRACT=\"%s\" /OUTPUT=%s",
module, hlpfile);
if (! (system (command)) & 1) /* VMS error status ? */
return NULL; /* Failure */
}
return fopen (hlpfile, "r");
}
/*******************************************************************************
HTRetrieve : Retrieves information from VMS help library.
Inputs :
char *arg : HT address
char *keywords : plus-separated keyword list, if any
int soc : output socket
WARNING : This function relies on the fact that the keywords string
immediately follows the arg string. It also modifies those strings.
------------------------------------------------------------------------------*/
int HTRetrieve
#ifdef __STDC__
(const char *arg, const char *keywords, const int soc)
#else
(arg, keywords, soc)
char *arg;
char *keywords;
int soc;
#endif
{
FILE *hlp;
char *help_file;
char *query;
char *s;
if (keywords)
*(keywords - 1) = '/'; /* Un-split arg from keywords */
/* address must be of the form /HELP[/@library][/topic[/subtopic...]] */
s = strtok (arg + 1, "/");
if (strcasecomp (s, "HELP")) {
DIAGNOSE ((diag, "Address should begin with /HELP : /%s\n", s), -1)
}
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;
}
/* The first word from the query is the module name (perhaps empty) */
if (! (hlp = open_hlp (help_file, query, soc))) {
DIAGNOSE ((diag, "Help library or module not found : %s/%s\n",
help_file, query), -1)
}
if (query) { /* Parse rest of address and process help file */
while (s = strtok (NULL, "/+"))
*(s - 1) = ' ';
return hlp_to_html (hlp, query, soc);
}
else /* Process library directory */
return lis_to_html (hlp, help_file, soc);
}