/*****************************************************************************/ /* Conan.c CGI-compliant script providing access to VMS help and text libraries. Can be used in the WASD CGI, WASD CGI-plus, OSU and vanilla CGI environments. "Conan The Librarian" AND REMEMBER ... Conan can be used as a CGIplus script. This may require the script to be purged from the server before new startup parameters come into effect. Use a command like the following: HTTPD /DO=DCL=PURGE WASD SERVER CONFIGURATION ------------------------- This script uses URL-style paths for accessing libraries. The HTTP server must therefore provide a mapping for all directories that libraries will be accessed. Something like: pass /sys$common/syshlp/* /sys$common/syshlp/* If auto-scripting is available (as with WASD) these should also be a configuration directive providing this (example from WASD): .HLB application/x-script /Conan VMS help library HELP LIBRARIES -------------- Help library modules have a reasonably complex internal structure described in the "Utility Routines Manual" and the "Command Definition, Librarian, and Message Utilities Manual". The structure will not be described here. KeyWord[1]...KeyWord[10] represent the help keywords 1...10 that are used as keys into help information. CGI ENVIRONMENT VARIABLES ------------------------- ?single=1 one module per line (rather than alphabetically grouped) ?extract=1 extract (rather than format) a Help module (the final 'e' in 'explode' is a link to this) LOGICAL NAMES ------------- CONAN$DBUG turns on all "if (Debug)" statements CONAN$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) HTTPD$GMT timezone offset from Greenwich Mean Time (e.g. "+09:30" and only needed if DTSS is not in use) QUALIFIERS ---------- /BUTTONS= custom button string /CHARSET= "Content-Type: text/html; charset=...", empty suppress charset /DBUG turns on all "if (Debug)" statements /[NO]ODS5 control extended file specification (basically for testing) /SINGLE one module per line (rather than alphabetically grouped) for all requests /STYLE= URL for site CSS style sheet BUILD DETAILS ------------- See BUILD_CONAN.COM procedure. COPYRIGHT --------- Copyright (C) 1996-2020 Mark G.Daniel This program, comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under the conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version. http://www.gnu.org/licenses/gpl.txt VERSION HISTORY (update SOFTWAREVN as well!) --------------- 31-AUG-2020 MGD v4.1.2, bugfix; eliminate &referer= XSS vulnerability 10-FEB-2020 MGD v4.1.1, put constraints on some parameters 28-JUL-2015 MGD v4.1.0, why not HEAD as it's just a foreshortened GET? 04-OCT-2014 MGD v4.0.0, a nod to the twenty-first century 30-DEC-2008 MGD v3.7.5, bugfix; OTS Bug? See notes in code below. 10-MAY-2005 MGD v3.7.4, SWS 2.0 ignore query string components supplied as command-line parameters differently to CSWS 1.2/3 23-DEC-2003 MGD v3.7.3, minor conditional mods to support IA64 12-APR-2003 MGD v3.7.2, link colour changed to 0000cc 20-AUG-2002 MGD v3.7.1, allow for HTTP/1.1 'Cache-control:' field used by Mozilla variants (instead of HTTP/1.0 'pragma:') 17-AUG-2002 MGD v3.7.0, changed help module formatting that absorbed all leading white-space destroying intentional formatting (surprised someone hasn't complained about it long before this - thanks Dave Pampreen.), extract a help module as a plain-text page (click on the final 'e' in 'explode'), some accomodations for CSWS v1.2, bugfix; allow for strings with space separated keywords as help keywords in OpenHelpModule() 01-JUL-2001 MGD v3.6.1, add 'SkipParameters' for direct OSU support 28-OCT-2000 MGD v3.6.0, use CGILIB object module 11-APR-2000 MGD v3.5.0, support extended file specifications (ODS-5), bugfix; eliminate extraneous comma in keyword list 07-AUG-1999 MGD v3.4.0, use more of the CGILIB functionality 29-JUN-1999 MGD v3.3.0, optional module name list of one per line (rather than alphabetically grouped) 24-APR-1999 MGD v3.2.0, use CGILIB.C, standard CGI environment (Netscape FastTrack) 02-OCT-1998 MGD v3.1.0, provide content-type "; charset=..." 08-AUG-1998 MGD v3.0.3, OSU output processing reworked, bugfix; TimeSetTimezone() 'Seconds' unsigned->signed 06-AUG-1998 MGD v3.0.2, accomodation for OSU ... reduce HTTP response header carriage control from to only (OSU/IE4 combination problematic) 01-AUG-1998 MGD v3.0.1, suppress table background colours if empty, accomodations for OSU environment 29-APR-1998 MGD v3.0.0, remove need for WASD MapUrl() functions (making it a GENERIC CGI script!!), some tidying up and cosmetic changes 26-FEB-1998 MGD v2.9.2, added HTML table around title in help library, TimeSetGmt() modified to be in line with HTTPd 19-AUG-1997 MGD v2.9.1, MapUrl() to MapUrl_Map() for conditional mapping 20-JUL-1997 MGD v2.9.0, added /BODY= qualifier (changed background colour) 20-JUN-1997 MGD v2.8.0, compatible with standard and CGIplus environments, "Pragma: no-cache" now overrides "If-Modified-Since:" 14-MAR-1997 MGD v2.7.1, bugfix; absorbtion of leading white-space 07-AUG-1996 MGD v2.7.0, allow help topic to be specified in path, for example "/conan/run/process" or "/help/delete/file" 16-MAY-1996 MGD v2.6.2, changed XBMs to tranparaent GIFs 23-FEB-1996 MGD v2.6.1, bugfix, after modifying the HTTPD$GMT format for the HTTPd server I had forgotten about some scripts 12-OCT-1995 MGD v2.6.0, added 'Referer:', 'Last-Modified:'/'If-Modified-Since:' 18-AUG-1995 MGD v2.5.0, added "search" form at appropriate places 20-JUN-1995 MGD v2.4.0, added "explode" feature, lists all help levels below the key specified in a single page 24-MAY-1995 MGD v2.3.0, minor changes for AXP compatibility 31-MAR-1995 MGD v2.2.0, ongoing maintenance 02-FEB-1995 MGD v2.1.0, add "Conan the Librarian" icon 13-DEC-1994 MGD v2.0.0, made CGI compliant 09-JUL-1994 MGD v1.0.0, initial development */ /*****************************************************************************/ #define SOFTWAREVN "4.1.2" #define SOFTWARENM "CONAN" #define SOFTWARECR "Copyright (C) 1996-2020 Mark G.Daniel" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include #include #include /* application related header files */ #include "enamel.h" #include #ifndef __VAX # pragma nomember_alignment #endif #ifndef __VAX # ifndef NO_ODS_EXTENDED # define ODS_EXTENDED 1 /* this is smaller than the technical maximum, but still quite large! */ # define ODS_MAX_FILE_NAME_LENGTH 511 # define ODS_MAX_FILESYS_NAME_LENGTH 264 # endif #endif #define ODS2_MAX_FILE_NAME_LENGTH 255 #ifndef ODS_MAX_FILE_NAME_LENGTH # define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH #endif #if ODS_MAX_FILE_NAME_LENGTH < ODS2_MAX_FILE_NAME_LENGTH # define ODS_MAX_FILE_NAME_LENGTH ODS2_MAX_FILE_NAME_LENGTH #endif #define DEFAULT_STYLE "\ body { margin:0.5em; background-color:white; color:black; \ font-family:sans-serif; }\n\ a { text-decoration:none; color:black; \ border:1px solid slategray; border-radius:3px; padding:1px 5px 1px 5px; }\n\ .header { padding:0.5em 0.5em 0.5em 1em; background-color: gainsboro; \ border:1px solid lightslategray; border-radius:1px; \ font-size: 140%; font-weight: bold; }\n\ .helpmodules, .textmodules { width:90%; margin:1.0em 0 1.0em 1.4em; }\n\ .helpmodules a, .textmodules a { line-height:1.7em; \ background-color:whitesmoke; \ white-space:nowrap; margin-right:0.1em; }\n\ .helpmodules p, .textmodules p { margin:0.6em 0 0.5em 0; }\n\ .searchform { margin:1em; }\n\ .results { margin:1.5em 2em 1em 2em; }\n\ .libcontent { margin:1em; }\n\ .libcontent a { background-color:whitesmoke; white-space:nowrap; }\n\ .addinfo { margin:1em; max-width:50em; }\n\ .addinfo a { line-height:1.7em; white-space:nowrap; margin-right:0.1em; }\n\ .module { white-space:pre; }\n\ .buttonbar { padding:0.7em 1em 0.7em 1em; \ border:1px solid lightslategray; border-radius:1px; \ background-color: gainsboro; min-height:1.3em; }\n\ .buttonbar a { text-decoration:none; color:inherit; \ border:1px solid slategray; border-radius:3px; padding:1px 5px 2px 5px; }\n\ .explode { padding-left:4em; font-size:70%; }\n\ .explevel { font-weight:bold; font-size:105% }\n\ .explevel1 { font-weight:bold; font-size:120% }\n\ .helpresult { white-space:nowrap; }\n\ .textresult { font-family:monospace; white-space:nowrap; }\n\ .highlight { border: 1px dotted slategray; \ padding:0 2px 0 2px; background-color:yellow; }\n\ .searchstats { font-size:90%; }\n\ " #define boolean int #define true 1 #define false 0 #define VMSok(x) ((x) & STS$M_SUCCESS) #define VMSnok(x) !(((x) & STS$M_SUCCESS)) #define FI_LI __FILE__, __LINE__ #define DEFAULT_LIBRARY "SYS$HELP:HELPLIB.HLB" #define DEFAULT_BUTTONS "↶Back=javascript:parent.history.go(-1)$\ Close$^Help=/conan/-/conanhelp.html" char Utility [] = "CONAN"; boolean Debug, CliListSingle, DoHeaderInformation, DoListLibraries, DoLibraryMenu, DoLibraryOpen, DoSearch, DoSearchStatistics, ExplodeHelp, ExtractHelp, MethodGet, MethodHead, KeyNameWildcard, ListSingle, OsuEnvironment, StdCgiEnvironment, TimeAheadOfGmt; int SearchHitCount, KeyWordCount, OdsExtended, RecordsSearched, SearchStringLength; unsigned long LibraryIndex, IndexNumber; unsigned long IfModifiedSinceBinaryTime [2], TimeGmtDeltaBinary [2]; char ContentTypeCharset [64], DefaultButtons [] = DEFAULT_BUTTONS, DefaultStyle [] = DEFAULT_STYLE, HtmlFormTitle [256], HtmlLibraryTitle [256], HtmlReferer [512], HtmlSearchString [512], LastModifiedGmDateTime [32], LibraryDirPath [ODS_MAX_FILE_NAME_LENGTH+1], LibraryFileName [ODS_MAX_FILE_NAME_LENGTH+1], LibraryPathInfo [ODS_MAX_FILE_NAME_LENGTH+1], LibrarySpec [ODS_MAX_FILE_NAME_LENGTH+1], LibraryTitle [256], TimeGmtString [32], TimeGmtVmsString [32], UnixDateTime [64], UriFormTitle [256], UriReferer [512]; char *ButtonsPtr = DefaultButtons, *CgiHttpIfModifiedSincePtr, *CgiHttpPragmaPtr, *CgiHttpCacheControlPtr, *CgiLibEnvironmentPtr, *CgiPathInfoPtr, *CgiPathTranslatedPtr, *CgiRequestMethodPtr, *CgiScriptNamePtr, *CgiServerSoftwarePtr, *CgiFormDoPtr, *CgiFormExplodePtr, *CgiFormExtractPtr, *CgiFormKeyPtr, *CgiFormRefererPtr, *CgiFormTitlePtr, *CgiFormSearchPtr, *CgiFormSinglePtr, *CliCharsetPtr, *CharsetPtr, *StyleSheetPtr = ""; /* element 0 is not used, keywords number from 1 to 10 */ char KeyWord [11][256], KeyWordHtml [11][256], KeyName [11][256], KeyNameHtml [11][256], SoftwareCopy [] = SOFTWARECR, SoftwareID [48]; struct lhidef LibraryHeader; struct { unsigned long lo32; unsigned long hi32; } ModuleRFA; /* CGIplus specific */ int IsCgiPlus, CgiPlusUsageCount; /* required function prototypes */ char* ButtonBarButton (char*, char*); char* SearchText (char*, char*, boolean); int ListHelpModule (struct dsc$descriptor_s*, void*); int ListTextModule (struct dsc$descriptor_s*, void*); char* MungeUnderScores (char*); char* FormatRequestKeys (boolean, int); int ExtractHelpModule (struct dsc$descriptor_s*, void*); int OpenHelpModule (struct dsc$descriptor_s*, void*); int OpenTextModule (struct dsc$descriptor_s*, void*); int SearchHelpModule (struct dsc$descriptor_s*, void*); int SearchTextModule (struct dsc$descriptor_s*, void*); struct dsc$descriptor_s* KeyNameDsc (); char* XSSuspect (char*); /*****************************************************************************/ /* 'argc' and 'argv' are only required to support CgiLibEnvironmentInit(), in particular the OSU environment. */ main ( int argc, char *argv[] ) { int idx, status; char *cptr, *sptr; /*********/ /* begin */ /*********/ sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion()); if (getenv ("CONAN$DBUG")) Debug = true; CgiLibEnvironmentSetDebug (Debug); if (Debug) fputs ("Content-Type: text/plain\n\n", stdout); CgiLibEnvironmentInit (argc, argv, false); CgiLibEnvironmentPtr = CgiLibEnvironmentName(); GetParameters (); CgiLibResponseSetCharset (CliCharsetPtr); CgiLibResponseSetSoftwareID (SoftwareID); CgiLibResponseSetErrorMessage ("Reported by Conan The Librarian"); IsCgiPlus = CgiLibEnvironmentIsCgiPlus (); #ifdef ODS_EXTENDED OdsExtended = (GetVmsVersion() >= 72); ENAMEL_NAML_SANITY_CHECK #endif /* ODS_EXTENDED */ if (IsCgiPlus) { for (;;) { /* block waiting for the next request */ CgiLibVar (""); CgiPlusUsageCount++; if (Debug) fputs ("Content-Type: text/plain\n\n", stdout); ConanTheLibrarian (); CgiLibCgiPlusEOF (); } } else ConanTheLibrarian (); exit (SS$_NORMAL); } /*****************************************************************************/ /* Get "command-line" parameters, whether from the command-line or from a configuration symbol or logical containing the equivalent. OSU scripts have the 'method', 'url' and 'protocol' supplied as P1, P2, P3 (these being detected and used by CGILIB), and are of no interest to this function. */ GetParameters () { static char CommandLine [256]; static unsigned long Flags = 0; int status, SkipParameters; unsigned short Length; char ch; char *aptr, *cptr, *clptr, *sptr; $DESCRIPTOR (CommandLineDsc, CommandLine); /*********/ /* begin */ /*********/ if (!(clptr = getenv ("CONAN$PARAM"))) { /* get the entire command line following the verb */ if (VMSnok (status = lib$get_foreign (&CommandLineDsc, 0, &Length, &Flags))) exit (status); (clptr = CommandLine)[Length] = '\0'; } /* if [C]SWS (VMS Apache) */ if (CgiLibEnvironmentIsApache()) { /* CSWS 1.2/3 look for something non-space outside of quotes */ for (cptr = clptr; *cptr; cptr++) { if (isspace(*cptr)) continue; if (*cptr != '\"') break; cptr++; while (*cptr && *cptr != '\"') cptr++; if (*cptr) cptr++; } /* CSWS 1.2/3 if nothing outside of quotes then ignore command line */ if (!*cptr) return; /* SWS 2.0 doesn't begin with /APACHE from DCL procedure wrapper */ if (!strsame (cptr, "/APACHE", 7)) return; } /* if OSU environment then skip P1, P2, P3 */ if (CgiLibEnvironmentIsOsu()) SkipParameters = 3; else SkipParameters = 0; aptr = NULL; ch = *clptr; for (;;) { if (aptr) *aptr = '\0'; if (!ch) break; *clptr = ch; if (Debug) fprintf (stdout, "clptr |%s|\n", clptr); while (*clptr && isspace(*clptr)) *clptr++ = '\0'; aptr = clptr; if (*clptr == '/') clptr++; while (*clptr && !isspace (*clptr) && *clptr != '/') { if (*clptr != '\"') { clptr++; continue; } cptr = clptr; clptr++; while (*clptr) { if (*clptr == '\"') if (*(clptr+1) == '\"') clptr++; else break; *cptr++ = *clptr++; } *cptr = '\0'; if (*clptr) clptr++; } ch = *clptr; if (*clptr) *clptr = '\0'; if (Debug) fprintf (stdout, "aptr |%s|\n", aptr); if (!*aptr) continue; if (SkipParameters) { SkipParameters--; continue; } if (strsame (aptr, "/APACHE", 4)) { /* just skip this marker for command-line parameters under SWS 2.0 */ continue; } if (strsame (aptr, "/BUTTONS=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; if (*cptr == '+') { /* append buttons to defaults */ aptr = calloc (1, strlen(ButtonsPtr)+strlen(cptr)+2); strcpy (aptr, ButtonsPtr); strcat (aptr, "$"); strcat (aptr, cptr+1); ButtonsPtr = aptr; } else ButtonsPtr = cptr; continue; } if (strsame (aptr, "/CHARSET=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; CliCharsetPtr = cptr; continue; } if (strsame (aptr, "/DBUG", -1)) { Debug = true; continue; } if (strsame (aptr, "/ODS5", 5)) { OdsExtended = true; continue; } if (strsame (aptr, "/NOODS5", 7)) { OdsExtended = false; continue; } if (strsame (aptr, "/SINGLE", -1)) { CliListSingle = true; continue; } if (strsame (aptr, "/STYLE=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; StyleSheetPtr = cptr; continue; } if (*aptr != '/') { fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n", Utility, aptr); exit (STS$K_ERROR | STS$M_INHIB_MSG); } else { fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n", Utility, aptr+1); exit (STS$K_ERROR | STS$M_INHIB_MSG); } } } /*****************************************************************************/ /* */ ButtonBar (int Top1Bottom2) { char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBar() %d\n", Top1Bottom2); fprintf (stdout, "
"); cptr = ButtonsPtr; /* back */ cptr = ButtonBarButton (cptr, ""); /* close */ if (CgiFormRefererPtr[0]) cptr = ButtonBarButton (cptr, HtmlReferer); else cptr = ButtonBarButton (cptr, NULL); /* further buttons (starting with "help") */ while (*cptr) cptr = ButtonBarButton (cptr, NULL); fprintf (stdout, "
\n"); } /*****************************************************************************/ /* Generate a single "button" inside the context created by ButtonBar(). */ char* ButtonBarButton ( char *ButtonLabel, char *ButtonPath ) { char *cptr, *sptr, *tptr, *uptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBarButton() |%s|\n", ButtonLabel); if (!*ButtonLabel) for (cptr = ButtonLabel = "*ERROR*"; *cptr; cptr++); else for (cptr = ButtonLabel; *cptr && *cptr != '=' && *cptr != '$'; cptr++); if (*ButtonLabel == '^') { ButtonLabel++; tptr = " target=\"_blank\""; } else tptr = ""; if (*cptr == '=') for (sptr = uptr = cptr+1; *sptr && *sptr != '$'; sptr++); else if (ButtonPath) sptr = (uptr = ButtonPath) + strlen(ButtonPath); else sptr = (uptr = "*ERROR*") + 8; if (!memcmp(uptr,"javascript:", 11)) fprintf (stdout, "\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); else fprintf (stdout, "%*.*s\n", tptr, sptr-uptr, sptr-uptr, uptr, cptr-ButtonLabel, cptr-ButtonLabel, ButtonLabel); while (*cptr && *cptr != '$') cptr++; while (*cptr && *cptr == '$') cptr++; return (cptr); } /*****************************************************************************/ /* */ ConanTheLibrarian () { int idx, status; unsigned long UnixTime; char *cptr, *sptr; struct tm *UnixTmPtr; /*********/ /* begin */ /*********/ time (&UnixTime); UnixTmPtr = localtime (&UnixTime); if (!strftime (UnixDateTime, sizeof(UnixDateTime), "%a, %d %b %Y %T", UnixTmPtr)) strcpy (UnixDateTime, "[error]"); if (Debug) fprintf (stdout, "UnixDateTime |%s|\n", UnixDateTime); /* initialize anything specifically required for CGIplus */ DoHeaderInformation = DoListLibraries = DoLibraryMenu = DoSearch = KeyNameWildcard = ExplodeHelp = ExtractHelp = ListSingle = false; DoSearchStatistics = true; for (idx = 0; idx < 11; idx++) KeyWord[idx][0] = KeyWordHtml[idx][0] = KeyName[idx][0] = KeyNameHtml[idx][0] = '\0'; CgiServerSoftwarePtr = CgiLibVar ("WWW_SERVER_SOFTWARE"); CgiRequestMethodPtr = CgiLibVar ("WWW_REQUEST_METHOD"); MethodGet = MethodHead = false; if (!(MethodGet = (strcmp (CgiRequestMethodPtr, "GET") == 0)) && !(MethodHead = (strcmp (CgiRequestMethodPtr, "HEAD") == 0))) { CgiLibResponseHeader (501, "text/html"); fprintf (stdout, "Not implemented!\n"); return; } CgiPathInfoPtr = CgiLibVar ("WWW_PATH_INFO"); CgiPathTranslatedPtr = CgiLibVar ("WWW_PATH_TRANSLATED"); CgiScriptNamePtr = CgiLibVar ("WWW_SCRIPT_NAME"); CgiHttpPragmaPtr = CgiLibVar ("WWW_HTTP_PRAGMA"); CgiHttpCacheControlPtr = CgiLibVar ("WWW_HTTP_CACHE_CONTROL"); CgiHttpIfModifiedSincePtr = CgiLibVar ("WWW_HTTP_IF_MODIFIED_SINCE"); CgiFormDoPtr = CgiLibVar ("WWW_FORM_DO"); CgiFormExplodePtr = CgiLibVar ("WWW_FORM_EXPLODE"); CgiFormExtractPtr = CgiLibVar ("WWW_FORM_EXTRACT"); CgiFormSinglePtr = CgiLibVar ("WWW_FORM_SINGLE"); CgiFormKeyPtr = CgiLibVar ("WWW_FORM_KEY"); CgiFormTitlePtr = CgiLibVar ("WWW_FORM_TITLE"); CgiFormSearchPtr = CgiLibVar ("WWW_FORM_SEARCH"); if (!CgiFormSearchPtr[0]) CgiFormSearchPtr = CgiLibVar ("WWW_KEY_1"); CgiFormRefererPtr = CgiLibVar ("WWW_FORM_REFERER"); if (!CgiFormRefererPtr[0]) CgiFormRefererPtr = CgiLibVar ("WWW_HTTP_REFERER"); /* OWASP ZAP demonstrates unusual parameters can cause spits */ if (strlen (CgiPathInfoPtr) > 255 || strlen (CgiPathTranslatedPtr) > 255 || strlen (CgiFormKeyPtr) > 255 || strlen (CgiFormRefererPtr) > 255 || strlen (CgiFormSearchPtr) > 255 || strlen (CgiFormTitlePtr) > 255) { CgiLibResponseError (FI_LI, 0, "Parameter (significantly) out of range."); return; } /* if a bit suspect (XSS) then give it the chop! (q&d) */ if (cptr = XSSuspect (CgiFormRefererPtr)) { CgiLibResponseError (FI_LI, 0, cptr); return; } if (CgiFormRefererPtr[0]) { /* re-escape the URL-escaped percentages */ CgiLibUrlEncode (CgiFormRefererPtr, -1, UriReferer, sizeof(UriReferer)); CgiLibHtmlEscape (CgiFormRefererPtr, -1, HtmlReferer, sizeof(HtmlReferer)); } else UriReferer[0] = HtmlReferer[0] = '\0'; if (VMSnok (status = TimeSetGmt ())) { if (status != SS$_NOLOGNAM) { CgiLibResponseError (FI_LI, status, CgiScriptNamePtr); exit (SS$_NORMAL); } } if (CgiHttpIfModifiedSincePtr[0]) { if (VMSnok (HttpGmTime (CgiHttpIfModifiedSincePtr, &IfModifiedSinceBinaryTime))) { if (Debug) fprintf (stdout, "If-Modified-Since: NBG!\n"); IfModifiedSinceBinaryTime[0] = IfModifiedSinceBinaryTime[0] = 0; CgiHttpIfModifiedSincePtr = ""; } } strcpy (LibraryPathInfo, CgiPathInfoPtr); if (Debug) fprintf (stdout, "|%s|\n", LibraryPathInfo); /**************/ /* initialize */ /**************/ idx = 0; if (CgiFormKeyPtr[0]) { /*****************************************/ /* get keywords from "key=" query string */ /*****************************************/ cptr = CgiFormKeyPtr; while (*cptr) { if (idx <= 10) idx++; if (*cptr == '~') cptr++; sptr = KeyWord[idx]; while (*cptr && *cptr != '~') *sptr++ = *cptr++; *sptr = '\0'; CgiLibHtmlEscape (KeyWord[idx], -1, KeyWordHtml[idx], -1); if (Debug) fprintf (stdout, "KeyWord[%d] (form) |%s|%s|\n", idx, KeyWord[idx], KeyWordHtml[idx]); } } else if (!strstr (CgiPathTranslatedPtr, ".HLB") && !strstr (CgiPathTranslatedPtr, ".hlb") && !strstr (CgiPathTranslatedPtr, ".TLB") && !strstr (CgiPathTranslatedPtr, ".tlb") && !strchr (CgiPathTranslatedPtr, '*') && !strchr (CgiPathTranslatedPtr, '%')) { /*******************************/ /* get keywords from path info */ /*******************************/ /* make sure the default help library is used */ CgiPathTranslatedPtr = ""; idx = 0; /* by comparing, scan past the script component of the path */ sptr = CgiScriptNamePtr; cptr = CgiPathInfoPtr; while (toupper(*cptr) == toupper(*sptr)) { cptr++; sptr++; } while (*cptr) { if (idx <= 10) idx++; if (*cptr == '/') cptr++; sptr = KeyWord[idx]; if (*cptr == '_' || *cptr == '/') { /* Some other HTML help systems use an underscore for qualifiers. This implementation also allows two concurrent forward-slashes so that the qualifier may be entered literally! */ *sptr++ = '/'; cptr++; } while (*cptr && *cptr != '/') *sptr++ = *cptr++; *sptr = '\0'; CgiLibHtmlEscape (KeyWord[idx], -1, KeyWordHtml[idx], -1); if (Debug) fprintf (stdout, "KeyWord[%d] (path) |%s|%s|\n", idx, KeyWord[idx], KeyWordHtml[idx]); } /* finished with the path if we've used it for keywords! */ CgiPathInfoPtr = ""; LibraryPathInfo[0] = '\0'; } KeyWordCount = idx; while (idx++ < 10) KeyWord[idx][0] = KeyWordHtml[idx][0] = '\0'; /* isolate the directory component of the path */ strcpy (LibraryDirPath, LibraryPathInfo); for (cptr = LibraryDirPath; *cptr; cptr++); if (cptr > LibraryDirPath) cptr--; while (cptr > LibraryDirPath && *cptr != '/') cptr--; if (*cptr == '/') cptr++; *cptr = '\0'; if (Debug) fprintf (stdout, "|%s|\n", LibraryDirPath); strcpy (LibrarySpec, CgiPathTranslatedPtr); for (sptr = LibrarySpec; *sptr && *sptr != '*' && *sptr != '%'; sptr++); if (*sptr) DoListLibraries = true; switch (tolower(CgiFormDoPtr[0])) { case 'h' : DoHeaderInformation = true; break; case 'l' : DoListLibraries = true; break; case 'm' : DoLibraryMenu = true; break; case 'o' : DoLibraryOpen = true; break; case 's' : DoSearch = true; break; default : for (cptr = CgiPathTranslatedPtr; *cptr; cptr++) if (*cptr == '*' || *cptr == '%') DoListLibraries = true; } if (CgiFormExplodePtr[0]) ExplodeHelp = true; else ExplodeHelp = false; if (CgiFormExtractPtr[0]) ExtractHelp = true; else ExtractHelp = false; if (CgiFormSinglePtr[0] || CliListSingle) ListSingle = true; else ListSingle = false; if (CgiFormSearchPtr[0]) { DoSearch = true; SearchStringLength = strlen(CgiFormSearchPtr); CgiLibHtmlEscape (CgiFormSearchPtr, -1, HtmlSearchString, -1); } if (DoSearch && !CgiFormSearchPtr[0]) { CgiLibResponseError (FI_LI, 0, "Search string not supplied."); return; } if (!KeyWord[1][0]) { /* no module keyname supplied, wildcard to list all modules */ strcpy (KeyWord[1], "*"); KeyNameWildcard = true; } else { /* check for a wildcard in the keyname, list all modules if there is */ for (sptr = KeyWord[1]; *sptr && *sptr != '*' && *sptr != '%'; sptr++); if (*sptr) KeyNameWildcard = true; } LibraryTitle[0] = '\0'; if (!CgiPathTranslatedPtr[0]) CgiFormTitlePtr = "VMS Help"; if (CgiFormTitlePtr[0]) strcpy (LibraryTitle, CgiFormTitlePtr); if (LibraryTitle[0]) CgiLibHtmlEscape (LibraryTitle, -1, HtmlLibraryTitle, -1); else { sprintf (LibraryTitle, "Library %s", LibraryPathInfo); CgiLibHtmlEscape (LibraryTitle, -1, HtmlLibraryTitle, -1); } if (Debug) fprintf (stdout, "LibraryTitle |%s|\n", LibraryTitle); CgiLibUrlEncode (LibraryTitle, -1, UriFormTitle, -1); CgiLibHtmlEscape (LibraryTitle, -1, HtmlFormTitle, -1); /***********/ /* process */ /***********/ if (DoListLibraries) ListLibraries (); else if (DoLibraryMenu) LibraryMenu (); else if (DoLibraryOpen) Librarian (); else Librarian (); } /****************************************************************************/ /* Search for library files according to the the supplied specification (defaults to help libraries; SYS$HELP:*.HLB). Display the name of each library in lower case as a list item. */ ListLibraries () { boolean SupportedLibraryType; int len, status, FileCount = 0; char PrevFirstChar = '\0'; char *cptr, *sptr; char ResFileName [ODS_MAX_FILE_NAME_LENGTH+1], ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1], String [2048], UrlEncoded [ODS_MAX_FILE_NAME_LENGTH+64+1]; struct FAB FileFab; #ifdef ODS_EXTENDED struct NAML FileNaml; #endif /* ODS_EXTENDED */ struct NAM FileNam; if (Debug) fprintf (stdout, "ListLibraries() |%s|\n", LibrarySpec); /* if no library specifcation provided then default to help libraries */ if (!LibrarySpec[0]) strcpy (LibrarySpec, "SYS$HELP:*.HLB;0"); if (Debug) fprintf (stdout, "|%s|\n", LibrarySpec); FileFab = cc$rms_fab; FileFab.fab$l_fop = FAB$V_NAM; FileFab.fab$l_nam = &FileNam; #ifdef ODS_EXTENDED if (OdsExtended) { FileFab.fab$l_fna = (char*)-1; FileFab.fab$b_fns = 0; FileFab.fab$l_nam = (struct namdef*)&FileNaml; ENAMEL_RMS_NAML(FileNaml) FileNaml.naml$l_long_filename = LibrarySpec; FileNaml.naml$l_long_filename_size = strlen(LibrarySpec); FileNaml.naml$l_long_expand = ExpFileName; FileNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; FileNaml.naml$l_long_result = ResFileName; FileNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1; } else #endif /* ODS_EXTENDED */ { FileFab.fab$l_fna = LibrarySpec; FileFab.fab$b_fns = strlen(LibrarySpec); FileFab.fab$l_nam = &FileNam; FileNam = cc$rms_nam; FileNam.nam$l_esa = ExpFileName; FileNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; FileNam.nam$l_rsa = ResFileName; FileNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } if (VMSnok (status = sys$parse (&FileFab, 0, 0))) { CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } BeginPage (LibraryPathInfo, LibraryPathInfo); fprintf (stdout, "
\n"); while (VMSok (status = sys$search (&FileFab, 0, 0))) { FileCount++; #ifdef ODS_EXTENDED if (OdsExtended) { if (strsame(FileNaml.naml$l_long_type, ".DIR;", 5)) continue; } else #endif /* ODS_EXTENDED */ if (strsame(FileNam.nam$l_type, ".DIR;", 5)) continue; #ifdef ODS_EXTENDED if (OdsExtended) { cptr = FileNaml.naml$l_long_name; if (strsame (FileNaml.naml$l_long_type, ".HLB;", 5) || strsame (FileNaml.naml$l_long_type, ".TLB;", 5)) SupportedLibraryType = true; else SupportedLibraryType = false; } else #endif /* ODS_EXTENDED */ { cptr = FileNam.nam$l_type; if (strsame (FileNam.nam$l_type, ".HLB;", 5) || strsame (FileNam.nam$l_type, ".TLB;", 5)) SupportedLibraryType = true; else SupportedLibraryType = false; } if (!SupportedLibraryType) continue; sptr = String; if (PrevFirstChar && tolower(*cptr) != tolower(PrevFirstChar)) sptr += sprintf (sptr, "

"); PrevFirstChar = *cptr; if (SupportedLibraryType) { #ifdef ODS_EXTENDED if (OdsExtended) { CgiLibUrlEncode (LibraryDirPath, -1, UrlEncoded, -1); sptr += sprintf (sptr, "", UriFormTitle, UriReferer); } /* if there was wildcard in the file type then include it */ #ifdef ODS_EXTENDED if (OdsExtended) { if (FileNaml.naml$l_fnb & NAM$M_WILD_TYPE) for (cptr = FileNaml.naml$l_long_name; cptr < FileNaml.naml$l_long_ver; *sptr++ = tolower(*cptr++)); else for (cptr = FileNaml.naml$l_long_name; cptr < FileNaml.naml$l_long_type; *sptr++ = tolower(*cptr++)); } else #endif /* ODS_EXTENDED */ { if (FileNam.nam$l_fnb & NAM$M_WILD_TYPE) for (cptr = FileNam.nam$l_name; cptr < FileNam.nam$l_ver; *sptr++ = tolower(*cptr++)); else for (cptr = FileNam.nam$l_name; cptr < FileNam.nam$l_type; *sptr++ = tolower(*cptr++)); } if (SupportedLibraryType) sptr += sprintf (sptr, ""); *sptr++ = '\n'; *sptr = '\0'; fputs (String, stdout); } if (!FileCount) fprintf (stdout, "

No files found.\n"); if (status == RMS$_FNF || status == RMS$_NMF) status = SS$_NORMAL; if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } fprintf (stdout, "

\n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); return (status); } /*****************************************************************************/ /* For the specified library file provide a menu of four services; librarian, searching, library header and help. */ LibraryMenu () { char *cptr, *sptr; char UrlEncoded [ODS_MAX_FILE_NAME_LENGTH+64+1]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "LibraryMenu() |%s|\n", LibrarySpec); strcpy (LibraryTitle, LibraryPathInfo); CgiLibHtmlEscape (LibraryPathInfo, -1, HtmlLibraryTitle, -1); CgiLibUrlEncode (LibraryTitle, -1, UriFormTitle, -1); CgiLibHtmlEscape (LibraryTitle, -1, HtmlFormTitle, -1); BeginPage (LibraryTitle, HtmlLibraryTitle); CgiLibUrlEncode (LibraryPathInfo, -1, UrlEncoded, -1); fprintf (stdout, "
\n\

Open Library\n\

Library Header\n\

\n\
\n\ \n\ \n\ \n\ \n\ \n\ \n\
\n\
\n\
\n", CgiScriptNamePtr, UrlEncoded, UriFormTitle, UriReferer, CgiScriptNamePtr, UrlEncoded, UriFormTitle, UriReferer, CgiScriptNamePtr, UrlEncoded, HtmlReferer, HtmlFormTitle); fprintf (stdout, "\n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* Initialise library control. Open the library specified by LibrarySpec. Get header information from the library so its internal format can be determined (help or text, other are not supported). Call ProcessLibrary() to perform the requested access according to the library type. */ Librarian () { int status; unsigned long Function = LBR$C_READ, FileNameLength; char *cptr; char String [1024]; $DESCRIPTOR (LibrarySpecDsc, ""); static struct dsc$descriptor_s LibraryDefDsc = { sizeof(DEFAULT_LIBRARY)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S, DEFAULT_LIBRARY }, LibraryFileNameDsc = { sizeof(LibraryFileName)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S, LibraryFileName }; /****************************************/ /* initialize, open library, get header */ /****************************************/ if (Debug) fprintf (stdout, "Librarian()\n"); if (VMSnok (status = lbr$ini_control (&LibraryIndex, &Function, 0, 0))) { CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } LibrarySpecDsc.dsc$w_length = strlen(LibrarySpec); LibrarySpecDsc.dsc$a_pointer = LibrarySpec; if (VMSnok (status = lbr$open (&LibraryIndex, &LibrarySpecDsc, 0, &LibraryDefDsc, 0, &LibraryFileNameDsc, &FileNameLength))) { if (Debug) fprintf (stdout, "lib$open() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /* terminate, removing the trailing version number */ LibraryFileName[FileNameLength] = '\0'; for (cptr = LibraryFileName; *cptr && *cptr != ';'; cptr++); *cptr = '\0'; if (Debug) fprintf (stdout, "LibraryFileName |%s|\n", LibraryFileName); if (VMSnok (status = lbr$get_header (&LibraryIndex, &LibraryHeader))) { CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } if (CgiHttpIfModifiedSincePtr[0]) { if (VMSnok (ModifiedSince (&IfModifiedSinceBinaryTime, &LibraryHeader.lhi$l_updtim))) { /* library has not been modified since the date/time, don't send */ status = lbr$close (&LibraryIndex); return (SS$_NORMAL); } } HttpGmTimeString (LastModifiedGmDateTime, &LibraryHeader.lhi$l_updtim); /*******************/ /* process library */ /*******************/ if (DoHeaderInformation) HeaderInformation (); else if (LibraryHeader.lhi$l_type == LBR$C_TYP_TXT) { if (DoSearch) SearchTextLibraryModules (); else if (KeyNameWildcard) ListTextLibraryModules (); else OpenTextLibraryModule (); } else if (LibraryHeader.lhi$l_type == LBR$C_TYP_HLP) { if (DoSearch) SearchHelpLibraryModules (); else if (KeyNameWildcard) ListHelpLibraryModules (); else OpenHelpLibraryModule (); } else { sprintf (String, "Library type %d not supported. (Only HELP and TEXT libraries are valid)", LibraryHeader.lhi$l_type); CgiLibResponseError (FI_LI, 0, String); return (SS$_NORMAL); } status = lbr$close (&LibraryIndex); return (SS$_NORMAL); } /*****************************************************************************/ /* Provide selected library header information. */ HeaderInformation () { static char *LibraryTypes[] = { "Unknown", "VAX Object", "Macro", "Help", "Text", "VAX Sharable Image", "NCS", "Alpha Object", "Alpha Sharable Image", "?" }; static $DESCRIPTOR (CreatedFaoDsc, "Created:!%D\n"); static $DESCRIPTOR (RevisedFaoDsc, "Revised:!%D\n"); unsigned short slen; char String [1024]; $DESCRIPTOR (StringDsc, String); /*********/ /* begin */ /*********/ BeginPage (LibraryTitle, LibraryTitle); fprintf (stdout, "
\n\ \n"); if (LibraryHeader.lhi$l_type > 8) LibraryHeader.lhi$l_type = 9; fprintf (stdout, "\n", LibraryTypes[LibraryHeader.lhi$l_type]); fprintf (stdout, "\n", *(char*)LibraryHeader.lhi$t_lbrver, (char*)LibraryHeader.lhi$t_lbrver+1); fprintf (stdout, "\n", LibraryHeader.lhi$l_majorid, LibraryHeader.lhi$l_minorid); sys$fao (&CreatedFaoDsc, &slen, &StringDsc, &LibraryHeader.lhi$l_credat); String[slen] = '\0'; fprintf (stdout, "%s", String); sys$fao (&RevisedFaoDsc, &slen, &StringDsc, &LibraryHeader.lhi$l_updtim); String[slen] = '\0'; fprintf (stdout, "%s", String); if (LibraryHeader.lhi$l_libstatus) fprintf (stdout, "\n"); else fprintf (stdout, "\ \n"); fprintf (stdout, "
Type:%s
Creator:%-*s
Format:%d.%d
Status:OK
Status:PROBLEM
\n"); fprintf (stdout, "
\n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ struct dsc$descriptor_s* KeyNameDsc () { static char Key [256]; static $DESCRIPTOR (KeyDsc, Key); char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "KeyNameDsc()\n"); /* although spaces *look* better, in fact underscores */ zptr = (sptr = Key) + sizeof(Key)-1; for (cptr = KeyWord[1]; *cptr && sptr < zptr; cptr++) { if (*cptr == ' ') *sptr++ = '_'; else *sptr++ = toupper(*cptr); } *sptr = '\0'; KeyDsc.dsc$w_length = sptr - Key; if (Debug) fprintf (stdout, "|%s|\n", Key); return (&KeyDsc); } /*****************************************************************************/ /* */ int ListHelpLibraryModules () { int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListHelpLibraryModules()\n"); BeginPage (LibraryTitle, HtmlLibraryTitle); fprintf (stdout, "
\n\
\n\ \n\ \n\ \n\ \n\ \n\ \n\
\n\
\n\
\n", CgiScriptNamePtr, CgiPathInfoPtr, HtmlReferer, HtmlFormTitle); fflush (stdout); IndexNumber = 1; if (VMSnok (status = lbr$get_index (&LibraryIndex, &IndexNumber, &ListHelpModule, KeyNameDsc()))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } fprintf (stdout, "\n"); fprintf (stdout, "
\n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int ListHelpModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static char PrevAlpha = 0, PrevChar = 0xff; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListHelpModule()\n"); strncpy (KeyWord[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyWord[1][KeyNameDscPtr->dsc$w_length] = '\0'; strcpy (KeyName[1], KeyWord[1]); CgiLibHtmlEscape (KeyWord[1], -1, KeyNameHtml[1], -1); if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyWord[1]); if (ListSingle) { if ((unsigned char)KeyName[1][0] >= (unsigned char)PrevChar) fprintf (stdout, "\n
  • "); else fprintf (stdout, "
  • "); } else if (!isalpha(KeyName[1][0]) || toupper(KeyName[1][0]) == PrevAlpha) { if ((unsigned char)KeyName[1][0] >= (unsigned char)PrevChar) fprintf (stdout, "\n"); } else { if ((unsigned char)KeyName[1][0] >= (unsigned char)PrevChar) fprintf (stdout, "\n

    "); PrevAlpha = toupper(KeyName[1][0]); } fprintf (stdout, "%s\n", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,1), UriFormTitle, UriReferer, MungeUnderScores(KeyNameHtml[1])); PrevChar = KeyName[1][0]; return (SS$_NORMAL); } /*****************************************************************************/ /* */ int OpenHelpLibraryModule () { int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "OpenHelpLibraryModule()\n"); IndexNumber = 1; if (ExtractHelp) { status = lbr$get_index (&LibraryIndex, &IndexNumber, &ExtractHelpModule, KeyNameDsc()); if (VMSnok (status)) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); } return (SS$_NORMAL); } status = lbr$get_index (&LibraryIndex, &IndexNumber, &OpenHelpModule, KeyNameDsc()); if (VMSnok (status)) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } fprintf (stdout, "\n"); ButtonBar (2); fprintf (stdout, "\n\n"); return (SS$_NORMAL); } /*****************************************************************************/ /* This is a reasonably complex function, and reflects the complexities internal to help library modules! */ int OpenHelpModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static int MatchedCount = 0, PreviousCgiPlusUsageCount = 0; boolean AdditionalInformation = true, ExplodedHeading = false, OutputModuleTitle = true; int status, AdditionalInformationCount = 0, BlankLineCount = 0, Count, ExplodeLevel = 0, HelpLevel = 0, Length, MatchedLevel = 0, MatchesToLevel = 0, PreviousHelpLevel = 0, TextLineCount = 0; int KeyCount [11]; char PrevFirstBufferChar = '\0'; char *cptr, *sptr; char Buffer [1024], PrevAdditionalChar [2] = "\0\0", String [1024]; $DESCRIPTOR (InBufferDsc, Buffer); $DESCRIPTOR (OutBufferDsc, Buffer); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "OpenHelpModule()\n"); if (CgiPlusUsageCount != PreviousCgiPlusUsageCount) { MatchedCount = 0; PreviousCgiPlusUsageCount = CgiPlusUsageCount; } strncpy (KeyName[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyName[1][KeyNameDscPtr->dsc$w_length] = '\0'; if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyName[1]); CgiLibHtmlEscape (KeyName[1], -1, KeyWordHtml[1], -1); if (!KeyWordCount) { strcpy (KeyWord[1], KeyName[1]); KeyWordCount = 1; } if (ExplodeHelp) for (Count = 0; Count < 11; KeyCount[Count++] = 0); if (VMSnok (status = lbr$lookup_key (&LibraryIndex, KeyNameDscPtr, &ModuleRFA))) { if (Debug) fprintf (stdout, "lib$lookup_key() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /**************************************/ /* loop through all records in module */ /**************************************/ for (;;) { status = lbr$get_record (&LibraryIndex, &InBufferDsc, &OutBufferDsc); if (Debug) fprintf (stdout, "lbr$get_record() %%X%08.08X\n", status); if (VMSnok (status)) break; /* terminate output buffer, removing any trailing white space */ cptr = Buffer+OutBufferDsc.dsc$w_length-1; while (cptr >= Buffer && isspace(*cptr)) cptr--; *++cptr = '\0'; /* comment record (line) in module */ if (Buffer[0] == '!') continue; if (Debug) fprintf (stdout, "Buffer |%s|\n", Buffer); /* 30-DEC-2008 OTS Bug? (V7.3-1 and V8.3 at least) Use of isdigit() of copyright char 0xa9 resulted in ACCVIO LIBOTS 0 00000000000217B0 FFFFFFFF809117B0 DECC$SHR_EV56 0 00000000000CD0E4 FFFFFFFF80B650E4 DECC$SHR_EV56 0 000000000014504C FFFFFFFF80BDD04C DECC$SHR_EV56 0 0000000000144FAC FFFFFFFF80BDCFAC Explicit test of digit range worked around issue. */ if ((Buffer[0] >= '0' && Buffer[0] <= '9' && Buffer[1] == ' ') || Buffer[0] == '/') { /***************************/ /* help level or qualifier */ /***************************/ PreviousHelpLevel = HelpLevel; if (Buffer[0] == '/') { /* if no previous qualifier encountered at this help level */ /* then the qualifier effectively creates a new help level */ if (PrevFirstBufferChar != '/') HelpLevel++; cptr = Buffer; for (sptr = cptr; *sptr && !isspace(*sptr); sptr++); } else { /* help level topic (line begins with a digit then a space) */ HelpLevel = Buffer[0] - '0'; for (cptr = Buffer + 1; isspace(*cptr); cptr++); for (sptr = cptr; *sptr && !isspace(*sptr); sptr++); } strncpy (KeyName[HelpLevel], cptr, sptr-cptr); KeyName[HelpLevel][sptr-cptr] = '\0'; CgiLibHtmlEscape (cptr, -1, KeyNameHtml[HelpLevel], -1); PrevFirstBufferChar = Buffer[0]; if (MatchesToLevel >= HelpLevel) { /* if the topic's been matched and output, finish up */ if (MatchedLevel) break; MatchesToLevel = ExplodeLevel = 0; for (Count = 1; Count <= HelpLevel; Count++) { if (strsame (KeyWord[Count], KeyName[Count], -1)) MatchesToLevel = Count; else break; } } else { for (Count = MatchesToLevel + 1; Count <= HelpLevel; Count++) { if (strsame (KeyWord[Count], KeyName[Count], -1)) MatchesToLevel = Count; else break; } } if (MatchesToLevel == KeyWordCount && MatchesToLevel == HelpLevel) { /****************/ /* module title */ /****************/ MatchedCount++; sptr = String; sptr += sprintf (sptr, "%s", LibraryTitle); for (Count = 1; Count <= HelpLevel; Count++) { if (Count > 1) for (cptr = KeyNameHtml[Count-1]; *cptr && *cptr != '_' && *cptr != ' '; cptr++); if (Count > 1 && *cptr) sptr += sprintf (sptr, ", %s", MungeUnderScores(KeyNameHtml[Count])); else if (Count > 1) sptr += sprintf (sptr, "  %s", MungeUnderScores(KeyNameHtml[Count])); else sptr += sprintf (sptr, "  —  %s", MungeUnderScores(KeyNameHtml[Count])); } BeginPage (LibraryTitle, String); if (ExplodeHelp) { ExplodedHeading = true; ExplodeLevel = HelpLevel + 1; } MatchedLevel = HelpLevel; } else if (ExplodeLevel && HelpLevel >= ExplodeLevel) { /******************/ /* exploding help */ /******************/ if (TextLineCount) if (PreviousHelpLevel == ExplodeLevel - 1) fprintf (stdout, "\n

    "); else fprintf (stdout, "\n"); TextLineCount = 0; ExplodedHeading = true; for (Count = HelpLevel+1; Count < 11; KeyCount[Count++] = 0); KeyCount[HelpLevel]++; fprintf (stdout, "

    ", ExplodeLevel == HelpLevel ? "1" : ""); for (Count = ExplodeLevel; Count <= HelpLevel; Count++) if (Count < HelpLevel) fprintf (stdout, "%d.", KeyCount[Count]); else fprintf (stdout, "%d", KeyCount[Count]); if (KeyNameHtml[HelpLevel][0] == '/') fprintf (stdout, "    %s\n", MungeUnderScores(KeyNameHtml[HelpLevel])); else fprintf (stdout, "  –  %s\n", MungeUnderScores(KeyNameHtml[HelpLevel])); } else if (MatchesToLevel == KeyWordCount && HelpLevel == KeyWordCount+1) { /*************/ /* subtopics */ /*************/ if (AdditionalInformation) { if (TextLineCount) fprintf (stdout, "\n"); if (HelpLevel == 2) { fprintf (stdout, "

    \n\
    \n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\
    \n\
    \n", CgiScriptNamePtr, CgiPathInfoPtr, HtmlReferer, HtmlFormTitle, FormatRequestKeys(false,HelpLevel-1), KeyWordHtml[1]); } fprintf (stdout, "Additional Information:\n\ \n\ explode\n\ extract\n\ \n\
    \n", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,HelpLevel-1), UriFormTitle, UriReferer, CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,HelpLevel-1)); AdditionalInformation = false; } else { if (TextLineCount) fprintf (stdout, "\n"); } if (PrevAdditionalChar[0] || !isalpha(KeyNameHtml[HelpLevel][0]) || islower(KeyNameHtml[HelpLevel][0])) { if (isalpha(KeyNameHtml[HelpLevel][0])) { if (!isalpha(PrevAdditionalChar[0]) || (isalpha(PrevAdditionalChar[0]) && KeyNameHtml[HelpLevel][0] != PrevAdditionalChar[0])) fprintf (stdout, "
    "); } else { if (isalpha(PrevAdditionalChar[0]) || KeyNameHtml[HelpLevel][1] != PrevAdditionalChar[1]) fprintf (stdout, "
    "); } } if (PrevAdditionalChar[0] || !isalpha(KeyNameHtml[HelpLevel][0]) || islower(KeyNameHtml[HelpLevel][0])) { PrevAdditionalChar[0] = KeyNameHtml[HelpLevel][0]; PrevAdditionalChar[1] = KeyNameHtml[HelpLevel][1]; } fprintf (stdout, "%s\n", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,HelpLevel), UriFormTitle, UriReferer, MungeUnderScores(KeyNameHtml[HelpLevel])); AdditionalInformationCount++; TextLineCount = 0; } } else { /*************/ /* help text */ /*************/ if ((ExplodeLevel && HelpLevel >= ExplodeLevel) || (MatchesToLevel == KeyWordCount && HelpLevel == KeyWordCount)) { /* check to see if this module record has any non-space chars */ for (cptr = Buffer; *cptr && (*cptr == ' ' || *cptr == '\t'); cptr++); /* ignore special directive for DECwindows on-line help */ if (*cptr && *cptr != '=') { if (!TextLineCount) fprintf (stdout, "
    ");
                   sptr = String;
                   if (ExplodedHeading)
                   {
                      ExplodedHeading = false;
                      BlankLineCount = 0;
                   }
                   if (BlankLineCount && TextLineCount) *sptr++ = '\n';
                   if (BlankLineCount) BlankLineCount = 0;
                   *sptr++ = ' ';
                   cptr = Buffer;
                   while (*cptr) *sptr++ = *cptr++;
                   *sptr++ = '\n';
                   *sptr = '\0';
                   HtmlOutputString (String, true);
                   TextLineCount++;
                }
                else
                if (!*cptr)
                   BlankLineCount++;
             }
          }
       }
     
       if (AdditionalInformationCount)
       {
          ListOtherHelp ();
          fprintf (stdout, "
    \n"); } else if (TextLineCount) fprintf (stdout, "\n"); else if (!MatchedCount) return (0x00268838); return (SS$_NORMAL); } /****************************************************************************/ /* Search for library files with names containing the first keyword. The idea is that an in-program help file might exist for additional information, so for the example of TELNET this function searches for SYS$COMMON:[SYSHLP]*TELNET*.HLB and lists any matching. Help topic HELP lists all those SYS$COMMON:[SYSHLP]*HELP*.HLB :-) */ ListOtherHelp () { int len, status, FileCount = 0; char *cptr, *sptr, *zptr; char HelpSpec [ODS_MAX_FILE_NAME_LENGTH+1], ResFileName [ODS_MAX_FILE_NAME_LENGTH+1], ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1], SimplePath [ODS_MAX_FILE_NAME_LENGTH+1], String [2048], UrlEncoded [ODS_MAX_FILE_NAME_LENGTH+64+1]; struct FAB FileFab; #ifdef ODS_EXTENDED struct NAML FileNaml; #endif /* ODS_EXTENDED */ struct NAM FileNam; if (Debug) fprintf (stdout, "ListOtherHelp() |%s|\n", LibraryFileName); sptr = HelpSpec; for (cptr = LibraryFileName; *cptr && *(cptr-1) != ']' && *(cptr-2) != '^' && *(cptr-2) != '.'; *sptr++ = *cptr++); *sptr++ = '*'; for (cptr = KeyWord[1]; *cptr; *sptr++ = *cptr++); for (cptr = "*.HLB"; *cptr; *sptr++ = *cptr++); *sptr++ = '\0'; if (Debug) fprintf (stdout, "|%s|\n", HelpSpec); FileFab = cc$rms_fab; FileFab.fab$l_fop = FAB$V_NAM; FileFab.fab$l_nam = &FileNam; #ifdef ODS_EXTENDED if (OdsExtended) { FileFab.fab$l_fna = (char*)-1; FileFab.fab$b_fns = 0; FileFab.fab$l_nam = (struct namdef*)&FileNaml; ENAMEL_RMS_NAML(FileNaml) FileNaml.naml$l_long_filename = HelpSpec; FileNaml.naml$l_long_filename_size = sptr - HelpSpec; FileNaml.naml$l_long_expand = ExpFileName; FileNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1; FileNaml.naml$l_long_result = ResFileName; FileNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1; } else #endif /* ODS_EXTENDED */ { FileFab.fab$l_fna = HelpSpec; FileFab.fab$b_fns = sptr - HelpSpec; FileFab.fab$l_nam = &FileNam; FileNam = cc$rms_nam; FileNam.nam$l_esa = ExpFileName; FileNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH; FileNam.nam$l_rsa = ResFileName; FileNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH; } if (VMSnok (status = sys$parse (&FileFab, 0, 0))) return (SS$_NORMAL); while (VMSok (status = sys$search (&FileFab, 0, 0))) { #ifdef ODS_EXTENDED if (OdsExtended) { cptr = FileNaml.naml$l_long_dev; zptr = FileNaml.naml$l_long_ver; } else #endif /* ODS_EXTENDED */ { cptr = FileNam.nam$l_dev; zptr = FileNam.nam$l_ver; } /* q&d conversion to URL format */ sptr = SimplePath; while (cptr < zptr) { if (*cptr == ':' && *(cptr+1) == '[') { *sptr++ = '/'; cptr += 2; } else if (*cptr == '.' && *(cptr+1) == ']' && *(cptr+1) == '[') { *sptr++ = '/'; cptr += 3; } else if (*cptr == ']') { *sptr++ = '/'; cptr++; } else *sptr++ = tolower(*cptr++); } *sptr = '\0'; CgiLibUrlEncode (SimplePath, -1, UrlEncoded, -1); sptr = String; if (!FileCount++) sptr += sprintf (sptr, "

    "); sptr += sprintf (sptr, ""); for (cptr = FileNaml.naml$l_long_name; cptr < FileNaml.naml$l_long_ver; *sptr++ =*cptr++); } else #endif /* ODS_EXTENDED */ { for (cptr = FileNam.nam$l_name; cptr < FileNam.nam$l_ver; *sptr++ = *cptr++); sptr += sprintf (sptr, "\">"); for (cptr = FileNam.nam$l_name; cptr < FileNam.nam$l_ver; *sptr++ = *cptr++); } sptr += sprintf (sptr, "\n"); fputs (String, stdout); } if (status == RMS$_FNF || status == RMS$_NMF) status = SS$_NORMAL; return (status); } /*****************************************************************************/ /* Just suck the brains out of the module as a plain-text document. */ int ExtractHelpModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { int status; char Buffer [1024]; $DESCRIPTOR (InBufferDsc, Buffer); $DESCRIPTOR (OutBufferDsc, Buffer); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ExtractHelpModule()\n"); if (VMSnok (status = lbr$lookup_key (&LibraryIndex, KeyNameDscPtr, &ModuleRFA))) { if (Debug) fprintf (stdout, "lib$lookup_key() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /**************************************/ /* loop through all records in module */ /**************************************/ CgiLibResponseHeader (200, "text/plain", "Last-Modified: %s\n", LastModifiedGmDateTime); if (MethodHead) return (SS$_NORMAL); for (;;) { status = lbr$get_record (&LibraryIndex, &InBufferDsc, &OutBufferDsc); if (Debug) fprintf (stdout, "lbr$get_record() %%X%08.08X\n", status); if (VMSnok (status)) break; Buffer[OutBufferDsc.dsc$w_length] = '\n'; Buffer[OutBufferDsc.dsc$w_length+1] = '\0'; fputs (Buffer, stdout); } return (SS$_NORMAL); } /*****************************************************************************/ /* */ int SearchHelpLibraryModules () { int status; char *cptr, *sptr; char String [1024]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchHelpLibraryModules()\n"); sprintf (String, "Search "%s" for "%s"", HtmlLibraryTitle, HtmlSearchString); BeginPage (String, String); fprintf (stdout, "

    \n"); RecordsSearched = SearchHitCount = 0; IndexNumber = 1; if (DoSearchStatistics) lib$init_timer (0); if (VMSnok (status = lbr$get_index (&LibraryIndex, &IndexNumber, &SearchHelpModule, KeyNameDsc()))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } if (!SearchHitCount) fprintf (stdout, "Not found!\n

    "); if (DoSearchStatistics) SearchStatistics (); fprintf (stdout, "

    \n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* This is a reasonably complex function, and reflects the complexities internal to help library modules! */ int SearchHelpModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static boolean HitThisTopic = false; static char PrevFirstBufferChar = '\0'; int status, Count, HelpLevel = 0, Length; char ch; char *cptr, *hptr, *sptr; char Buffer [1024], String [8192]; $DESCRIPTOR (InBufferDsc, Buffer); $DESCRIPTOR (OutBufferDsc, Buffer); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchHelpModule()\n"); strncpy (KeyWord[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyWord[1][KeyNameDscPtr->dsc$w_length] = '\0'; strcpy (KeyName[1], KeyWord[1]); CgiLibHtmlEscape (KeyWord[1], -1, KeyWordHtml[1], -1); if (!KeyWordCount) KeyWordCount = 1; if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyWord[1]); if (VMSnok (status = lbr$lookup_key (&LibraryIndex, KeyNameDscPtr, &ModuleRFA))) { if (Debug) fprintf (stdout, "lib$lookup_key() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /**************************************/ /* loop through all records in module */ /**************************************/ for (;;) { status = lbr$get_record (&LibraryIndex, &InBufferDsc, &OutBufferDsc); if (Debug) fprintf (stdout, "lbr$get_record() %%X%08.08X\n", status); if (VMSnok (status)) break; /* terminate output buffer, removing any trailing white space */ cptr = Buffer+OutBufferDsc.dsc$w_length-1; while (cptr >= Buffer && isspace(*cptr)) cptr--; *++cptr = '\0'; /* comment record (line) in module */ if (Buffer[0] == '!') continue; if (Debug) fprintf (stdout, "Buffer |%s|\n", Buffer); /* 30-DEC-2008 OTS Bug? (V7.3-1 and V8.3 at least) Use of isdigit() of copyright char 0xa9 resulted in ACCVIO LIBOTS 0 00000000000217B0 FFFFFFFF809117B0 DECC$SHR_EV56 0 00000000000CD0E4 FFFFFFFF80B650E4 DECC$SHR_EV56 0 000000000014504C FFFFFFFF80BDD04C DECC$SHR_EV56 0 0000000000144FAC FFFFFFFF80BDCFAC Explicit test of digit range worked around issue. */ if ((Buffer[0] >= '0' && Buffer[0] <= '9' && Buffer[1] == ' ') || Buffer[0] == '/') { /***************************/ /* help level or qualifier */ /***************************/ if (Buffer[0] == '/') { /* if no previous qualifier encountered at this help level */ /* then the qualifier effectively creates a new help level */ if (PrevFirstBufferChar != '/') HelpLevel++; cptr = Buffer; } else { /* help level topic (line begins with a digit then a space) */ HelpLevel = Buffer[0] - '0'; for (cptr = Buffer + 1; isspace(*cptr); cptr++); } PrevFirstBufferChar = Buffer[0]; strcpy (KeyName[HelpLevel], cptr); CgiLibHtmlEscape (cptr, -1, KeyNameHtml[HelpLevel], -1); /* if previous topic had a hit then end the list of the hit(s) */ if (HitThisTopic) { fprintf (stdout, "\n"); fflush (stdout); } HitThisTopic = false; } else { /*************/ /* help text */ /*************/ /* check to see if this module record has any non-space chars */ for (cptr = Buffer; *cptr && isspace(*cptr); cptr++); if (*cptr) { if (*(hptr = SearchText (cptr, CgiFormSearchPtr, false))) { /********/ /* hit! */ /********/ if (Debug) fprintf (stdout, "Hit |%s|\n", hptr); SearchHitCount++; sptr = String; if (!HitThisTopic) { for (Count = 1; Count <= HelpLevel; Count++) { if (Count > 1) *sptr++ = ' '; sptr += sprintf (sptr, "%s\n", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,Count), UriFormTitle, UriReferer, MungeUnderScores(KeyNameHtml[Count])); } sptr += sprintf (sptr, "
      \n
    • "); HitThisTopic = true; } else { /* same topic, new hit */ sptr += sprintf (sptr, "
    • "); } ch = cptr[hptr-cptr]; cptr[hptr-cptr] = '\0'; sptr += CgiLibHtmlEscape (cptr, -1, sptr, -1); cptr[hptr-cptr] = ch; sptr += sprintf (sptr, ""); strncpy (sptr, hptr, SearchStringLength); sptr += SearchStringLength; sptr += sprintf (sptr, ""); sptr += CgiLibHtmlEscape (hptr+SearchStringLength, -1, sptr, -1); sprintf (sptr, "
    • \n"); fputs (String, stdout); } } } } if (HitThisTopic) { fprintf (stdout, "
    \n"); fflush (stdout); HitThisTopic = false; } return (SS$_NORMAL); } /*****************************************************************************/ /* String together the keys associated with this help item. */ char* FormatRequestKeys ( boolean ForUri, int ToLevel ) { static char RequestKeys [256]; int idx; char *sptr; sptr = RequestKeys; for (idx = 1; idx <= ToLevel; idx++) { if (ForUri) sptr += CgiLibUrlEncode (KeyName[idx], -1, sptr, -1); else sptr += CgiLibHtmlEscape (KeyName[idx], -1, sptr, -1); *sptr++ = '~'; } /* if needed, get rid of that last '~'! */ if (idx > 1) sptr[-1] = '\0'; else *sptr = '\0'; return (RequestKeys); } /*****************************************************************************/ /* */ int ListTextLibraryModules () { int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListTextLibraryModules()\n"); BeginPage (LibraryTitle, HtmlLibraryTitle); fprintf (stdout, "
    \n\
    \n\ \n\ \n\ \n\ \n\ \n\ \n\
    \n\
    \n\
    \n", CgiScriptNamePtr, CgiPathInfoPtr, HtmlReferer, HtmlFormTitle); IndexNumber = 1; if (VMSnok (status = lbr$get_index (&LibraryIndex, &IndexNumber, &ListTextModule, KeyNameDsc()))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } fprintf (stdout, "
    \n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int ListTextModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static int PreviousCgiPlusUsageCount = 0; static char PrevAlpha = '\0'; char String [1024], UriKeyName [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListTextModule()\n"); strncpy (KeyWord[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyWord[1][KeyNameDscPtr->dsc$w_length] = '\0'; strcpy (KeyName[1], KeyWord[1]); CgiLibUrlEncode (KeyWord[1], -1, UriKeyName, -1); CgiLibHtmlEscape (KeyWord[1], -1, KeyNameHtml[1], -1); if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyWord[1]); if (ListSingle) { if (CgiPlusUsageCount == PreviousCgiPlusUsageCount) fprintf (stdout, "\n

    "); else fprintf (stdout, "

    "); } else if (!isalpha(KeyName[1][0]) || KeyName[1][0] == PrevAlpha) { if (CgiPlusUsageCount == PreviousCgiPlusUsageCount) fprintf (stdout, "\n"); } else { if (CgiPlusUsageCount == PreviousCgiPlusUsageCount) fprintf (stdout, "\n

    "); PrevAlpha = KeyName[1][0]; } fprintf (stdout, "%s\n", CgiScriptNamePtr, LibraryPathInfo, UriKeyName, UriFormTitle, UriReferer, KeyNameHtml[1]); PreviousCgiPlusUsageCount = CgiPlusUsageCount; return (SS$_NORMAL); } /*****************************************************************************/ /* */ int OpenTextLibraryModule () { int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "OpenTextLibraryModule()\n"); CgiLibResponseHeader (200, "text/plain", "Last-Modified: %s\n", LastModifiedGmDateTime); if (MethodHead) return (SS$_NORMAL); IndexNumber = 1; if (VMSnok (status = lbr$get_index (&LibraryIndex, &IndexNumber, &OpenTextModule, KeyNameDsc()))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } return (status); } /*****************************************************************************/ /* */ int OpenTextModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { int status; char Buffer [1024]; $DESCRIPTOR (InBufferDsc, Buffer); $DESCRIPTOR (OutBufferDsc, Buffer); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "OpenTextModule()\n"); strncpy (KeyWord[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyWord[1][KeyNameDscPtr->dsc$w_length] = '\0'; if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyWord[1]); if (VMSnok (status = lbr$lookup_key (&LibraryIndex, KeyNameDscPtr, &ModuleRFA))) { if (Debug) fprintf (stdout, "lib$lookup_key() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /**************************************/ /* loop through all records in module */ /**************************************/ for (;;) { status = lbr$get_record (&LibraryIndex, &InBufferDsc, &OutBufferDsc); if (Debug) fprintf (stdout, "lbr$get_record() %%X%08.08X\n", status); if (VMSnok (status)) break; /* add HTTP required carriage-control */ Buffer[OutBufferDsc.dsc$w_length] = '\n'; Buffer[OutBufferDsc.dsc$w_length+1] = '\0'; fputs (Buffer, stdout); } return (SS$_NORMAL); } /*****************************************************************************/ /* */ int SearchTextLibraryModules () { int status; char String [1024]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchTextLibraryModules()\n"); sprintf (String, "Search "%s" for "%s"\n", HtmlLibraryTitle, HtmlSearchString); BeginPage (String, String); fprintf (stdout, "

    \n"); RecordsSearched = SearchHitCount = 0; IndexNumber = 1; if (DoSearchStatistics) lib$init_timer (0); if (VMSnok (status = lbr$get_index (&LibraryIndex, &IndexNumber, &SearchTextModule, KeyNameDsc()))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } if (!SearchHitCount) fprintf (stdout, "Not found!\n

    "); if (DoSearchStatistics) SearchStatistics (); fprintf (stdout, "

    \n\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int SearchTextModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { boolean HitThisModule = false; int status; char ch; char *cptr, *hptr, *sptr; char Buffer [1024], String [1024]; $DESCRIPTOR (InBufferDsc, Buffer); $DESCRIPTOR (OutBufferDsc, Buffer); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchTextModule()\n"); strncpy (KeyName[1], KeyNameDscPtr->dsc$a_pointer, KeyNameDscPtr->dsc$w_length); KeyName[1][KeyNameDscPtr->dsc$w_length] = '\0'; CgiLibHtmlEscape (KeyName[1], -1, KeyNameHtml[1], -1); if (Debug) fprintf (stdout, "KeyNameDsc |%s|\n", KeyName[1]); if (VMSnok (status = lbr$lookup_key (&LibraryIndex, KeyNameDscPtr, &ModuleRFA))) { if (Debug) fprintf (stdout, "lib$get_index() %%X%08.08X\n", status); CgiLibResponseError (FI_LI, status, HtmlLibraryTitle); return (SS$_NORMAL); } /**************************************/ /* loop through all records in module */ /**************************************/ for (;;) { status = lbr$get_record (&LibraryIndex, &InBufferDsc, &OutBufferDsc); if (Debug) fprintf (stdout, "lbr$get_record() %%X%08.08X\n", status); if (VMSnok (status)) break; Buffer[OutBufferDsc.dsc$w_length] = '\0'; if (Debug) fprintf (stdout, "Buffer |%s|\n", Buffer); /* check to see if this module record has any non-space chars */ for (cptr = Buffer; *cptr && isspace(*cptr); cptr++); if (*cptr) { if (*(hptr = SearchText (cptr, CgiFormSearchPtr, false))) { /********/ /* hit! */ /********/ if (Debug) fprintf (stdout, "Hit |%s|\n", sptr); SearchHitCount++; sptr = String; if (!HitThisModule) { sptr += sprintf (sptr, "%s\n
      \n", CgiScriptNamePtr, LibraryPathInfo, KeyName[1], UriFormTitle, UriReferer, KeyNameHtml[1]); HitThisModule = true; } sptr += sprintf (sptr, "
    • "); ch = cptr[hptr-cptr]; cptr[hptr-cptr] = '\0'; sptr += CgiLibHtmlEscape (cptr, -1, sptr, -1); cptr[hptr-cptr] = ch; sptr += sprintf (sptr, ""); strncpy (sptr, hptr, SearchStringLength); sptr += SearchStringLength; sptr += sprintf (sptr, ""); sptr += CgiLibHtmlEscape (hptr+SearchStringLength, -1, sptr, -1); sprintf (sptr, "
    • \n"); fputs (String, stdout); } } } if (HitThisModule) { fprintf (stdout, "
    \n"); fflush (stdout); } return (SS$_NORMAL); } /*****************************************************************************/ /* Case sensistive or case insensistive search of the string pointed to by 'sptr' for the string pointed to by 'SearchString'. */ char* SearchText ( char *tptr, char *SearchString, boolean CaseSensitive ) { char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchText()\n"); RecordsSearched++; sptr = SearchString; if (CaseSensitive) { while (*tptr) { if (*tptr++ != *sptr) continue; /* first character of search string matched record character */ sptr++; cptr = tptr; while (*cptr && *sptr) { if (*cptr != *sptr) break; cptr++; sptr++; } if (!*sptr) { tptr--; break; } sptr = SearchString; } } else { while (*tptr) { if (toupper(*tptr++) != toupper(*sptr)) continue; /* first character of search string matched record character */ sptr++; cptr = tptr; while (*cptr && *sptr) { if (toupper(*cptr) != toupper(*sptr)) break; cptr++; sptr++; } if (!*sptr) { tptr--; break; } sptr = SearchString; } } return (tptr); } /*****************************************************************************/ /* */ SearchStatistics () { static long LibElapsedTime = 1, LibCpuTime = 2; static $DESCRIPTOR (ElapsedFaoDsc, "!%T"); static $DESCRIPTOR (SearchStats1FaoDsc, "
    \ Elapsed: !AZ  \ CPU: !2ZL:!2ZL.!2ZL  \ records (lines): !UL\
    \n"); unsigned long ElapsedTime [2]; unsigned long CpuTime; unsigned short Length; char Elapsed [32], String [512]; $DESCRIPTOR (ElapsedDsc, Elapsed); $DESCRIPTOR (StringDsc, String); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchStatistics()\n"); lib$stat_timer (&LibElapsedTime, &ElapsedTime, 0); lib$stat_timer (&LibCpuTime, &CpuTime, 0); sys$fao (&ElapsedFaoDsc, &Length, &ElapsedDsc, &ElapsedTime); Elapsed[Length] = '\0'; sys$fao (&SearchStats1FaoDsc, &Length, &StringDsc, Elapsed+3, CpuTime/6000, CpuTime/100, CpuTime%100, RecordsSearched); String[Length] = '\0'; fprintf (stdout, "%s", String); } /*****************************************************************************/ /* Output the HTTP header and page header. */ BeginPage ( char *TitleString, char *HeaderString ) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "BeginPage()\n"); CgiLibResponseHeader (200, "text/html", "Last-Modified: %s\n", LastModifiedGmDateTime); if (MethodHead) return; fprintf (stdout, "\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n\ %s\n\ \n\ %s\ \n\ \n", SoftwareID, SoftwareCopy, CgiLibEnvironmentPtr, UnixDateTime, TitleString, DefaultStyle, StyleSheetPtr); fprintf (stdout, "
    %s
    \n\
    \n", HeaderString); fflush (stdout); } /*****************************************************************************/ /* In a help key string turn underscores into spaces mixed case. */ char* MungeUnderScores (char *InString) { static char OutString [128]; char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MungeUnderScores()\n"); sptr = OutString; if (InString[0] != '/') { /* not a qualifier, see if it's all the one case! */ if (isupper(InString[0])) { for (cptr = InString; *cptr; cptr++) if (isalpha(*cptr) && !isupper(*cptr)) break; } else { for (cptr = InString; *cptr; cptr++) if (isalpha(*cptr) && !islower(*cptr)) break; } } if (InString[0] != '/' && *cptr) { /* probably an underscore-joined topic string */ for (cptr = InString; *cptr; cptr++) { if (*cptr == '_') *sptr++ = ' '; else *sptr++ = *cptr; } } else { /* all upper-case, probably some particular VMS parameter */ for (cptr = InString; *cptr; cptr++) *sptr++ = *cptr; } *sptr = '\0'; return (OutString); } /*****************************************************************************/ /* If the object has been modified since the specified date and time then return a normal status indicating that the data transfer is to continue. If not modified then send a "not modified" HTTP header and return an error status to indicate the object should not be sent. */ int ModifiedSince ( unsigned long *SinceBinaryTimePtr, unsigned long *BinaryTimePtr ) { static unsigned long OneSecondDelta [2] = { -10000000, -1 }; int status; unsigned long AdjustedBinTime [2], ScratchBinTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ModifiedSince()\n"); /* if request asks for a "reload" (not cached) then give it regardless */ if (strsame (CgiHttpPragmaPtr, "no-cache", -1)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "no-cache", 8)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "no-store", 8)) return (SS$_NORMAL); if (strsame (CgiHttpCacheControlPtr, "max-age=0", 9)) return (SS$_NORMAL); /* Add one second to the modified time. Ensures a negative time for VMS where fractional seconds may result in inconclusive results when the target time is being specified in whole seconds. */ if (VMSnok (status = lib$add_times (SinceBinaryTimePtr, &OneSecondDelta, &AdjustedBinTime))) { CgiLibResponseError (FI_LI, status, "IfModifiedSince:"); return (status); } if (Debug) fprintf (stdout, "sys$add_times() %%X%08.08X\n", status); /* if a positive time results the file has been modified */ if (VMSok (status = lib$sub_times (BinaryTimePtr, &AdjustedBinTime, &ScratchBinTime))) return (status); if (Debug) fprintf (stdout, "sys$sub_times() %%X%08.08X\n", status); if (status != LIB$_NEGTIM) { CgiLibResponseError (FI_LI, status, "IfModifiedSince:"); return (status); } /* not modified */ CgiLibResponseHeader (304, "text/html"); fprintf (stdout, "Not modified!\n"); return (LIB$_NEGTIM); } /*****************************************************************************/ /* Create an HTTP format Greenwich Mean Time (UTC) time string in the storage pointed at by 'TimeString', e.g. "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123). This must be at least 30 characters capacity. If 'BinTimePtr' is null the time string represents the current time. If it points to a quadword, VMS time value the string represents that time. 'TimeString' must point to storage large enough for 31 characters. */ int HttpGmTimeString ( char *TimeString, unsigned long *BinTimePtr ) { static char *DayNames [] = { "", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun" }; static char *MonthName [] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; static $DESCRIPTOR (HttpTimeFaoDsc, "!AZ, !2ZW !AZ !4ZW !2ZW:!2ZW:!2ZW GMT"); static $DESCRIPTOR (TimeStringDsc, ""); int status; unsigned long BinTime [2], GmTime [2]; unsigned short Length; unsigned short NumTime [7]; unsigned long DayOfWeek; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HttpGmTimeString()\n"); if (!BinTimePtr) sys$gettim (&GmTime); else { GmTime[0] = BinTimePtr[0]; GmTime[1] = BinTimePtr[1]; } if (VMSnok (status = TimeAdjustGMT (true, &GmTime))) return (status); status = sys$numtim (&NumTime, &GmTime); if (Debug) fprintf (stdout, "sys$numtim() %%X%08.08X %d %d %d %d %d %d %d\n", status, NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); if (VMSnok (status = lib$day_of_week (&GmTime, &DayOfWeek))) return (status); if (Debug) fprintf (stdout, "lib$day_of_week() %%X%08.08X is %d\n", status, DayOfWeek); /* set the descriptor address and size of the resultant time string */ TimeStringDsc.dsc$w_length = 30; TimeStringDsc.dsc$a_pointer = TimeString; if (VMSnok (status = sys$fao (&HttpTimeFaoDsc, &Length, &TimeStringDsc, DayNames[DayOfWeek], NumTime[2], MonthName[NumTime[1]], NumTime[0], NumTime[3], NumTime[4], NumTime[5]))) { if (Debug) fprintf (stdout, "sys$fao() %%X%08.08X\n", status); TimeString[0] = '\0'; } else TimeString[Length] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", TimeString); return (status); } /*****************************************************************************/ /* Given a string such as "Fri, 25 Aug 1995 17:32:40 GMT" (RFC 1123), or "Friday, 25-Aug-1995 17:32:40 GMT" (RFC 1036), create an internal, local, binary time with the current GMT offset. See complementary function HttpGmTimeString(). */ int HttpGmTime ( char *TimeString, unsigned long *BinTimePtr ) { static char *MonthName [] = { "", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; int status; char *tptr; unsigned short Length; unsigned short NumTime [7] = { 0,0,0,0,0,0,0 }; unsigned long DayOfWeek; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HttpGmTime() |%s|\n", TimeString); tptr = TimeString; /* hunt straight for the comma after the weekday name! */ while (*tptr && *tptr != ',') tptr++; if (*tptr) tptr++; /* span white space between weekday name and date */ while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the date and then skip to month name */ if (isdigit(*tptr)) NumTime[2] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the month number from the name and skip to the year */ for (NumTime[1] = 1; NumTime[1] <= 12; NumTime[1]++) if (strsame (tptr, MonthName[NumTime[1]], 3)) break; if (NumTime[1] > 12) return (STS$K_ERROR); while (*tptr && isalpha(*tptr)) tptr++; while (*tptr && (*tptr == '-' || isspace(*tptr))) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the year and then skip to the hour */ if (isdigit(*tptr)) { NumTime[0] = atoi (tptr); if (NumTime[0] < 100) NumTime[0] += 1900; } while (*tptr && isdigit(*tptr)) tptr++; while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!*tptr) return (STS$K_ERROR); /* get the hour, minute and second */ if (isdigit(*tptr)) NumTime[3] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (isdigit(*tptr)) NumTime[4] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (isdigit(*tptr)) NumTime[5] = atoi (tptr); while (*tptr && isdigit(*tptr)) tptr++; if (*tptr == ':') tptr++; if (!*tptr) return (STS$K_ERROR); /* the only thing remaining should be the "GMT" */ while (*tptr && isspace(*tptr)) tptr++; if (Debug) fprintf (stdout, "tptr |%s|\n", tptr); if (!strsame (tptr, "GMT", 3)) return (STS$K_ERROR); /*******************************************/ /* convert what looks like legitimate GMT! */ /*******************************************/ if (Debug) fprintf (stdout, "NumTime[] %d %d %d %d %d %d %d\n", NumTime[0], NumTime[1], NumTime[2], NumTime[3], NumTime[4], NumTime[5], NumTime[6]); status = lib$cvt_vectim (&NumTime, BinTimePtr); if (VMSnok (status)) return (status); if (Debug) fprintf (stdout, "lib$cvt_vectim() %%X%08.08X\n", status); return (TimeAdjustGMT (false, BinTimePtr)); } /*****************************************************************************/ /* Determine the offset from GMT (UTC) using either the HTTPD$GMT or SYS$TIMEZONE_DIFFERENTIAL logicals. If HTTPD$GMT is not defined time should be set from the timezone differential logical. Function RequestBegin() calls this every hour to recheck GMT offset and detect daylight saving or other timezone changes. */ int TimeSetGmt () { static boolean UseTimezoneDifferential = false; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetGmt()\n"); if (!UseTimezoneDifferential) { status = TimeSetHttpdGmt(); /* return if error and that error was not that the name did not exist */ if (VMSok (status) || status != SS$_NOLOGNAM) return (status); } UseTimezoneDifferential = true; return (TimeSetTimezone()); } /*****************************************************************************/ /* The SYS$TIMEZONE_DIFFERENTIAL logical contains the number of seconds offset from GMT (UTC) as a positive (ahead) or negative (behind) number. Set the 'TimeGmtString' global storage to a "+hh:mm" or "-hh:mm" string equivalent, and the 'TimeAheadOfGmt' global boolean and 'TimeGmtVmsString' delta-time global string. */ int TimeSetTimezone () { static unsigned short Length; static $DESCRIPTOR (TimezoneLogicalNameDsc, "SYS$TIMEZONE_DIFFERENTIAL"); static $DESCRIPTOR (LnmSystemDsc, "LNM$SYSTEM"); static $DESCRIPTOR (TimeGmtStringFaoDsc, "!AZ!2ZL:!2ZL"); static $DESCRIPTOR (TimeGmtVmsStringFaoDsc, "0 !2ZL:!2ZL"); static $DESCRIPTOR (TimeGmtStringDsc, TimeGmtString); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length }, { 0,0,0,0 } }; int status; long Hours, Minutes, Seconds; char *SignPtr; $DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetTimezone()\n"); status = sys$trnlnm (0, &LnmSystemDsc, &TimezoneLogicalNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (VMSnok (status)) return (status); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); Seconds = atol(TimeGmtString); if (Seconds < 0) { TimeAheadOfGmt = false; Seconds = -Seconds; SignPtr = "-"; } else { TimeAheadOfGmt = true; SignPtr = "+"; } Hours = Seconds / 3600; Minutes = (Seconds - Hours * 3600) / 60; if (Debug) fprintf (stdout, "%d %s%d:%d\n", Seconds, SignPtr, Hours, Minutes); sys$fao (&TimeGmtStringFaoDsc, &Length, &TimeGmtStringDsc, SignPtr, Hours, Minutes); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); sys$fao (&TimeGmtVmsStringFaoDsc, &Length, &TimeGmtVmsStringDsc, Hours, Minutes); TimeGmtVmsString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString); TimeGmtVmsStringDsc.dsc$w_length = Length; if (VMSnok (status = sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary))) return (status); if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1]) return (status); /* time must have been zero, make it one, one-hundreth of a second */ TimeGmtDeltaBinary[0] = -100000; TimeGmtDeltaBinary[1] = -1; return (SS$_NORMAL); } /*****************************************************************************/ /* Translate the logical HTTPD$GMT (defined to be something like "+10:30" or "- 01:15") and convert it into a delta time structure and store in 'TimeGmtDeltaBinary'. Store whether it is in advance or behind GMT in boolean 'TimeAheadOfGmt'. Store the logical string in 'TimeGmtString'. */ int TimeSetHttpdGmt () { static unsigned short Length; static $DESCRIPTOR (GmtLogicalNameDsc, "HTTPD$GMT"); static $DESCRIPTOR (LnmFileDevDsc, "LNM$FILE_DEV"); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } LnmItems [] = { { sizeof(TimeGmtString)-1, LNM$_STRING, TimeGmtString, &Length }, { 0,0,0,0 } }; int status; char *cptr, *sptr; $DESCRIPTOR (TimeGmtVmsStringDsc, TimeGmtVmsString); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeSetHttpdGmt()\n"); status = sys$trnlnm (0, &LnmFileDevDsc, &GmtLogicalNameDsc, 0, &LnmItems); if (Debug) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (VMSnok (status)) return (status); TimeGmtString[Length] = '\0'; if (Debug) fprintf (stdout, "TimeGmtString |%s|\n", TimeGmtString); if (TimeGmtString[0] == '$') return (SS$_NORMAL); if (*(cptr = TimeGmtString) == '-') TimeAheadOfGmt = false; else TimeAheadOfGmt = true; if (*cptr == '+' || *cptr == '-') cptr++; sptr = TimeGmtVmsString; *sptr++ = '0'; *sptr++ = ' '; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; if (Debug) fprintf (stdout, "TimeGmtVmsString |%s|\n", TimeGmtVmsString); TimeGmtVmsStringDsc.dsc$w_length = sptr - TimeGmtVmsString; if (VMSnok (status = sys$bintim (&TimeGmtVmsStringDsc, &TimeGmtDeltaBinary))) return (status); if (TimeGmtDeltaBinary[0] || TimeGmtDeltaBinary[1]) return (status); /* time must have been zero, make it one, one-hundreth of a second */ TimeGmtDeltaBinary[0] = -100000; TimeGmtDeltaBinary[1] = -1; return (SS$_NORMAL); } /*****************************************************************************/ /* The GMT is generated by calculating using an offset using 'TimeGmtDeltaOffset' and boolean 'TimeAheadOfGmt'. Adjust either to or from GMT. */ int TimeAdjustGMT ( boolean ToGmTime, unsigned long *BinTimePtr ) { int status; unsigned long AdjustedTime [2]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "TimeAdjustGMT() ToGmTime: %d\n", ToGmTime); if ((ToGmTime && TimeAheadOfGmt) || (!ToGmTime && !TimeAheadOfGmt)) { /* to GMT from local and ahead of GMT, or to local from GMT and behind */ status = lib$sub_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime); if (Debug) fprintf (stdout, "lib$sub_times() %%X%08.08X\n", status); } else { /* to GMT from local and behind GMT, or to local from GMT and ahead */ status = lib$add_times (BinTimePtr, &TimeGmtDeltaBinary, &AdjustedTime); if (Debug) fprintf (stdout, "lib$add_times() %%X%08.08X\n", status); } if (Debug) { unsigned short Length; char String [64]; $DESCRIPTOR (AdjustedTimeFaoDsc, "AdjustedTime: |!%D|\n"); $DESCRIPTOR (StringDsc, String); sys$fao (&AdjustedTimeFaoDsc, &Length, &StringDsc, &AdjustedTime); String[Length] = '\0'; fprintf (stdout, "%s", String); } BinTimePtr[0] = AdjustedTime[0]; BinTimePtr[1] = AdjustedTime[1]; return (status); } /*****************************************************************************/ /* Output an HTML-escaped, URL-anchored, and "$ HELP"-anchored string. */ HtmlOutputString ( char *InString, boolean AnchorUrls ) { char OutString [1024]; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "HtmlOutputString()\n"); CgiLibAnchorHtmlEscape (InString, -1, OutString, sizeof(OutString), AnchorUrls); if (cptr = strstr (OutString, "$ HELP")) { char *sptr, *tptr; char Scratch [1024]; cptr += 2; tptr = cptr + 4; sptr = Scratch; while (*tptr && *tptr != '\n') { switch (*tptr) { case ' ' : *sptr++ = '/'; break; case '/' : *sptr++ = '/'; *sptr++ = '/'; break; default : *sptr++ = *tptr; } tptr++; } *sptr = '\0'; *tptr = '\0'; fwrite (OutString, cptr-OutString, 1, stdout); fprintf (stdout, "%s\n", CgiScriptNamePtr, Scratch, cptr); } else fputs (OutString, stdout); } /****************************************************************************/ /* Return an integer reflecting the major and minor version of VMS (e.g. 60, 61, 62, 70, 71, 72, etc.) */ #ifdef ODS_EXTENDED int GetVmsVersion () { static char SyiVersion [16]; static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } SyiItems [] = { { 8, SYI$_VERSION, &SyiVersion, 0 }, { 0,0,0,0 } }; int status, version; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GetVmsVersion()\n"); if (VMSnok (status = sys$getsyiw (0, 0, 0, &SyiItems, 0, 0, 0))) exit (status); SyiVersion[8] = '\0'; version = ((SyiVersion[1]-48) * 10) + (SyiVersion[3]-48); if (Debug) fprintf (stdout, "|%s| %d\n", SyiVersion, version); return (version); } #endif /* ODS_EXTENDED */ /*****************************************************************************/ /* Return non-NULL pointer if looks a bit suspect! */ char* XSSuspect (char *cptr) { static char XSSredacted [] = "XSS_redacted! (tsk-tsk)"; char *sptr, *zptr; char buf [1024]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "XSSuspect() |%s|\n", cptr); if (!cptr || !*cptr) return (NULL); if (strstr (cptr, "= zptr || strstr (buf, "