home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Divers / lynx2.8.1dev.10.tar.gz / lynx2.8.1dev.10.tar / lynx2-8 / src / LYCgi.c < prev    next >
C/C++ Source or Header  |  1998-04-23  |  17KB  |  643 lines

  1. /*                   Lynx CGI support                              LYCgi.c
  2. **                   ================
  3. **
  4. ** Authors
  5. **          GL      George Lindholm <George.Lindholm@ubc.ca>
  6. **
  7. ** History
  8. **      15 Jun 95   Created as way to provide a lynx based service with
  9. **                  dynamic pages without the need for a http daemon. GL
  10. **      27 Jun 95   Added <index> (command line) support. Various cleanup
  11. **                  and bug fixes. GL
  12. **    04 Sep 97   Added support for PATH_INFO scripts.  JKT
  13. **
  14. ** Bugs
  15. **      If the called scripts aborts before sending the mime headers then
  16. **      lynx hangs.
  17. **
  18. **      Should do something about SIGPIPE, (but then it should never happen)
  19. **
  20. **      No support for redirection. Or mime-types.
  21. **
  22. **      Should try and parse for a HTTP 1.1 header in case we are "calling" a
  23. **      nph- script.
  24. */ 
  25.  
  26. #include <HTUtils.h>
  27. #include <tcp.h>
  28. #include <HTTP.h>
  29. #include <HTParse.h>
  30. #include <HTTCP.h>
  31. #include <HTFormat.h>
  32. #include <HTFile.h>
  33. #include <HTAlert.h>
  34. #include <HTMIME.h>
  35. #include <HTAABrow.h>
  36.  
  37. #include <LYGlobalDefs.h>
  38. #include <LYUtils.h>
  39. #include <HTML.h>
  40. #include <HTInit.h>
  41. #include <LYGetFile.h>
  42. #include <LYBookmark.h>
  43. #include <GridText.h>
  44. #include <ctype.h>
  45. #include <LYCgi.h>
  46. #include <LYSignal.h>
  47. #include <LYLocal.h>
  48.  
  49. #include <LYLeaks.h>
  50.  
  51. #define FREE(x) if (x) {free(x); x = NULL;}
  52.  
  53. struct _HTStream 
  54. {
  55.   HTStreamClass * isa;
  56. };
  57.  
  58. PRIVATE char **env = NULL;  /* Environment variables */
  59. PRIVATE int envc_size = 0;  /* Slots in environment array */
  60. PRIVATE int envc = 0;        /* Slots used so far */
  61. #ifdef LYNXCGI_LINKS
  62. PRIVATE char user_agent[64];
  63. PRIVATE char server_software[64];
  64. #endif /* LYNXCGI_LINKS */
  65.  
  66. PRIVATE void add_environment_value PARAMS((char *env_value));
  67.  
  68.  
  69. /*
  70.  * Simple routine for expanding the environment array and adding a value to
  71.  * it
  72.  */
  73. PRIVATE void add_environment_value ARGS1(
  74.     char *,    env_value)
  75. {
  76.     if (envc == envc_size) {   /* Need some more slots */
  77.     envc_size += 10;
  78.     if (env)
  79.         env = (char **)realloc(env,
  80.                    sizeof(env[0]) * (envc_size + 2));
  81.                         /* + terminator and base 0 */
  82.     else
  83.         env = (char **)malloc(sizeof(env[0]) * (envc_size + 2));
  84.                         /* + terminator and base 0 */
  85.     if (env == NULL) {
  86.         outofmem(__FILE__, "LYCgi");
  87.     }
  88.     }
  89.  
  90.     env[envc++] = env_value;
  91.     env[envc] = NULL;      /* Make sure it is always properly terminated */
  92. }
  93.     
  94. /*
  95.  * Add the value of an existing environment variable to those passed on to the
  96.  * lynxcgi script.
  97.  */
  98. PUBLIC void add_lynxcgi_environment ARGS1(
  99.     CONST char *,    variable_name)
  100. {
  101.     char *env_value;
  102.  
  103.     env_value = getenv(variable_name);
  104.     if (env_value != NULL) {
  105.     char *add_value = NULL;
  106.  
  107.     add_value = (char *)malloc(strlen(variable_name) +
  108.                    strlen(env_value) + 2);
  109.     if (add_value == NULL) {
  110.         outofmem(__FILE__, "LYCgi");
  111.     }
  112.     strcpy(add_value, variable_name);
  113.     strcat(add_value, "=");
  114.     strcat(add_value, env_value);
  115.     add_environment_value(add_value);
  116.     }
  117. }
  118.  
  119. PRIVATE int LYLoadCGI ARGS4(
  120.     CONST char *,         arg,
  121.     HTParentAnchor *,    anAnchor,
  122.     HTFormat,        format_out,
  123.     HTStream*,        sink)
  124. {
  125.     int status;
  126. #ifdef LYNXCGI_LINKS
  127. #ifndef VMS
  128.     char *cp;
  129.     struct stat stat_buf;
  130.     char *pgm = NULL;                /* executable */
  131.     char *pgm_args = NULL;            /* and its argument(s) */
  132.     int statrv;
  133.     char *orig_pgm = NULL;        /* Path up to ? as given, URL-escaped*/
  134.     char *document_root = NULL;        /* Corrected value of DOCUMENT_ROOT  */
  135.     char *path_info = NULL;             /* PATH_INFO extracted from pgm      */
  136.     char *pgm_buff = NULL;        /* PATH_INFO extraction buffer       */
  137.     char *path_translated;        /* From document_root/path_info      */
  138.  
  139.     if (!arg || !*arg || strlen(arg) <= 8) {
  140.     HTAlert(BAD_REQUEST);
  141.     status = -2;
  142.     return(status);
  143.  
  144.     } else {
  145.     if (strncmp(arg, "lynxcgi://localhost", 19) == 0) {
  146.         StrAllocCopy(pgm, arg+19);
  147.     } else {
  148.         StrAllocCopy(pgm, arg+8);
  149.     }
  150.     if ((cp=strchr(pgm, '?')) != NULL) { /* Need to terminate executable */
  151.         *cp++ = '\0';
  152.         pgm_args = cp;
  153.     }
  154.     }
  155.  
  156.     StrAllocCopy(orig_pgm, pgm);
  157.     if ((cp=strchr(pgm, '#')) != NULL) {
  158.     /*
  159.      *  Strip a #fragment from path.  In this case any pgm_args
  160.      *  found above will also be bogus, since the '?' came after
  161.      *  the '#' and is part of the fragment.  Note that we don't
  162.      *  handle the case where a '#' appears after a '?' properly
  163.      *  according to URL rules. - kw
  164.      */
  165.     *cp = '\0';
  166.     pgm_args = NULL;
  167.     }
  168.     HTUnEscape(pgm);
  169.  
  170.     /* BEGIN WebSter Mods */
  171.     /* If pgm is not stat-able, see if PATH_INFO data is at the end of pgm */
  172.     if ((statrv = stat(pgm, &stat_buf)) < 0) {
  173.     StrAllocCopy(pgm_buff, pgm);
  174.     while (statrv < 0 || (statrv = stat(pgm_buff, &stat_buf)) < 0) {
  175.         if ((cp=strrchr(pgm_buff, '/')) != NULL) {
  176.         *cp = '\0';
  177.         statrv = 999;    /* force new stat()  - kw */
  178.         } else {
  179.         if (TRACE)
  180.             perror("LYNXCGI: strrchr(pgm_buff, '/') returned NULL");
  181.             break;
  182.         }
  183.         }
  184.  
  185.     if (statrv < 0) {
  186.         /* Did not find PATH_INFO data */
  187.         if (TRACE) 
  188.         perror("LYNXCGI: stat() of pgm_buff failed");
  189.     } else {
  190.         /* Found PATH_INFO data. Strip it off of pgm and into path_info. */
  191.         StrAllocCopy(path_info, pgm+strlen(pgm_buff));
  192.         strcpy(pgm, pgm_buff);
  193.         CTRACE(tfp, "LYNXCGI: stat() of %s succeeded, path_info=\"%s\".\n",
  194.             pgm_buff, path_info);
  195.     }
  196.     FREE(pgm_buff);
  197.     }
  198.     /* END WebSter Mods */
  199.  
  200.     if (statrv != 0) {
  201.     /*
  202.      *  Neither the path as given nor any components examined by
  203.      *  backing up were stat()able. - kw
  204.      */
  205.     HTAlert("Unable to access cgi script");
  206.     if (TRACE) {
  207.         perror("LYNXCGI: stat() failed");
  208.     }
  209.     status = -4;
  210.  
  211.     } else if (!(S_ISREG(stat_buf.st_mode) &&
  212.          stat_buf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
  213.     /*
  214.      *  Not a runnable file, See if we can load it using "file:" code.
  215.      */
  216.     char *new_arg = NULL;
  217.  
  218.     /*
  219.      *  But try "file:" only if the file we are looking at is the path
  220.      *  as given (no path_info was extracted), otherwise it will be
  221.      *  to confusing to know just what file is loaded. - kw
  222.      */
  223.     if (path_info) {
  224.         CTRACE(tfp, "%s is not a file and %s not an executable, giving up.\n",
  225.             orig_pgm, pgm);
  226.         FREE(path_info);
  227.         FREE(pgm);
  228.         FREE(orig_pgm);
  229.         status = -4;
  230.         return(status);
  231.     }
  232.         
  233.     StrAllocCopy(new_arg, "file://localhost");
  234.     StrAllocCat(new_arg, orig_pgm);
  235.  
  236.     CTRACE(tfp, "%s is not an executable file, passing the buck.\n", arg);
  237.     status = HTLoadFile(new_arg, anAnchor, format_out, sink);
  238.     FREE(new_arg);
  239.  
  240.     } else if (path_info &&
  241.            anAnchor != HTMainAnchor &&
  242.            !(reloading && anAnchor->document) &&
  243.            strcmp(arg, HTLoadedDocumentURL()) &&
  244.            HText_AreDifferent(anAnchor, arg) &&
  245.            HTUnEscape(orig_pgm) &&
  246.            !exec_ok(HTLoadedDocumentURL(), orig_pgm,
  247.             CGI_PATH)) { /* exec_ok gives out msg. */
  248.     /*
  249.      *  If we have extra path info and are not just reloading
  250.      *  the current, check the full file path (after unescaping)
  251.      *  now to catch forbidden segments. - kw
  252.      */
  253.     status = HT_NOT_LOADED;
  254.  
  255.     } else if (no_lynxcgi) {
  256.     _statusline(CGI_DISABLED);
  257.     sleep(MessageSecs);
  258.     status = HT_NOT_LOADED;
  259.  
  260.     } else if (no_bookmark_exec &&
  261.            anAnchor != HTMainAnchor &&
  262.            !(reloading && anAnchor->document) &&
  263.            strcmp(arg, HTLoadedDocumentURL()) &&
  264.            HText_AreDifferent(anAnchor, arg) &&
  265.             HTLoadedDocumentBookmark()) {
  266.     /*
  267.      *  If we are reloading a lynxcgi document that had already been
  268.      *  loaded, the various checks above should allow it even if
  269.      *  no_bookmark_exec is TRUE an we are not now coming from a
  270.      *  bookmark page. - kw
  271.      */
  272.     _statusline(BOOKMARK_EXEC_DISABLED);
  273.     sleep(MessageSecs);
  274.     status = HT_NOT_LOADED;
  275.  
  276.     } else if (anAnchor != HTMainAnchor &&
  277.            !(reloading && anAnchor->document) &&
  278.            strcmp(arg, HTLoadedDocumentURL()) &&
  279.            HText_AreDifferent(anAnchor, arg) &&
  280.            !exec_ok(HTLoadedDocumentURL(), pgm,
  281.             CGI_PATH)) { /* exec_ok gives out msg. */
  282.     /*
  283.      *  If we are reloading a lynxcgi document that had already been
  284.      *  loaded, the various checks above should allow it even if
  285.      *  exec_ok() would reject it because we are not now coming from
  286.      *  a document with a URL allowed by TRUSTED_LYNXCGI rules. - kw
  287.      */
  288.     status = HT_NOT_LOADED;
  289.  
  290.     } else {
  291.     HTFormat format_in;
  292.     HTStream *target  = NULL;        /* Unconverted data */
  293.     int fd1[2], fd2[2];
  294.     char buf[1024];
  295.     pid_t pid;
  296. #if HAVE_TYPE_UNIONWAIT
  297.     union wait wstatus;
  298. #else
  299.     int wstatus;
  300. #endif
  301.  
  302.     if (anAnchor->isHEAD || keep_mime_headers) {
  303.  
  304.         /* Show output as plain text */
  305.         format_in = WWW_PLAINTEXT;
  306.     } else {
  307.         
  308.         /* Decode full HTTP response */
  309.         format_in = HTAtom_for("www/mime");
  310.     }
  311.         
  312.     target = HTStreamStack(format_in,
  313.                    format_out,
  314.                    sink, anAnchor);
  315.         
  316.     if (!target || target == NULL) {
  317.         sprintf(buf, CANNOT_CONVERT_I_TO_O,
  318.             HTAtom_name(format_in), HTAtom_name(format_out));
  319.         _statusline(buf);
  320.         sleep(AlertSecs);
  321.         status = HT_NOT_LOADED;
  322.  
  323.     } else if (anAnchor->post_data && pipe(fd1) < 0) {
  324.         HTAlert(CONNECT_SET_FAILED);
  325.         if (TRACE) {
  326.         perror("LYNXCGI: pipe() failed");
  327.         }
  328.         status = -3;
  329.         
  330.     } else if (pipe(fd2) < 0) {
  331.         HTAlert(CONNECT_SET_FAILED);
  332.         if (TRACE) {
  333.         perror("LYNXCGI: pipe() failed");
  334.         }
  335.         close(fd1[0]);
  336.         close(fd1[1]);
  337.         status = -3;
  338.         
  339.     } else {    
  340.         static BOOL first_time = TRUE;      /* One time setup flag */
  341.  
  342.         if (first_time) {    /* Set up static environment variables */
  343.         first_time = FALSE;    /* Only once */
  344.         
  345.         add_environment_value("REMOTE_HOST=localhost");
  346.         add_environment_value("REMOTE_ADDR=127.0.0.1");
  347.         
  348.         sprintf(user_agent, "HTTP_USER_AGENT=%s/%s libwww/%s",
  349.             LYNX_NAME, LYNX_VERSION, HTLibraryVersion);
  350.         add_environment_value(user_agent);
  351.         
  352.         sprintf(server_software, "SERVER_SOFTWARE=%s/%s",
  353.             LYNX_NAME, LYNX_VERSION);
  354.         add_environment_value(server_software);
  355.         }
  356.         
  357.         if ((pid = fork()) > 0) { /* The good, */
  358.         int chars, total_chars;
  359.         
  360.         close(fd2[1]);
  361.         
  362.         if (anAnchor->post_data) {
  363.             int written, remaining, total_written = 0;
  364.             close(fd1[0]);
  365.  
  366.             /* We have form data to push across the pipe */
  367.             CTRACE(tfp, "LYNXCGI: Doing post, content-type '%s'\n",
  368.                 anAnchor->post_content_type);
  369.             CTRACE(tfp, "LYNXCGI: Writing:\n%s----------------------------------\n",
  370.                 anAnchor->post_data);            
  371.             remaining = strlen(anAnchor->post_data);
  372.             while ((written = write(fd1[1],
  373.                         anAnchor->post_data + total_written,
  374.                         remaining)) != 0) {
  375.             if (written < 0) {
  376. #ifdef EINTR
  377.                 if (errno == EINTR)
  378.                 continue;
  379. #endif /* EINTR */
  380. #ifdef ERESTARTSYS
  381.                 if (errno == ERESTARTSYS)
  382.                 continue;
  383. #endif /* ERESTARTSYS */
  384.                 if (TRACE) {
  385.                 perror("LYNXCGI: write() of POST data failed");
  386.                 }
  387.                 break;
  388.             }
  389.             CTRACE(tfp, "LYNXCGI: Wrote %d bytes of POST data.\n",
  390.                     written);
  391.             total_written += written;
  392.             remaining -= written;
  393.             if (remaining == 0)
  394.                 break;
  395.             }
  396.             if (remaining != 0) {
  397.             CTRACE(tfp, "LYNXCGI: %d bytes remain unwritten!\n",
  398.                     remaining);
  399.             }
  400.             close(fd1[1]);
  401.         }
  402.         
  403.         total_chars = 0;
  404.         while((chars = read(fd2[0], buf, sizeof(buf))) > 0) {
  405.             char line[40];
  406.             
  407.             total_chars += chars;
  408.             sprintf (line, "Read %d bytes of data.", total_chars);
  409.             HTProgress(line);
  410.             CTRACE(tfp, "LYNXCGI: Rx: %.*s\n", chars, buf);
  411.             
  412.             (*target->isa->put_block)(target, buf, chars);
  413.         }
  414. #if !HAVE_WAITPID
  415.         while (wait(&wstatus) != pid)
  416.             ; /* do nothing */
  417. #else
  418.         while (-1 == waitpid(pid, &wstatus, 0)) { /* wait for child */
  419. #ifdef EINTR
  420.             if (errno == EINTR)
  421.             continue;
  422. #endif /* EINTR */
  423. #ifdef ERESTARTSYS
  424.             if (errno == ERESTARTSYS)
  425.             continue;
  426. #endif /* ERESTARTSYS */
  427.             break;
  428.         }
  429. #endif /* !HAVE_WAITPID */
  430.         close(fd2[0]);
  431.         status = HT_LOADED;
  432.         
  433.         } else if (pid == 0) { /* The Bad, */
  434.         char **argv = NULL;
  435.         char post_len[32];
  436.         int argv_cnt = 3; /* name, one arg and terminator */
  437.         char **cur_argv = NULL;
  438.         char buf[BUFSIZ];
  439.  
  440.         /* Set up output pipe */
  441.         close(fd2[0]);
  442.         dup2(fd2[1], fileno(stdout)); /* Should check success code */
  443.         dup2(fd2[1], fileno(stderr));
  444.         close(fd2[1]);
  445.  
  446.         sprintf(buf, "HTTP_ACCEPT_LANGUAGE=%.*s",
  447.                  (int)(sizeof(buf) - 22), language);
  448.         buf[(sizeof(buf) - 1)] = '\0';
  449.         add_environment_value(buf);
  450.  
  451.         if (pref_charset) {
  452.             cp = NULL;
  453.             StrAllocCopy(cp, "HTTP_ACCEPT_CHARSET=");
  454.             StrAllocCat(cp, pref_charset);
  455.             add_environment_value(cp);
  456.         }
  457.  
  458.         if (anAnchor->post_data &&
  459.             anAnchor->post_content_type) {
  460.             cp = NULL;
  461.             StrAllocCopy(cp, "CONTENT_TYPE=");
  462.             StrAllocCat(cp, anAnchor->post_content_type);
  463.             add_environment_value(cp);
  464.         }
  465.  
  466.         if (anAnchor->post_data) { /* post script, read stdin */
  467.             close(fd1[1]);
  468.             dup2(fd1[0], fileno(stdin));
  469.             close(fd1[0]);
  470.  
  471.             /* Build environment variables */
  472.  
  473.             add_environment_value("REQUEST_METHOD=POST");
  474.  
  475.             sprintf(post_len, "CONTENT_LENGTH=%d",
  476.                 strlen(anAnchor->post_data));
  477.             add_environment_value(post_len);
  478.         } else {
  479.             close(fileno(stdin));
  480.  
  481.             if (anAnchor->isHEAD) {
  482.             add_environment_value("REQUEST_METHOD=HEAD");
  483.             }
  484.         }
  485.  
  486.         /* 
  487.          * Set up argument line, mainly for <index> scripts
  488.          */
  489.         if (pgm_args != NULL) {
  490.             for (cp = pgm_args; *cp != '\0'; cp++) {
  491.             if (*cp == '+') {
  492.                 argv_cnt++;
  493.             }
  494.             }
  495.         }
  496.  
  497.         argv = (char**)malloc(argv_cnt * sizeof(char*));
  498.         if (argv == NULL) {
  499.             outofmem(__FILE__, "LYCgi");
  500.         }
  501.         cur_argv = argv + 1;        /* For argv[0] */
  502.         if (pgm_args != NULL) {        
  503.             char *cr;
  504.  
  505.             /* Data for a get/search form */
  506.             if (is_www_index) {
  507.             add_environment_value("REQUEST_METHOD=SEARCH");
  508.             } else if (!anAnchor->isHEAD) {
  509.             add_environment_value("REQUEST_METHOD=GET");
  510.             }
  511.             
  512.             cp = NULL;
  513.             StrAllocCopy(cp, "QUERY_STRING=");
  514.             StrAllocCat(cp, pgm_args);
  515.             add_environment_value(cp);
  516.  
  517.             /*
  518.              * Split up arguments into argv array
  519.              */
  520.             cp = pgm_args;
  521.             cr = cp;
  522.             while(1) {
  523.             if (*cp == '\0') {
  524.                 *(cur_argv++) = HTUnEscape(cr);
  525.                 break;
  526.                 
  527.             } else if (*cp == '+') {
  528.                 *cp++ = '\0';
  529.                 *(cur_argv++) = HTUnEscape(cr);
  530.                 cr = cp;
  531.             }
  532.             cp++;
  533.             }
  534.         }
  535.         *cur_argv = NULL;    /* Terminate argv */        
  536.         argv[0] = pgm;
  537.  
  538.         /* Begin WebSter Mods  -jkt */                
  539.         if (LYCgiDocumentRoot != NULL) {
  540.             /* Add DOCUMENT_ROOT to env */
  541.             cp = NULL;
  542.             StrAllocCopy(cp, "DOCUMENT_ROOT=");
  543.             StrAllocCat(cp, LYCgiDocumentRoot);
  544.             add_environment_value(cp);
  545.         }
  546.         if (path_info != NULL ) {
  547.             /* Add PATH_INFO to env */
  548.             cp = NULL;
  549.             StrAllocCopy(cp, "PATH_INFO=");
  550.             StrAllocCat(cp, path_info);
  551.             add_environment_value(cp);
  552.         }
  553.         if (LYCgiDocumentRoot != NULL && path_info != NULL ) {
  554.             /* Construct and add PATH_TRANSLATED to env */
  555.             StrAllocCopy(document_root, LYCgiDocumentRoot);
  556.             if (document_root[strlen(document_root) - 1] == '/') {
  557.             document_root[strlen(document_root) - 1] = '\0';
  558.             }
  559.             path_translated = document_root;
  560.             StrAllocCat(path_translated, path_info);
  561.             cp = NULL;
  562.             StrAllocCopy(cp, "PATH_TRANSLATED=");
  563.             StrAllocCat(cp, path_translated);
  564.             add_environment_value(cp);
  565.             FREE(path_translated);
  566.         }
  567.         /* End WebSter Mods  -jkt */
  568.  
  569.         execve(argv[0], argv, env);
  570.         if (TRACE) {
  571.             perror("LYNXCGI: execve failed");
  572.         }
  573.         
  574.         } else {    /* and the Ugly */
  575.         HTAlert(CONNECT_FAILED);
  576.         if (TRACE) {
  577.             perror("LYNXCGI: fork() failed");
  578.         }
  579.         status = HT_NO_DATA;
  580.         close(fd1[0]);
  581.         close(fd1[1]);
  582.         close(fd2[0]);
  583.         close(fd2[1]);
  584.         status = -1;
  585.         }
  586.  
  587.     }
  588.     if (target != NULL) {
  589.         (*target->isa->_free)(target);
  590.     }
  591.     }
  592.     FREE(path_info);
  593.     FREE(pgm);
  594.     FREE(orig_pgm);
  595. #else  /* VMS */
  596.     HTStream *target;
  597.     char buf[256];
  598.  
  599.     target = HTStreamStack(WWW_HTML, 
  600.                    format_out,
  601.                    sink, anAnchor);
  602.  
  603.     sprintf(buf,"<head>\n<title>Good Advice</title>\n</head>\n<body>\n");
  604.     (*target->isa->put_block)(target, buf, strlen(buf));
  605.     
  606.     sprintf(buf,"<h1>Good Advice</h1>\n");
  607.     (*target->isa->put_block)(target, buf, strlen(buf));
  608.  
  609.     sprintf(buf, "An excellent http server for VMS is available via <a\n");
  610.     (*target->isa->put_block)(target, buf, strlen(buf));
  611.  
  612.     sprintf(buf,
  613.      "href=\"http://kcgl1.eng.ohio-state.edu/www/doc/serverinfo.html\"\n");
  614.     (*target->isa->put_block)(target, buf, strlen(buf));
  615.  
  616.     sprintf(buf, ">this link</a>.\n");
  617.     (*target->isa->put_block)(target, buf, strlen(buf));
  618.  
  619.     sprintf(buf,
  620.         "<p>It provides <b>state of the art</b> CGI script support.\n");
  621.     (*target->isa->put_block)(target, buf, strlen(buf));
  622.  
  623.     sprintf(buf,"</body>\n");
  624.     (*target->isa->put_block)(target, buf, strlen(buf));
  625.  
  626.     (*target->isa->_free)(target);
  627.     status = HT_LOADED;
  628. #endif /* VMS */
  629. #else /* LYNXCGI_LINKS */
  630.     _statusline(CGI_NOT_COMPILED);
  631.     sleep(MessageSecs);
  632.     status = HT_NOT_LOADED;
  633. #endif /* LYNXCGI_LINKS */
  634.     return(status);
  635. }
  636.  
  637. #ifdef GLOBALDEF_IS_MACRO
  638. #define _LYCGI_C_GLOBALDEF_1_INIT { "lynxcgi", LYLoadCGI, 0 }
  639. GLOBALDEF (HTProtocol,LYLynxCGI,_LYCGI_C_GLOBALDEF_1_INIT);
  640. #else
  641. GLOBALDEF PUBLIC HTProtocol LYLynxCGI = { "lynxcgi", LYLoadCGI, 0 };
  642. #endif /* GLOBALDEF_IS_MACRO */
  643.