/*****************************************************************************/ /* [WASD]Query.c A CGI-compliant script to search plain-text and HTML files on ODS-2 and ODS-5 volumes. Extended file specifications may be expressed using either RMS-escaped ("^_") or URL-escaped ("%nn") forbidden characters. If a version delimiter (';', with or without version number) is present in the path specification then this script displays and anchors RMS-escaped and VMS syntax file names. If none is present it supplies URL-encoded file names. Query works in concert with EXTRACT.C. Accepts query-based searches (e.g. "/web/*.*?find+this+phrase") or form-based fields briefly discussed below. By default searches the URL-supplied path, or will use a path supplied with the "path=" query form field (via request redirection to get the server to map the supplied path). A VMS directory elipsis (i.e. "...") may be supplied in the path to result in a directory tree search. Hits in both plain-text files and HTML files provide a link to the extract script. With plain text files the extract script extracts a section of the file and presents it with some buttons to allow retrieving other sections or all the document. When extracting HTML files it returns the entire document but with each occurance of the hit enclosed by a '' (yes, an oh not a zero) anchor that allows the specific hit to be jumped to with relative document syntax. The following tags do not have any content included: , , , , , . Using a script-name prefixed path such as "/query/web/.../*.*" returns a simple form for generating a search. "Text" file extensions are predefined in the DEFAULT_TEXT_TYPES and DEFAULT_HTML_TYPES macros. To specify a custom list use /TEXT= and /HTML= or to add other extensions to be considered text or HTML use /ADDTEXT= and /ADDHTML= (not this is a comma-separated list with no extension period). File extensions may contain the asterisk wildcard character, representing zero or more matching characters (e.g. "REPORT_*"). NOTE ON EXTENDED FILE SPECS --------------------------- This script is ODS-5 volume compliant and will process extended file naming (excluding those using Unicode characters). It's design allows building on VAX VMS (which excludes ODS-5 support) and Alpha VMS all versions. When building on VAX all extended ODS code is "#ifdef"ed out for efficiency reasons. If built under an Alpha version that does not support extended naming the ENAMEL.H header file provides an equivalent (a build allowed by the bogus NAML created by ENAMEL.H) then it can only process ODS-2 file names, however if built with VMS 7.2ff it will process both, and the object module can still be linked and used on an earlier version (although without ODS-5 capabilities of course). This Alpha backward/forward compatibility is provided at runtime by checking the version of VMS it is currently executing under. If extended file specification compliant then NAML structures are always used, otherwise NAMs. Hence, a VMS version that doesn't know about extended file specifications (and doesn't have any ODS-5 volumes of course) never gets passed a NAML! LOGICAL NAMES ------------- QUERY$DBUG turns on all "if (Debug)" statements QUERY$PARAM equivalent to (overrides) the command line parameters/qualifiers (define as a system-wide logical) HTML FORM ELEMENTS ------------------ case= case sensitive search (Y or N) exact= exact number of records (for extract utility, Y or N) extract= number of line to pass to extract utility hits= show all hits or document only (D or A) html= comma-separated list of HTML file extensions (overrides the /HTML and /ADDHTML qualifiers) plain= if "yes" then treat HTML files as if plain-text (i.e. search markup tags, everything!) path= form supplied path (otherwise URL path) search= text to search for target= open new page in (e.g. "_blank", "_self") text= comma-separated list of text file extensions (overrides the /TEXT and /ADDTEXT qualifiers) what= title on search results page These could be used as in the following example (note that as this is in a C-language comment "@" has been substituted for "*", they're just wildcards!):

case sensitive? N Y
extract this number of lines around a "hit":
QUALIFIERS ---------- /ABOUT= synonym for /HELP /ADDHTML= additional list of comma separated HTML file types /ADDTEXT= additional list of comma separated TEXT file types /CHARSET= "Content-Type: text/html; charset=...", empty suppress charset /DBUG turns on all "if (Debug)" statements /EXTRACT= path to extract script /HELP= URL for help on searching /HTML= complete list of comma separated HTML file types /[NO]ODS5 control extended file specification (basically for testing) /STYLE= URL for site CSS style sheet /TEXT= complete list of comma separated TEXT file types /TIMEFMT= strftime() format string for last-modified time BUILD DETAILS ------------- See BUILD_QUERY.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!) --------------- 28-JUL-2020 MGD v4.2.0, use "athitof" instead of "__hit__" bugfix; style tag processing 28-JUN-2019 MGD v4.1.0, allow scripts to contain anything but QueryHtmlEscape() allow HTML entities remain intact 04-OCT-2014 MGD v4.0.0, a nod to the twenty-first century 10-MAY-2005 MGD v3.2.9, SWS 2.0 ignore query string components supplied as command-line parameters differently to CSWS 1.2/3 23-DEC-2003 MGD v3.2.8, minor conditional mods to support IA64 15-AUG-2003 MGD v3.2.7, bugfix; move fragment *after* query string 23-JUN-2003 MGD v3.2.6, record size increased to maximum (32767 bytes), ignore RMS$_WLK from sys$open() in line with RMS$_PRV 12-APR-2003 MGD v3.2.5, link colour changed to 0000cc 15-AUG-2002 MGD v3.2.4, GetParameters() mod for direct CSWS 1.2 support 01-JUL-2001 MGD v3.2.3, add 'SkipParameters' for direct OSU support 19-MAY-2001 MGD v3.2.2, remove limitation in SameFileType() preventing searching of multiple file versions 25-JAN-2001 MGD v3.2.1, use to terminate processing 28-OCT-2000 MGD v3.2.0, use CGILIB object module 02-MAR-2000 MGD v3.1.2, bugfix;ed again:^( rework SameFileType() 28-FEB-2000 MGD v3.1.1, bugfix; SameFileType() wildcard processing 15-FEB-2000 MGD v3.1.0, allow wildcarded file types 18-JAN-2000 MGD v3.0.0, support extended file specifications (ODS-5) 07-AUG-1999 MGD v2.9.0, use more of the CGILIB functionality, plain-text files described using file name 24-APR-1999 MGD v2.8.0, use CGILIB.C, standard CGI environment (e.g. Netscape FastTrack) 18-FEB-1999 MGD v2.7.2, Search[Text/Html]File() handling of SS$_PRV (files with protection violations now just ignored) 20-NOV-1998 MGD v2.7.1, exclude certain content (e.g. ", 9)) { rptr += 8; InsideScript = false; } else rptr++; continue; } if (InsideStyle) { /* pretty much anything goes except... */ if (strsame (rptr, "", 8)) { rptr += 7; InsideStyle = false; } else rptr++; continue; } /* less-thans are forbidden inside tags! */ if ((TagCharCount & 1) && rptr[0] == '<') break; /* consider adjacent white-space when determining what is a tag */ if ((rptr[0] == '<' && rptr[1] && !isspace(rptr[1])) || ((TagCharCount & 1) && rptr[0] == '>')) { TagCharCount++; rptr++; if (TagCharCount & 1) { /* checks to detect start and end of some tags */ if (tolower(*rptr) == 'a' && (strsame (rptr, "applet>", 7) || strsame (rptr, "applet ", 7))) { rptr += 6; InsideApplet = true; } else if (strsame (rptr, "body>", 5) || strsame (rptr, "body ", 4)) { rptr += 4; InsideHead = false; } else if (tolower(*rptr) == 'h' && strsame (rptr, "head>", 5)) { rptr += 4; InsideHead = true; } else if (tolower(*rptr) == 's') { if (strsame (rptr, "script>", 7) || strsame (rptr, "script ", 7)) { rptr += 6; InsideScript = true; } else if (strsame (rptr, "server>", 7) || strsame (rptr, "server ", 7)) { rptr += 6; InsideServer = true; } if (strsame (rptr, "style>", 6) || strsame (rptr, "style ", 6)) { rptr += 5; InsideStyle = true; } } else if (tolower(*rptr) == 't' && strsame (rptr, "title>", 6)) { rptr += 5; dptr = DocumentName; InsideTitle = true; } else if (*rptr == '/') { if (strsame (rptr, "/applet>", 8)) { rptr += 7; InsideApplet = false; } else if (strsame (rptr, "/head>", 6)) { rptr += 5; InsideHead = false; } else if (strsame (rptr, "/script>", 8)) { rptr += 7; InsideScript = false; } else if (strsame (rptr, "/style>", 7)) { rptr += 6; InsideStyle = false; } else if (strsame (rptr, "/server>", 8)) { rptr += 7; InsideServer = false; } else if (strsame (rptr, "/title>", 7)) { rptr += 6; *dptr = '\0'; InsideTitle = false; } } } } else { if (InsideTitle) { if (dptr < DocumentName+255) *dptr++ = *rptr++; else rptr++; } else if (InsideApplet || InsideHead || InsideScript || InsideServer || InsideStyle) { rptr++; } else if (TagCharCount & 1) rptr++; else { if (*rptr == '&') { if (strsame (rptr, "<", 4)) { *tptr++ = '<'; rptr += 4; } else if (strsame (rptr, ">", 4)) { *tptr++ = '>'; rptr += 4; } else if (strsame (rptr, "&", 5)) { *tptr++ = '&'; rptr += 5; } else if (strsame (rptr, """, 6)) { *tptr++ = '\"'; rptr += 6; } else if (strsame (rptr, " ", 6)) { *tptr++ = ' '; rptr += 6; } else if (*(rptr+1) == '#') { for (cptr = rptr+2; *cptr && isdigit(*cptr); cptr++); if (*cptr == ';') { ch = atoi(rptr+2) & 0xff; *tptr++ = ch; rptr = cptr + 1; } else *tptr++ = *rptr++; } else *tptr++ = *rptr++; } else *tptr++ = *rptr++; } } } /* while (*rptr) */ if (Debug) fprintf (stdout, "+++++2 %d\n", TagCharCount); *tptr = '\0'; if (!Text[0]) continue; tptr = SearchTextString (Text, SearchString, CaseSensitive, true, &MatchedLength); if (tptr != NULL) { /********/ /* hit! */ /********/ if (Debug) fprintf (stdout, "Hit |%s|\n", tptr); TotalHitCount++; #ifdef ODS_EXTENDED if (OdsExtended) FileNamePathPtr = (char*)CgiLibUrlEncodeFileName (FileNamePath, NULL, 0, FormatLikeVms, !FormatLikeVms); else #endif /* ODS_EXTENDED */ FileNamePathPtr = FileNamePath; if (!HitCount++) { /*************************************/ /* first hit, entire document anchor */ /*************************************/ UnixTime = decc$fix_time (&FileXabDat.xab$q_rdt); UnixTmPtr = localtime (&UnixTime); if (!strftime (LastModifiedTime, sizeof(LastModifiedTime), LastModifiedTimeFormatPtr, UnixTmPtr)) strcpy (LastModifiedTime, "**ERROR**"); /* true for non-VAR record formats, almost true for those :^) */ if (FileXabFhc.xab$l_ebk) ByteCount = ((FileXabFhc.xab$l_ebk - 1) * 512) + FileXabFhc.xab$w_ffb; else ByteCount = FileXabFhc.xab$w_ffb; if (ByteCount < 1000) sprintf (FileSize, "%d bytes", ByteCount); else if (ByteCount % 1000 < 500) sprintf (FileSize, "%d kB", ByteCount/1000); else sprintf (FileSize, "%d kB", (ByteCount/1000)+1); if (!ListStarted) { ListStarted = true; fputs ("
    \n", stdout); } fprintf (stdout, "
  1. %s \ HTML  %s  %s\n", TargetPtr, FileNamePathPtr, DocumentName, LastModifiedTime, FileSize); if (DocumentOnly) break; fprintf (stdout, "
      \n"); } /***********************/ /* file extract anchor */ /***********************/ sptr = String; strcpy (sptr, "
    1. "); sptr += 25; /* absorb leading white-space */ for (cptr = Text; *cptr && (*cptr == ' ' || *cptr == '\t'); cptr++); /* copy the record up to the first character of the search string */ sptr += QueryHtmlEscape (cptr, tptr-cptr, sptr, -1); /* add the HTML anchor */ sptr += sprintf (sptr, "", TargetPtr, ExtractScriptNamePtr, FileNamePathPtr, UrlEncodedSearchString, FormText, FormHtml, FormPlainPtr, HitCount); /* matched string, highlighted within the anchor */ sptr += QueryHtmlEscape (tptr, MatchedLength, sptr, -1); strcpy (sptr, ""); sptr += 4; /* rest of record after the matched search string */ sptr += QueryHtmlEscape (tptr+MatchedLength, -1, sptr, -1); strcpy (sptr, "\n"); sptr += 8; if (Debug) fprintf (stdout, "String |%s|\n", String); fputs (String, stdout); } } if (Debug) fprintf (stdout, "+++++3 %d\n", TagCharCount); /***************/ /* end of file */ /***************/ if (status == RMS$_EOF) status = SS$_NORMAL; sys$close (&FileFab, 0, 0); if (VMSnok (status)) { if (!ListStarted) { ListStarted = true; fputs ("
        \n", stdout); } fprintf (stdout, "
      1. ERROR reading file (%%X%08.08X) \ %s \n", status, FileNamePath, ResFileName); } if (HitCount) { if (!DocumentOnly) fputs ("
      \n", stdout); fflush (stdout); FileHitCount++; } if (Debug) fprintf (stdout, "+++++4 %d\n", TagCharCount); if (TagCharCount & 1) { /* must have encountered an opening '<' without a closing '>' */ if (!ListStarted) { ListStarted = true; fputs ("
        \n", stdout); } fprintf (stdout, "
      1. HTML unbalanced <> in \ %s \n", TargetPtr, FileNamePath, FileNamePath, ResFileName); } RecordCount += RecordNumber; return (status); } /*****************************************************************************/ /* Search each record of what is presumed to be a plain-text file for the supplied string. When the first hit occurs output an HTML anchor for retrieving the entire file. For each hit output an HTML anchor to extract a specified range of record (lines) from the file. This search only checks for at least one match in a line before considering it a hit. */ SearchTextFile () { int idx, status, LastSectionRecordNumber = 1, RecordNumber = 0, ByteCount, HitCount = 0, MatchedLength; unsigned long UnixTime; char *cptr, *rptr, *sptr, *tptr, *CaseSensitivePtr = "", *FileNamePathPtr; char FileSize [32], LastModifiedTime [64], Record [MAX_RECORD_SIZE+1], String [MAX_RECORD_SIZE*2]; struct tm *UnixTmPtr; struct FAB FileFab; #ifdef ODS_EXTENDED struct NAML FileNaml; #endif /* ODS_EXTENDED */ struct RAB FileRab; struct XABDAT FileXabDat; struct XABFHC FileXabFhc; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "SearchTextFile() |%s|%s|\n", ResFileName, SearchString); FileFab = cc$rms_fab; FileFab.fab$b_fac = FAB$M_GET; FileFab.fab$b_shr = FAB$M_SHRGET | FAB$M_SHRPUT; FileFab.fab$l_xab = &FileXabDat; FileXabDat = cc$rms_xabdat; FileXabDat.xab$l_nxt = &FileXabFhc; FileXabFhc = cc$rms_xabfhc; #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 = ResFileName; FileNaml.naml$l_long_filename_size = ResFileNameLength; } else #endif /* ODS_EXTENDED */ { FileFab.fab$l_fna = ResFileName; FileFab.fab$b_fns = ResFileNameLength; } status = sys$open (&FileFab, 0, 0); if (Debug) fprintf (stdout, "sys$open() %%X%08.08X\n", status); if (VMSnok (status)) { if (status == RMS$_PRV) return (status); if (!ListStarted) { ListStarted = true; fputs ("
          \n", stdout); } fprintf (stdout, "
        1. ERROR opening file (%%X%08.08X) \ %s \n", status, FileNamePath, ResFileName); return (SS$_NORMAL); } FileRab = cc$rms_rab; FileRab.rab$l_fab = &FileFab; /* 2 buffers and read ahead performance option */ FileRab.rab$b_mbf = 2; FileRab.rab$l_rop = RAB$M_RAH; FileRab.rab$l_ubf = Record; FileRab.rab$w_usz = sizeof(Record)-1; if (VMSnok (status = sys$connect (&FileRab, 0, 0))) { if (Debug) fprintf (stdout, "sys$connect() %%X%08.08X\n", status); sys$close (&FileFab, 0, 0); return (status); } if (CaseSensitive) CaseSensitivePtr = "&case=yes"; /**********************/ /* search all records */ /**********************/ while (VMSok (status = sys$get (&FileRab, 0, 0))) { RecordNumber++; Record[FileRab.rab$w_rsz] = '\0'; /* terminate on any carriage control that may be in the record */ for (rptr = Record; *rptr && *rptr != '\r' && *rptr != '\n'; rptr++); *rptr = '\0'; /* if necessary generate document name from first non-blank line */ if (!DocumentName[0]) GenerateDocumentName (Record, DocumentName); if (*(rptr = Record)) while (*rptr && isspace(*rptr)) rptr++; if (!*rptr) { /* the line contained none, or only white-space characters */ LastSectionRecordNumber = RecordNumber + 1; continue; } tptr = SearchTextString (Record, SearchString, CaseSensitive, true, &MatchedLength); if (tptr != NULL) { /********/ /* hit! */ /********/ if (Debug) fprintf (stdout, "Hit |%s|\n", tptr); TotalHitCount++; #ifdef ODS_EXTENDED if (OdsExtended) FileNamePathPtr = (char*)CgiLibUrlEncodeFileName (FileNamePath, NULL, 0, FormatLikeVms, !FormatLikeVms); else #endif /* ODS_EXTENDED */ FileNamePathPtr = FileNamePath; if (!HitCount++) { /*************************************/ /* first hit, entire document anchor */ /*************************************/ UnixTime = decc$fix_time (&FileXabDat.xab$q_rdt); UnixTmPtr = localtime (&UnixTime); if (!strftime (LastModifiedTime, sizeof(LastModifiedTime), LastModifiedTimeFormatPtr, UnixTmPtr)) strcpy (LastModifiedTime, "**ERROR**"); /* true for non-VAR record formats, almost true for those :^) */ if (FileXabFhc.xab$l_ebk) ByteCount = ((FileXabFhc.xab$l_ebk - 1) * 512) + FileXabFhc.xab$w_ffb; else ByteCount = FileXabFhc.xab$w_ffb; if (ByteCount < 1000) sprintf (FileSize, "%d bytes", ByteCount); else if (ByteCount % 1000 < 500) sprintf (FileSize, "%d kB", ByteCount/1000); else sprintf (FileSize, "%d kB", (ByteCount/1000)+1); if (!ListStarted) { ListStarted = true; fputs ("
            \n", stdout); } fprintf (stdout, "
          1. \ %s \ TEXT  %s  %s\n", TargetPtr, ExtractScriptNamePtr, FileNamePathPtr, UrlEncodedSearchString, CaseSensitivePtr, FormText, FormHtml, RequeryPtr, DocumentName, LastModifiedTime, FileSize); if (DocumentOnly) break; fprintf (stdout, "
              \n"); } /***********************/ /* file extract anchor */ /***********************/ sptr = String; strcpy (sptr, "
            1. "); sptr += 25; /* absorb leading white-space */ for (cptr = Record; *cptr && (*cptr == ' ' || *cptr == '\t'); cptr++); /* copy the record up to the first character of the search string */ sptr += CgiLibHtmlEscape (cptr, tptr-cptr, sptr, -1); /* add the HTML anchor */ sptr += sprintf (sptr, "", RecordNumber, RecordNumber+ExtractNumberOfRecords-1, FormText, FormHtml); else sptr += sprintf (sptr, "&start=%d&end=%d%s%s\">", LastSectionRecordNumber, RecordNumber+ExtractNumberOfRecords-1, FormText, FormHtml); /* matched string, highlighted within the anchor */ sptr += CgiLibHtmlEscape (tptr, MatchedLength, sptr, -1); strcpy (sptr, ""); sptr += 4; /* rest of record after the matched search string */ sptr += CgiLibHtmlEscape (tptr+MatchedLength, -1, sptr, -1); strcpy (sptr, "\n"); sptr += 8; if (Debug) fprintf (stdout, "String |%s|\n", String); fputs (String, stdout); } } /***************/ /* end of file */ /***************/ if (status == RMS$_EOF) status = SS$_NORMAL; sys$close (&FileFab, 0, 0); if (VMSnok (status)) { if (!ListStarted) { ListStarted = true; fputs ("
                \n", stdout); } fprintf (stdout, "
              1. ERROR reading file (%%X%08.08X) \ %s \n", status, FileNamePath, ResFileName); } if (HitCount) { if (!DocumentOnly) fputs ("
              \n", stdout); fflush (stdout); FileHitCount++; } RecordCount += RecordNumber; return (status); } /*****************************************************************************/ /* Escape HTML forbidden characters such as '<', '>' and '&' - except when the '&' is the introducer for an HTML entity. Leave the entity intact. */ int QueryHtmlEscape ( char *string, int length, char *buf, int size ) { char *aptr, *cptr, *czptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "QueryHtmlEscape() |%s|\n", string); if (size < 0) size = 1024; /* somewhat arbitrary */ zptr = (sptr = buf) + size; if (length < 0) for (cptr = czptr = string; *czptr; czptr++); else czptr = (cptr = string) + length; for (; *cptr && cptr < czptr && sptr < zptr; cptr++) { if (*cptr == '&') { aptr = cptr + 1; if (*aptr == '#') aptr++; while (*aptr && !isspace(*aptr) && !ispunct(*aptr)) aptr++; if (*aptr == ';') { while (cptr < aptr && sptr < zptr) *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr; } else for (aptr = "&"; *aptr && sptr < zptr; *sptr++ = *aptr++); } else if (*cptr == '<') for (aptr = "<"; *aptr && sptr < zptr; *sptr++ = *aptr++); else if (*cptr == '>') for (aptr = ">"; *aptr && sptr < zptr; *sptr++ = *aptr++); else *sptr++ = *cptr; } *sptr = '\0'; length = sptr - buf; if (sptr >= zptr && length > 10) memcpy (buf, "[OVERFLOW]", 10); return (length); } /*****************************************************************************/ /* This function serves to generate a document name string (for use in the HTML tag) for a plain-text file from the first line containing alpha- numeric characters. Copy the string pointed to by 'sptr' into the string pointed to by 'NamePtr', compressing white-space. */ GenerateDocumentName ( char *sptr, char *nptr ) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "GenerateDocumentName() |%s|\n", sptr); /* skip leading non-alphanumerics */ while (*sptr && !isalnum(*sptr)) sptr++; while (*sptr) { /* copy alphanumeric element */ while (*sptr && !isspace(*sptr) && isalnum(*sptr)) *nptr++ = *sptr++; if (!*sptr) break; /* skip intervening/trailing non-alphanumerics */ while (*sptr && !isalnum(*sptr)) sptr++; if (!*sptr) break; /* add a single space between alphanumeric elements */ *nptr++ = ' '; } *nptr = '\0'; } /*****************************************************************************/ /* */ QueryForm () { char *cptr; char EscapedPath [ODS_MAX_FILE_NAME_LENGTH+1]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "QueryForm()\n"); FormatLikeVms = false; for (cptr = CgiPathInfoPtr; *cptr; cptr++); while (cptr > CgiPathInfoPtr && *cptr != '/') { if (*cptr == ';') FormatLikeVms = true; cptr--; } if (FormatLikeVms) CgiLibHtmlEscape (CgiPathTranslatedPtr, -1, EscapedPath, sizeof(EscapedPath)); else CgiLibHtmlEscape (CgiPathInfoPtr, -1, EscapedPath, sizeof(EscapedPath)); CgiLibResponseHeader (200, "text/html"); fprintf (stdout, "<!DOCTYPE html>\n\ <html>\n\ <head>\n\ <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n\ <meta name=\"generator\" content=\"%s\">\n\ <meta name=\"environment\" content=\"%s\">\n\ <title>Search %s\n\ \n\ %s\ \n\ \n", SoftwareID, CgiEnvironmentPtr, EscapedPath, DefaultStyle, StyleSheetPtr); fprintf (stdout, "
              Search %s
              \n", EscapedPath); fprintf (stdout, "
              \n\
              \n\  \n\  \n\ \n\

              Case sensitive? \n\ No\n\ Yes\n\
              Treat HTML as if plain-text? \n\ No\n\ Yes\n\
              Open hit in new page? \n\ No\n\ Yes\n\
              Extract this number of lines around a plain-text "hit": \n\ \n\

              \n\
              \n", CgiScriptNamePtr, CgiPathInfoPtr); fprintf (stdout, "
              \n\
              \n\ about\n\
              \n\
              \n", AboutPathPtr); fprintf (stdout, "\n\n"); } /****************************************************************************/ /* 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. */ BOOL 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); } /*****************************************************************************/