/*****************************************************************************/
/*
[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!):
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-2021 Mark G.Daniel
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
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. carriage-control with single ,
still acceptable for HTTP, slightly more efficient
24-MAY-1995 MGD v2.2.0, minor changes for AXP compatibility
21-APR-1995 MGD v2.1.1, added 'FormWhat'
27-MAR-1995 MGD v2.1.0, modifications to CGI interface
05-DEC-1994 MGD v2.0.0, major revision, URL mapping, CGI-like interface
10-JUN-1994 MGD v1.0.0, initial development
*/
/*****************************************************************************/
#define SOFTWAREVN "4.2.0"
#define SOFTWARENM "QUERY"
#define SOFTWARECR "Copyright (C) 1996-2020 Mark G.Daniel"
#ifdef __ALPHA
# define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN
#endif
#ifdef __ia64
# define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN
#endif
#ifdef __VAX
# define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN
#endif
#ifdef __x86_64
# define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN
#endif
/* standard C header files */
#include
#include
#include
#include
#include
#include
/* VMS-related header files */
#include
#include
#include
#include
#include
#include
#include
/* application header files */
#include "enamel.h"
#include "query.h"
#include
/* mainly to allow easy use of the __unaligned directive */
#define ULONGPTR __unaligned unsigned long*
#define USHORTPTR __unaligned unsigned short*
#define INT64PTR __unaligned __int64*
#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 BOOL 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__
/* convenience macro, returns a pointer to calloc()ed HTML-escaped string */
#define HTMLESC(str) ((char*)CgiLibHtmlEscape(str,-1,NULL,-1))
char DefaultHtmlTypes [] = DEFAULT_HTML_TYPES,
DefaultStyle [] = DEFAULT_QUERY_STYLE,
DefaultTextTypes [] = DEFAULT_TEXT_TYPES,
Utility [] = "QUERY";
BOOL CaseSensitive,
Debug,
DocumentOnly,
ExactNumberOfRecords,
ThereHasBeenOutput,
ListStarted,
TreatHtmlAsPlain,
FormatLikeVms;
int ExtractNumberOfRecords,
KeyCount,
FileCount,
FileHitCount,
ResFileNameLength,
NotSearchedFileCount,
OdsExtended,
TotalHitCount,
RecordCount,
SearchStringLength;
char DocumentName [2048],
ExpFileName [ODS_MAX_FILE_NAME_LENGTH+1],
FileNamePath [ODS_MAX_FILE_NAME_LENGTH+1],
FormHtml [256],
FormPath [256],
FormText [256],
KeyName [16],
ResFileName [ODS_MAX_FILE_NAME_LENGTH+1],
SearchString [256],
SoftwareCopy [] = SOFTWARECR,
SoftwareID [48],
UrlEncodedFileNamePath [ODS_MAX_FILE_NAME_LENGTH+1],
UrlEncodedSearchString [256];
char *AboutPathPtr = DEFAULT_ABOUT_PATH,
*CgiEnvironmentPtr,
*CgiFormAboutPtr,
*CgiFormCasePtr,
*CgiFormExactPtr,
*CgiFormExtractPtr,
*CgiFormHitsPtr,
*CgiFormHtmlPtr,
*CgiFormPathPtr,
*CgiFormPlainPtr,
*CgiFormSearchPtr,
*CgiFormTextPtr,
*CgiFormTargetPtr,
*CgiFormWhatPtr,
*CgiKeyCountPtr,
*CgiKeyValuePtr,
*CgiPathInfoPtr,
*CgiPathTranslatedPtr,
*CgiQueryStringPtr,
*CgiRequestMethodPtr,
*CgiRequestUriPtr,
*CgiScriptNamePtr,
*CgiServerSoftwarePtr,
*CharsetPtr,
*CliCharsetPtr,
*ExtractScriptNamePtr,
*FormPlainPtr,
*HtmlFileTypesPtr = DefaultHtmlTypes,
*LastModifiedTimeFormatPtr = DEFAULT_STRFTIME_FORMAT,
*RequeryPtr,
*StyleSheetPtr = "",
*TargetPtr = "",
*TextFileTypesPtr = DefaultTextTypes;
struct FAB SearchFab;
struct NAM SearchNam;
#ifdef ODS_EXTENDED
struct NAML SearchNaml;
#endif /* ODS_EXTENDED */
/* required prototypes */
int QueryHtmlEscape (char*, int, char*, int);
char* SearchTextString (char*, char*, BOOL, BOOL, int*);
/*****************************************************************************/
/*
'argc/argv' are only required to support OSU, in particular CgiLibOsuInit().
*/
main
(
int argc,
char *argv[]
)
{
int status,
Count;
char *cptr;
/*********/
/* begin */
/*********/
sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CgiLibEnvironmentVersion());
if (getenv ("QUERY$DBUG") != NULL) Debug = true;
if (Debug)
{
CgiLibResponseHeader (200, "text/plain");
CgiLibEnvironmentSetDebug (Debug);
}
CgiLibEnvironmentInit (argc, argv, false);
CgiEnvironmentPtr = CgiLibEnvironmentName ();
GetParameters ();
CgiLibResponseSetCharset (CliCharsetPtr);
CgiLibResponseSetSoftwareID (SoftwareID);
CgiLibResponseSetErrorMessage ("Reported by WASDquery");
if (StyleSheetPtr[0])
{
cptr = calloc (1, 64+strlen(StyleSheetPtr));
sprintf (cptr, "\n",
StyleSheetPtr);
StyleSheetPtr = cptr;
}
#ifdef ODS_EXTENDED
OdsExtended = (GetVmsVersion() >= 72);
ENAMEL_NAML_SANITY_CHECK
#endif /* ODS_EXTENDED */
CgiServerSoftwarePtr = CgiLibVar ("WWW_SERVER_SOFTWARE");
CgiRequestMethodPtr = CgiLibVar ("WWW_REQUEST_METHOD");
if (strcmp (CgiRequestMethodPtr, "GET"))
{
CgiLibResponseHeader (501, "text/html");
fprintf (stdout, "Not implemented!\n");
return;
}
CgiScriptNamePtr = CgiLibVar ("WWW_SCRIPT_NAME");
CgiPathInfoPtr = CgiLibVar ("WWW_PATH_INFO");
CgiQueryStringPtr = CgiLibVar ("WWW_QUERY_STRING");
if (!CgiQueryStringPtr[0])
{
QueryForm ();
return;
}
/* script name and any path */
CgiRequestUriPtr = CgiLibVar ("WWW_REQUEST_URI");
for (cptr = CgiRequestUriPtr; *cptr && *cptr != '?'; cptr++);
RequeryPtr = calloc (1, cptr - CgiRequestUriPtr + 1);
memcpy (RequeryPtr, CgiRequestUriPtr, cptr - CgiRequestUriPtr);
/* kludge around a path not being MAP=ELLIPSIS */
cptr = CgiPathTranslatedPtr = CgiLibVar ("WWW_PATH_TRANSLATED");
while (*cptr)
{
if (cptr[0] == '|' && cptr[1] == '|' && cptr[2] == '|' && cptr[3] != '|')
{
cptr[0] = cptr[1] = cptr[2] = '.';
break;
}
cptr++;
}
CgiFormAboutPtr = CgiLibVar ("WWW_FORM_ABOUT");
CgiFormCasePtr = CgiLibVar ("WWW_FORM_CASE");
CgiFormExactPtr = CgiLibVar ("WWW_FORM_EXACT");
CgiFormExtractPtr = CgiLibVar ("WWW_FORM_EXTRACT");
CgiFormHitsPtr = CgiLibVar ("WWW_FORM_HITS");
CgiFormHtmlPtr = CgiLibVar ("WWW_FORM_HTML");
CgiFormPathPtr = CgiLibVar ("WWW_FORM_PATH");
CgiFormPlainPtr = CgiLibVar ("WWW_FORM_PLAIN");
CgiFormSearchPtr = CgiLibVar ("WWW_FORM_SEARCH");
CgiFormTargetPtr = CgiLibVar ("WWW_FORM_TARGET");
CgiFormTextPtr = CgiLibVar ("WWW_FORM_TEXT");
CgiFormWhatPtr = CgiLibVar ("WWW_FORM_WHAT");
CgiKeyCountPtr = CgiLibVar ("WWW_KEY_COUNT");
SearchString[0] = '\0';
KeyCount = atoi(CgiKeyCountPtr);
for (Count = 1; Count <= KeyCount; Count++)
{
sprintf (KeyName, "WWW_KEY_%d", Count);
CgiKeyValuePtr = CgiLibVar (KeyName);
if (SearchString[0]) strcat (SearchString, " ");
strcat (SearchString, CgiKeyValuePtr);
}
if (SearchString[0])
CgiFormSearchPtr = SearchString;
else
strcpy (SearchString, CgiFormSearchPtr);
if (strsame (CgiFormAboutPtr, "SEARCH", 6))
{
CgiLibResponseRedirect (AboutPathPtr);
exit (SS$_NORMAL);
}
if (CgiFormPathPtr[0])
{
/* path supplied indirectly via form field, redirect to translate */
CgiScriptNamePtr = CgiLibVar ("WWW_SCRIPT_NAME");
CgiQueryStringPtr = CgiLibVar ("WWW_QUERY_STRING");
CgiLibResponseRedirect ("%s%s%s%s",
CgiScriptNamePtr, CgiFormPathPtr,
CgiQueryStringPtr[0] ? "?" : "", CgiQueryStringPtr);
exit (SS$_NORMAL);
}
if (!CgiPathTranslatedPtr[0])
{
CgiLibResponseError (FI_LI, 0, "Search path not supplied.");
exit (SS$_NORMAL);
}
if (!CgiFormSearchPtr[0])
{
CgiLibResponseError (FI_LI, 0, "Search string not supplied.");
exit (SS$_NORMAL);
}
if (CgiFormExtractPtr[0] && !isdigit(CgiFormExtractPtr[0]))
{
CgiLibResponseError (FI_LI, 0, "Invalid number of lines to extract.");
exit (SS$_NORMAL);
}
ExtractNumberOfRecords = atoi(CgiFormExtractPtr);
if (!ExtractNumberOfRecords)
ExtractNumberOfRecords = DEFAULT_EXTRACT_RECORDS;
if (toupper(CgiFormExactPtr[0]) == 'Y')
ExactNumberOfRecords = true;
else
ExactNumberOfRecords = false;
if (toupper(CgiFormCasePtr[0]) == 'Y')
CaseSensitive = true;
else
CaseSensitive = false;
if (toupper(CgiFormHitsPtr[0]) == 'D')
DocumentOnly = true;
else
DocumentOnly = false;
if (toupper(CgiFormPlainPtr[0]) == 'Y')
{
TreatHtmlAsPlain = true;
FormPlainPtr = "&plain=yes";
}
else
{
TreatHtmlAsPlain = false;
FormPlainPtr = "";
}
if (CgiFormHtmlPtr[0])
{
HtmlFileTypesPtr = CgiFormHtmlPtr;
sprintf (FormHtml, "&html=%s", CgiFormHtmlPtr);
}
if (CgiFormTextPtr[0])
{
TextFileTypesPtr = CgiFormTextPtr;
sprintf (FormText, "&text=%s", CgiFormTextPtr);
}
if (ExtractScriptNamePtr == NULL)
{
if (CgiLibEnvironmentIsWasd() ||
CgiLibEnvironmentIsCgiPlus())
ExtractScriptNamePtr = DEFAULT_WASD_EXTRACT;
else
if (CgiLibEnvironmentIsOsu())
ExtractScriptNamePtr = DEFAULT_OSU_EXTRACT;
else
ExtractScriptNamePtr = DEFAULT_CGI_EXTRACT;
}
if (CgiFormTargetPtr[0])
{
TargetPtr = calloc (1, 32+strlen(CgiFormTargetPtr));
sprintf (TargetPtr, " target=\"%s\"", CgiFormTargetPtr);
}
SearchFiles ();
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 ("QUERY$PARAM")) == NULL)
{
/* 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 != NULL) *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, "/ABOUT=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
AboutPathPtr = cptr;
continue;
}
if (strsame (aptr, "/ADDHTML=", 7))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
sptr = malloc (strlen(HtmlFileTypesPtr)+strlen(cptr)+2);
strcpy (sptr, HtmlFileTypesPtr);
strcat (sptr, ",");
strcat (sptr, cptr);
HtmlFileTypesPtr = sptr;
continue;
}
if (strsame (aptr, "/ADDTEXT=", 7))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
sptr = malloc (strlen(TextFileTypesPtr)+strlen(cptr)+2);
strcpy (sptr, TextFileTypesPtr);
strcat (sptr, ",");
strcat (sptr, cptr);
TextFileTypesPtr = sptr;
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, "/EXTRACT=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
ExtractScriptNamePtr = cptr;
continue;
}
if (strsame (aptr, "/HTML=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
HtmlFileTypesPtr = cptr;
continue;
}
if (strsame (aptr, "/ODS5", 5))
{
OdsExtended = true;
continue;
}
if (strsame (aptr, "/NOODS5", 7))
{
OdsExtended = false;
continue;
}
if (strsame (aptr, "/STYLE=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
StyleSheetPtr = cptr;
continue;
}
if (strsame (aptr, "/TEXT=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
TextFileTypesPtr = cptr;
continue;
}
if (strsame (aptr, "/TIMEFMT=", 4))
{
for (cptr = aptr; *cptr && *cptr != '='; cptr++);
if (*cptr) cptr++;
LastModifiedTimeFormatPtr = cptr;
continue;
}
if (*aptr != '/')
{
fprintf (stdout, "%%%s-E-MAXPARM, too many parameters\n \\%s\\\n",
Utility, aptr);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
else
{
fprintf (stdout, "%%%s-E-IVQUAL, unrecognized qualifier\n \\%s\\\n",
Utility, aptr+1);
exit (STS$K_ERROR | STS$M_INHIB_MSG);
}
}
}
/*****************************************************************************/
/*
Search files in specification for specified string. Unless a file type
(extension) matches an HTML type then consider the file to be a plain-text
file. The two need to searched differently because of the HTML markup tags
compsrising an HTML file.
*/
SearchFiles ()
{
static int TimerElapsed = 1,
TimerCpu = 2,
TimerBio = 3,
TimerDio = 4;
static $DESCRIPTOR (ElapsedTimeFaoDsc, "!%T");
static $DESCRIPTOR (StatisticsFaoDsc,
"Elapsed: !AZ CPU: !2ZL:!2ZL.!2ZL I/O: !UL Disk: !UL \
Records (lines): !UL");
int status,
BioCount,
CpuTime,
DioCount,
ExpandedNameOffset,
ResultVersionLength;
unsigned long ResultFnb;
unsigned long ElapsedTime [2];
unsigned short Length;
char *cptr, *sptr,
*FilePtr,
*FileHitPtr,
*FileNamePathPtr,
*ResultNamePtr,
*ResultTypePtr,
*ResultVersionPtr,
*TotalHitPtr;
char ElapsedString [32],
EscapedPath [ODS_MAX_FILE_NAME_LENGTH+1],
EscapedSearchString [256],
NotSearchedString [32],
Statistics [256],
String [1024];
$DESCRIPTOR (ElapsedStringDsc, ElapsedString);
$DESCRIPTOR (StatisticsDsc, Statistics);
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "SearchFiles() |%s|%s|\n",
CgiPathTranslatedPtr, SearchString);
lib$init_timer (0);
ListStarted = false;
FormatLikeVms = false;
for (cptr = CgiPathInfoPtr; *cptr; cptr++);
while (cptr > CgiPathInfoPtr && *cptr != '/')
{
if (*cptr == ';')
{
FormatLikeVms = true;
break;
}
cptr--;
}
if (FormatLikeVms)
CgiLibHtmlEscape (CgiPathTranslatedPtr, -1,
EscapedPath, sizeof(EscapedPath));
else
{
if (!CgiPathInfoPtr[0]) CgiPathInfoPtr = "/";
CgiLibHtmlEscape (CgiPathInfoPtr, -1, EscapedPath, sizeof(EscapedPath));
}
if (SearchString[0])
{
SearchStringLength = strlen(SearchString);
CgiLibUrlEncode (SearchString, -1, UrlEncodedSearchString, -1);
CgiLibHtmlEscape (SearchString, -1,
EscapedSearchString, sizeof(EscapedSearchString));
}
else
{
SearchStringLength = 0;
EscapedSearchString[0] = UrlEncodedSearchString[0] = '\0';
}
sptr = String;
if (CgiFormWhatPtr[0])
sptr += sprintf (sptr, "Search %s for "%s"",
CgiFormWhatPtr, EscapedSearchString);
else
sptr += sprintf (sptr, "Search %s for "%s"",
EscapedPath, EscapedSearchString);
SearchFab = cc$rms_fab;
SearchFab.fab$l_dna = "*.*;0";
SearchFab.fab$b_dns = 5;
#ifdef ODS_EXTENDED
if (OdsExtended)
{
SearchFab.fab$l_fna = (char*)-1;
SearchFab.fab$b_fns = 0;
SearchFab.fab$l_nam = (struct namdef*)&SearchNaml;
ENAMEL_RMS_NAML(SearchNaml)
SearchNaml.naml$l_long_filename = CgiPathTranslatedPtr;
SearchNaml.naml$l_long_filename_size = strlen(CgiPathTranslatedPtr);
SearchNaml.naml$l_long_expand = ExpFileName;
SearchNaml.naml$l_long_expand_alloc = sizeof(ExpFileName)-1;
SearchNaml.naml$l_long_result = ResFileName;
SearchNaml.naml$l_long_result_alloc = sizeof(ResFileName)-1;
}
else
#endif /* ODS_EXTENDED */
{
SearchFab.fab$l_fna = CgiPathTranslatedPtr;
SearchFab.fab$b_fns = strlen(CgiPathTranslatedPtr);
SearchFab.fab$l_nam = &SearchNam;
SearchNam = cc$rms_nam;
SearchNam.nam$l_esa = ExpFileName;
SearchNam.nam$b_ess = ODS2_MAX_FILE_NAME_LENGTH;
SearchNam.nam$l_rsa = ResFileName;
SearchNam.nam$b_rss = ODS2_MAX_FILE_NAME_LENGTH;
}
if (VMSnok (status = sys$parse (&SearchFab, 0, 0)))
{
CgiLibResponseError (FI_LI, status, HTMLESC(CgiPathInfoPtr));
return (status);
}
if (Debug) fprintf (stdout, "ExpFileName |%s|\n", ExpFileName);
/* establish the base for generated file name used in the file path */
#ifdef ODS_EXTENDED
if (OdsExtended)
sptr = SearchNaml.naml$l_long_name - 1;
else
#endif /* ODS_EXTENDED */
sptr = SearchNam.nam$l_name - 1;
cptr = ExpFileName;
while (cptr < sptr)
{
if (*cptr == '*' || *cptr == '%' ||
(cptr[0] == '.' && cptr[1] == '.' && cptr[2] == '.'))
{
if (cptr[0] != '.' || cptr[1] != '.' || cptr[2] != '.') cptr--;
break;
}
cptr++;
}
while (cptr > ExpFileName && *cptr != '[' && *cptr != '.' && *cptr != ']')
cptr--;
ExpandedNameOffset = cptr - ExpFileName + 1;
/* establish the base for generated file path */
sptr = FileNamePath;
cptr = CgiPathInfoPtr;
while (*cptr)
{
if (*cptr == '*' || *cptr == '%')
{
cptr--;
sptr--;
break;
}
if (cptr[0] == '.' && cptr[1] == '.' && cptr[2] == '.') break;
*sptr++ = *cptr++;
}
if ((cptr[0] == '.' && cptr[1] == '.' && cptr[2] == '.') &&
sptr[-1] != '/' && sptr[-1] != '*' && sptr[-1] != '%')
*sptr++ = '/';
else
{
while (sptr > FileNamePath && *sptr != '/') sptr--;
*sptr++;
}
*sptr = '\0';
FileNamePathPtr = sptr;
if (Debug) fprintf (stdout, "FileNamePath |%s|\n", FileNamePath);
/*************************/
/* begin the page output */
/*************************/
CgiLibResponseHeader (200, "text/html");
fprintf (stdout,
"\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
%s\n\
\n\
%s\
\n\
\n",
SoftwareID, SoftwareCopy,
CgiEnvironmentPtr,
TextFileTypesPtr,
HtmlFileTypesPtr,
String,
DefaultStyle,
StyleSheetPtr);
fprintf (stdout, "
%s
\n", String);
fflush (stdout);
ThereHasBeenOutput = true;
/***************/
/* file search */
/***************/
for (;;)
{
status = sys$search (&SearchFab, 0, 0);
if (Debug) fprintf (stdout, "sys$search() %%X%08.08X\n", status);
if (status == RMS$_PRV) continue;
if (VMSnok (status)) break;
#ifdef ODS_EXTENDED
if (OdsExtended)
{
ResultFnb = SearchNaml.naml$l_fnb;
ResultNamePtr = SearchNaml.naml$l_long_name;
ResultTypePtr = SearchNaml.naml$l_long_type;
ResultVersionPtr = SearchNaml.naml$l_long_ver;
ResultVersionLength = SearchNaml.naml$l_long_ver_size;
ResFileNameLength = SearchNaml.naml$l_long_ver -
SearchNaml.naml$l_long_result;
}
else
#endif /* ODS_EXTENDED */
{
ResultFnb = SearchNam.nam$l_fnb;
ResultNamePtr = SearchNam.nam$l_name;
ResultTypePtr = SearchNam.nam$l_type;
ResultVersionPtr = SearchNam.nam$l_ver;
ResultVersionLength = SearchNam.nam$b_ver;
ResFileNameLength = SearchNam.nam$l_ver -
SearchNam.nam$l_rsa;
}
if (FormatLikeVms)
{
ResultVersionPtr[ResultVersionLength] = '\0';
ResFileNameLength += ResultVersionLength;
}
else
ResultVersionPtr[0] = '\0';
if (Debug)
fprintf (stdout, "ResFileName %d |%s|\n",
ResFileNameLength, ResFileName);
/* generate a file path from the base file path and base file name */
sptr = FileNamePathPtr;
cptr = ResFileName + ExpandedNameOffset;
while (*cptr && *cptr != ']' && cptr < ResultNamePtr)
{
if (CgiLibEnvironmentIsWasd() &&
!memcmp (cptr, "000000.", 7) ||
!memcmp (cptr, "000000]", 7)) cptr += 7;
if (*cptr == '.')
{
*sptr++ = '/';
cptr++;
}
else
*sptr++ = tolower(*cptr++);
}
if (*cptr == ']')
{
cptr++;
*sptr++ = '/';
}
while (*cptr) *sptr++ = tolower(*cptr++);
*sptr = '\0';
if (Debug) fprintf (stdout, "FileNamePath |%s|\n", FileNamePath);
if (SameFileType (HtmlFileTypesPtr, ResultTypePtr+1))
{
/* if HTML document's can't be resolved then use file name */
#ifdef ODS_EXTENDED
if (OdsExtended)
cptr = (char*)CgiLibUrlEncodeFileName (ResultNamePtr, NULL, 0,
FormatLikeVms,
!FormatLikeVms);
else
#endif /* ODS_EXTENDED */
cptr = ResultNamePtr;
sprintf (DocumentName,
"%s [file name]",
cptr);
if (TreatHtmlAsPlain)
{
if (VMSok (status = SearchTextFile ()))
FileCount++;
else
if (status != RMS$_PRV &&
status != RMS$_WLK)
{
CgiLibResponseError (FI_LI, status, FileNamePath);
return (SS$_NORMAL);
}
}
else
{
if (VMSok (status = SearchHtmlFile ()))
FileCount++;
else
if (status != RMS$_PRV &&
status != RMS$_WLK)
{
CgiLibResponseError (FI_LI, status, FileNamePath);
return (SS$_NORMAL);
}
}
}
else
if (SameFileType (TextFileTypesPtr, ResultTypePtr+1))
{
#ifdef ODS_EXTENDED
if (OdsExtended)
cptr = (char*)CgiLibUrlEncodeFileName (ResultNamePtr, NULL, 0,
FormatLikeVms,
!FormatLikeVms);
else
#endif /* ODS_EXTENDED */
cptr = ResultNamePtr;
strcpy (DocumentName, cptr);
if (VMSok (status = SearchTextFile ()))
FileCount++;
else
if (status != RMS$_PRV &&
status != RMS$_WLK)
{
CgiLibResponseError (FI_LI, status, FileNamePath);
return (SS$_NORMAL);
}
}
else
NotSearchedFileCount++;
if (!FormatLikeVms)
{
/* restore the version delimiter */
ResultVersionPtr[0] = ';';
}
}
if (Debug) fprintf (stdout, "sys$search() %%X%08.08X\n", status);
/*******************/
/* end file search */
/*******************/
/* if its a search list treat directory not found as if file not found */
if ((ResultFnb & NAM$M_SEARCH_LIST) && status == RMS$_DNF)
status = RMS$_FNF;
if (status == RMS$_FNF || status == RMS$_NMF) status = SS$_NORMAL;
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, HTMLESC(CgiPathInfoPtr));
return (SS$_NORMAL);
}
/*********************/
/* results of search */
/*********************/
lib$stat_timer (&TimerElapsed, &ElapsedTime, 0);
lib$stat_timer (&TimerCpu, &CpuTime, 0);
lib$stat_timer (&TimerBio, &BioCount, 0);
lib$stat_timer (&TimerDio, &DioCount, 0);
sys$fao (&ElapsedTimeFaoDsc, &Length, &ElapsedStringDsc, &ElapsedTime);
ElapsedString[Length] = '\0';
sys$fao (&StatisticsFaoDsc, &Length, &StatisticsDsc,
ElapsedString+3, CpuTime/6000, CpuTime/100, CpuTime%100,
BioCount, DioCount, RecordCount);
Statistics[Length] = '\0';
if (!NotSearchedFileCount)
NotSearchedString[0] = '\0';
else
if (NotSearchedFileCount == 1)
strcpy (NotSearchedString, " (1 not)");
else
sprintf (NotSearchedString, " (%d not)", NotSearchedFileCount);
if (ListStarted) fputs ("\n", stdout);
fputs ("\n",
RequeryPtr, AboutPathPtr, Statistics);
if (VMSnok (status))
{
CgiLibResponseError (FI_LI, status, HTMLESC(CgiPathInfoPtr));
return (SS$_NORMAL);
}
fprintf (stdout, "\n\n");
return (status);
}
/*****************************************************************************/
/*
This function accepts a comma-separated list of (possibly wildcarded) file
types (extensions, e.g. "TXT,TEXT,COM,C,PAS,FOR,RPT*") and a VMS file type
(e.g. ".TXT;", ".TXT", "TXT"). Returns true if the file type is in the list,
false if not.
*/
BOOL SameFileType
(
char *TypeList,
char *TypePtr
)
{
char ch;
char *cptr, *sptr, *zptr;
char FileType [256];
/*********/
/* begin */
/*********/
if (Debug) fprintf (stdout, "SameFileType() |%s|%s|\n", FileType, TypeList);
zptr = (sptr = FileType) + sizeof(FileType)-1;
for (cptr = TypePtr;
*cptr && *cptr != ';' && sptr < zptr;
*sptr++ = *cptr++);
*sptr = '\0';
cptr = TypeList;
while (*cptr)
{
for (sptr = cptr; *sptr && *sptr != ','; sptr++);
ch = *sptr;
*sptr = '\0';
if (Debug) fprintf (stdout, "|%s|%s|\n", FileType, cptr);
if ((SearchTextString (FileType, cptr, false, false, NULL)) != NULL)
{
*sptr = ch;
return (true);
}
if (*sptr = ch) sptr++;
cptr = sptr;
}
return (false);
}
/*****************************************************************************/
/*
String search allowing wildcard "*" (matching any multiple characters) and "%"
(matching any single character). Returns NULL if not found or a pointer to
start of matched string. Setting 'ImpliedWildcards' means the 'SearchFor'
string is processed as if enclosed by '*' wildcard characters.
*/
char* SearchTextString
(
char *SearchIn,
char *SearchFor,
BOOL CaseSensitive,
BOOL ImpliedWildcards,
int *MatchedLengthPtr
)
{
char *cptr, *sptr, *inptr,
*RestartCptr,
*RestartInptr,
*MatchPtr;
/*********/
/* begin */
/*********/
if (Debug)
fprintf (stdout, "SearchTextString() |%s|%s|\n", SearchIn, SearchFor);
if (MatchedLengthPtr != NULL) *MatchedLengthPtr = 0;
if (!*(cptr = SearchFor)) return (NULL);
inptr = MatchPtr = SearchIn;
if (ImpliedWildcards)
{
/* skip leading text up to first matching character (if any!) */
if (*cptr != '*' && *cptr != '%')
{
if (CaseSensitive)
while (*inptr && *inptr != *cptr) inptr++;
else
while (*inptr && toupper(*inptr) != toupper(*cptr)) inptr++;
if (Debug && !*inptr) fprintf (stdout, "1. NOT matched!\n");
if (!*inptr) return (NULL);
cptr++;
MatchPtr = inptr++;
}
}
for (;;)
{
if (CaseSensitive)
{
while (*cptr && *inptr && *cptr == *inptr)
{
cptr++;
inptr++;
}
}
else
{
while (*cptr && *inptr && toupper(*cptr) == toupper(*inptr))
{
cptr++;
inptr++;
}
}
if (ImpliedWildcards)
{
if (!*cptr)
{
if (Debug) fprintf (stdout, "1. matched!\n");
if (MatchedLengthPtr != NULL) *MatchedLengthPtr = inptr - MatchPtr;
return (MatchPtr);
}
}
else
{
if (!*cptr && !*inptr)
{
if (Debug) fprintf (stdout, "2. matched!\n");
if (MatchedLengthPtr != NULL) *MatchedLengthPtr = inptr - MatchPtr;
return (MatchPtr);
}
if (*cptr != '*' && *cptr != '%')
{
if (Debug && !*inptr) fprintf (stdout, "3. NOT matched!\n");
return (NULL);
}
}
if (*cptr != '*' && *cptr != '%')
{
if (!*inptr)
{
if (Debug) fprintf (stdout, "4. NOT matched!\n");
return (NULL);
}
cptr = SearchFor;
MatchPtr = ++inptr;
continue;
}
if (*cptr == '%')
{
/* single char wildcard processing */
if (!*inptr) break;
cptr++;
inptr++;
continue;
}
/* asterisk wildcard matching */
while (*cptr == '*') cptr++;
/* an asterisk wildcard at end matches all following */
if (!*cptr)
{
if (Debug) fprintf (stdout, "5. matched!\n");
while (*inptr) inptr++;
if (MatchedLengthPtr != NULL) *MatchedLengthPtr = inptr - MatchPtr;
return (MatchPtr);
}
/* note the current position in the string (first after the wildcard) */
RestartCptr = cptr;
for (;;)
{
/* find first char in SearchIn matching char after wildcard */
if (CaseSensitive)
while (*inptr && *cptr != *inptr) inptr++;
else
while (*inptr && toupper(*cptr) != toupper(*inptr)) inptr++;
/* if did not find matching char in SearchIn being searched */
if (Debug && !*inptr) fprintf (stdout, "6. NOT matched!\n");
if (!*inptr) return (NULL);
/* note the current position in SearchIn being searched */
RestartInptr = inptr;
/* try to match the remainder of the string and SearchIn */
if (CaseSensitive)
{
while (*cptr && *inptr && *cptr == *inptr)
{
cptr++;
inptr++;
}
}
else
{
while (*cptr && *inptr && toupper(*cptr) == toupper(*inptr))
{
cptr++;
inptr++;
}
}
/* if reached the end of both string and SearchIn - match! */
if (ImpliedWildcards)
{
if (!*cptr)
{
if (Debug) fprintf (stdout, "7. matched!\n");
if (MatchedLengthPtr != NULL)
*MatchedLengthPtr = inptr - MatchPtr;
return (MatchPtr);
}
}
else
{
if (!*cptr && !*inptr)
{
if (Debug) fprintf (stdout, "8. matched!\n");
if (MatchedLengthPtr != NULL)
*MatchedLengthPtr = inptr - MatchPtr;
return (MatchPtr);
}
}
/* break to the external loop if we encounter another wildcard */
if (*cptr == '*' || *cptr == '%') break;
/* lets have another go */
cptr = RestartCptr;
/* starting the character following the previous attempt */
inptr = MatchPtr = RestartInptr + 1;
}
}
}
/*****************************************************************************/
/*
Search an HTML marked up file. Simply count the number of '<' and '>'
characters, which should be balanced, and when not inside an HTML markup tag
search the text. As HTML files cannot easily have text extracted from within
them without the results being unpredictable simply return the document as
having the search string hit. The HTML tag (if present) is used as
the document name.
*/
SearchHtmlFile ()
{
BOOL InsideApplet = false,
InsideComment = false,
InsideHead = false,
InsideScript = false,
InsideServer = false,
InsideStyle = false,
InsideTitle = false;
int status,
ByteCount,
HitCount = 0,
MatchedLength,
RecordNumber = 0,
TagCharCount = 0;
unsigned long UnixTime;
char ch;
char *cptr, *dptr, *rptr, *sptr, *tptr,
*CaseSensitivePtr = "",
*FileNamePathPtr;
char FileSize [32],
LastModifiedTime [64],
Record [MAX_RECORD_SIZE+1],
String [MAX_RECORD_SIZE*2],
Text [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, "SearchHtmlFile() |%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;
FileFab.fab$l_fop = FAB$M_NAM;
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,
"
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 */
/**********************/
if (Debug) fprintf (stdout, "+++++0 %d\n", TagCharCount);
while (VMSok (status = sys$get (&FileRab, 0, 0)))
{
RecordNumber++;
if (!FileRab.rab$w_rsz) continue;
Record[FileRab.rab$w_rsz] = '\0';
if (Debug)
fprintf (stdout, "Record |%s|\n%d %d %d %d %d %d %d %d\n",
Record, TagCharCount, InsideApplet, InsideComment,
InsideHead, InsideScript, InsideServer, InsideStyle,
InsideTitle);
if (Debug) fprintf (stdout, "+++++1 %d\n", TagCharCount);
/* terminate on any carriage control that may be in the record */
for (rptr = Record; *rptr && *rptr != '\r' && *rptr != '\n'; rptr++);
*rptr = '\0';
/* continue if empty line */
if (!Record[0]) continue;
/**************************************/
/* retrieve text not inside HTML tags */
/**************************************/
tptr = Text;
rptr = Record;
while (*rptr)
{
if (InsideComment)
{
if (rptr[0] == '-' && rptr[1] == '-' && rptr[2] == '>')
{
InsideComment = false;
rptr += 3;
}
else
rptr++;
continue;
}
if (*rptr == '<' && *(ULONGPTR)rptr == '\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,
"
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,
"
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,
"
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,
"\n\
\n\
\n\
\n\
\n\
\n\
Search %s\n\
\n\
%s\
\n\
\n",
SoftwareID,
CgiEnvironmentPtr,
EscapedPath,
DefaultStyle,
StyleSheetPtr);
fprintf (stdout, "
Search %s
\n", EscapedPath);
fprintf (stdout,
"
\n\
\n\
\n",
CgiScriptNamePtr, CgiPathInfoPtr);
fprintf (stdout,
"\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);
}
/*****************************************************************************/