/* 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, "<TITLE>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); }