/*****************************************************************************/ /* render.c This renders HTML5 syntax. https://www.w3.org/TR/html50/ https://www.w3.org/TR/html52/ https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5 Some HTML tags designated self-closing https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags (
  • , , , ) are left explicitly unclosed by this render engine. wasDOC TAGS ----------- There are basically two types of tag. First; those that have punctuation immediately following the introducing vbar. These are text highlight flags and generally continue up to the first alphabetic or period, escape or vbar character. They are intended as terse and syntactically efficient mechanisms to highlight narrative text. Second; an alphabetic keyword follows the introducing vbar. The keyword optionally can have trailing highlight and formatting punctuation and allied characters. For this purpose the heading tags (|0..|4 and |9) are considered keyword tags. A keyword tag commonly (but by no means exclusively) operates on an immediately following, vbar terminated parameter. Sometimes more than one. No white-space is permitted between the keyword tag and any parameter. Source documents may be made more readable by judicious use of the line-escape (\). The markup syntax has a lot of commonality but ultimately is pragmatic and some inconsistency is sprinkled throughout the application. Highlight and formatting characters similarly have much in common but ultimately prostrate themselves before what was necessary and worked. And, of course, the absence of a detailed design at the outset tends to manifest itself. Brief mnemonic for tags: |*bolded text| |/italicised text| |-strikethrough text| |_underscored text| |=monospace text| |>>no wrap| |< #include #include #include #include #include #ifndef STYLE_DEFAULT #define STYLE_DEFAULT "\n" #endif char styleDefault [] = STYLE_DEFAULT; extern int bvbar, dbug, CgiPlusCount, isCgi, isCgiPlus; extern char CopyrightDate [], SoftwareID [], SoftwareVersion []; /*****************************************************************************/ /* Parse the source text markup rendering the HTML document. */ int renderParse (struct wasdoc_st *docptr) { static char vtime [18]; static $DESCRIPTOR (vtimeDsc, vtime); static $DESCRIPTOR (faoDsc, "!17%D\0"); int ch, error, idx, length; char *aptr, *cptr, *sptr, *rowptr = NULL, *tagptr, *tptr, *zptr; char buf [1024], frag [128]; /*********/ /* begin */ /*********/ if (dbug>1) dbugThis (FI_LI, "renderParse()"); docptr->asis = docptr->insightAt = docptr->noescape = 0; memset (docptr->section, 0, sizeof(docptr->section)); for (idx = 0; idx < docptr->flagCount; idx++) free (docptr->flags[idx]); docptr->flagCount = 0; docptr->conditional[docptr->conLevel = 0] = 1; if (!(tptr = docptr->text)) RETURN_FI_LI (docptr, SS$_NOSUCHOBJECT) /* to be applied to the whole document it needs to be handled up-front */ for (cptr = tptr; *cptr && *cptr != '\n'; cptr++); while (*cptr == '\n') cptr++; if (MATCH12 (cptr, "|set|insight")) { error = renderSet (docptr, cptr+5); if (error) { SET_FI_LI (docptr); goto param_error; } } for (;;) { if (HMORE(docptr)) wasDocMoreHtml (docptr); if (!docptr->hlength) { sys$fao (&faoDsc, 0, &vtimeDsc, 0); error = wasDocPrintf (docptr, "\n\ \n\ \n\ \n\ \n\
    \n\ \n", SoftwareID, isCgi ? " CGI" : "", isCgiPlus ? "plus" : "", docptr->isDynamic ? " CLI" : "", CopyrightDate, vtime); if (docptr->errorValue) GOTO_FI_LI (docptr, insert_error) /* must follow the */ if (docptr->insight >= 2) { wasDocInsight (docptr, "BEGIN"); if (docptr->insight >= 2) wasDocInsight (docptr, "%d/%d bytes", docptr->hlength, docptr->hsize); } } /* Th-th-th-that's all folks! */ if (!*tptr) break; if (docptr->conditional[docptr->conLevel] <= 0) { /******************/ /* not processing */ /******************/ while (*tptr) { while (*tptr && *tptr != '|') tptr++; if (!*tptr) break; if (MATCH4 (tptr, "|if|") || MATCH6 (tptr, "|elif|") || MATCH6 (tptr, "|else|") || MATCH7 (tptr, "|endif|")) { TPUT (docptr, tptr); error = renderCondition (docptr); if (error) GOTO_FI_LI (docptr, conditional_error) TGET (docptr, tptr); if (docptr->conditional[docptr->conLevel] > 0) break; } else if (MATCH7 (tptr, "|break|")) { wasDocAsIs (docptr, "\n\n"); break; } else if (MATCH6 (tptr, "|exit|")) { wasDocAsIs (docptr, "\n\n"); return (SS$_ABORT); } else tptr++; } continue; } if (!isspace(*tptr)) { if (!(*tptr == '|' && (MATCH5 (tptr, "|set|") || MATCH7 (tptr, "|style|")))) { /* provide default wasDOC style if not yet otherwise specified */ if (!docptr->styled) { renderStyle (docptr, NULL); continue; } } } if (docptr->asis) { /********/ /* asis */ /********/ /* as-is only terminated by \n||||\n (i.e 4 on a line by itself) */ while (*tptr) { if (HMORE(docptr)) wasDocMoreHtml (docptr); if (*tptr == '\n') { if (MATCH7 (tptr, "\n\\||||\n")) { /* seems obvious this is needed if to be documented :-) */ HONE (docptr, tptr); } else if (MATCH6 (tptr, "\n||||\n")) { if (docptr->insight >= 4) renderInsightTag (docptr); /* 1 is "standalone" while 2 is a
    */ if (docptr->asis == 2) { if (docptr->stindex > 0) { if (MATCH4 (docptr->cstack[docptr->stindex], "||||")) { /* turn it into a
    closure */ wasDocAsIs (docptr, "
    "); docptr->stindex--; } } } tptr += 6; docptr->asis = 0; break; } } HONE (docptr, tptr); } continue; } if (docptr->noescape) { /******************/ /* no-HTML-escape */ /******************/ /* can only be terminated by || or |!"| */ while (*tptr) { if (HMORE(docptr)) wasDocMoreHtml (docptr); if (docptr->insight >= 2) if (MATCH4 (tptr, "\\/
    */ if (docptr->noescape == 1) tptr += 2; docptr->noescape = 0; break; } if (MATCH4 (tptr, "|!\"|")) { /* ditto */ if (docptr->noescape == 1) tptr += 4; docptr->noescape = 0; break; } if (*tptr == '\n') { if (MATCH3 (tptr, "\n|\n")) { /* a non-breaking newline */ tptr += 2; } } HONE (docptr, tptr); } continue; } while (*tptr && *tptr != '|') { /***************/ /* HTML escape */ /***************/ if (HMORE(docptr)) wasDocMoreHtml (docptr); if (docptr->insight >= 2) if (MATCH4 (tptr, "\\') { tptr++; wasDocAsIs (docptr, ">"); continue; } if (*tptr == '&') { tptr++; wasDocAsIs (docptr, "&"); continue; } if (*tptr == '\"') { tptr++; wasDocAsIs (docptr, """); continue; } if (*tptr == '$') { /* wasDOC uses back-to-back $s for control sequences */ tptr++; wasDocAsIs (docptr, "$"); continue; } if (*tptr == '\\') { if (MATCH2 (tptr, "\\\n")) { /* escaped newline - no newline at all */ tptr += 2; continue; } if (MATCH2 (tptr, "\\ ")) { /* escaped space */ tptr += 2; wasDocAsIs (docptr, " "); continue; } if (MATCH2 (tptr, "\\|")) { /* escaped bar */ tptr += 2; wasDocAsIs (docptr, "|"); continue; } if (MATCH4 (tptr, "\\\\\\\\")) { /* four consecutive backslashes just ignore */ tptr += 4; continue; } if (!MATCH2 (tptr, "\\\0")) { /* escape any other character */ tptr++; HONE (docptr, tptr); continue; } } if (MATCH2 (tptr, "//") && *(tptr-1) == '\n') { /* line begins with // */ while (*tptr && *tptr != '\n') tptr++; while (*tptr && *tptr == '\n') tptr++; continue; } if (*tptr == '\n') { if (MATCH3 (tptr, "\n|\n")) { /* a non-breaking newline */ tptr += 2; } } HONE (docptr, tptr); } if (!*tptr) break; /*****************/ /* must be a '|' */ /*****************/ if (ENDTAG (tptr)) { /***************/ /* end element */ /***************/ tagptr = tptr; if (MATCH2 (tptr, "||")) tptr += 2; else if (MATCH2 (tptr, "|!")) { /* documented end */ if (isspace(*(tptr+2))) GOTO_FI_LI (docptr, BADPARAM_error) for (cptr = tptr + 2; *cptr && *cptr != '\n' && *cptr != '|'; cptr++); if (*cptr != '|') GOTO_FI_LI (docptr, BADPARAM_error) tptr = cptr + 1; } else tptr++; if (!docptr->stindex) GOTO_FI_LI (docptr, stack_uflow) aptr = docptr->cstack[docptr->stindex]; if (MATCH3 (aptr, "notable == NOTE_HEADING) { /* explicit heading has been provided so now in |note| text */ docptr->notable = NOTE_TEXT; wasDocAsIs (docptr, "\n
    "); } } else if (MATCH8 (aptr, "")) { if (docptr->table) docptr->table--; /* for the outermost table cancel any row highlight */ if (!docptr->table) docptr->rowlight = 0; wasDocAsIs (docptr, aptr); } else if (MATCH6 (aptr, "<-div>")) { /* faux tag; reset any flag and convert to a div closure */ if (!docptr->bfindex) GOTO_FI_LI (docptr, stack_uflow) docptr->bflag[docptr->bfindex--] = 0; wasDocAsIs (docptr, "
    "); } else if (MATCH7 (aptr, "<-note>")) { /* faux tag similar in purpose to above */ if (!docptr->bfindex) GOTO_FI_LI (docptr, stack_uflow) docptr->notable = 0; docptr->bflag[docptr->bfindex--] = 0; wasDocAsIs (docptr, "
    \n
    "); } else if (aptr) wasDocAsIs (docptr, aptr); /* already checked the stack depth above */ docptr->stindex--; if (docptr->insight >= 4) renderInsightTag (docptr); continue; } /*****************/ /* special cases */ /*****************/ if (docptr->insight >= 4) if (MATCH5 (tptr, "|...|") || MATCH5 (tptr, "|:::|")) renderInsightTag (docptr); /* must be parsed before table |. */ if (MATCH5 (tptr, "|...|")) { tptr += 5; wasDocAsIs (docptr, "…"); continue; } if (MATCH5 (tptr, "|:::|")) { tptr += 5; wasDocAsIs (docptr, "⋮"); continue; } if (docptr->table) { /********************/ /* table processing */ /********************/ if (MATCH2 (tptr, "|~") || MATCH4 (tptr, "|row") || MATCH3 (tptr, "|tr")) { /*******/ /* row */ /*******/ /* table insight is messy! */ rowptr = tptr; /* compress intervening white-space */ cptr = renderTrimWhite (docptr); /* ensuring that empty elements work with CSS :empty */ if (!MATCH4 (cptr, "insight >= 4) { /* table insight is messy! */ if (rowptr) { renderInsightTag (docptr); rowptr = NULL; } renderInsightTag (docptr); } continue; } if (MATCH2 (tptr, "|.") || (MATCH3 (tptr, "|td") && ispunct(tptr[3])) || (MATCH3 (tptr, "|td") && isdigit(tptr[3])) || (MATCH5 (tptr, "|data") && ispunct(tptr[5])) || (MATCH5 (tptr, "|data") && isdigit(tptr[5]))) { /********/ /* data */ /********/ tagptr = tptr; /* compress intervening white-space */ cptr = renderTrimWhite (docptr); /* ensuring that empty elements work with CSS :empty */ if (!MATCH4 (cptr, "insight >= 4) { /* table insight is messy! */ if (rowptr) { renderInsightTag (docptr); rowptr = NULL; } renderInsightTag (docptr); } continue; } } else if (MATCH2 (tptr, "|~") || MATCH2 (tptr, "|:") || MATCH2 (tptr, "|.")) GOTO_FI_LI (docptr, outside_table_error) else if ((MATCH3 (tptr, "|tr") && ispunct(tptr[3])) || (MATCH4 (tptr, "|row") && ispunct(tptr[4]))) GOTO_FI_LI (docptr, outside_table_error) else if ((MATCH3 (tptr, "|th") && ispunct(tptr[3])) || (MATCH5 (tptr, "|head") && ispunct(tptr[5]))) GOTO_FI_LI (docptr, outside_table_error) else if ((MATCH3 (tptr, "|td") && ispunct(tptr[3])) || (MATCH5 (tptr, "|data") && ispunct(tptr[5]))) GOTO_FI_LI (docptr, outside_table_error) /* a few cycles could have been saved by stepping over the '|' */ /* just seemed to make the tags clearer in the code left in :-/ */ /*********/ /* other */ /*********/ if (docptr->insight >= 4) renderInsightTag (docptr); if (MATCH2 (tptr, "|\"")) { /* 1 is "standalone" and 2 is a /
    */ docptr->noescape = 1; tptr += 2; continue; } if (MATCH2 (tptr, "|\\")) { /* leading backslash; ignore the rest of the tag */ for (tptr++; *tptr; tptr++) { if (MATCH2 (tptr, "\\|")) tptr += 2; else if (*tptr == '|' && ENDTAG (tptr)) break; } if (!*tptr) break; if (MATCH2 (tptr, "||")) tptr++; tptr++; continue; } /* especially this one needs to be before "strike" */ if (MATCH3 (tptr, "|-|")) { tptr += 3; wasDocAsIs (docptr, "–"); continue; } if (MATCH4 (tptr, "|--|")) { tptr += 4; wasDocAsIs (docptr, "—"); continue; } if (MATCH3 (tptr, "|//")) { /* comment (must come before "|/" - italic) */ if (docptr->stindex++ >= STACK_MAX) GOTO_FI_LI (docptr, stack_oflow) docptr->tstack[docptr->stindex] = tptr; docptr->cstack[docptr->stindex] = " -->"; wasDocAsIs (docptr, ""); continue; } if (MATCH7 (tptr, "|index|")) { /* insert a marker for where the INDEX will be */ tptr += 7; if (MATCH2 (tptr, "||")) tptr++; tptr++; wasDocAsIs (docptr, ""); continue; } if (MATCH7 (tptr, "|break|")) { wasDocAsIs (docptr, "\n\n"); TPUT (docptr, tptr); break; } if (MATCH6 (tptr, "|exit|")) { wasDocAsIs (docptr, "\n\n"); TPUT (docptr, tptr); return (SS$_ABORT); } if (MATCH10 (tptr, "|insight!|")) { tptr += 10; TPUT (docptr, tptr); renderInsightStack (docptr); TGET (docptr, tptr); continue; } if (MATCH10 (tptr, "|insight@|")) { tptr += 10; if (docptr->isDynamic && docptr->insight) { wasDocAsIs (docptr, "
    \n\ \n\ \n\ \n\
    \n"); docptr->insightAt = 1; } continue; } /***********/ /* unknown */ /***********/ GOTO_FI_LI (docptr, unknown_tag) } /*******/ /* end */ /*******/ if (docptr->stindex > 0) GOTO_FI_LI (docptr, unbalanced_error) if (pass2 (docptr) < 0) { TPUT (docptr, tptr); if (docptr->errorValue == SS$_NOSUCHOBJECT) renderErrorReport (docptr, "\n
    Link target %s not found
    \n", docptr->pass2At); else { cptr = strdup (strerror(EVMSERR,docptr->errorValue)); *cptr = toupper(*cptr); renderErrorReport (docptr, "\n
    %s %s
    \n", cptr, docptr->pass2At); free (cptr); } renderErrorReport (docptr, NULL); return (docptr->errorValue); } if (docptr->insight >= 2) wasDocInsight (docptr, "END"); return (0); post_condition_error: aptr = docptr->html + docptr->hparse; TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    %s post processing error %s \ at "%s" %s
    \n", cptr, strerror(EVMSERR,error), renderLineEscape(aptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); unknown_tag: TPUT (docptr, tptr); renderErrorReport (docptr, "
    Unknown tag "%s" at \ "%s" %s
    \n", renderTag(tptr), renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); insert_error: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    Insert failed %s at \ "%s" %s
    \n", strerror(EVMSERR,error), renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); BADPARAM_error: error = SS$_BADPARAM; goto param_error; param_error: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    Parameter error %s at \ \"%s\" %s
    \n", strerror(EVMSERR,error), renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); vms_status_error: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    VMS status %%X%08.08X error %s at \ \"%s\" %s
    \n", error, strerror(EVMSERR,error), renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); outside_table_error: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    Tag "%s" used outside table at \ "%s" %s
    \n", renderTag(tptr), renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); stack_oflow: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    Overflow at "%s"%s
    \n", renderLine(docptr)); renderErrorReport (docptr, NULL); return (error); stack_uflow: TPUT (docptr, tptr); renderErrorReport (docptr, "\n
    Underflow at "%s" %s
    \n", renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); conditional_error: if (!error) cptr = "unbalanced"; else cptr = strerror(EVMSERR,error); TPUT (docptr, tptr); renderErrorReport (docptr, "
    Conditional %s at \ "%s" %s
    ", cptr, renderLine(docptr), renderSource(docptr)); renderErrorReport (docptr, NULL); return (error); unbalanced_error: TPUT (docptr, tptr); renderErrorReport (docptr, "
    Unbalanced at "%s" %s", renderLine(docptr), renderSource(docptr)); while (docptr->stindex) { renderErrorReport (docptr, "
    %d "%s"", docptr->stindex, renderLine(docptr)); docptr->stindex--; } renderErrorReport (docptr, "
    "); renderErrorReport (docptr, NULL); return (error); } /*****************************************************************************/ /* If the primary document reports an error in some indeterminate state (e.g. inside a heading, monospace, italic, table, etc.) to get the error report out a markup-clean environment is needed. Hence the IFRAME. The iframe is populated using JavaScript. An initial call sets up the dynamic created IFRAME and formats an initial report line. Subsequent calls can add lines to the report. A final "cleanup" call (mandatory) loses and displays the report. */ int renderErrorReport ( struct wasdoc_st *docptr, char *fmt, ... ) { static int init = 0; int cnt, linum, retval, size; char *aptr, *bptr, *fiptr, *cptr, *sptr, *tptr, *zptr; char finame [64]; va_list args; if (dbug>2) dbugThis (FI_LI, "renderErrorReport() %s", dbugMax(fmt)); if (!fmt) { /* end call */ wasDocPrintf (docptr, "\n"); init = 0; return (0); } va_start (args, fmt); size = vsnprintf (NULL, 0, fmt, args); va_end (args); if (size < 0) return (vaxc$errno); bptr = calloc (1, size); if (!bptr) exit (vaxc$errno); va_start (args, fmt); retval = vsprintf (bptr, fmt, args); va_end (args); if (retval < 0) return (retval); if (retval != size) wasDocBugcheck(FI_LI); /* escape any forbiddens in the string */ cnt = 0; for (cptr = bptr; *cptr; cptr++) if (*cptr == '\n' || *cptr == '\'' || *cptr == '\\') cnt++; if (cnt) { aptr = sptr = calloc (1, size + cnt); if (!aptr) exit (vaxc$errno); for (cptr = bptr; *cptr; cptr++) { if (*cptr == '\n' || *cptr == '\'' || *cptr == '\\') *sptr++ = '\\'; if (*cptr == '\n') *sptr++ = 'n'; else if (isprint(*cptr)) *sptr++ = *cptr; } free (bptr); bptr = aptr; } if (!init) { if (fiptr = docptr->fiptr) { while (*fiptr) fiptr++; while (*(fiptr-1) != ']' && fiptr > docptr->fiptr) fiptr--; zptr = (sptr = finame) + sizeof(finame)-1; while (*fiptr && *fiptr != ';' && sptr < zptr) *sptr++ = *fiptr++; *sptr = '\0'; } linum = docptr->linum; wasDocPrintf (docptr, "", fiptr ? finame : "(null)", linum); } wasDocPrintf (docptr, "\n
    \n\ \n", bptr); init = 1; free (bptr); return (0); } /*****************************************************************************/ /* Return a pointer to the tag as a string. */ char* renderTag (char *tptr) { static char buf [32+3]; char *sptr, *zptr; if (dbug>2) dbugThis (FI_LI, "renderTag() %s", dbugMax(tptr)); zptr = (sptr = buf) + sizeof(buf)-3; *sptr++ = bvbar; if (*tptr == '|') *sptr++ = *tptr++; if (isalpha(*tptr)) { while (*tptr && *tptr != '|' && sptr < zptr) *sptr++ = *tptr++; *sptr++ = '|'; } else if (isdigit(*tptr)) while (*tptr && isdigit(*tptr)&& sptr < zptr) *sptr++ = *tptr++; else *sptr++ = *tptr; *sptr++ = bvbar; *sptr = '\0'; return (buf); } /*****************************************************************************/ /* Return a pointer to the line as a string. Call pointing at start of line. */ char* renderLine (struct wasdoc_st *docptr) { static char buf [96]; char *cptr, *sptr, *zptr; if (dbug>2) dbugThis (FI_LI, "renderLine() %s", dbugMax(docptr->text+docptr->tparse)); zptr = (sptr = buf) + sizeof(buf)-1; cptr = docptr->text + docptr->tparse; while (*cptr && *cptr != '\n') cptr++; while (*cptr == '\n') cptr++; while (cptr > docptr->text && *(cptr-1) == '\n') cptr--; while (cptr > docptr->text && *(cptr-1) != '\n') cptr--; while (*cptr && *cptr != '\n' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; cptr = dbugAll (buf); if (dbug>2) dbugThis (FI_LI, "%s", cptr); return (cptr); } /*****************************************************************************/ /* Return a pointer to a buffer containing the line number and source file name (see ConditionFile()). */ char* renderSource (struct wasdoc_st *docptr) { static char buf [128]; int clnum = 1, slnum = 0; char *cptr, *sptr, *tptr, *tzptr, *zptr; char sname [96]; strcpy (buf, "BUGCHECK!"); strcpy (sname, "BUGCHECK!"); tzptr = docptr->text + docptr->tparse; for (tptr = docptr->text; *tptr && tptr < tzptr; tptr++) { if (*tptr == '\n') clnum++; if (*tptr != '\\') continue; if (!MATCH4 (tptr, "\\' : for (cptr = ">"; *cptr && sptr < zptr; *sptr++ = *cptr++); break; case '&' : for (cptr = "&"; *cptr && sptr < zptr; *sptr++ = *cptr++); break; default: *sptr++ = *hptr; } hptr++; } *sptr = '\0'; return (buf); } /*****************************************************************************/ /* Evaluate the conditional depending on the current conditioning status. Returns ECHILD on underflow, E2BIG on overflow, and SS$_BADPARAM if unknown. The docptr->conditional datum has the value 1) when processing -1) has processed but hit |elif| or |else| and so no longer 0) not processing Document content is only processed when it has a value > 0. So if an |elif| hits a 1 at its own level then that level goes to -1. Same for an |else|. The condition processor parses all levels in nested conditionals, even when not actually processing more nested levels due to non-processing less nested levels, and so should catch and report syntactic errors of all levels. */ int renderCondition (struct wasdoc_st *docptr) { int error = 0, elif = 0, insight, isnot = 0, minus1, yes = 0; char *aptr, *bptr, *cptr, *tptr; char buf [1024]; TGET (docptr, tptr); if (dbug>1) dbugThis (FI_LI, "renderCondition() %d %d %s", docptr->conLevel, docptr->conditional[docptr->conLevel], dbugMax(tptr)); /* some processing determined by the preceding level */ if (docptr->conLevel) minus1 = docptr->conditional[docptr->conLevel-1]; else minus1 = 1; /* if deeper not processing then no insight unless 5 or more */ insight = docptr->insight; if (minus1 <= 0) if (docptr->insight < 5) docptr->insight = 0; if ((elif = MATCH6 (tptr, "|elif|")) && !docptr->conLevel) error = ECHILD; else if (MATCH4 (tptr, "|if|") || elif) { tptr += 3; if (elif) tptr += 2; if (ENDTAG(tptr)) buf[0] = '\0'; else { tptr++; TPUT (docptr, tptr); renderGetParam (docptr, buf, sizeof(buf)); TGET (docptr, tptr); } if (MATCH2 (tptr, "||")) tptr++; if (*tptr == '|') tptr++; if (docptr->insight >= 4) wasDocInsight (docptr, "%s", buf); bptr = buf; if (*bptr == '!') { isnot = 1; bptr++; } else if (MATCH2 (bptr, "\\!")) bptr++; if (!bptr[0]) /* no conditional is false */ yes = 0; else if (MATCH2 (bptr, "0")) yes = 0; else if (MATCH2 (bptr, "1")) yes = 1; else if (MATCH8 (bptr, "dynamic")) yes = docptr->isDynamic; else if (MATCH7 (bptr, "static")) yes = docptr->isStatic; else if (MATCH7 (bptr, "single")) yes = !docptr->chunked; else if (MATCH6 (bptr, "multi")) yes = docptr->chunked; else if (MATCH5 (bptr, "hide")) yes = CgiLibVarNull("WWW_FORM_NOHIDE") != NULL; else if (MATCH5 (bptr, "wasd")) yes = CgiLibEnvironmentIsWasd(); else if (MATCH7 (bptr, "apache")) yes = CgiLibEnvironmentIsApache(); else if (MATCH4 (bptr, "osu")) yes = CgiLibEnvironmentIsOsu(); else if (MATCH4 (bptr, "cgi=")) { if (isCgi || isCgiPlus) { /* [:] */ for (aptr = bptr + 4; *aptr && *aptr != ':'; aptr++); if (*aptr) *aptr++ = '\0'; cptr = CgiLibVarNull (bptr+4); yes = testCondition (docptr, cptr, aptr); } else yes = 0; } else if (MATCH4 (bptr, "lnm=")) { /* [;][:] */ for (aptr = bptr + 4; *aptr && *aptr != ':'; aptr++); if (*aptr) *aptr++ = '\0'; cptr = renderTrnLnm (docptr, bptr+7, 0); yes = testCondition (docptr, cptr, aptr); } else if (MATCH6 (bptr, "spawn=")) { /* spawn=[:] */ for (aptr = cptr = bptr + 6; *aptr && *aptr != ':'; aptr++) { /* escape to allow ':' in the spawn command */ if (*aptr == '\\' && *(aptr+1) == ':') aptr++; *cptr++ = *aptr; } if (*aptr) *aptr++ = '\0'; *cptr = '\0'; yes = SpawnCommand (docptr, bptr+6, aptr) < 0; } else if (MATCH4 (bptr, "syi=")) { if (isCgi || isCgiPlus) { /* [:] */ for (aptr = bptr + 4; *aptr && *aptr != ':'; aptr++); if (*aptr) *aptr++ = '\0'; cptr = renderGetSyi (docptr, bptr+4); yes = testCondition (docptr, cptr, aptr); } else yes = 0; } else if (MATCH5 (bptr, "time:")) yes = testTime (docptr, bptr+5); else if (MATCH8 (bptr, "insight:")) { /* is insight enabled is it at this value */ if (isdigit(bptr[8])) yes = atoi(bptr+8) >= docptr->insight; else yes = docptr->insight > 0; } else if (isupper(bptr[0])) { /* [:] */ for (aptr = bptr; *aptr && *aptr != ':'; aptr++); if (*aptr) *aptr++ = '\0'; cptr = renderFlag (docptr, bptr, 0); if (cptr) { if (*aptr) yes = testCondition (docptr, cptr, aptr); else { while (cptr && isspace(*cptr)) cptr++; if (isdigit(*cptr)) yes = atoi(cptr); else yes = !*cptr; } } else error = SS$_BADPARAM; } else error = SS$_BADPARAM; if (!error) { if (minus1 > 0) { /* deeper is processing */ if (isnot) yes = !yes; if (elif) if (docptr->conditional[docptr->conLevel] == 0) docptr->conditional[docptr->conLevel] = yes; else docptr->conditional[docptr->conLevel] = -1; else if (docptr->conditional[docptr->conLevel] > 0) if (docptr->conLevel < CONDITION_MAX) docptr->conditional[++docptr->conLevel] = yes; else error = E2BIG; else docptr->conditional[++docptr->conLevel] = -1; } else { /* deeper is NOT processing */ if (elif) docptr->conditional[docptr->conLevel] = -1; else if (docptr->conLevel < CONDITION_MAX) docptr->conditional[++docptr->conLevel] = -1; else error = E2BIG; } } } else if (MATCH6 (tptr, "|else|")) { if (docptr->insight >= 4) wasDocInsight (docptr, "|else|"); if (!docptr->conLevel) error = ECHILD; else if (minus1 > 0) /* deeper is processing */ if (docptr->conditional[docptr->conLevel] == 0) docptr->conditional[docptr->conLevel] = 1; else docptr->conditional[docptr->conLevel] = -1; else /* deeper is NOT processing */ docptr->conditional[docptr->conLevel] = -1; tptr += 6; } else if (MATCH7 (tptr, "|endif|")) { if (docptr->insight >= 4) wasDocInsight (docptr, "|endif|"); if (!docptr->conLevel) error = ECHILD; else docptr->conditional[docptr->conLevel--] = 0; tptr += 7; } else error = SS$_BADPARAM; if (docptr->insight >= 4) wasDocInsight (docptr, "%s%d/%d", docptr->conditional[docptr->conLevel] > 0 ? "✓" : "✗", docptr->conLevel, docptr->conditional[docptr->conLevel]); if (*tptr == '\n') tptr++; TPUT (docptr, tptr); docptr->insight = insight; if (dbug>1) dbugThis (FI_LI, "%d %d %s", docptr->conLevel, docptr->conditional[docptr->conLevel], dbugMax(tptr)); return (error); } /*****************************************************************************/ /* Copy a bar-delimited string into the supplied buffer. Return 0 for success or any other vaxc%errno for error. */ int renderGetParam ( struct wasdoc_st *docptr, char *buf, int size ) { char *sptr, *tptr, *zptr; TGET (docptr, tptr); if (dbug>1) dbugThis (FI_LI, "renderGetParam() %s", dbugMax(tptr)); if (size) buf[0] = '\0'; /* look slightly behind to check if one might not be supplied */ if (ENDTAG (tptr-1) && !MATCH2 (tptr-1, "|!")) return (0); zptr = (sptr = buf) + size - 1; if (zptr <= sptr) RETURN_FI_LI (docptr, SS$_RESULTOVF) while (*tptr && *tptr != '|' && sptr < zptr) { if (*tptr == '\\') { tptr++; if (*tptr == '\n') { tptr++; continue; } if (!*tptr) break; } *sptr++ = *tptr++; } if (*tptr != '|') RETURN_FI_LI (docptr, SS$_BADPARAM) if (sptr >= zptr) RETURN_FI_LI (docptr, SS$_RESULTOVF) *sptr = '\0'; TPUT (docptr, tptr); return (0); } /*****************************************************************************/ /* Set document characteristic. */ int renderSet ( struct wasdoc_st *docptr, char *param ) { int error = 0, number; char *cptr, *sptr, *tptr, *zptr; if (dbug>1) dbugThis (FI_LI, "renderSet() %s", dbugMax(param)); if (MATCH8 (param, "chunked=")) { /* 1 enables, 0 let the client decide, -1 disables */ int chunked; chunked = atoi(param+8); if (chunked < 0 || !docptr->chunked) docptr->chunked = chunked; } else if (MATCH6 (param, "found=")) { /* explicitly set the default of 'this "in" that' */ zptr = (sptr = docptr->setFoundIn) + sizeof(docptr->setFoundIn)-1; for (cptr = param + 6; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } else if (MATCH9 (param, "idx=cols=")) { /* number of columns in the index */ number = atoi(param+9); if (number == 1 || number == 2 || number == 3 || number == 4) docptr->setIdxCols = number; else error = SS$_BADPARAM; } else if (MATCH9 (param, "idx=sort=")) { /* index entries sorted is 1, unsorted is 0 */ docptr->setIdxSort = atoi(param+9); } else if (MATCH12 (param, "idx=collate=")) { /* collation characters for index (26 Latin alphabetics default) */ zptr = (sptr = docptr->idxCollate) + sizeof(docptr->idxCollate)-1; /* if plus then append to existing collation, otherwise replace */ cptr = param + 12; if (*cptr == '+') { for (sptr = docptr->idxCollate; *sptr; sptr++); cptr++; } while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; } else if (MATCH8 (param, "insight=")) { /* is explicitly looked for *very* early in document processing */ if (docptr->insight = atoi(param+8)) if (cptr = CgiLibVarNull ("FORM_INSIGHT")) docptr->insight = atoi(cptr); } else if (MATCH7 (param, "locale=")) { if (MATCH0 (param, "locale=ctype=", 13)) { if (!setlocale (LC_CTYPE, param+13)) error = vaxc$errno; } else if (MATCH7 (param, "locale=") && isdigit(param[7]) && param[8] == '=') { if (!setlocale (atoi(param+8), param+10)) error = vaxc$errno; } else if (MATCH7 (param, "locale=")) { if (!setlocale (LC_ALL, param+7)) error = vaxc$errno; } } else if (MATCH9 (param, "navigate=")) { /* leading integer can enable/disable navigation icons */ cptr = param + 9; if (isdigit(*cptr)) { docptr->setNavigate = atoi(cptr); while (isdigit(*cptr)) cptr++; if (*cptr == '|') cptr++; } if (!*cptr) return (0); /* navigation icons, |||| */ if (*cptr != '|') { zptr = (sptr = docptr->iconBack) + sizeof(docptr->iconBack)-1; while (*cptr && *cptr != '|' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '|') cptr++; } if (*cptr) cptr++; if (*cptr != '|') { zptr = (sptr = docptr->iconPrev) + sizeof(docptr->iconPrev)-1; while (*cptr && *cptr != '|' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '|') cptr++; } if (*cptr) cptr++; if (*cptr != '|') { zptr = (sptr = docptr->iconTop) + sizeof(docptr->iconTop)-1; while (*cptr && *cptr != '|' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '|') cptr++; } if (*cptr) cptr++; if (*cptr != '|') { zptr = (sptr = docptr->iconNext) + sizeof(docptr->iconNext)-1; while (*cptr && *cptr != '|' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '|') cptr++; } if (*cptr) cptr++; if (*cptr != '|') { zptr = (sptr = docptr->iconForw) + sizeof(docptr->iconForw)-1; while (*cptr && *cptr != '|' && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; while (*cptr && *cptr != '|') cptr++; } } else if (MATCH5 (param, "note=")) { /* explicitly set the default title of a note */ zptr = (sptr = docptr->setNote) + sizeof(docptr->setNote)-1; for (cptr = param + 5; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } else if (MATCH9 (param, "paginate=")) { /* enable automatic pagination */ docptr->setPaginate = atoi(param+9); } else if (MATCH10 (param, "table=margin=")) { /* table margin (+, -, \0) */ docptr->setTabMargin = param[13]; docptr->bflag[docptr->bfindex] &= ~BFLAG_CENTER; } else if (MATCH6 (param, "title=")) { /* explicitly set the title of the document */ zptr = (sptr = docptr->title) + sizeof(docptr->title)-1; for (cptr = param + 6; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } else if (MATCH12 (param, "toc2=next=cols=")) { /* number of columns in NEXT secondary table of content */ number = atoi(cptr = param+15); if (number == 1 || number == 2 || number == 3 || number == 4) /* propagate into second pass */ wasDocPrintf (docptr, "", param); else error = SS$_BADPARAM; } else if (MATCH10 (param, "toc2=cols=")) { /* number of columns in secondary table of content */ number = atoi(cptr = param+10); if (number == 1 || number == 2 || number == 3 || number == 4) { docptr->setToc2Cols = number; while (*cptr && isdigit(*cptr)) cptr++; while (*cptr && !isdigit(*cptr)) cptr++; if (isdigit(*cptr)) if ((number = atoi(cptr)) <= 100) docptr->setToc2ColsWidth = number; } else error = SS$_BADPARAM; } else if (MATCH5 (param, "toc2=")) { /* whether a secondary TOC is generated */ cptr = param+5; if (isdigit(*cptr)) docptr->setToc2 = atoi(param+5); else error = SS$_BADPARAM; } else if (MATCH9 (param, "toc=cols=")) { /* number of colmns in table of content */ number = atoi(param+9); if (number == 1 || number == 2 || number == 3 || number == 4) docptr->setTocCols = number; else error = SS$_BADPARAM; } else if (MATCH11 (param, "toc=format=")) { /* explicitly set the format of the TOC */ zptr = (sptr = docptr->setTocForm) + sizeof(docptr->setTocForm)-1; for (cptr = param + 11; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } else if (isupper (param[0])) { /* setting a flag */ if (!renderFlag (docptr, param, 1)) error = SS$_BADPARAM; } else error = SS$_BADPARAM; return (error); } /*****************************************************************************/ /* Flags begin with an upper-case alphabetic, have all upper-case alphabetics and digits. Flag strings are |set|FLAG[=|]| by renderSet(). Search the array of flags strings for the specified flag. Can set a new flag, reset an existing flag, and also just search for a flag. If the parameter contains an equate symbol (=) then it's considered to be a (re)set. A set/reset returns a pointer for success or a NULL if something failed. If not found return NULL to indicate that. If found and if the flag has a value return a pointer to that (even if empty). */ char* renderFlag ( struct wasdoc_st *docptr, char *flag, int set ) { int idx; char *cptr, *sptr; if (dbug>1) dbugThis (FI_LI, "renderFlag() %d %s", set, dbugAll(flag)); /* search for an existing flag */ for (idx = 0; idx < docptr->flagCount; idx++) { sptr = flag; for (cptr = docptr->flags[idx]; *cptr && *cptr != '=' && *sptr && *sptr != '='; cptr++, sptr++) if (*cptr != *sptr) break; if (*cptr && *cptr != '=') continue; if (*sptr && *sptr != '=') continue; break; } if (idx >= docptr->flagCount) { /* not found */ if (!set) return (NULL); /* check not an empty name and is all upper */ for (cptr = flag; *cptr && *cptr != '=' && (isupper(*cptr) || isdigit(*cptr)); cptr++); if (cptr == flag) return (NULL); if (*cptr && *cptr != '=' && !isupper(*cptr) && !isdigit(*cptr)) return (NULL); /* set a new flag */ if (docptr->flagCount >= FLAG_MAX) RETURN_FI_LI (docptr, NULL) docptr->flags[docptr->flagCount] = calloc (strlen(flag), 1); if (!docptr->flags[docptr->flagCount]) RETURN_FI_LI (docptr, NULL) strcpy (docptr->flags[docptr->flagCount], flag); docptr->flagCount++; for (cptr = flag; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; return (cptr); } /* if reset an existing flag */ if (*sptr) { free (docptr->flags[idx]); docptr->flags[idx] = calloc (strlen(flag), 1); if (!docptr->flags[idx]) RETURN_FI_LI (docptr, NULL) strcpy (docptr->flags[idx], flag); for (cptr = flag; *cptr && *cptr != '='; cptr++); if (*cptr) cptr++; return (cptr); } /* if just searching for a flag */ if (*cptr) cptr++; return (cptr); } /*****************************************************************************/ /* The |1..|4 represent the primary TOC hierarchy. |0 represents a secondary heading without a primary TOC. |0 is transformed into an
    heading. Cross-reference |9 into a
    . Prefixing any section digit (01, 02, 03, 04) with a zero represents a heading without a primary TOC entry. */ int renderInsertHeading (struct wasdoc_st *docptr) { int error = 0; char nine9, zero0, zero1, zero2, zero3, zero4, zero9; char *cptr, *tptr; if (dbug>1) dbugThis (FI_LI, "renderInsertHeading()"); TGET (docptr, tptr); if (tptr[0] != '|') RETURN_FI_LI (docptr, SS$_FORMAT) if (docptr->stindex >= STACK_MAX) RETURN_FI_LI (docptr, SS$_RESULTOVF) docptr->tstack[++docptr->stindex] = tptr; /* leading zero suppresses primary TOC entry */ cptr = tptr + 1; /* bit sloppy and inefficient but */ zero0 = MATCH2(cptr, "00"); zero1 = MATCH2(cptr, "01"); zero2 = MATCH2(cptr, "02"); zero3 = MATCH2(cptr, "03"); zero4 = MATCH2(cptr, "04"); zero9 = MATCH2(cptr, "09"); nine9 = MATCH2(cptr, "99"); if (isdigit(*cptr) && isdigit(*(cptr+1))) cptr++; /* first visible heading becomes top of document */ if (*cptr >= '0' && *cptr <= '4') if (!(docptr->section[1] || docptr->section[2] || docptr->section[3] || docptr->section[4] || docptr->section[5])) wasDocAsIs (docptr, "\n"); switch (*cptr) { case '0' : if (zero0) docptr->section[5] = SECTION_NOT_IN; else docptr->section[5]++; docptr->section[6] = 0; docptr->cstack[docptr->stindex] = "
    "; cptr = "section[5]++; else { docptr->section[1]++; docptr->chunkTotal++; docptr->section[2] = docptr->section[3] = docptr->section[4] = docptr->section[5] = docptr->section[6] = 0; } docptr->cstack[docptr->stindex] = ""; cptr = "section[5]++; else { docptr->section[2]++; docptr->section[3] = docptr->section[4] = docptr->section[5] = docptr->section[6] = 0; } docptr->cstack[docptr->stindex] = ""; cptr = "section[5]++; else { docptr->section[3]++; docptr->section[4] = docptr->section[5] = docptr->section[6] = 0; } docptr->cstack[docptr->stindex] = ""; cptr = "section[5]++; else { docptr->section[4]++; docptr->section[5] = docptr->section[6] = 0; } docptr->cstack[docptr->stindex] = ""; cptr = "section[6] = SECTION_NO_INDEX; else if (nine9) docptr->section[6] = SECTION_NOT_IN; else docptr->section[6]++; docptr->cstack[docptr->stindex] = ""; cptr = "", renderNumericId(docptr->section)); if (error) RETURN_FI_LI (docptr, error); error = renderInsertTag (docptr, cptr); if (error) return (error); return (0); } /*****************************************************************************/ /* */ int renderInsertNote (struct wasdoc_st *docptr) { int error; char *cptr; if (dbug>1) dbugThis (FI_LI, "renderInsertNote()"); /* use the default heading */ if (!*(cptr = docptr->setNote)) cptr = DEFAULT_NOTE; /* a note is implemented as a level 6 heading */ docptr->section[6]++; error = wasDocPrintf (docptr, "\
    %s
    \n\
    \n", renderNumericId(docptr->section), cptr); return (error); } /*****************************************************************************/ /* The parameter URL can be absolute, relative or in-document fragment generated from a heading title. */ int renderInsertLink ( struct wasdoc_st *docptr, char *param ) { char *cptr, *sptr, *zptr; char param2 [512]; if (dbug>1) dbugThis (FI_LI, "renderInsertLink() %s", dbugAll(param)); for (cptr = param; *cptr && isalpha(*cptr); cptr++); if (MATCH3 (cptr, "://") || MATCH7 (param, "mailto:")) { /* looks like a URL scheme */ wasDocAsIs (docptr, param); } else if (strstr (cptr, "##")) { /* another document, munge the URI */ zptr = (sptr = param2) + sizeof(param2)-1; /* any trailing chunk faux "directory" must be allowed for */ if (docptr->chunked && docptr->isDynamic) if (MATCH2 (param, "./") || MATCH3 (param, "../")) for (cptr = "../"; *cptr; *sptr++ = *cptr++); /* essentially make it ODS-2 compliant */ for (cptr = param; *cptr && *cptr != '#' && sptr < zptr; cptr++) { if (isalnum(*cptr) || *cptr == '-' || *cptr == '$' || *cptr == '.' || *cptr == '/') *sptr++ = tolower(*cptr); else *sptr++ = '_'; } if (MATCH3 (cptr, "##\0")) { for (cptr = "#0."; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; wasDocAsIs (docptr, param2); } else if (MATCH2 (cptr, "##")) { for (cptr++; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; wasDocAsIs (docptr, param2); renderFragment (docptr); } } else if (param[0] == '#') { /* explicit fragment */ wasDocEntify (docptr, param); renderFragment (docptr); } else if (param[0] == '/' || MATCH2 (param, "./") || MATCH3 (param, "../")) { /* any trailing chunk faux "directory" must be allowed for */ if (docptr->chunked && docptr->isDynamic) if (MATCH2 (param, "./") || MATCH3 (param, "../")) wasDocAsIs (docptr, "../"); /* relative URL */ wasDocEntify (docptr, param); renderFragment (docptr); } else { /* to fragment ID added via heading */ wasDocPrintf (docptr, ""); } return (0); } /*****************************************************************************/ /* Create a link to another document like "Heading Name in Document Name". The parameter looks like "##++in++". */ int renderInsertLinkIn ( struct wasdoc_st *docptr, char *param, int target ) { char *cptr, *sptr, *zptr; char heading [256], inof [256], title [256], url [256]; if (dbug>1) dbugThis (FI_LI, "renderInsertLinkIn() %d %s", target, dbugAll(param)); zptr = (sptr = url) + sizeof(url)-1; /* any trailing chunk faux "directory" must be allowed for */ if (docptr->chunked && docptr->isDynamic) if (MATCH2 (param, "./") || MATCH3 (param, "../")) for (cptr = "../"; *cptr; *sptr++ = *cptr++); /* essentially make it ODS-2 compliant */ for (cptr = param; *cptr && !MATCH2(cptr,"##") && sptr < zptr; cptr++) { if (isalnum(*cptr) || *cptr == '-' || *cptr == '$' || *cptr == '.' || *cptr == '/') *sptr++ = tolower(*cptr); else *sptr++ = '_'; } *sptr = '\0'; if (*cptr != '#' || sptr >= zptr || !url[0]) RETURN_FI_LI (docptr, SS$_BADPARAM) zptr = (sptr = heading) + sizeof(heading)-1; for (cptr+=2; *cptr && !MATCH2(cptr,"++") && sptr < zptr; *sptr++ = *cptr++) if (*cptr == '\\' && *(cptr+1)) cptr++; *sptr = '\0'; if (*cptr != '+' || !heading[0] || sptr >= zptr) RETURN_FI_LI (docptr, SS$_BADPARAM) zptr = (sptr = inof) + sizeof(inof)-1; for (cptr+=2; *cptr && !MATCH2(cptr,"++") && sptr < zptr; *sptr++ = *cptr++) if (*cptr == '\\' && *(cptr+1)) cptr++; *sptr = '\0'; if (*cptr != '+' || !inof[0] || sptr >= zptr) RETURN_FI_LI (docptr, SS$_BADPARAM) zptr = (sptr = title) + sizeof(title)-1; for (cptr+=2; *cptr && sptr < zptr; *sptr++ = *cptr++) if (*cptr == '\\' && *(cptr+1)) cptr++; *sptr = '\0'; if (sptr >= zptr || !title[0]) RETURN_FI_LI (docptr, SS$_BADPARAM) /* the "); wasDocAsIs (docptr, heading); wasDocAsIs (docptr, " "); wasDocAsIs (docptr, inof); if (target) wasDocAsIs (docptr, " "); wasDocAsIs (docptr, title); wasDocAsIs (docptr, ""); return (0); } /*****************************************************************************/ /* The parameter URL can be absolute or relative. */ int renderInsertUrl ( struct wasdoc_st *docptr, char *param ) { char ch; char *cptr, *sptr, *zptr; char uri [256]; if (dbug>1) dbugThis (FI_LI, "renderInsertUrl() %s", dbugAll(param)); /* look for what might be a URL */ for (cptr = param; *cptr && (isalpha(*cptr) || !MATCH3(cptr,"://")); cptr++); if (MATCH3(cptr,"://") || MATCH7 (param, "mailto:")) { /* looks like a URL scheme */ wasDocAsIs (docptr, param); } else if (isalnum(*param) || MATCH2 (param, "./") || MATCH3 (param, "../")) { /* relative to the document location (which may be a script) */ if (docptr->isDynamic) { zptr = (sptr = uri) + sizeof(uri)-1; for (cptr = docptr->pinfo; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; while (sptr > uri && *sptr != '/') sptr--; if (MATCH2 (cptr = param, "./")) cptr += 2; while (sptr > uri) { if (!MATCH3 (cptr, "../")) break; if (sptr > uri) sptr--; while (sptr > uri && *sptr != '/') sptr--; cptr += 3; } if (sptr == uri) RETURN_FI_LI (docptr, SS$_BADPARAM) if (sptr < zptr) *sptr++ = '/'; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; if (sptr >= zptr) RETURN_FI_LI (docptr, SS$_RESULTOVF) wasDocAsIs (docptr, uri); } else wasDocAsIs (docptr, param); } else wasDocAsIs (docptr, param); return (0); } /*****************************************************************************/ /* Insert the specified item into the HTML, escaped as required. */ int renderInsertOther ( struct wasdoc_st *docptr, char *param ) { static char RepeatFao [] = "!#*?\0"; static $DESCRIPTOR (DateFaoDsc, "!%D\0"); static $DESCRIPTOR (DateWidthFaoDsc, "!#%D\0"); static $DESCRIPTOR (TimeFaoDsc, "!%T\0"); static $DESCRIPTOR (TimeWidthFaoDsc, "!#%T\0"); static $DESCRIPTOR (RepeatFaoDsc, RepeatFao); int error, noescape, ignore = 0, number = 0, status, trim = 0; char *cptr, *sptr; char buf [256]; time_t t; struct tm *tmp; $DESCRIPTOR (bdsc, buf); if (dbug>1) dbugThis (FI_LI, "renderInsertOther() %s", dbugAll(param)); if (!param[0]) RETURN_FI_LI (docptr, SS$_BADPARAM) *(cptr = buf) = '\0'; if (MATCH4 (param, "cgi=")) { noescape = docptr->noescape; cptr = param + 4; while (*cptr == '!' || *cptr == '\"') { if (*cptr == '!') ignore = *cptr++; if (*cptr == '\"') noescape = *cptr++; } cptr = CgiLibVarNull (cptr); if (!cptr) if (ignore) { wasDocInsertStatus (docptr, SS$_BADPARAM); return (0); } else RETURN_FI_LI (docptr, SS$_BADPARAM) } else if (MATCH4 (param, "env=")) { noescape = docptr->noescape; cptr = param + 4; while (*cptr == '!' || *cptr == '\"') { if (*cptr == '!') ignore = *cptr++; if (*cptr == '\"') noescape = *cptr++; } cptr = getenv (cptr); if (!cptr) if (ignore) { wasDocInsertStatus (docptr, SS$_BADPARAM); return (0); } else RETURN_FI_LI (docptr, SS$_BADPARAM) } else if (MATCH4 (param, "fao=")) { param += 4; cptr = buf; noescape = docptr->noescape; if (isdigit(*param)) { number = atoi(param); if (number > sizeof(buf)-1) number = sizeof(buf)-1; while (isdigit(*param)) param++; } if (MATCH2 (param, "%D")) if (number) sys$fao (&DateWidthFaoDsc, 0, &bdsc, number, 0); else sys$fao (&DateFaoDsc, 0, &bdsc, 0); else if (MATCH2 (param, "%T")) if (number) sys$fao (&TimeWidthFaoDsc, 0, &bdsc, number, 0); else sys$fao (&TimeFaoDsc, 0, &bdsc, 0); else if (param[0] == '*' && param[1]) { RepeatFao[3] = param[1]; sys$fao (&RepeatFaoDsc, 0, &bdsc, number, 0); } else cptr = NULL; } else if (MATCH5 (param, "file=")) return (renderInsertFile (docptr, param+5, 0)); else if (MATCH12 (param, "locale=ctype")) cptr = setlocale (LC_CTYPE, NULL); else if (MATCH7 (param, "locale=")) cptr = setlocale (atoi(param+8), NULL); else if (MATCH7 (param, "locale")) cptr = setlocale (LC_ALL, NULL); else if (MATCH8 (param, "include=")) return (renderIncludeFile (docptr, param+8)); else if (MATCH8 (param, "Include=")) return (0); else if (MATCH6 (param, "spawn=")) return (SpawnCommand (docptr, param+6, NULL)); else if (MATCH4 (param, "syi=")) cptr = renderGetSyi (docptr, param+4); else if (MATCH4 (param, "time")) { if (param[4] == '=') cptr = param + 5; else cptr = "%a, %d %b %Y %T %z"; t = time(NULL); tmp = localtime(&t); if (!tmp) RETURN_FI_LI (docptr, SS$_BADPARAM) if (!strftime (buf, sizeof(buf), cptr, tmp)) RETURN_FI_LI (docptr, SS$_BADPARAM) cptr = buf; } else if (MATCH4 (param, "lnm=")) { int report = 0; noescape = docptr->noescape; for (cptr = sptr = param + 4; *cptr == '!' || *cptr == '\"'; cptr++) { if (*cptr == '!') report = 1; if (*cptr == '\"') noescape = 1; } cptr = renderTrnLnm (docptr, cptr, report); } else if (MATCH7 (param, "wasdoc=")) { if (MATCH8 (param+7, "version")) cptr = SoftwareVersion; else if (MATCH8 (param+7, "software")) cptr = SoftwareID; else if (MATCH5 (param+7, "path")) cptr = docptr->pinfo; else if (MATCH10 (param+7, "directory")) cptr = docptr->dname; } else if (isupper(param[0])) { /* insert the flag value */ noescape = 1; cptr = renderFlag (docptr, param, 0); } if (!cptr || !*cptr) RETURN_FI_LI (docptr, SS$_BADPARAM) if (noescape) wasDocAsIs (docptr, cptr); else wasDocEntify (docptr, cptr); return (0); } /*****************************************************************************/ /* Insert the content of the specified file into the HTML, escaped as required. */ int renderInsertFile ( struct wasdoc_st *docptr, char *fname, int noescape ) { int bytes, error, ignore = 0; char *cptr, *fnptr, *sptr, *zptr; FILE *ifptr; stat_t fstatbuf; if (dbug>1) dbugThis (FI_LI, "renderInsertFile() %d %s", noescape, dbugAll(fname)); if (!noescape) noescape = docptr->noescape ? 1 : 0; fnptr = fname; while (*fnptr == '!' || *fnptr == '\"') { if (*fnptr == '!') ignore = *fnptr++; if (*fnptr == '\"') noescape = *fnptr++; } if (!*fnptr) { /* empty specification means include self */ if (noescape) wasDocAsIs (docptr, docptr->text); else wasDocEntify (docptr, docptr->text); return (0); } cptr = getenv("PATH"); chdir (docptr->dname); ifptr = fopen (fnptr, "r"); error = vaxc$errno; chdir (cptr); if (!ifptr) { if (!ignore) return (error); wasDocInsertStatus (docptr, error); return (0); } if (fstat (fileno(ifptr), &fstatbuf) < 0) { error = vaxc$errno; fclose (ifptr); if (!ignore) return (error); wasDocInsertStatus (docptr, error); return (0); } /* extra bytes allow EOF when reading past the EOF */ bytes = fstatbuf.st_size; bytes += 32; cptr = sptr = calloc (1, bytes); if (!cptr) exit (vaxc$errno); /* read the entire file into memory */ while (fgets (sptr, bytes-(sptr-cptr), ifptr)) while (*sptr) sptr++; fclose (ifptr); if (noescape) wasDocAsIs (docptr, cptr); else wasDocEntify (docptr, cptr); free (cptr); return (0); } /*****************************************************************************/ /* Insert the content of the specified file into the source TEXT at the current parse position. The parser will then just continue with the included text. If it has a ".css" type then we'll assume style has been set. */ int renderIncludeFile ( struct wasdoc_st *docptr, char *fname ) { static char asis1 [] = "\n|asis|\n", asis2 [] = "||||\n"; int asis = 0, bytes, error, ignore = 0; char *aptr, *cptr, *fnptr, *sptr, *tptr, *zptr; FILE *ifptr; stat_t fstatbuf; if (dbug>1) dbugThis (FI_LI, "renderIncludeFile() %s", dbugAll(fname)); fnptr = fname; while (*fnptr == '!' || *fnptr == '\"') { if (*fnptr == '!') ignore = *fnptr++; if (*fnptr == '\"') asis = *fnptr++; } /* if file extension is ".css" then assume the document has been styled */ for (cptr = fnptr; *cptr; cptr++); while (cptr > fnptr && *cptr != '.' && !MATCH2((cptr-1), "^.")) cptr--; if (!strcasecmp (cptr, ".css")) docptr->styled = 1; if (dbug>1) dbugThis (FI_LI, "%s\n", dbugAll(fnptr)); cptr = getenv("PATH"); chdir (docptr->dname); ifptr = fopen (fnptr, "r"); error = vaxc$errno; chdir (cptr); if (!ifptr) { if (!ignore) return (error); wasDocInsertStatus (docptr, error); return (0); } if (fstat (fileno(ifptr), &fstatbuf) < 0) { error = vaxc$errno; fclose (ifptr); if (ignore) return (0); return (error); } /* extra bytes allow EOF when reading past the EOF */ bytes = fstatbuf.st_size + 32; aptr = sptr = calloc (1, bytes); if (!cptr) exit (vaxc$errno); /* read the entire file into memory */ while (fgets (sptr, bytes-(sptr-aptr), ifptr)) while (*sptr) sptr++; fclose (ifptr); /* adjust bytes to reflect the actual content */ bytes = sptr - aptr; docptr->tsize += bytes; if (asis) { asis = sizeof(asis1)-1 + sizeof(asis2)-1; docptr->tsize += asis; } docptr->text = realloc (docptr->text, docptr->tsize); if (!docptr->text) exit (vaxc$errno); /* current parse position should be just before the end of the tag */ tptr = docptr->text + docptr->tparse; if (MATCH2 (tptr, "||")) tptr++; if (*tptr == '|') tptr++; if (isspace(*tptr)) tptr++; /* first move the current content up */ memmove (tptr + bytes + asis, sptr = tptr, docptr->tlength - (tptr - docptr->text)); /* then insert the text into the space created */ if (asis) for (cptr = asis1; *cptr; *sptr++ = *cptr++); memmove (sptr, aptr, bytes); sptr += bytes; if (asis) for (cptr = asis2; *cptr; *sptr++ = *cptr++); free (aptr); if (isspace(*(tptr-1))) tptr--; docptr->tparse = tptr - docptr->text; docptr->tlength += bytes + asis; return (0); } /*****************************************************************************/ /* The trailing document text needs to be reduced to an alphanumeric ID (fragment). It is bounded by a leading hash. Squash it down to that ID and adjust the document length. */ void renderFragment (struct wasdoc_st *docptr) { char *aptr, *cptr, *sptr; for (sptr = docptr->html + docptr->hlength; sptr > docptr->html && *sptr != '#' && *sptr != '\"' && *sptr != '>'; sptr--); if (dbug>1) dbugThis (FI_LI, "renderFragment() %s", dbugAll(aptr = sptr)); if (*sptr != '#') return; for (cptr = ++sptr; *cptr; cptr++) if (isalnum (*cptr)) *sptr++ = tolower(*cptr); *sptr = '\0'; if (dbug>1) dbugThis (FI_LI, "%s", dbugAll(aptr)); docptr->hlength = sptr - docptr->html; } /*****************************************************************************/ /* Insert a tag and any tag attributes into the HTML. An attribute (&-introduced and |-delimited) string can be used. Characters may be escaped. It a style string. Return zero for success or an error code. */ int renderInsertTag ( struct wasdoc_st *docptr, char *tag ) { int div = 0, clidx, head = 0, idx, image = 0, link = 0, linkimg = 0, list = 0, noescape = 0, note = 0, span = 0, table = 0, tabr = 0, tabh = 0, tabd = 0, tenths = 0; char *cptr, *sptr, *tptr, *zptr; char *align = "", *backlight = "", *blank = "", *bold = "", *inblock = "", *indent = "", *italic = "", *wrap = "", *mono = "", *strike = "", *tabauto = "", *target = "", *under = "", *valign = "", *zero = ""; char *class [8]; char colspan [32] = "", empad [32] = "", fname[64], rowspan [32] = ""; for (clidx = 0; (clidx * sizeof(char*)) < sizeof(class); clidx++) class[clidx] = NULL; clidx = 0; if (HMORE (docptr)) wasDocMoreHtml (docptr); tptr = docptr->text + docptr->tparse; sptr = docptr->html + docptr->hlength; /* right to the end of the buffer */ zptr = docptr->html + docptr->hsize; if (dbug>1) dbugThis (FI_LI, "renderInsertTag() %c%s%c %s", bvbar, tag, bvbar, dbugMax(tptr)); if (!(span = MATCH5 (tag, "bflag[++docptr->bfindex] = 0; if (span) { cptr = "bflag[docptr->bfindex] & BFLAG_CENTER) tabauto = " tabauto"; } else if (tabr) { if (MATCH2 (tptr, "|~")) tptr++; cptr = "notable == NOTE_HEADING) cptr = " class=\"head center"; else cptr = " class=\"head"; } } if (cptr) { while (*cptr && sptr < zptr) *sptr++ = *cptr++; /* span over the tag characters */ if (head) for (tptr++; isdigit(*tptr); tptr++); else for (tptr++; isalpha(*tptr); tptr++); } for (; *tptr; tptr++) { /* special case, cross-reference elipsis */ if (head && MATCH3 (tptr, "...")) break; if (MATCH2 (tptr, "\\\n")) { tptr++; continue; } if ((tabd || tabh) && *tptr >= '1' && *tptr <= '9') { /* table heads and data are allowed digits */ if (tabh || tabd) { if (!colspan[0]) sprintf (colspan, " colspan=\"%c\"", *tptr); else sprintf (rowspan, " rowspan=\"%c\"", *tptr); } continue; } if (*tptr == '&') break; /* rest of string is style */ /* vbar or period or not-punctuation ends markup */ if (*tptr == '|' || *tptr == '.' || *tptr == '\\' || !ispunct(*tptr)) break; /* and if a table element then a space too */ if ((tabr || tabd || tabh) && isspace(*tptr)) break; if (*tptr == '*') bold = " bold"; else if (*tptr == '/') italic = " italic"; else if (*tptr == '_') under = " under"; else if (*tptr == '=') mono = " monosp"; else if (*tptr == '-' && span) strike = " strike"; else if (*tptr == '-' && !span) indent = " noindent"; else if (*tptr == '+' && !span) indent = " indent"; else if (MATCH2 (tptr, "<<")) { wrap = " prewrap"; tptr++; } else if (MATCH2 (tptr, ">>")) { wrap = " nowrap"; tptr++; } else if (MATCH2 (tptr, "><")) { if (table) tabauto = " tabauto"; else align = " center"; if (div) docptr->bflag[docptr->bfindex] |= BFLAG_CENTER; tptr++; } else if (*tptr == '>') { align = " right"; if (div) docptr->bflag[docptr->bfindex] &= ~BFLAG_CENTER; } else if (*tptr == '<') { align = " left"; if (div) docptr->bflag[docptr->bfindex] &= ~BFLAG_CENTER; } else if (MATCH2 (tptr, "#+")) { /* vertical align top */ valign = " valtop"; tptr++; } else if (MATCH2 (tptr, "#=")) { /* vertical align middle */ valign = " valmid"; tptr++; } else if (MATCH2 (tptr, "#-")) { /* vertical align bottom */ valign = " valbot"; tptr++; } else if (MATCH2 (tptr, "#*")) { /* enable row highlight only for any outermost table */ if (tabr && docptr->table == 1 && !docptr->rowlight) docptr->rowlight++; else /* otherwise apply a backlight to the element */ if (!tabr) backlight = " backlight"; tptr++; } else if (MATCH3 (tptr, "#^*")) { /* make this row the same highlight as the previous row */ if (tabr && docptr->table == 1) if (docptr->rowlight & 1) docptr->rowlight--; else if (docptr->rowlight) docptr->rowlight++; tptr += 2; } else if (MATCH3 (tptr, "#!*")) { /* disable row highlight only for any outermost table */ if (tabr && docptr->table == 1) docptr->rowlight = 0; tptr += 2; } else if (MATCH3 (tptr, "#1*")) { /* one-shot to any element but escpecially for table row */ backlight = " backlight"; tptr += 2; } else if (MATCH2 (tptr, "##")) { /* padding in 0.1 ems */ tptr++; tenths = atoi(tptr+1); while (isdigit (*(tptr+1))) tptr++; if (tenths) sprintf (empad, "padding:%1.1fem;", (float)tenths / 10.0); } else if (MATCH3 (tptr, "#::")) { /* inline-block */ inblock = " inblock"; tptr + 2; } else if (*tptr == '#') { if (list) zero = " list0"; else if (tabr || tabh || tabd) zero = " tab0"; else zero = " display0"; } else if (*tptr == '%') { if (link) { if (!linkimg) blank = " blank"; target = " target=\"_blank\""; } else if (image) { /* % used to create a link to the image, see |image| */ tptr++; if (*tptr == '%') tptr++; } } else if (*tptr == '\'') { /* single quote - append this class */ if (tptr[1] == '_' || isalpha(tptr[1])) { class[clidx] = tptr + 1; if ((clidx * sizeof(char*)) < sizeof(class)) clidx++; if (tptr[1] == '_') tptr++; while (isalnum(tptr[1])) tptr++; } } else if (*tptr == '\"') /* 1 is "standalone" and 2 is a /
    */ noescape = docptr->noescape = 2; } /* apply row highlight only for any outermost table */ if (tabr && docptr->rowlight && docptr->table == 1) if (docptr->rowlight++ & 1) backlight = " backlight"; for (cptr = align; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = backlight; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = blank; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = bold; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = inblock; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = indent; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = italic; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = mono; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = strike; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = tabauto; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = under; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = valign; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = wrap; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = zero; *cptr && sptr < zptr; *sptr++ = *cptr++); /* append additional explicitly specified class names */ for (clidx = 0; (clidx * sizeof(char*)) < sizeof(class); clidx++) if (class[clidx]) { if (sptr < zptr) *sptr++ = ' '; for (cptr = class[clidx]; *cptr && (*cptr == '_' || isalnum(*cptr)) && sptr < zptr; *sptr++ = *cptr++); } /* end of classes */ if (sptr < zptr) *sptr++ = '\"'; /* links can have additional attributes */ for (cptr = target; *cptr && sptr < zptr; *sptr++ = *cptr++); /* table rows and columns can have additional attributes */ for (cptr = colspan; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = rowspan; *cptr && sptr < zptr; *sptr++ = *cptr++); if (*tptr == '&' || empad[0]) { /* append style string */ for (cptr = " style=\""; *cptr && sptr < zptr; *sptr++ = *cptr++); if (*tptr == '&') { for (tptr++; *tptr && sptr < zptr;) { if (MATCH2 (tptr, "\\\n")) { tptr++; continue; } if (*tptr == '|') break; if (MATCH2 (tptr, ";.") || MATCH2 (tptr, ";\\")) { *sptr++ = *tptr++; if (MATCH2 (tptr, "\\\n")) continue; break; } if (*tptr == '\\' && tptr[1]) tptr++; *sptr++ = *tptr++; } } for (cptr = empad; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = '\"'; } if (image) for (cptr = " src=\""; *cptr && sptr < zptr; *sptr++ = *cptr++); else if (link) for (cptr = " href=\""; *cptr && sptr < zptr; *sptr++ = *cptr++); else if (sptr < zptr) *sptr++ = '>'; *sptr = '\0'; if (!(head && MATCH3 (tptr, "..."))) { if (*tptr == '.') tptr++; else /* a " flag must be terminated by a period */ if (noescape && *tptr != '\\' && *tptr != '|') RETURN_FI_LI (docptr, SS$_BADPARAM) } if (dbug>1) dbugThis (FI_LI, "%s", dbugMax(tptr)); docptr->tparse = tptr - docptr->text; docptr->hlength = sptr - docptr->html; if (HMORE (docptr)) wasDocMoreHtml (docptr); if (sptr >= zptr) return (SS$_BADPARAM); else return (0); } /*****************************************************************************/ /* Set document style element. If parameter is NULL then default style. */ int renderStyle ( struct wasdoc_st *docptr, char *param ) { int error = 0; char *cptr, *sptr, *zptr; if (dbug>1) dbugThis (FI_LI, "renderStyle() %s", dbugAll(param)); if (!param || MATCH7 (param, "default")) { if (!docptr->styled) wasDocAsIs (docptr, styleDefault); docptr->styled = 1; } else if (MATCH6 (param, "chunk=")) { if (docptr->chunked) wasDocPrintf (docptr, "", param+6); } else if (MATCH5 (param, "file=")) { renderInsertFile (docptr, param+5, 1); docptr->styled = 1; } else if (MATCH6 (param, "sheet=")) { wasDocPrintf (docptr, "\n", param + 6); docptr->styled = 1; } else { for (cptr = param; *cptr && *cptr != '=' && *cptr != '{'; cptr++); if (*cptr == '=') error = SS$_BADPARAM; else wasDocPrintf (docptr, "", param); } return (error); } /*****************************************************************************/ /* Translate a logical name. The parameter name can followed by a semi-colon and a table name, otherwise LNM$FILE_DEV is used. Returns a pointer to the value or NULL if an error (including not existing). Document insight can show the status returned. */ char* renderTrnLnm ( struct wasdoc_st *docptr, char *param, int report ) { static unsigned short slen; static char value [256]; static $DESCRIPTOR (nameDsc, ""); static $DESCRIPTOR (tableDsc, ""); static struct { short int buf_len; short int item; void *buf_addr; unsigned short *ret_len; } items [] = { { 255, LNM$_STRING, value, &slen }, { 0,0,0,0 } }; int status; char *cptr; if (dbug>1) fprintf (stdout, "renderTrnLnm() %s\n", param); /* [;]*/ for (cptr = param; *cptr && *cptr != ';'; cptr++); nameDsc.dsc$a_pointer = param; nameDsc.dsc$w_length = cptr - nameDsc.dsc$a_pointer; if (*cptr == ';') { tableDsc.dsc$a_pointer = ++cptr; while (*cptr) cptr++; tableDsc.dsc$w_length = cptr - tableDsc.dsc$a_pointer; } else { tableDsc.dsc$a_pointer = "LNM$FILE_DEV"; tableDsc.dsc$w_length = 12; } status = sys$trnlnm (0, &tableDsc, &nameDsc, 0, &items); if (dbug>1) fprintf (stdout, "sys$trnlnm() %%X%08.08X\n", status); if (!(status & 1)) { if (!report) { if (docptr->insight >= 5) wasDocInsight (docptr, "%%X%08.08X", status); return (NULL); } sprintf (value, "%%X%08.08X", status); } else value[slen] = '\0'; if (docptr->insight >= 5) wasDocInsight (docptr, "%s", value); return (value); } /*****************************************************************************/ /* Copy a string where a '|' terminates and the '\' escapes the next character. */ int renderGetEscaped ( char *string, char *buf, int size ) { char *cptr, *sptr, *zptr; zptr = (sptr = buf) + size - 1; if (sptr > zptr) return (SS$_RESULTOVF); for (cptr = string; *cptr && sptr < zptr; cptr++) { if (*cptr == '|') break; if (*cptr == '\\' && cptr[1]) cptr++; *sptr++ = *cptr; } *sptr = '\0'; if (sptr > zptr) return (SS$_RESULTOVF); return (0); } /*****************************************************************************/ /* Remove preceding white-space. */ char* renderTrimWhite (struct wasdoc_st *docptr) { char *cptr; while (docptr->hlength && isspace(docptr->html[docptr->hlength-1])) docptr->hlength--; docptr->html[docptr->hlength] = '\0'; cptr = docptr->html + docptr->hlength; if (docptr->hlength) cptr--; return (cptr); } /*****************************************************************************/ /* Return a pointer the string representation of the specified $GETSYI item. Return NULL on error. Definitely not reentrant. */ char* renderGetSyi ( struct wasdoc_st *docptr, char *param ) { static int PrevCgiPlusCount; static ulong SyiClusterEVotes, SyiClusterMember, SyiClusterNodes, SyiClusterQuorum, SyiClusterVotes, SyiMemSize, SyiPageSize; static ulong SyiBootTime [2], time64 [2], upTime64 [2]; static char bootTime [24], clevotes [16], clmember [16], clnodes [16], clquorum [16], clvotes [16], memSize [16], SyiArchName [16], SyiHwName [64], SyiNodeName [16], SyiVersion [12], upTime [24]; static struct { short buf_len; short item; char *buf_addr; short *short_ret_len; } SyiItem [] = { { sizeof(SyiArchName), SYI$_ARCH_NAME, (char*)&SyiArchName, 0 }, { sizeof(SyiBootTime), SYI$_BOOTTIME, (char*)&SyiBootTime, 0 }, { sizeof(SyiClusterEVotes), SYI$_CLUSTER_EVOTES, (char*)&SyiClusterEVotes, 0 }, { sizeof(SyiClusterMember), SYI$_CLUSTER_MEMBER, (char*)&SyiClusterMember, 0 }, { sizeof(SyiClusterNodes), SYI$_CLUSTER_NODES, (char*)&SyiClusterNodes, 0 }, { sizeof(SyiClusterQuorum), SYI$_CLUSTER_QUORUM, (char*)&SyiClusterQuorum, 0 }, { sizeof(SyiClusterVotes), SYI$_CLUSTER_VOTES, (char*)&SyiClusterVotes, 0 }, { sizeof(SyiHwName), SYI$_HW_NAME, (char*)&SyiHwName, 0 }, { sizeof(SyiMemSize), SYI$_MEMSIZE, (char*)&SyiMemSize, 0 }, { sizeof(SyiNodeName), SYI$_NODENAME, (char*)&SyiNodeName, 0 }, { sizeof(SyiPageSize), SYI$_PAGE_SIZE, (char*)&SyiPageSize, 0 }, { sizeof(SyiVersion), SYI$_VERSION, (char*)&SyiVersion, 0 }, { 0,0,0,0 } }; $DESCRIPTOR (bootFaoDsc, "!20%D\0"); $DESCRIPTOR (bootTimeDsc, bootTime); $DESCRIPTOR (upFaoDsc, "!%D"); $DESCRIPTOR (upTimeDsc, upTime); int status; short slen; double fmem; char *cptr = NULL; if (dbug>1) dbugThis (FI_LI, "renderGetSyi() %s", dbugAll(param)); if (!param[0]) return (NULL); if (CgiPlusCount > PrevCgiPlusCount) { PrevCgiPlusCount = CgiPlusCount; SyiArchName[0] = '\0'; } if (!SyiArchName[0]) if ((status = sys$getsyi (0, 0, 0, &SyiItem, 0, 0, 0)) & 1) { fmem = (double)SyiMemSize * (double)SyiPageSize; if (fmem >= 1073741824.0) sprintf (memSize, "%.02fGB", fmem / 1073741824.0); else if (fmem >= 1048576.0) sprintf (memSize, "%.02fMB", fmem / 1048576.0); else sprintf (memSize, "%.02fkB", fmem / 1024.0); sys$fao (&bootFaoDsc, 0, &bootTimeDsc, &SyiBootTime); sys$gettim (&time64); lib$sub_times (&time64, &SyiBootTime, &upTime64); sys$fao (&upFaoDsc, &slen, &upTimeDsc, &upTime64); upTime[slen-3] = '\0'; } else { sprintf (SyiArchName, "%%X%08.08X", status); strcpy (bootTime, SyiArchName); strcpy (memSize, SyiArchName); strcpy (SyiHwName, SyiArchName); strcpy (SyiNodeName, SyiArchName); strcpy (SyiVersion, SyiArchName); strcpy (upTime, SyiArchName); SyiMemSize = 0; } if (MATCH7 (param, "arch_name")) cptr = SyiArchName; else if (MATCH8 (param, "cluster=")) { if (MATCH6 (param+8, "evotes")) sprintf (cptr = clevotes, "%d", SyiClusterEVotes); else if (MATCH6 (param+8, "member")) sprintf (cptr = clmember, "%d", SyiClusterMember); else if (MATCH5 (param+8, "nodes")) sprintf (cptr = clnodes, "%d", SyiClusterNodes); else if (MATCH6 (param+8, "quorum")) sprintf (cptr = clquorum, "%d", SyiClusterQuorum); else if (MATCH5 (param+8, "votes")) sprintf (cptr = clvotes, "%d", SyiClusterVotes); } else if (MATCH8 (param, "boottime")) cptr = bootTime; else if (MATCH7 (param, "hw_name")) cptr = SyiHwName; else if (MATCH7 (param, "memsize")) cptr = memSize; else if (MATCH8 (param, "nodename")) cptr = SyiNodeName; else if (MATCH6 (param, "uptime")) cptr = upTime; else if (MATCH7 (param, "version")) cptr = SyiVersion; if (!cptr || !*cptr) return (NULL); return (cptr); } /*****************************************************************************/ /* "n." or "n.n" or "n.n.n" or ... */ char* renderNumericId (int *digit6) { static char buf [48]; int cnt, idx; char *sptr; sptr = buf; for (cnt = 6; cnt > 1 && !digit6[cnt]; cnt--); for (idx = 1; idx <= cnt; idx++) sptr += sprintf (sptr, "%d.", digit6[idx]); if (cnt > 1) *(sptr-1) = '\0'; if (dbug>1) dbugThis (FI_LI, "renderNumericId() %s", buf); return (buf); } /*****************************************************************************/ /* Delineate a tag. See module prologue for a description of tag syntax. */ void renderInsightTag (struct wasdoc_st *docptr) { char *aptr, *cptr, *sptr, *tptr, *zptr; char buf [256]; tptr = docptr->text + docptr->tparse; if (dbug>1) dbugThis (FI_LI, "renderInsightTag() %s", dbugMax(tptr)); if (MATCH6 (tptr, "\n||||\n")) { wasDocInsight (docptr, "||||"); return; } if (MATCH2 (tptr, "||") || MATCH2 (tptr, "| ") || MATCH2 (tptr, "|\n") || MATCH2 (tptr, "|\t")) { wasDocInsight (docptr, "||"); return; } if (MATCH2 (tptr, "|!")) { for (cptr = tptr + 2; *cptr && *cptr != '|'; cptr++); wasDocInsight (docptr, "%*.*s", cptr-tptr+1, cptr-tptr+1, tptr); return; } if (MATCH5 (tptr, "|...|") || MATCH5 (tptr, "|:::|")) { wasDocInsight (docptr, "%5.5s", tptr); return; } if (MATCH4 (tptr, "|--|")) { wasDocInsight (docptr, "%4.4s", tptr); return; } if (MATCH3 (tptr, "|-|") || MATCH3 (tptr, "|^+") || MATCH3 (tptr, "|^-")) { wasDocInsight (docptr, "%3.3s", tptr); return; } if (MATCH3 (tptr, "|^ ") || MATCH3 (tptr, "|^\t") || MATCH3 (tptr, "|^\n")) { wasDocInsight (docptr, "%2.2s", tptr); return; } if (MATCH2 (tptr, "|0") || MATCH2 (tptr, "|1") || MATCH2 (tptr, "|2") || MATCH2 (tptr, "|3") || MATCH2 (tptr, "|4") || MATCH2 (tptr, "|9")) { if (docptr->insight >= 5) { for (cptr = tptr + 1; *cptr && *cptr != '|'; cptr++); wasDocInsight (docptr, "%*.*s|", cptr-tptr, cptr-tptr, tptr); } else if (docptr->insight >= 4) { for (cptr = tptr + 1; *cptr && !isalpha(*cptr); cptr++); wasDocInsight (docptr, "%*.*s|", cptr-tptr, cptr-tptr, tptr); } else wasDocInsight (docptr, "%2.2s", tptr); return; } if (MATCH2 (tptr, "|~") || MATCH2 (tptr, "|:") || MATCH2 (tptr, "|.") || MATCH2 (tptr, "|&")) { if (docptr->insight >= 5) for (cptr = tptr; *cptr && !isspace(*cptr); cptr++); else cptr = tptr + 2; wasDocInsight (docptr, "%*.*s", cptr-tptr, cptr-tptr, tptr); return; } if (*tptr == '|' && ispunct(tptr[1])) { zptr = (sptr = buf) + sizeof(buf)-1; *sptr++ = *tptr++; for (cptr = tptr; *cptr && sptr < zptr; cptr++) { if (*cptr == '&' || *cptr == '\\' || *cptr == '.' || *cptr == '|') { if (*cptr == '&') *sptr++ = *cptr; break; } if (*cptr == '\'') { *sptr++ = *cptr; for (cptr++; isalpha(*cptr); cptr++); continue; } if (isalpha(*cptr)) break; *sptr++ = *cptr; } *sptr = '\0'; wasDocInsight (docptr, "%s", buf); return; } if ((MATCH5 (tptr, "|asis") && ispunct(tptr[5])) || (MATCH6 (tptr, "|block") && ispunct(tptr[6])) || (MATCH6 (tptr, "|break")&& ispunct(tptr[6])) || (MATCH7 (tptr, "|bullet") && ispunct(tptr[7])) || (MATCH5 (tptr, "|code") && ispunct(tptr[5])) || (MATCH5 (tptr, "|data") && ispunct(tptr[5])) || (MATCH5 (tptr, "|draw") && ispunct(tptr[5])) || (MATCH5 (tptr, "|elif") && ispunct(tptr[5])) || (MATCH5 (tptr, "|else") && ispunct(tptr[5])) || (MATCH6 (tptr, "|endif") && ispunct(tptr[6])) || (MATCH8 (tptr, "|example") && ispunct(tptr[8])) || (MATCH5 (tptr, "|exit") && ispunct(tptr[5])) || (MATCH5 (tptr, "|head") && ispunct(tptr[5])) || (MATCH5 (tptr, "|hide") && ispunct(tptr[5])) || (MATCH6 (tptr, "|image") && ispunct(tptr[6])) || (MATCH6 (tptr, "|index") && ispunct(tptr[6])) || (MATCH7 (tptr, "|insert") && ispunct(tptr[7])) || (MATCH9 (tptr, "|insight!") && ispunct(tptr[9])) || (MATCH9 (tptr, "|insight@") && ispunct(tptr[9])) || (MATCH3 (tptr, "|if") && ispunct(tptr[3])) || (MATCH7 (tptr, "|inline") && ispunct(tptr[7])) || (MATCH6 (tptr, "|insert") && ispunct(tptr[6])) || (MATCH5 (tptr, "|item") && ispunct(tptr[5])) || (MATCH3 (tptr, "|li") && ispunct(tptr[3])) || (MATCH5 (tptr, "|link") && ispunct(tptr[5])) || (MATCH7 (tptr, "|mailto") && ispunct(tptr[7])) || (MATCH5 (tptr, "|mono") && ispunct(tptr[5])) || (MATCH5 (tptr, "|note") && ispunct(tptr[5])) || (MATCH8 (tptr, "|noprint") && ispunct(tptr[8])) || (MATCH7 (tptr, "|number") && ispunct(tptr[7])) || (MATCH3 (tptr, "|ol") && ispunct(tptr[3])) || (MATCH6 (tptr, "|ppage") && ispunct(tptr[6])) || (MATCH6 (tptr, "|print") && ispunct(tptr[6])) || (MATCH5 (tptr, "|prop") && ispunct(tptr[5])) || (MATCH6 (tptr, "|quote") && ispunct(tptr[6])) || (MATCH4 (tptr, "|row") && ispunct(tptr[4])) || (MATCH3 (tptr, "|tr") && ispunct(tptr[3])) || (MATCH4 (tptr, "|set") && ispunct(tptr[4])) || (MATCH7 (tptr, "|simple") && ispunct(tptr[7])) || (MATCH3 (tptr, "|sl") && ispunct(tptr[3])) || (MATCH6 (tptr, "|style") && ispunct(tptr[6])) || (MATCH6 (tptr, "|table") && ispunct(tptr[6])) || (MATCH8 (tptr, "|tabular") && ispunct(tptr[8])) || (MATCH4 (tptr, "|toc") && ispunct(tptr[4])) || (MATCH3 (tptr, "|ul") && ispunct(tptr[3]))) { if (docptr->insight >= 5) { if (MATCH4 (tptr, "|set") || MATCH6 (tptr, "|style")) { for (cptr = tptr + 1; *cptr && *cptr != '|'; cptr++); if (*cptr) cptr++; while (*cptr && *cptr != '|') cptr++; } else for (cptr = tptr + 1; *cptr; cptr++) if (*cptr == '\\' || *cptr == '.' || *cptr == '|') break; } else for (cptr = tptr + 1; isalpha(*cptr); cptr++); wasDocInsight (docptr, "%*.*s|", cptr-tptr, cptr-tptr, tptr); return; } cptr = tptr + 1; if (*cptr == '%') cptr++; while (*cptr && cptr < tptr+8 && isalpha(*cptr)) cptr++; if (MATCH3 (cptr, "://")) { wasDocInsight (docptr, "|link|"); return; } wasDocInsight (docptr, "\?\?\?"); } /*****************************************************************************/ /* Display the source text file name. */ void renderInsightSource ( struct wasdoc_st *docptr, char *tptr ) { char *cptr; for (cptr = tptr; *cptr && *cptr != ' '; cptr++); wasDocInsight (docptr, "%*.*s", cptr-tptr, cptr-tptr, tptr); } /*****************************************************************************/ /* Document the stack contents. */ void renderInsightStack (struct wasdoc_st *docptr) { int idx; char *aptr, *cptr, *sptr, *zptr; char buf [1024]; for (idx = 1; idx < STACK_MAX && docptr->cstack[idx]; idx++); idx--; for (; idx; idx--) { if (!docptr->cstack[idx]) continue; zptr = (sptr = buf) + sizeof(buf)-1; sptr += sprintf (sptr, "☰%d%s", idx, idx == docptr->stindex ? "✓ ※" : " ※"); for (cptr = docptr->cstack[idx]; *cptr && *cptr != '\n'; cptr++) { aptr = NULL; if (*cptr == '<') aptr = "<"; if (*cptr == '>') aptr = ">"; if (*cptr == '&') aptr = "&"; if (aptr) while (*aptr && sptr < zptr) *sptr++ = *aptr++; else if (sptr < zptr) *sptr++ = *cptr; } for (cptr = " ※"; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = docptr->tstack[idx]; *cptr && *cptr != '\n'; cptr++) { aptr = NULL; if (*cptr == '<') aptr = "<"; if (*cptr == '>') aptr = ">"; if (*cptr == '&') aptr = "&"; if (aptr) while (*aptr && sptr < zptr) *sptr++ = *aptr++; else if (sptr < zptr) *sptr++ = *cptr; } *sptr = '\0'; wasDocInsight (docptr, "%s", buf); } } /*****************************************************************************/