/*****************************************************************************/ /* liner.c Display a file as plain-text (actually escaped HTML), prefixing each line with an ascending line number in square brackets. The response does everything it can to ensure content is not cached so any repeat access should be just as expensive as the original :-) Two-pass formatting to allow cut-and-paste unencumbered by line numbers. Adding a hash fragment integer to the URL (e.g. "/liner.c#100") will cause the browser to move the window to that line number in the source (if it exists). Appending "$.txt" or "$.htm" to any file name (in fact "$.anythingatall") is allowed to try and get certain operating systems (whose name cannot be spoken) to treat the content as text (i.e. not using the returned content-type, rather the trailing three characters of the file name). If the supplied file name cannot be opened the utility removes any faux "$.extension" and attempts to open the resulting file name. Can be accessed using /cgi-bin/liner/path/to/file.txt or abbreviated using the WASD_CONFIG_MAP mapping rule script /liner/* /cgi-bin/liner* and used as /liner/path/to/file.txt A directory structure (e.g. source code tree) can have this mapping applied to all file accesses using the "?httpd=index" facility. For example /wasd_root/src/*.*?httpd=index&script=liner SYNTAX HIGHLIGHTING ------------------- To add syntax highlighting for displayed text go to https://highlightjs.org/download/ and download a custom package with the languages of interest. Restore the ZIPed archive into the source directory (or other web server accessible location). $ SET DEFAULT WASD_ROOT:[src] $ CREATE /DIRECTORY [.highlightjs] $ SET DEFAULT [.highlightjs] $ CREATE .www_hidden ^Z $ UNZIP HIGHLIGHT.ZIP To activate LINER.C syntax highlighting define a logical name with the path to the highlight directory. $ DEFINE /SYSTEM /EXEC WASD_LINER_HIGHLIGHTJS_PATH - "/wasd_root/src/highlightjs/" Themes and rendering may be explored at https://highlightjs.org/static/demo/ An alternate theme may be set using the logical name. $ DEFINE /SYSTEM /EXEC WASD_LINER_HIGHLIGHTJS_THEME "xcode" And by inclusion in the directory URL. /wasd_root/src/*.*?httpd=index&script=liner&theme=xcode File types may be explicitly provided with a language type. The format is a one or more comma-separated <.type>= defined in a logical name. For example; the following renders .COB and .COM files as plain text. $ DEFINE /SYSTEM /EXEC WASD_LINER_HIGHLIGHTJS_TYPE - ".cob=plaintext,.com=plaintext" A wrapper procedure and symbols alternatively could be used. $! CGI_BIN:LINER.COM $ WASD_LINER_HIGHLIGHTJS_PATH = "/wasd_root/src/highlightjs/" $ WASD_LINER_HIGHLIGHTJS_THEME = "xcode" $ WASD_LINER_HIGHLIGHTJS_TYPE = ".cob=plaintext,.com=plaintext" $ MCR CGI_EXE:LINER BUILD DETAILS ------------- $ @BUILD_LINER BUILD !compile+link $ @BUILD_LINER LINK !link-only COPYRIGHT --------- Copyright (C) 2013-2022 Mark G.Daniel Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. VERSION HISTORY --------------- 20-JAN-2022 MGD v2.0.0, adapt for https://highlightjs.org 13-MAY-2015 MGD v1.0.1, bugfix; closing and 23-MAY-2013 MGD v1.0.0, initial (q&d) development */ /*****************************************************************************/ #define SOFTWAREVN "2.0.0" #define SOFTWARENM "LINER" #ifdef __ALPHA # define SOFTWAREID SOFTWARENM " AXP-" SOFTWAREVN #endif #ifdef __ia64 # define SOFTWAREID SOFTWARENM " IA64-" SOFTWAREVN #endif #ifdef __VAX # error VAX no longer implemented #endif #ifdef __x86_64 # define SOFTWAREID SOFTWARENM " X86-" SOFTWAREVN #endif /* standard C header files */ #include #include #include #include #include /* CGILIB header file */ #include "cgilib.h" /* macros */ #define FI_LI __FILE__, __LINE__ /*****************************************************************************/ /* */ main (int argc, char** argv) { int idx, LineCount = 0; char *aptr, *cptr, *hptr, *lptr, *sptr, *tptr; char LangClass [256], Line [1024], HtmlEnc [sizeof(Line)*6]; /* worst case */ FILE *fptr; /*********/ /* begin */ /*********/ CgiLibEnvironmentInit (argc, argv, 0); CgiLibResponseSetErrorMessage ("Reported by LINER"); if (strcmp (CgiLibVar ("REQUEST_METHOD"), "GET")) { CgiLibResponseSetErrorStatus (501); CgiLibResponseError (FI_LI, 0, "Only supports GET method!"); return; } aptr = CgiLibVar ("PATH_INFO"); if (!*aptr) { CgiLibResponseSetErrorStatus (501); CgiLibResponseError (FI_LI, 0, "Supply a file name!"); return; } sptr = CgiLibVar ("PATH_TRANSLATED"); if (!*sptr) { CgiLibResponseSetErrorStatus (501); CgiLibResponseError (FI_LI, 0, "PATH_TRANSLATED?"); return; } cptr = CgiLibVar ("CONTENT_TYPE"); if (strncasecmp (cptr, "text/", 5)) { CgiLibResponseSetErrorStatus (501); CgiLibResponseError (FI_LI, 0, "Not a \"text/\" MIME type."); return; } if ((fptr = fopen (sptr, "r", "shr=get")) == NULL) { for (cptr = sptr; *cptr; cptr++); while (cptr > sptr && *cptr != '$' && *cptr != ']') cptr--; if (*cptr == '$' && *(cptr+1) == '.') { /* chop these off and see if that opens */ *cptr-- = '\0'; while (cptr > sptr && *cptr != ']' && *cptr != '.' && *cptr != '$') cptr--; if (cptr > sptr && *cptr == '.') { if (cptr > sptr && *(cptr-1) == '^') { /* eliminate the EFS escape */ for (; *cptr; cptr++) *(cptr-1) = *cptr; *(cptr-1) = '\0'; } fptr = fopen (sptr, "r", "shr=get"); } else if (cptr > sptr && *cptr == '$') { /* non-EFS convert substituted dollar back to a period */ *cptr = '.'; fptr = fopen (sptr, "r", "shr=get"); } } if (fptr == NULL) { CgiLibResponseSetErrorStatus (404); CgiLibResponseError (FI_LI, vaxc$errno, aptr); return; } lptr = cptr; } else { for (cptr = sptr; *cptr; cptr++); while (cptr > sptr && *cptr != '.' && *cptr != ']') cptr--; lptr = cptr; } if (hptr = getenv ("WASD_LINER_HIGHLIGHTJS_PATH")) { if (!(tptr = getenv ("WASD_LINER_HIGHLIGHTJS_THEME"))) tptr = "default"; if (cptr = CgiLibVarNull ("FORM_THEME")) tptr = cptr; aptr = getenv ("WASD_LINER_HIGHLIGHTJS_TYPE"); } else aptr = NULL; if (hptr && aptr) { while (*aptr) { cptr = aptr; while (*aptr && *aptr != '=') aptr++; if (!*aptr) break; *aptr++ = '\0'; if (!strcasecmp (lptr, cptr)) { lptr = aptr; while (*aptr && *aptr != ',') aptr++; *aptr = '\0'; aptr = "\a"; break; } while (*aptr && *aptr != ',') aptr++; if (*aptr) aptr++; } if (*aptr == '\a') { sprintf (LangClass, " class=\"language-%s\"", lptr); lptr = LangClass; } else lptr = ""; } else lptr = ""; if ((stdout = freopen ("SYS$OUTPUT", "w", stdout, "ctx=bin")) == NULL) exit (vaxc$errno); CgiLibResponseSetStreamMode (1); aptr = CgiLibVar ("PATH_INFO"); CgiLibResponseHeader (200, "text/html", "Expires: Fri, 13 Jan 1978 14:00:00 GMT\r\n\ Cache-Control: no-cache, no-store, private, max-age=0, max-stale=0, \ must-revalidate, pre-check=0, post-check=0\r\n\ Pragma: no-cache\r\n"); for (cptr = aptr; *cptr; cptr++); while (cptr > aptr && *cptr != '/') cptr--; if (*cptr == '/') cptr++; fprintf (stdout, "\n\ \n\ \n\ %s\n\ \n\ \n", cptr, SOFTWAREID, hptr ? " margin-top:1em;" : ""); if (hptr) fprintf (stdout, "\n\ \n\ \n", hptr, tptr, hptr); fprintf (stdout, "\n\ \n\
");

   while (fgets (Line, sizeof(Line), fptr) != NULL)
   {
      LineCount++;
      if (LineCount <= 9999)
         cptr = "[%04.04d]\n";
      else
      if (LineCount <= 99999)
         cptr = "[%05.05d]\n";
      else
         cptr = "[%06.06d]\n";
      fprintf (stdout, cptr, LineCount, LineCount);
   }

   rewind (fptr);
   fprintf (stdout, "
\n
", lptr);

   while (fgets (Line, sizeof(Line), fptr) != NULL)
   {
      sptr = HtmlEnc;
      for (cptr = Line; *cptr; cptr++)
      {
         switch (*cptr)
         {
             /* control characters with a bullet symbol */
             case  0 : case  1 : case  2 : case  3 :
             case  4 : case  5 : case  6 : case  7 :
             case  8 :                     case 11 :
             case 12 :           case 14 : case 15 :
             case 16 : case 17 : case 18 : case 19 :
             case 20 : case 21 : case 22 : case 23 :
             case 24 : case 25 : case 26 : case 27 :
             case 28 : case 29 : case 30 : case 31 :
                        *(unsigned long*)sptr = '&bul'; sptr += 4;
                        *(unsigned short*)sptr = 'l;';  sptr += 2; break;
             /* HTML reserved */
             case '<' : *(unsigned long*)sptr = '<'; sptr += 4; break;
             case '>' : *(unsigned long*)sptr = '>'; sptr += 4; break;
             case '&' : *(unsigned long*)sptr = '&'; sptr += 4;
                        *sptr++ = ';'; break;
             /* the great unwashed */
             default : *sptr++ = *cptr;
         }
      }
      *sptr = '\0';
      fputs (HtmlEnc, stdout);
   }

   fputs ("
\n\n\n", stdout); fclose (fptr); } /*****************************************************************************/