/*****************************************************************************/ /* 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 '' anchor that allows the specific hit to be jumped to with relative document syntax. The following tags to not have any content included: ,
, ,"- "WASD HyperText Services | " 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="About=/query/-/aboutquery.html" 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="About=/query/-/aboutquery.html;Other Information=/info/" 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: $ QUERY$PARAM = "/BUTTON=""About=/query/-/aboutquery.html""" $ QUERY$PARAM = QUERY$PARAM + "/PBGCOLOR/PLINK/PVLINK" $ QUERY$PARAM = QUERY$PARAM + "/PHLOCAL="" | ... | """ $ RUN HT_EXE:QUERY 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 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 /BUTTONS= string containing button labels/paths /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) /TEXT= complete list of comma separated TEXT file types /TIMEFMT= strftime() format string for last-modified time /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 BUILD DETAILS ------------- See BUILD_QUERY.COM procedure. COPYRIGHT --------- Copyright (C) 1996-2005 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 2. VERSION HISTORY (update SOFTWAREVN as well!) --------------- 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.
\n",
PageScheme[PS_HEADBORDER],
PageScheme[PS_HEADBORDER],
PageScheme[PS_HEADBGCOLOR]);
if (ButtonCount == 0 || !Top1Bottom2)
fprintf (stdout, " \n");
else
{
fprintf (stdout,
"
|
\n", PageScheme[PS_HEADLOCAL], String); } else { fprintf (stdout, "
\n\
\n\
%s\n\
\n\
\ \n\ *WASDquery\n\ \n\ | %s
\n\
\n\\n "); } else { fprintf (stdout, " \n\
\n\\n"); } ButtonBar (2); 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. */ boolean 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, boolean CaseSensitive, boolean 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\n\
\n\\n\ \n\ \n ", PageScheme[PS_HEADBORDER], PageScheme[PS_HEADBGCOLOR], PageScheme[PS_HEADTEXT]); } if (TotalHitCount) { if (FileCount == 1) FilePtr = "file"; else FilePtr = "files"; if (FileHitCount == 1) FileHitPtr = "file"; else FileHitPtr = "files"; if (TotalHitCount == 1) TotalHitPtr = "hit"; else TotalHitPtr = "hits"; if (DocumentOnly) { fprintf (stdout, "%d %s searched%s with %d %s hit.", FileCount, FilePtr, NotSearchedString, FileHitCount, FileHitPtr); } else { fprintf (stdout, "%d %s searched%s with %d %s hit, for a total of %d %s.", FileCount, FilePtr, NotSearchedString, FileHitCount, FileHitPtr, TotalHitCount, TotalHitPtr); } } /* This cannot be an 'else' statement. Something wierd after change of carriage-control from '\n' to '\n', and, unfortunately, after update to DEC C v5.0 (so I don't know which caused it!) If it is an else the module access violates! */ if (!TotalHitCount) { if (FileCount) { if (FileCount == 1) FilePtr = "file"; else FilePtr = "files"; fprintf (stdout, "%d %s searched%s, string not found.", FileCount, FilePtr, NotSearchedString); } else fprintf (stdout, "No files found!"); } if (PageScheme[PS_LAYOUT][0] == '2') fprintf (stdout, ""); fprintf (stdout, "\n \n\ \n"); } else { fprintf (stdout, "\n\
%s\n", Statistics); if (VMSnok (status)) { CgiLibResponseError (FI_LI, status, HTMLESC(CgiPathInfoPtr)); return (SS$_NORMAL); } if (PageScheme[PS_LAYOUT][0] == '2') { fprintf (stdout, "tag (if present) is used as the document name. */ SearchHtmlFile () { boolean 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
\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++; 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); /* 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 (TagCharCount & 1) { /* must have encountered an opening '<' without a closing '>' */ if (!ListStarted) { ListStarted = true; fputs ("
\n
\n", stdout); } fprintf (stdout, "
HTML problem, unbalanced <> \ in %s \n", 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
\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
\n", stdout); } fprintf (stdout, "
\n", ExtractScriptNamePtr, FileNamePathPtr, UrlEncodedSearchString, CaseSensitivePtr, FormText, FormHtml, DocumentName, LastModifiedTime, FileSize); if (DocumentOnly) break; fprintf (stdout, " - %s \ [TEXT %s %s]
\n"); } /***********************/ /* file extract anchor */ /***********************/ sptr = String; strcpy (sptr, "
- "); sptr += 4; /* 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); *sptr++ = '\n'; *sptr = '\0'; 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
\n", stdout); } fprintf (stdout, "
\n", stdout); fflush (stdout); FileHitCount++; } RecordCount += RecordNumber; return (status); } /*****************************************************************************/ /* This function serves to generate a document name string (for use in the HTML
ERROR reading file (%%X%08.08X) \ %s \n", status, FileNamePath, ResFileName); } if (HitCount) { if (!DocumentOnly) fputs ("
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\ \n", SoftwareID, CgiEnvironmentPtr, TextFileTypesPtr, HtmlFileTypesPtr, EscapedPath, PageScheme[PS_BODYTAG]); ThereHasBeenOutput = true; if (PageScheme[PS_LAYOUT][0] == '2') { fprintf (stdout, "%s\n\ Search %s\n\ \n\Search %s \n\ \n\
\ \n\ *WASDquery\n\ \n\\n", PageScheme[PS_HEADLOCAL], EscapedPath); } else { fprintf (stdout, "
\n\
\n", PageScheme[PS_HEADBORDER], PageScheme[PS_HEADPADDING], PageScheme[PS_HEADBGCOLOR], PageScheme[PS_HEADTEXT], EscapedPath, PageScheme[PS_HEADTEXT], PageScheme[PS_HEADLOCAL]); } fprintf (stdout, "\n\ \n\ \n\ Search %s\n\ \n\ %s
\ \n\ *WASDquery\n\ \n\\n\
\n", CgiScriptNamePtr, CgiPathInfoPtr); ButtonBar (2); 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. */ 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); } /*****************************************************************************/