/*****************************************************************************/ /* wasDOC.C WASD document system. Yet Another Markup Language (YAML). In WASD's inimitable style of course :-/ A CLI/CGI/CGIplus utility to read directories containing files and output them as HTML documents. Suitable for VMS Apache, OSU and of course WASD. Directives use an embedded vertical bar symbol (Vbar). To escape a literal Vbar prefix it with a backslash. In between the Vbars are characters interpreted by the WASD document processor. See RENDER.C for a more detailed description of tag syntax. At startup wasDOC reads all .WASDOC files in the directory, assembling an in-memory copy of the text of those files, and from that creating an in-memory HTML representation of the content. As a CGIplus script, at a maximum default periuod of once a minute, each .WASDOC file is checked for modification since the document was processed and if modified the document is recreated. Table of content is generated by major and minor headings in the order processed. A referencing mechanism provides internal and external links. Use /cgi-bin/wasdoc/wasd_root/src/wasdoc/doc/ to view the wasDOC documentation. Alternatively, access /wasd_root/src/wasdoc/doc/index.html to access a static version of the same document. Copyright (C) 2019 Mark G.Daniel -------------------------------- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software 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 --------------- 14-JUL-2019 MGD v1.1.0, Bonne Fête Nationale expand conditionals bugfix; broken nested conditionals 28-JUN-2019 MGD v1.0.0, initial release 21-FEB-2019 MGD initial development */ /*****************************************************************************/ #define COPYRIGHT_DATE "2019" #define SOFTWAREVN "1.1.0" #define SOFTWARENM "WASDOC" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # define SOFTWAREID SOFTWARENM " VAX-" SOFTWAREVN #endif #include "wasdoc.h" #include #include #include int dbug, isCgi, isCgiPlus, CliChunk; char *CliDocDir, *CliIndex, *CliOutput; char CopyrightDate [] = COPYRIGHT_DATE, DefaultType [64] = DEFAULT_FILE_TYPE, SoftwareID [64], SoftwareVersion [] = SOFTWAREVN, Utility [] = "WASDOC"; /*****************************************************************************/ /* */ int main (int argc, char *argv[]) { sprintf (SoftwareID, "%s (%s)", SOFTWAREID, CGILIB_SOFTWAREID); CgiLibEnvironmentInit (argc, argv, 0); isCgi = CgiLibEnvironmentIsWasd() || (isCgiPlus = CgiLibEnvironmentIsCgiPlus()) || CgiLibEnvironmentIsApache() || CgiLibEnvironmentIsOsu(); if (isCgi) CgiDoc (); else CliDoc (argc, argv); } /*****************************************************************************/ /* Provide the infrastructure for the |insight@| facility. NOTE: writes directly to . */ void wasDocInsightAt (struct wasdoc_st *docptr) { char *cptr, *hptr, *sptr, *tptr, *zptr; if (!docptr->isDynamic || !docptr->insight) return; if (docptr->chunked > 0) { /* document provided in "chunks", check this chunk has the buttons */ for (hptr = docptr->html; *hptr; hptr++) { if (*hptr != '<' || !MATCH4 (hptr, "", 30)) continue; break; } /* if not found in this section */ if (!*hptr) return; } fprintf (stdout, "\n\n", docptr->ftotal, docptr->tlength, docptr->hlength, docptr->statistics); } /*****************************************************************************/ /* Initialise and return a document structure. */ struct wasdoc_st* InitDoc ( char *uri, char *pchunk, char *pinfo, char *dname ) { char *cptr, *sptr, *zptr; struct wasdoc_st *docptr; docptr = calloc (1, sizeof(struct wasdoc_st)); if (!docptr) exit (vaxc$errno); if (uri) { zptr = (sptr = docptr->uri) + sizeof(docptr->uri)-1; for (cptr = uri; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } if (pchunk && *pchunk) { docptr->chunked = 1; docptr->pchunk = atoi(pchunk); } if (pinfo) { zptr = (sptr = docptr->pinfo) + sizeof(docptr->pinfo)-1; for (cptr = pinfo; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } zptr = (sptr = docptr->dname) + sizeof(docptr->dname)-1; for (cptr = dname; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; zptr = (sptr = docptr->ftype) + sizeof(docptr->ftype)-1; for (cptr = DefaultType; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; docptr->ftlength = sptr - docptr->ftype; docptr->setToc2 = DEFAULT_TOC2; docptr->setTocCols = DEFAULT_TOC_COLS; docptr->setIdxCols = DEFAULT_IDX_COLS; docptr->setNavigate = DEFAULT_NAVIGATE; docptr->setPaginate = DEFAULT_PAGINATE; strcpy (docptr->setTocForm, DEFAULT_TOC_SCHEMA); strcpy (docptr->iconBack, DEFAULT_ICON_BACK); strcpy (docptr->iconForw, DEFAULT_ICON_FORW); strcpy (docptr->iconNext, DEFAULT_ICON_NEXT); strcpy (docptr->iconPrev, DEFAULT_ICON_PREV); strcpy (docptr->iconTop, DEFAULT_ICON_TOP); return (docptr); } /*****************************************************************************/ /* Free a document structure. */ void* FreeDoc (struct wasdoc_st *docptr) { int idx; struct wasdoc_st *tocptr; if (!docptr) return (NULL); free (docptr->html); free (docptr->text); free (docptr->spawnOutPtr); for (idx = 0; idx < docptr->flagCount; idx++) free (docptr->flags[idx]); free (docptr); return (NULL); } /*****************************************************************************/ /* If bytes less than equal to zero then just increment the HTML buffer otherwise ensure there is at least |bytes| much space on the HTML buffer. Return the number of bytes remaining in the buffer. */ int wasDocHtmlBuffer ( struct wasdoc_st *docptr, int bytes ) { if (bytes < 0) { docptr->hsize += HTML_INCREMENT; docptr->html = realloc (docptr->html, docptr->hsize); } else if (bytes == 0) { if (docptr->hzsize - docptr->hlength < (HTML_INCREMENT / 4)) { docptr->hsize += HTML_INCREMENT; docptr->html = realloc (docptr->html, docptr->hsize); } } else if (docptr->hzsize - docptr->hlength < bytes) { while (docptr->hsize - docptr->hlength < bytes) docptr->hsize += HTML_INCREMENT; docptr->html = realloc (docptr->html, docptr->hsize); } if (docptr->html == NULL) exit (vaxc$errno); docptr->hzsize = docptr->hsize - (HTML_INCREMENT / 8); return (docptr->hsize - docptr->hlength); } /*****************************************************************************/ /* Append a null-terminated string to the HTML buffer. Expand memory as required. If /length/ is negative then copy the complete string. Other copy only then specified number of bytes. Return the number of bytes copied. */ int wasDocHtmlCat ( struct wasdoc_st *docptr, char *string, int length ) { char *cptr, *sptr, *zptr; cptr = string; zptr = docptr->html + docptr->hsize; sptr = docptr->html + docptr->hlength; if (length < 0) length = INT_MAX; while (*cptr && length--) { if (sptr >= zptr) { wasDocHtmlBuffer (docptr, -1); DOC_HTML (docptr, sptr, zptr); } *sptr++ = *cptr++; } docptr->hlength = sptr - docptr->html; *sptr = '\0'; return (cptr - string); } /*****************************************************************************/ /* Formatted print to HTML buffer. */ int wasDocHtmlPrint ( struct wasdoc_st* docptr, const char *fmt, ... ) { int error = 0, size; va_list args; va_start (args, fmt); size = vsnprintf (NULL, 0, fmt, args); va_end (args); if (size < 0) RETURN_FI_LI (docptr, EINVAL); wasDocHtmlBuffer (docptr, size); va_start (args, fmt); error = vsprintf (docptr->html + docptr->hlength, fmt, args); va_end (args); if (error < 0) RETURN_FI_LI (docptr, EINVAL); if (error != size) exit (SS$_BUGCHECK); docptr->hlength += size; return (0); } /*****************************************************************************/ /* Insert |bytes| of HTML into the document to the location |at| bytes. The |excise| number of bytes at |at| are to be removed from the buffer. In fact the insertion can be zero bytes in which case only the excise is performed. Return zero for success or an errno. */ int wasDocInsertAt ( struct wasdoc_st *docptr, int at, int excise, char *html, int bytes ) { if (!bytes && !excise) return (0); if (bytes < 0 || excise < 0) RETURN_FI_LI (docptr, EINVAL); if (at < 0 || at > docptr->hlength) RETURN_FI_LI (docptr, EINVAL); wasDocHtmlBuffer (docptr, bytes); /* first move the current content up/down */ memmove (docptr->html + at + bytes, docptr->html + at + excise, docptr->hlength - at - excise); /* then as required introduce the new content */ if (html && bytes) memmove (docptr->html + at, html, bytes); docptr->hlength += bytes - excise; /* end of HTML may have moved in either direction */ docptr->html[docptr->hlength] = '\0'; /* set to the end of the newly inserted HTML */ docptr->hparse = at + bytes - excise; return (0); } /*****************************************************************************/ /* Get the status/errno message and insert it into the document. */ void wasDocInsertStatus ( struct wasdoc_st *docptr, int status, int error ) { ushort slen; char *cptr, *sptr, *zptr; char msg [256]; $DESCRIPTOR (MsgDsc, msg); if (status) { status = sys$getmsg (status, &slen, &MsgDsc, 0, 0); if (status & 1) msg[slen] = '\0'; else sprintf (msg, "%%X%0808X", status); } else strcpy (msg, strerror(error)); sptr = docptr->html + docptr->hlength; zptr = docptr->html + docptr->hsize; for (cptr = msg; *cptr && sptr < zptr; *sptr++ = *cptr++); docptr->hlength = sptr - docptr->html; } /*****************************************************************************/ /* Some basic rendering stats. */ void wasDocStatistics ( struct wasdoc_st *docptr, int init ) { /* bit of a fudge to make the elapsed and CPU times look alike! */ static $DESCRIPTOR (StatFaoDsc, "elapsed:!%T0 cpu:!UL.!UL0 dio:!UL bio:!UL faults:!UL\0"); static unsigned long dsecs = LIB$K_DELTA_SECONDS_F, LibStatTimerReal = 1, LibStatTimerCpu = 2, LibStatTimerBio = 3, LibStatTimerDio = 4, LibStatTimerFaults = 5; static $DESCRIPTOR (StatStringDsc, ""); int status; unsigned long CpuBinTime, CountBio, CountDio, CountFaults; unsigned long RealTime64 [2]; float fsecs; char *cptr, *sptr; if (init) { lib$init_timer (0); docptr->statistics[0] = '\0'; return; } lib$stat_timer (&LibStatTimerReal, &RealTime64, 0); lib$stat_timer (&LibStatTimerCpu, &CpuBinTime, 0); lib$stat_timer (&LibStatTimerBio, &CountBio, 0); lib$stat_timer (&LibStatTimerDio, &CountDio, 0); lib$stat_timer (&LibStatTimerFaults, &CountFaults, 0); StatStringDsc.dsc$a_pointer = docptr->statistics; StatStringDsc.dsc$w_length = sizeof(docptr->statistics); status = sys$fao (&StatFaoDsc, 0, &StatStringDsc, &RealTime64, CpuBinTime/100, CpuBinTime%100, CountDio, CountBio, CountFaults); cptr = sptr = docptr->statistics + sizeof("elapsed:")-1; if (*cptr == '0') cptr++; if (*cptr == '0') cptr++; if (*cptr == ':') cptr++; if (*cptr == '0') cptr++; if (*cptr == '0') cptr++; if (*cptr == ':') cptr++; if (*cptr == '0') cptr++; while (*cptr) *sptr++ = *cptr++; *sptr = '\0'; if (docptr->insight >= 2) wasDocInsight (docptr, "%s bytes:%d/%d", docptr->statistics, docptr->hlength, docptr->hsize); if (!(status & 1)) exit (status); } /*****************************************************************************/ /* Read a directory looking for source files. Append each source file to the source text. */ int ProcessDirectory (struct wasdoc_st *docptr) { int error = 0; char *cptr, *fptr, *sptr, *zptr; char fname [256], pname [sizeof(fname)]; struct dirent *entptr; DIR *dirptr; /* pre-fill the file name buffer with the directory name */ zptr = (sptr = fname) + sizeof(fname)-1; for (cptr = docptr->dname; *cptr && sptr < zptr; *sptr++ = *cptr++); while (sptr > docptr->dname && *sptr != '/' && *sptr != ']') sptr--; if (sptr > docptr->dname) sptr++; *(fptr = sptr) = '\0'; if (dbug>0) dbugThis (FI_LI, "ProcessDirectory() %s", fname); dirptr = opendir (fname); if (!dirptr) return (errno); pname[0] = '\0'; while (entptr = readdir(dirptr)) { if (dbug>0) dbugThis (FI_LI, "%s", entptr->d_name); /* any file name not beginning with an alphanumeric is ignored */ if (!isalnum(*entptr->d_name)) continue; /* find the file type and ignore if not */ for (cptr = sptr = entptr->d_name; *sptr; sptr++); zptr = sptr; while (sptr > cptr && *sptr != ';' && *sptr != '.') sptr--; if (*sptr == ';') zptr = sptr; while (sptr > cptr && *sptr != '.') sptr--; /* if the type is not the same */ if (zptr - sptr != docptr->ftlength) continue; if (strncasecmp (sptr, docptr->ftype, docptr->ftlength)) continue; if (docptr->insight >= 2) wasDocInsight (docptr, "%s", fname); /* append the file name to the pre-filled directory name */ zptr = fname + sizeof(fname)-1; for (sptr = fptr; *cptr && *cptr != ';' && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; /* if another version of the name file */ if (dbug>0) dbugThis (FI_LI, "%s %s", pname, fname); if (!strcmp (fname, pname)) continue; strcpy (pname, fname); docptr->ftotal++; if (error = ProcessFile (docptr, fname)) break; } closedir (dirptr); return (error); } /*****************************************************************************/ /* Append the file content to the source text. Return 0 or errno. */ int ProcessFile ( struct wasdoc_st *docptr, char *fname ) { int bytes; char *cptr, *sptr; FILE *ifptr; stat_t fstatbuf; if (dbug>0) dbugThis (FI_LI, "ProcessFile() %s", fname); if ((ifptr = fopen (fname, "r")) == NULL) return (errno); if (fstat (fileno(ifptr), &fstatbuf) < 0) return (errno); /* extra bytes allow EOF when reading past the EOF */ bytes = fstatbuf.st_size; bytes += strlen(fname) + 32; docptr->tsize += bytes; docptr->text = realloc (docptr->text, docptr->tsize); if (!docptr->text) return (errno); sptr = docptr->text + docptr->tlength; /* the source should end up as an HTML comment */ for (cptr = "\\