/*****************************************************************************/ /* 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" PAGE LAYOUT ----------- Page layout and colouration may be specified via the appropriate command-line qualifiers (or corresponding logical/symbol name). Defaults apply for any not specified. See "Qualifiers" section below, and also about the logical name or symbol "CONAN$PARAM". An example of changing the page colour to white and the banner to red! /PBGCOLOR="#ffffff" /PHBGCOLOR="#ff0000" Don't like explicitly setting a browser's colours? A colour may be disabled by setting it to empty. The following example disables all colours. /PBGCOLOR/PBBGCOLOR/PHBGCOLOR/PHTEXT/PLINK/PTEXT/PVLINK The script can format a page in either of two layouts. 1. Tables are used to create a coloured header and button bar (DEFAULT). Default colours are white page with grey heading and button outlines. 2. Textual header, horizontal rules and a textual button bar. No default colours. Select other than the default using the following: /PLAYOUT=2 Local information may be included in the header. For layout 1 this should be integrated with the formatted header and to the right of the header information. Text, an image logo, just about anything could be included. This is a example of providing a textual form of a local logo: /PHLOCAL="" This is an example of providing a local graphical logo: /PHLOCAL="" Such local information with layout 2 is included immediately before the header information at the top of the page. Button labels are customizable (potentially to non-English language). They comprise a label, equate symbol and URL-style path suitable for creating a link. Multiple buttons are separated using the semicolon. Note that any such button customization must provide escaped HTML-forbidden characters in the button label and URI-forbidden characters in the path! The backslash character, "\", escapes characters, including the button-delimitting "=" and ";". There are defaults, see DEFAULT_BUTTONS. Here is an example of changing the button labels: /BUTTON="Close;Help=/conan/-/conanhelp.html" Always have the equivalent of "Close" for the first button! Additional buttons may be created by adding "label=path;" elements to the button string. In this way an additional information page could be referenced as follows: /BUTTON="Close;Help=/conan/-/conanhelp.html;Other VMS=/vms/" DIFFICULTY FITTING ALL THESE QUALIFIERS INTO A COMMAND LINE OR LOGICAL? Use an existing, or create a new, DCL wrapper procedure for the script (same name and directory) and build up a DCL symbol to provide the information. Up to 1024 characters can be included in this way. For example: $ CONAN$PARAM = "/BUTTON=""Close;Help=/conan/-/conanhelp.html""" $ CONAN$PARAM = CONAN$PARAM + "/PBGCOLOR/PLINK/PVLINK" $ CONAN$PARAM = CONAN$PARAM + "/PHLOCAL=""""" $ RUN WASD_EXE:CONAN 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= string containing button labels/paths /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) /PBACKGROUND= background image path /PBGCOLOR= background colour /PBBGCOLOR= button background color /PBBORDER= width of button border /PHBGCOLOR= heading background color /PHBORDER= width of heading and button-bar border /PHLOCAL= local information to be included in header /PHTEXT= heading text colour /PLAYOUT= 1 is coloured header & buttons, 2 is text & horizontal rules /PLINK= link colour /PTEXT= text colour /PVLINK= visited link colour /SINGLE one module per line (rather than alphabetically grouped) for all requests BUILD DETAILS ------------- See BUILD_CONAN.COM procedure. COPYRIGHT --------- Copyright (C) 2005-2008 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!) --------------- 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 "3.7.5" #define SOFTWARENM "CONAN" #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 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 "Close;HLB-list=/conan/sys$common/syshlp/*.hlb;TLB-list=/conan/sys$common/syslib/*.tlb;Help=/conan/-/conanhelp.html" /* this macro just plugs in some script-specific code into ButtonBar() */ #define SCRIPT_SPECIFIC_BUTTON_CODE \ ButtonInternal[0] = CgiFormRefererPtr; #define DEFAULT_PS_BGCOLOR "#ffffff" #define DEFAULT_PS_TEXT "#000000" #define DEFAULT_PS_LINK "#0000cc" #define DEFAULT_PS_VLINK "#0000cc" #define DEFAULT_PS_HEADBGCOLOR "#cccccc" #define DEFAULT_PS_HEADBORDER "0" #define DEFAULT_PS_HEADTEXT "#000000" #define DEFAULT_PS_BUTTONBGCOLOR "#ffffff" #define DEFAULT_PS_BUTTONBORDER "1" #define PS_BACKGROUND 0 #define PS_BGCOLOR 1 #define PS_TEXT 2 #define PS_LINK 3 #define PS_VLINK 4 #define PS_HEADBGCOLOR 5 #define PS_HEADTEXT 6 #define PS_HEADBORDER 7 #define PS_BUTTONBGCOLOR 8 #define PS_BUTTONBORDER 9 #define PS_BODYTAG 10 #define PS_LAYOUT 11 #define PS_HEADLOCAL 12 #define PS_HEADPADDING 13 char *PageScheme [16]; char Utility [] = "CONAN"; boolean Debug, CliListSingle, DoHeaderInformation, DoListLibraries, DoLibraryMenu, DoSearch, DoSearchStatistics, ErrorReported, ExplodeHelp, ExtractHelp, 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, HtmlFormTitle [256], HtmlLibraryTitle [256], HtmlReferer [512], HtmlSearchString [512], LastModifiedGmDateTime [32], LibraryDirPath [ODS_MAX_FILE_NAME_LENGTH+1], LibraryName [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 *ButtonPtr = DefaultButtons, *CgiHttpIfModifiedSincePtr, *CgiHttpPragmaPtr, *CgiHttpCacheControlPtr, *CgiLibEnvironmentPtr, *CgiPathInfoPtr, *CgiPathTranslatedPtr, *CgiRequestMethodPtr, *CgiScriptNamePtr, *CgiServerSoftwarePtr, *CgiFormDoPtr, *CgiFormExplodePtr, *CgiFormExtractPtr, *CgiFormKeyPtr, *CgiFormRefererPtr, *CgiFormTitlePtr, *CgiFormSearchPtr, *CgiFormSinglePtr, *CliCharsetPtr, *CharsetPtr; /* element 0 is not used, keywords number from 1 to 10 */ char KeyWord [11][256], KeyWordHtml [11][256], KeyName [11][256], KeyNameHtml [11][256], SoftwareID [48]; struct lhidef LibraryHeader; struct { unsigned long lo32; unsigned long hi32; } ModuleRFA; /* CGIplus specific */ int IsCgiPlus, CgiPlusUsageCount; /* required function prototypes */ 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*); /*****************************************************************************/ /* '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 (); SetPageScheme (); CgiLibResponseSetBody (PageScheme[PS_BODYTAG]); 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++; ButtonPtr = 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 (GetPageParameter (aptr)) 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); } } } /*****************************************************************************/ /* Get command-line parameters associated with page scheme. */ boolean GetPageParameter (char *aptr) { char *cptr; /*********/ /* begin */ /*********/ if (strsame (aptr, "/PBACKGROUND=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_BACKGROUND] = cptr; return (true); } if (strsame (aptr, "/PBGCOLOR=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_BGCOLOR] = cptr; return (true); } if (strsame (aptr, "/PBBGCOLOR=", 5)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_BUTTONBGCOLOR] = cptr; return (true); } if (strsame (aptr, "/PBBORDER=", 5)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_BUTTONBORDER] = cptr; return (true); } if (strsame (aptr, "/PHBGCOLOR=", 5)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_HEADBGCOLOR] = cptr; return (true); } if (strsame (aptr, "/PHBORDER=", 5)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_HEADBORDER] = cptr; return (true); } if (strsame (aptr, "/PHTEXT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_HEADTEXT] = cptr; return (true); } if (strsame (aptr, "/PLAYOUT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_LAYOUT] = cptr; return (true); } if (strsame (aptr, "/PLINK=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_LINK] = cptr; return (true); } if (strsame (aptr, "/PHLOCAL=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_HEADLOCAL] = cptr; return (true); } if (strsame (aptr, "/PTEXT=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_TEXT] = cptr; return (true); } if (strsame (aptr, "/PVLINK=", 4)) { for (cptr = aptr; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; PageScheme[PS_VLINK] = cptr; return (true); } return (false); } /*****************************************************************************/ /* Set the page layout and colouration. */ SetPageScheme () { int size; char *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SetPageScheme()\n"); if (!PageScheme[PS_LAYOUT]) PageScheme[PS_LAYOUT] = "1"; if (!PageScheme[PS_BACKGROUND]) PageScheme[PS_BACKGROUND] = ""; if (!PageScheme[PS_HEADLOCAL]) PageScheme[PS_HEADLOCAL] = ""; if (PageScheme[PS_LAYOUT][0] == '2') { if (!PageScheme[PS_BGCOLOR]) PageScheme[PS_BGCOLOR] = ""; if (!PageScheme[PS_TEXT]) PageScheme[PS_TEXT] = ""; if (!PageScheme[PS_LINK]) PageScheme[PS_LINK] = ""; if (!PageScheme[PS_VLINK]) PageScheme[PS_VLINK] = ""; if (!PageScheme[PS_HEADBGCOLOR]) PageScheme[PS_HEADBGCOLOR] = ""; if (!PageScheme[PS_HEADBORDER]) PageScheme[PS_HEADBORDER] = ""; if (!PageScheme[PS_HEADTEXT]) PageScheme[PS_HEADTEXT] = ""; if (!PageScheme[PS_BUTTONBGCOLOR]) PageScheme[PS_BUTTONBGCOLOR] = ""; if (!PageScheme[PS_BUTTONBORDER]) PageScheme[PS_BUTTONBORDER] = ""; } else { if (!PageScheme[PS_BGCOLOR]) PageScheme[PS_BGCOLOR] = DEFAULT_PS_BGCOLOR; if (!PageScheme[PS_TEXT]) PageScheme[PS_TEXT] = DEFAULT_PS_TEXT; if (!PageScheme[PS_LINK]) PageScheme[PS_LINK] = DEFAULT_PS_LINK; if (!PageScheme[PS_VLINK]) PageScheme[PS_VLINK] = DEFAULT_PS_VLINK; if (!PageScheme[PS_HEADBGCOLOR]) PageScheme[PS_HEADBGCOLOR] = DEFAULT_PS_HEADBGCOLOR; if (!PageScheme[PS_HEADBORDER]) PageScheme[PS_HEADBORDER] = DEFAULT_PS_HEADBORDER; if (!PageScheme[PS_HEADTEXT]) PageScheme[PS_HEADTEXT] = DEFAULT_PS_HEADTEXT; if (!PageScheme[PS_BUTTONBGCOLOR]) PageScheme[PS_BUTTONBGCOLOR] = DEFAULT_PS_BUTTONBGCOLOR; if (!PageScheme[PS_BUTTONBORDER]) PageScheme[PS_BUTTONBORDER] = DEFAULT_PS_BUTTONBORDER; } /* tag attributes */ size = strlen(PageScheme[PS_BACKGROUND]) + strlen(PageScheme[PS_BGCOLOR]) + strlen(PageScheme[PS_TEXT]) + strlen(PageScheme[PS_LINK]) + strlen(PageScheme[PS_VLINK]); if (size) { if (!(sptr = calloc (1, size+64))) exit (vaxc$errno); PageScheme[PS_BODYTAG] = sptr; if (PageScheme[PS_BACKGROUND][0]) sptr += sprintf (sptr, " BACKGROUND=\"%s\"", PageScheme[PS_BACKGROUND]); if (PageScheme[PS_BGCOLOR][0]) sptr += sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_BGCOLOR]); if (PageScheme[PS_TEXT][0]) sptr += sprintf (sptr, " TEXT=\"%s\"", PageScheme[PS_TEXT]); if (PageScheme[PS_LINK][0]) sptr += sprintf (sptr, " LINK=\"%s\"", PageScheme[PS_LINK]); if (PageScheme[PS_VLINK][0]) sptr += sprintf (sptr, " VLINK=\"%s\"", PageScheme[PS_VLINK]); } else PageScheme[PS_BODYTAG] = ""; if (PageScheme[PS_HEADBGCOLOR][0]) { if (!(sptr = calloc (1, strlen(PageScheme[PS_HEADBGCOLOR])+16))) exit (vaxc$errno); sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_HEADBGCOLOR]); PageScheme[PS_HEADBGCOLOR] = sptr; PageScheme[PS_HEADPADDING] = "10"; } else PageScheme[PS_HEADPADDING] = "0"; if (PageScheme[PS_BUTTONBGCOLOR][0]) { if (!(sptr = calloc (1, strlen(PageScheme[PS_BUTTONBGCOLOR])+16))) exit (vaxc$errno); sprintf (sptr, " BGCOLOR=\"%s\"", PageScheme[PS_BUTTONBGCOLOR]); PageScheme[PS_BUTTONBGCOLOR] = sptr; } } /*****************************************************************************/ /* Provides a divider for top and bottom of the content of the page. This can be a coloured bar (using
"- "WASD...
) or a horizontal rule depending on the page layout. "Buttons" providing script-internal and/or additional user-specified links ('ButtonPtr' string) can be placed with(in) this bar. All button labels are derived from 'ButtonPtr', with script-internal buttons using link-paths set up via 'ButtonInternal[]' array, or any user-specified path depending on requirement. An empty path (i.e. PathPtr[0] == '\0') obviously does not have a link created, it just displays the button label. For a button-bar at the top of the document use 1, bottom of the document use 2, and for just a bar with no buttons at all use 0. */ ButtonBar (int Top1Bottom2) { #define MAX_BUTTON_COUNT 8 static int ButtonCount = -1; static char *ButtonInternal [MAX_BUTTON_COUNT], *ButtonLabel [MAX_BUTTON_COUNT], *ButtonPath [MAX_BUTTON_COUNT]; int idx; char *PathPtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ButtonBar() %d\n", Top1Bottom2); if (ButtonCount == -1) { char *cptr, *sptr; if (Debug) fprintf (stdout, "|%s|\n", ButtonPtr); cptr = ButtonPtr; for (ButtonCount = 0; ButtonCount < MAX_BUTTON_COUNT && *cptr; ButtonCount++) { for (sptr = cptr; *sptr && *sptr != '=' && *sptr != ';'; sptr++) if (*sptr == '\\') memcpy (sptr, sptr+1, strlen(sptr)); if (*sptr == '=') *sptr++ = '\0'; ButtonLabel[ButtonCount] = cptr; cptr = sptr; for (sptr = cptr; *sptr && *sptr != ';'; sptr++) if (*sptr == '\\') memcpy (sptr, sptr+1, strlen(sptr)); if (*sptr) *sptr++ = '\0'; ButtonPath[ButtonCount] = cptr; cptr = sptr; } } if (Top1Bottom2) { /***********************************/ /* set up script-specified buttons */ /***********************************/ SCRIPT_SPECIFIC_BUTTON_CODE } if (PageScheme[PS_LAYOUT][0] == '2') { /************/ /* format 2 */ /************/ if (Top1Bottom2 == 2 || !Top1Bottom2) { fprintf (stdout, "
\n"); if (!Top1Bottom2) return; } fprintf (stdout, "\n"); for (idx = 0; idx < ButtonCount; idx++) { if (!ButtonInternal[idx]) PathPtr = ButtonPath[idx]; else PathPtr = ButtonInternal[idx]; if (idx) fprintf (stdout, " "); if (PathPtr[0]) fprintf (stdout, "[%s]\n", PathPtr, ButtonLabel[idx]); else fprintf (stdout, "[%s]\n", ButtonLabel[idx]); } fprintf (stdout, "\n"); if (Top1Bottom2 == 1) fprintf (stdout, "
\n"); } else { /************/ /* format 1 */ /************/ fprintf (stdout, "
\ \
\n\ \n\ \n", PageScheme[PS_HEADBORDER], PageScheme[PS_HEADBORDER], PageScheme[PS_HEADBGCOLOR]); if (ButtonCount == 0 || !Top1Bottom2) fprintf (stdout, " "); else { fprintf (stdout, "
\n", PageScheme[PS_BUTTONBORDER]); for (idx = 0; idx < ButtonCount; idx++) { if (!ButtonInternal[idx]) PathPtr = ButtonPath[idx]; else PathPtr = ButtonInternal[idx]; if (PathPtr[0]) fprintf (stdout, "\n", PageScheme[PS_BUTTONBGCOLOR], PathPtr, ButtonLabel[idx]); else fprintf (stdout, "\n", PageScheme[PS_BUTTONBGCOLOR], ButtonLabel[idx]); } fprintf (stdout, "
\   %s  \   %s  
\n"); } fprintf (stdout, "\n\ \n\ \ \
\n"); } fprintf (stdout, "
\ [legal]\n\ [privacy]\n\ [GNU]\n\ [policy]\n\ [netiquette]\n\ [sponsors]\n\ [FAQ]\n\
\n"); fprintf (stdout," \ \"\"\n\
\n\ Tweet\ \n\ \n\ \n\
Polarhome, production since 1999.\n\
Member of Polarhome portal.\n\


"); } /*****************************************************************************/ /* */ ConanTheLibrarian () { int idx; unsigned long UnixTime; char *cptr, *sptr; struct tm *UnixTmPtr; int status; /*********/ /* 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 = ErrorReported = 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"); if (strcmp (CgiRequestMethodPtr, "GET")) { 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"); if (CgiFormRefererPtr[0]) { /* re-escape the URL-escaped percentages */ CgiLibUrlEncode (CgiFormRefererPtr, -1, UriReferer, -1); CgiLibHtmlEscape (CgiFormRefererPtr, -1, HtmlReferer, -1); } 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 = ""; } } /**************/ /* 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[idx] |%s|%s|\n", KeyWord[idx], KeyWordHtml[idx]); } } else if (!strstr (CgiPathTranslatedPtr, ".HLB") && !strstr (CgiPathTranslatedPtr, ".hlb") && !strstr (CgiPathTranslatedPtr, ".TLB") && !strstr (CgiPathTranslatedPtr, ".tlb")) { /*******************************/ /* 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 implmentation 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[idx] |%s|%s|\n", KeyWord[idx], KeyWordHtml[idx]); } /* finished with the path if we've used it for keywords! */ CgiPathInfoPtr = ""; } KeyWordCount = idx; while (idx++ < 10) KeyWord[idx][0] = KeyWordHtml[idx][0] = '\0'; strcpy (LibraryPathInfo, CgiPathInfoPtr); /* isolate the directory component of the path */ strcpy (LibraryDirPath, CgiPathInfoPtr); 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 (toupper(CgiFormDoPtr[0])) { case 'H' : DoHeaderInformation = true; break; case 'L' : DoListLibraries = true; break; case 'M' : DoLibraryMenu = 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 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 *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()\n"); /* if no library specifcation provided then default to help libraries */ if (!LibrarySpec[0]) strcpy (LibrarySpec, "SYS$HELP:*.HLB;0"); 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); while (VMSok (status = sys$search (&FileFab, 0, 0))) { if (!FileCount++) fprintf (stdout, "
    \n"); /* ignore directories if the file type includes them (numeric ".DIR") */ #ifdef ODS_EXTENDED if (OdsExtended) { if (strsame(FileNaml.naml$l_long_type+1, "DIR;", 4)) continue; } else #endif /* ODS_EXTENDED */ if (strsame(FileNam.nam$l_type+1, "DIR;", 4)) continue; #ifdef ODS_EXTENDED if (OdsExtended) { if (strsame (FileNaml.naml$l_long_type+1, "HLB;", 4) || strsame (FileNaml.naml$l_long_type+1, "TLB;", 4)) SupportedLibraryType = true; else SupportedLibraryType = false; } else #endif /* ODS_EXTENDED */ { if (strsame (FileNam.nam$l_type+1, "HLB;", 4) || strsame (FileNam.nam$l_type+1, "TLB;", 4)) SupportedLibraryType = true; else SupportedLibraryType = false; } sptr = String; strcpy (sptr, "
  1. "); sptr += 4; 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) *(char*)FileNaml.naml$l_long_ver = '\0'; else *(char*)FileNaml.naml$l_long_type = '\0'; cptr = (char*)FileNaml.naml$l_long_name; while (*cptr) *sptr++ = tolower(*cptr++); *sptr = '\0'; if (FileNaml.naml$l_fnb & NAM$M_WILD_TYPE) *(char*)FileNaml.naml$l_long_ver = ';'; else *(char*)FileNaml.naml$l_long_type = '.'; } else #endif /* ODS_EXTENDED */ { if (FileNam.nam$l_fnb & NAM$M_WILD_TYPE) *(char*)FileNam.nam$l_ver = '\0'; else *(char*)FileNam.nam$l_type = '\0'; cptr = (char*)FileNam.nam$l_name; while (*cptr) *sptr++ = tolower(*cptr++); *sptr = '\0'; if (FileNam.nam$l_fnb & NAM$M_WILD_TYPE) *(char*)FileNam.nam$l_ver = ';'; else *(char*)FileNam.nam$l_type = '.'; } if (SupportedLibraryType) { strcpy (sptr, ""); sptr += 4; } *sptr++ = '\n'; *sptr = '\0'; fprintf (stdout, "%s", String); } if (FileCount) fprintf (stdout, "
\n"); else 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); } if (ErrorReported) return (status); fprintf (stdout, "\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\

\n\ \n\ \n\ \n\  \n\ \n\ \n\
\n", CgiScriptNamePtr, UrlEncoded, UriFormTitle, UriReferer, CgiScriptNamePtr, UrlEncoded, UriFormTitle, UriReferer, CgiScriptNamePtr, UrlEncoded, HtmlReferer, HtmlFormTitle); if (ErrorReported) return; 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, Length; 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 }, LibraryNameDsc = { sizeof(LibraryName)-1, DSC$K_DTYPE_T, DSC$K_CLASS_S, LibraryName }; /****************************************/ /* 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, &LibraryNameDsc, &Length))) { 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 */ LibraryName[Length] = '\0'; for (cptr = LibraryName; *cptr && *cptr != ';'; cptr++); *cptr = '\0'; if (Debug) fprintf (stdout, "LibraryName |%s|\n", LibraryName); 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 Length; 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, &Length, &StringDsc, &LibraryHeader.lhi$l_credat); String[Length] = '\0'; fprintf (stdout, "%s", String); sys$fao (&RevisedFaoDsc, &Length, &StringDsc, &LibraryHeader.lhi$l_updtim); String[Length] = '\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"); if (ErrorReported) return; fprintf (stdout, "
\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int ListHelpLibraryModules () { int status; char *cptr, *sptr; char KeyName [256], String [1024]; $DESCRIPTOR (KeyNameDsc, KeyName); if (Debug) fprintf (stdout, "ListHelpLibraryModules()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; BeginPage (LibraryTitle, HtmlLibraryTitle); fprintf (stdout, "

\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"); if (ErrorReported) return (status); fprintf (stdout, "
\n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int ListHelpModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static char PreviousAlphabetic = 0, PreviousCharacter = 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)PreviousCharacter) fprintf (stdout, "\n
  • "); else fprintf (stdout, "
  • "); } else if (!isalpha(KeyName[1][0]) || toupper(KeyName[1][0]) == PreviousAlphabetic) { if ((unsigned char)KeyName[1][0] >= (unsigned char)PreviousCharacter) fprintf (stdout, ",\n"); } else { if ((unsigned char)KeyName[1][0] >= (unsigned char)PreviousCharacter) fprintf (stdout, "\n

    "); PreviousAlphabetic = toupper(KeyName[1][0]); } fprintf (stdout, "%s", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,1), UriFormTitle, UriReferer, MungeUnderScores(KeyNameHtml[1])); PreviousCharacter = KeyName[1][0]; return (SS$_NORMAL); } /*****************************************************************************/ /* */ int OpenHelpLibraryModule () { int status; char *cptr, *sptr; char KeyName [256], String [512]; $DESCRIPTOR (KeyNameDsc, KeyName); if (Debug) fprintf (stdout, "OpenHelpLibraryModule()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; 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); } if (!ErrorReported) 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 PreviousFirstBufferChar = '\0'; char *cptr, *sptr; char Buffer [1024], 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); CgiLibHtmlEscape (KeyName[1], -1, KeyNameHtml[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 (PreviousFirstBufferChar != '/') 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); PreviousFirstBufferChar = 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
    \n", LibraryTitle); for (Count = 1; Count <= HelpLevel; Count++) { 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]++; if (ExplodeLevel < HelpLevel || KeyName[HelpLevel][0] == '/') fprintf (stdout, "

    "); else if (PageScheme[PS_LAYOUT][0] == '2') fprintf (stdout, "

    "); else fprintf (stdout, "

    \ \n\  \n\ \n", PageScheme[PS_HEADBORDER], PageScheme[PS_HEADBGCOLOR], PageScheme[PS_HEADTEXT]); for (Count = ExplodeLevel; Count <= HelpLevel; Count++) { if (Count < HelpLevel) fprintf (stdout, " %d.", KeyCount[Count]); else fprintf (stdout, "%d", KeyCount[Count]); } if (ExplodeLevel < HelpLevel || KeyName[HelpLevel][0] == '/') { fprintf (stdout, " - %s\n", MungeUnderScores(KeyNameHtml[HelpLevel])); } else if (PageScheme[PS_LAYOUT][0] == '2') { fprintf (stdout, " - %s\n", MungeUnderScores(KeyNameHtml[HelpLevel])); } else { fprintf (stdout, " - %s\n\ \n\ \n\
    \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", CgiScriptNamePtr, CgiPathInfoPtr, HtmlReferer, HtmlFormTitle, FormatRequestKeys(false,HelpLevel-1), KeyWordHtml[1]); } fprintf (stdout, "

    \   Additional Information \ (explod\ e) \ :\n\

    \

      \n", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,HelpLevel-1), UriFormTitle, UriReferer, CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,HelpLevel-1)); AdditionalInformation = false; } else { if (TextLineCount) fprintf (stdout, "\n"); } 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++ = '\r'; **/
                        *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)
            fprintf (stdout, "
    \n"); else if (TextLineCount) fprintf (stdout, "\n"); else if (!MatchedCount) return (0x00268838); if (ErrorReported) return (status); return (SS$_NORMAL); } /*****************************************************************************/ /* 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); 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 KeyName [256], String [1024]; $DESCRIPTOR (KeyNameDsc, KeyName); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchHelpLibraryModules()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; 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, &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"); 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 PreviousFirstBufferChar = '\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); CgiLibHtmlEscape (KeyWord[1], -1, KeyNameHtml[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 (PreviousFirstBufferChar != '/') 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++); } PreviousFirstBufferChar = 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) { strcpy (sptr, ""); sptr += 3; for (Count = 1; Count <= HelpLevel; Count++) { if (Count > 1) { *sptr++ = ','; *sptr++ = ' '; } sptr += sprintf (sptr, "%s", CgiScriptNamePtr, LibraryPathInfo, FormatRequestKeys(true,Count), UriFormTitle, UriReferer, MungeUnderScores(KeyNameHtml[Count])); } strcpy (sptr, "\n
      \n
    • "); sptr += 14; HitThisTopic = true; } else { /* same topic, new hit */ strcpy (sptr, "
    • "); sptr += 4; } ch = cptr[hptr-cptr]; cptr[hptr-cptr] = '\0'; sptr += CgiLibHtmlEscape (cptr, -1, sptr, -1); cptr[hptr-cptr] = ch; strcpy (sptr, ""); sptr += 3; strncpy (sptr, hptr, SearchStringLength); sptr += SearchStringLength; strcpy (sptr, ""); sptr += 4; sptr += CgiLibHtmlEscape (hptr+SearchStringLength, -1, sptr, -1); *sptr++ = '\n'; *sptr = '\0'; fprintf (stdout, "%s", String); } } } } 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; char *cptr, *sptr; char KeyName [256], String [1024]; $DESCRIPTOR (KeyNameDsc, KeyName); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "ListTextLibraryModules()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; BeginPage (LibraryTitle, HtmlLibraryTitle); fprintf (stdout, "

    \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); } if (ErrorReported) return (status); fprintf (stdout, "
    \n"); ButtonBar (2); fprintf (stdout, "\n\n"); } /*****************************************************************************/ /* */ int ListTextModule ( struct dsc$descriptor_s *KeyNameDscPtr, void *RFAptr ) { static int PreviousCgiPlusUsageCount = 0; static char PreviousAlphabetic = '\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] == PreviousAlphabetic) { if (CgiPlusUsageCount == PreviousCgiPlusUsageCount) fprintf (stdout, ",\n"); } else { if (CgiPlusUsageCount == PreviousCgiPlusUsageCount) fprintf (stdout, "\n

    "); PreviousAlphabetic = KeyName[1][0]; } fprintf (stdout, "%s", CgiScriptNamePtr, LibraryPathInfo, UriKeyName, UriFormTitle, UriReferer, KeyNameHtml[1]); PreviousCgiPlusUsageCount = CgiPlusUsageCount; return (SS$_NORMAL); } /*****************************************************************************/ /* */ int OpenTextLibraryModule () { int status; char *cptr, *sptr; char KeyName [256], String [512]; $DESCRIPTOR (KeyNameDsc, KeyName); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "OpenTextLibraryModule()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; CgiLibResponseHeader (200, "text/plain", "Last-Modified: %s\n", LastModifiedGmDateTime); 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 *sptr; char Buffer [1024], String [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 *cptr, *sptr; char KeyName [256], String [1024]; $DESCRIPTOR (KeyNameDsc, KeyName); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchTextLibraryModules()\n"); cptr = KeyName; for (sptr = KeyWord[1]; *sptr; *cptr++ = toupper(*sptr++)); *cptr = '\0'; KeyNameDsc.dsc$w_length = cptr - KeyName; 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"); 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; } strcpy (sptr, "
    • "); sptr += 14; ch = cptr[hptr-cptr]; cptr[hptr-cptr] = '\0'; sptr += CgiLibHtmlEscape (cptr, -1, sptr, -1); cptr[hptr-cptr] = ch; strcpy (sptr, ""); sptr += 3; strncpy (sptr, hptr, SearchStringLength); sptr += SearchStringLength; strcpy (sptr, ""); sptr += 4; sptr += CgiLibHtmlEscape (hptr+SearchStringLength, -1, sptr, -1); strcpy (sptr, ""); sptr += 12; *sptr++ = '\n'; *sptr = '\0'; fprintf (stdout, "%s", String); } } } 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, "\n\ \n\
    \n\  \n\ Elapsed: !AZ  \ CPU: !2ZL:!2ZL.!2ZL  \ Records (lines): !UL\n\  \n\
    \n"); static $DESCRIPTOR (SearchStats2FaoDsc, " \n\ \ Elapsed: !AZ  \ CPU: !2ZL:!2ZL.!2ZL  \ Records (lines): !UL\ \n\  \n\ \n\
    \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'; if (PageScheme[PS_LAYOUT][0] == '2') sys$fao (&SearchStats2FaoDsc, &Length, &StringDsc, Elapsed+3, CpuTime/6000, CpuTime/100, CpuTime%100, RecordsSearched); else sys$fao (&SearchStats1FaoDsc, &Length, &StringDsc, PageScheme[PS_HEADBGCOLOR], PageScheme[PS_HEADTEXT], 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 ) { char *cptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "BeginPage()\n"); CgiLibResponseHeader (200, "text/html", "Last-Modified: %s\n", LastModifiedGmDateTime); fprintf (stdout, "\n\ \n\ \n\ \n\ \n\ \n\ %s\n\ \n\ \n", SoftwareID, UnixDateTime, CgiLibEnvironmentPtr, TitleString, PageScheme[PS_BODYTAG]); if (PageScheme[PS_LAYOUT][0] == '2') { fprintf (stdout, "%s\n\ %s\n\ \n\
    \ \n\  *Conan The Librarian (sorry for the slow response - running on an old VAX)\n\ \n\
    \n\

    ", PageScheme[PS_HEADLOCAL], HeaderString); } else { fprintf (stdout, "\n\ %s\n\ \n\ \n\
    \n\ \n\ %s\n\ \n\
    \ \n\  *Conan The Librarian (sorry for the slow response - running on an old VAX)\n\ \n\
    \n\
    \n", PageScheme[PS_HEADBORDER], PageScheme[PS_HEADPADDING], PageScheme[PS_HEADBGCOLOR], PageScheme[PS_HEADTEXT], HeaderString, PageScheme[PS_HEADTEXT], PageScheme[PS_HEADLOCAL]); } 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 */ /****************************************************************************/ /* Does a case-insensitive, character-by-character string compare and returns true if two strings are the same, or false if not. If a maximum number of characters are specified only those will be compared, if the entire strings should be compared then specify the number of characters as 0. */ boolean strsame ( char *sptr1, char *sptr2, int count ) { while (*sptr1 && *sptr2) { if (toupper (*sptr1++) != toupper (*sptr2++)) return (false); if (count) if (!--count) return (true); } if (*sptr1 || *sptr2) return (false); else return (true); } /*****************************************************************************/