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 / HTFWriter.c < prev    next >
C/C++ Source or Header  |  1998-05-02  |  36KB  |  1,292 lines

  1. /*        FILE WRITER                HTFWrite.h
  2. **        ===========
  3. **
  4. **    This version of the stream object just writes to a C file.
  5. **    The file is assumed open and left open.
  6. **
  7. **    Bugs:
  8. **        strings written must be less than buffer size.
  9. */
  10.  
  11. #include <HTUtils.h>
  12. #include <tcp.h>
  13. #include <LYCurses.h>
  14. #include <HTFWriter.h>
  15. #include <HTSaveToFile.h>
  16.  
  17. #include <HTFormat.h>
  18. #include <UCDefs.h>
  19. #include <HTAlert.h>
  20. #include <HTFile.h>
  21. #include <HTPlain.h>
  22. #include <HTFile.h>
  23. #ifdef VMS
  24. #include <HTVMSUtils.h>
  25. #endif /* VMS */
  26. #ifdef DOSPATH
  27. #include <HTDOS.h>
  28. #endif
  29.  
  30. #include <LYStrings.h>
  31. #include <LYUtils.h>
  32. #include <LYGlobalDefs.h>
  33. #include <LYSignal.h>
  34. #include <LYSystem.h>
  35. #include <GridText.h>
  36. #include <LYexit.h>
  37. #include <LYLeaks.h>
  38. #include <LYKeymap.h>
  39.  
  40. PUBLIC char * WWW_Download_File=NULL; /* contains the name of the temp file
  41.                       ** which is being downloaded into
  42.                       */
  43. PUBLIC char LYCancelDownload=FALSE;   /* exported to HTFormat.c in libWWW */
  44.  
  45. #ifdef VMS
  46. extern BOOLEAN HadVMSInterrupt;      /* flag from cleanup_sig()     */
  47. PRIVATE char * FIXED_RECORD_COMMAND = NULL;
  48. #ifdef USE_COMMAND_FILE          /* Keep this as an option. - FM    */
  49. #define FIXED_RECORD_COMMAND_MASK "@Lynx_Dir:FIXED512 %s"
  50. #else
  51. #define FIXED_RECORD_COMMAND_MASK "%s"
  52. PUBLIC unsigned long LYVMS_FixedLengthRecords PARAMS((char *filename));
  53. #endif /* USE_COMMAND_FILE */
  54. #endif /* VMS */
  55.  
  56. PUBLIC HTStream* HTSaveToFile PARAMS((
  57.     HTPresentation *       pres,
  58.     HTParentAnchor *       anchor,
  59.     HTStream *           sink));
  60.  
  61. #define FREE(x) if (x) {free(x); x = NULL;}
  62.  
  63.  
  64. /*    Stream Object
  65. **    -------------
  66. */
  67. struct _HTStream {
  68.     CONST HTStreamClass *    isa;
  69.  
  70.     FILE *            fp;        /* The file we've opened */
  71.     char *            end_command;    /* What to do on _free.  */
  72.     char *            remove_command; /* What to do on _abort. */
  73.     char *            viewer_command; /* Saved external viewer */
  74.     HTFormat        input_format;  /* Original pres->rep     */
  75.     HTFormat        output_format; /* Original pres->rep_out */
  76.     HTParentAnchor *    anchor;     /* Original stream's anchor. */
  77.     HTStream *        sink;        /* Original stream's sink.     */
  78. #ifdef FNAMES_8_3
  79.     int            idash; /* remember position to become '.'*/
  80. #endif
  81. };
  82.  
  83.  
  84. /*_________________________________________________________________________
  85. **
  86. **            A C T I O N    R O U T I N E S
  87. **  Bug:
  88. **    All errors are ignored.
  89. */
  90.  
  91. /*    Character handling
  92. **    ------------------
  93. */
  94. PRIVATE void HTFWriter_put_character ARGS2(HTStream *, me, char, c)
  95. {
  96.     putc(c, me->fp);
  97. }
  98.  
  99. /*    String handling
  100. **    ---------------
  101. **
  102. **    Strings must be smaller than this buffer size.
  103. */
  104. PRIVATE void HTFWriter_put_string ARGS2(HTStream *, me, CONST char*, s)
  105. {
  106.     fputs(s, me->fp);
  107. }
  108.  
  109. /*    Buffer write.  Buffers can (and should!) be big.
  110. **    ------------
  111. */
  112. PRIVATE void HTFWriter_write ARGS3(HTStream *, me, CONST char*, s, int, l)
  113. {
  114.     fwrite(s, 1, l, me->fp);
  115. }
  116.  
  117.  
  118.  
  119.  
  120. /*    Free an HTML object
  121. **    -------------------
  122. **
  123. **    Note that the SGML parsing context is freed, but the created
  124. **    object is not,
  125. **    as it takes on an existence of its own unless explicitly freed.
  126. */
  127. PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
  128. {
  129.     FILE *fp = NULL;
  130.     int len;
  131.     char *path = NULL;
  132.     char *addr = NULL;
  133.     int status;
  134.     BOOL use_gzread = NO;
  135.  
  136.     fflush(me->fp);
  137.     if (me->end_command) {        /* Temp file */
  138.     fclose(me->fp);
  139. #ifdef VMS
  140.     if (0 == strcmp(me->end_command, "SaveVMSBinaryFile")) {
  141.     /*
  142.      *  It's a binary file saved to disk on VMS, which
  143.      *  we want to convert to fixed records format. - FM
  144.      */
  145. #ifdef USE_COMMAND_FILE
  146.         system(FIXED_RECORD_COMMAND);
  147. #else
  148.         LYVMS_FixedLengthRecords(FIXED_RECORD_COMMAND);
  149. #endif /* USE_COMMAND_FILE */
  150.         FREE(FIXED_RECORD_COMMAND);
  151.  
  152.         if (me->remove_command) {
  153.         /* NEVER REMOVE THE FILE unless during an abort!*/
  154.         /* system(me->remove_command); */
  155.         FREE(me->remove_command);
  156.         }
  157.     } else
  158. #endif /* VMS */
  159.     if (me->input_format == HTAtom_for("www/compressed")) {
  160.         /*
  161.          *    It's a compressed file supposedly cached to
  162.          *    a temporary file for uncompression. - FM
  163.          */
  164.         if (me->anchor->FileCache != NULL) {
  165.         BOOL skip_loadfile = (me->viewer_command != NULL);
  166.         /*
  167.          *  Save the path with the "gz" or "Z" suffix trimmed,
  168.          *  and remove any previous uncompressed copy. - FM
  169.          */
  170.         StrAllocCopy(path, me->anchor->FileCache);
  171.         if ((len = strlen(path)) > 2) {
  172.             if (!strcasecomp((char *)&path[len-2], "gz")) {
  173. #ifdef USE_ZLIB
  174.             if (!skip_loadfile) {
  175.                 use_gzread = YES;
  176.             } else
  177. #endif /* USE_ZLIB */
  178.             {
  179.                 path[len-3] = '\0';
  180.                 remove(path);
  181.             }
  182.             } else if (!strcasecomp((char *)&path[len-1], "Z")) {
  183.             path[len-2] = '\0';
  184.             remove(path);
  185.             }
  186.         }
  187.         if (!use_gzread) {
  188.             if (!dump_output_immediately) {
  189.             /*
  190.              *  Tell user what's happening. - FM
  191.              */
  192.             _HTProgress(me->end_command);
  193.             }
  194.             /*
  195.              *    Uncompress it. - FM
  196.              */
  197.             if (me->end_command && me->end_command[0])
  198.             system(me->end_command);
  199.             fp = fopen(me->anchor->FileCache, "r");
  200.         }
  201.         if (fp != NULL) {
  202.             /*
  203.              *    It's still there with the "gz" or "Z" suffix,
  204.              *    so the uncompression failed. - FM
  205.              */
  206.             fclose(fp);
  207.             fp = NULL;
  208.             if (!dump_output_immediately) {
  209.             lynx_force_repaint();
  210.             refresh();
  211.             }
  212.             HTAlert(ERROR_UNCOMPRESSING_TEMP);
  213.             remove(me->anchor->FileCache);
  214.             FREE(me->anchor->FileCache);
  215.         } else {
  216.             /*
  217.              *    Succeeded!  Create a complete address
  218.              *    for the uncompressed file and invoke
  219.              *    HTLoadFile() to handle it. - FM
  220.              */
  221. #ifdef FNAMES_8_3
  222.             /*
  223.              *    Assuming we have just uncompressed e.g.
  224.              *    FILE-mpeg.gz -> FILE-mpeg, restore/shorten
  225.              *    the name to be fit for passing to an external
  226.              *    viewer, by renaming FILE-mpeg -> FILE.mpe  - kw
  227.              */
  228.             if (skip_loadfile) {
  229.             char *new_path = NULL;
  230.             if (me->idash > 1 && path[me->idash] == '-') {
  231.                 StrAllocCopy(new_path, path);
  232.                 new_path[me->idash] = '.';
  233.                 if (strlen(new_path + me->idash) > 4)
  234.                 new_path[me->idash + 4] = '\0';
  235.                 if (rename(path, new_path) == 0) {
  236.                 FREE(path);
  237.                 path = new_path;
  238.                 } else {
  239.                 FREE(new_path);
  240.                 }
  241.             }
  242.             }
  243. #endif /* FNAMES_8_3 */
  244.             StrAllocCopy(addr, "file://localhost");
  245. #ifdef DOSPATH
  246.             StrAllocCat(addr, "/");
  247.             StrAllocCat(addr, HTDOS_wwwName(path));
  248. #else
  249. #ifdef VMS
  250.             StrAllocCat(addr, HTVMS_wwwName(path));
  251. #else
  252.             StrAllocCat(addr, path);
  253. #endif /* VMS */
  254. #endif /* DOSPATH */
  255.             if (!use_gzread) {
  256.             StrAllocCopy(me->anchor->FileCache, path);
  257.             StrAllocCopy(me->anchor->content_encoding, "binary");
  258.             }
  259.             FREE(path);
  260.             if (!skip_loadfile) {
  261.             /*
  262.              *  Lock the chartrans info we may possibly have,
  263.              *  so HTCharsetFormat() will not apply the default
  264.              *  for local files. - KW
  265.              */
  266.             if (HTAnchor_getUCLYhndl(me->anchor,
  267.                          UCT_STAGE_PARSER) < 0 ) {
  268.                 /*
  269.                  *    If not yet set - KW
  270.                  */
  271.                 HTAnchor_copyUCInfoStage(me->anchor,
  272.                              UCT_STAGE_PARSER,
  273.                              UCT_STAGE_MIME,
  274.                              UCT_SETBY_DEFAULT+1);
  275.             }
  276.             HTAnchor_copyUCInfoStage(me->anchor,
  277.                          UCT_STAGE_PARSER,
  278.                          UCT_STAGE_MIME, -1);
  279.             }
  280.             /*
  281.              *    Create a complete address for
  282.              *    the uncompressed file. - FM
  283.              */
  284.             if (!dump_output_immediately) {
  285.             /*
  286.              *  Tell user what's happening. - FM
  287.              */
  288.             _user_message(WWW_USING_MESSAGE, addr);
  289.             }
  290.  
  291.             if (skip_loadfile) {
  292.             /*
  293.              *  It's a temporary file we're passing to a
  294.              *  viewer or helper application.
  295.              *  Loading the temp file through HTLoadFile()
  296.              *  would result in yet another HTStream (created
  297.              *  with HTSaveAndExecute()) which would just
  298.              *  copy the temp file to another temp file
  299.              *  (or even the same!).  We can skip this
  300.              *  needless duplication by using the
  301.              *  viewer_command which has already been
  302.              *  determind when the HTCompressed stream was
  303.              *  created. - kw
  304.              */
  305.             FREE(me->end_command);
  306.             me->end_command = (char *)calloc (
  307.                 (strlen (me->viewer_command) + 10 +
  308.                  strlen(me->anchor->FileCache))
  309.                 * sizeof (char),1);
  310.             if (me->end_command == NULL)
  311.                 outofmem(__FILE__, "HTFWriter_free (HTCompressed)");
  312.  
  313.             sprintf(me->end_command,
  314.                 me->viewer_command, me->anchor->FileCache,
  315.                 "", "", "", "", "", "");
  316.             if (!dump_output_immediately) {
  317.                 /*
  318.                  *    Tell user what's happening. - FM
  319.                  */
  320.                 HTProgress(me->end_command);
  321.                 stop_curses();
  322.             }
  323.             system(me->end_command);
  324.  
  325.             if (me->remove_command) {
  326.                 /* NEVER REMOVE THE FILE unless during an abort!!!*/
  327.                 /* system(me->remove_command); */
  328.                 FREE(me->remove_command);
  329.             }
  330.             if (!dump_output_immediately)
  331.                 start_curses();
  332.             } else
  333.             status = HTLoadFile(addr,
  334.                     me->anchor,
  335.                     me->output_format,
  336.                     me->sink);
  337.             if (dump_output_immediately &&
  338.             me->output_format == HTAtom_for("www/present")) {
  339.             FREE(addr);
  340.             remove(me->anchor->FileCache);
  341.             FREE(me->anchor->FileCache);
  342.             FREE(me->remove_command);
  343.             FREE(me->end_command);
  344.             FREE(me->viewer_command);
  345.             FREE(me);
  346.             return;
  347.             }
  348.         }
  349.         FREE(addr);
  350.         }
  351.         if (me->remove_command) {
  352.         /* NEVER REMOVE THE FILE unless during an abort!!!*/
  353.         /* system(me->remove_command); */
  354.         FREE(me->remove_command);
  355.         }
  356.     } else if (strcmp(me->end_command, "SaveToFile")) {
  357.         /*
  358.          *    It's a temporary file we're passing to a
  359.          *    viewer or helper application. - FM
  360.          */
  361.         if (!dump_output_immediately) {
  362.         /*
  363.          *  Tell user what's happening. - FM
  364.          */
  365.         _HTProgress(me->end_command);
  366.         stop_curses();
  367.         }
  368.         system(me->end_command);
  369.  
  370.         if (me->remove_command) {
  371.         /* NEVER REMOVE THE FILE unless during an abort!!!*/
  372.         /* system(me->remove_command); */
  373.         FREE(me->remove_command);
  374.         }
  375.         if (!dump_output_immediately)
  376.         start_curses();
  377.     } else {
  378.         /*
  379.          *    It's a file we saved to disk for handling
  380.          *    via a menu. - FM
  381.          */
  382.         if (me->remove_command) {
  383.         /* NEVER REMOVE THE FILE unless during an abort!!!*/
  384.         /* system(me->remove_command); */
  385.         FREE(me->remove_command);
  386.         }
  387.     }
  388.     FREE(me->end_command);
  389.     }
  390.     FREE(me->viewer_command);
  391.  
  392.     if (dump_output_immediately) {
  393.     if (me->anchor->FileCache)
  394.         remove(me->anchor->FileCache);
  395.     FREE(me);
  396. #ifndef NOSIGHUP
  397.     (void) signal(SIGHUP, SIG_DFL);
  398. #endif /* NOSIGHUP */
  399.     (void) signal(SIGTERM, SIG_DFL);
  400. #ifndef VMS
  401.     (void) signal(SIGINT, SIG_DFL);
  402. #endif /* !VMS */
  403. #ifdef SIGTSTP
  404.     if (no_suspend)
  405.       (void) signal(SIGTSTP,SIG_DFL);
  406. #endif /* SIGTSTP */
  407.     exit(0);
  408.     }
  409.  
  410.     FREE(me);
  411.     return;
  412. }
  413.  
  414. /*    Abort writing
  415. **    -------------
  416. */
  417. PRIVATE void HTFWriter_abort ARGS2(
  418.     HTStream *,    me,
  419.     HTError,    e GCC_UNUSED)
  420. {
  421.     CTRACE(tfp,"HTFWriter_abort called\n");
  422.     fclose(me->fp);
  423.     FREE(me->viewer_command);
  424.     if (me->end_command) {        /* Temp file */
  425.     CTRACE(tfp, "HTFWriter: Aborting: file not executed.\n");
  426.     FREE(me->end_command);
  427.     if (me->remove_command) {
  428.         system(me->remove_command);
  429.         FREE(me->remove_command);
  430.     }
  431.     }
  432.  
  433.     FREE(WWW_Download_File);
  434.  
  435.     FREE(me);
  436. }
  437.  
  438. /*    Structured Object Class
  439. **    -----------------------
  440. */
  441. PRIVATE CONST HTStreamClass HTFWriter = /* As opposed to print etc */
  442. {
  443.     "FileWriter",
  444.     HTFWriter_free,
  445.     HTFWriter_abort,
  446.     HTFWriter_put_character,    HTFWriter_put_string,
  447.     HTFWriter_write
  448. };
  449.  
  450. /*    Subclass-specific Methods
  451. **    -------------------------
  452. */
  453. PUBLIC HTStream* HTFWriter_new ARGS1(FILE *, fp)
  454. {
  455.     HTStream* me;
  456.  
  457.     if (!fp)
  458.     return NULL;
  459.  
  460.     me = (HTStream*)calloc(sizeof(*me),1);
  461.     if (me == NULL)
  462.     outofmem(__FILE__, "HTFWriter_new");
  463.     me->isa = &HTFWriter;
  464.  
  465.     me->fp = fp;
  466.     me->end_command = NULL;
  467.     me->remove_command = NULL;
  468.     me->anchor = NULL;
  469.     me->sink = NULL;
  470.  
  471.     return me;
  472. }
  473.  
  474. /*    Make system command from template
  475. **    ---------------------------------
  476. **
  477. **    See mailcap spec for description of template.
  478. */
  479. /* @@ to be written.  sprintfs will do for now.  */
  480.  
  481. #ifndef VMS
  482. #define REMOVE_COMMAND "/bin/rm -f %s"
  483. #else
  484. #define REMOVE_COMMAND "delete/noconfirm/nolog %s;"
  485. #endif /* VMS */
  486.  
  487. /*    Take action using a system command
  488. **    ----------------------------------
  489. **
  490. **    originally from Ghostview handling by Marc Andreseen.
  491. **    Creates temporary file, writes to it, executes system command
  492. **    on end-document.  The suffix of the temp file can be given
  493. **    in case the application is fussy, or so that a generic opener can
  494. **    be used.
  495. */
  496. PUBLIC HTStream* HTSaveAndExecute ARGS3(
  497.     HTPresentation *,    pres,
  498.     HTParentAnchor *,    anchor,
  499.     HTStream *,        sink)
  500. {
  501.     char fnam[256];
  502.     CONST char *suffix;
  503.     char *cp;
  504.     HTStream* me;
  505.     FILE *fp = NULL;
  506.  
  507.     if (traversal) {
  508.     LYCancelledFetch = TRUE;
  509.     return(NULL);
  510.     }
  511.  
  512. #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
  513.     if (pres->quality == 999.0) { /* exec link */
  514.     if (dump_output_immediately) {
  515.         LYCancelledFetch = TRUE;
  516.         return(NULL);
  517.     }
  518.     if (no_exec) {
  519.         _statusline(EXECUTION_DISABLED);
  520.         sleep(AlertSecs);
  521.         return HTPlainPresent(pres, anchor, sink);
  522.     }
  523.     if (!local_exec)
  524.         if (local_exec_on_local_files &&
  525.         (LYJumpFileURL ||
  526.          !strncmp(anchor->address,"file://localhost",16))) {
  527.         /* allow it to continue */
  528.         } else {
  529.         char buf[512];
  530.  
  531.         sprintf(buf, EXECUTION_DISABLED_FOR_FILE,
  532.                  key_for_func(LYK_OPTIONS));
  533.         _statusline(buf);
  534.         sleep(AlertSecs);
  535.         return HTPlainPresent(pres, anchor, sink);
  536.         }
  537.     }
  538. #endif /* EXEC_LINKS || EXEC_SCRIPTS */
  539.  
  540.     if (dump_output_immediately) {
  541.     return(HTSaveToFile(pres, anchor, sink));
  542.     }
  543.  
  544.     me = (HTStream*)calloc(sizeof(*me),1);
  545.     if (me == NULL)
  546.     outofmem(__FILE__, "HTSaveAndExecute");
  547.     me->isa = &HTFWriter;
  548.     me->input_format = pres->rep;
  549.     me->output_format = pres->rep_out;
  550.     me->anchor = anchor;
  551.     me->sink = sink;
  552.  
  553.     if (anchor->FileCache) {
  554.     strcpy(fnam, anchor->FileCache);
  555.     FREE(anchor->FileCache);
  556.     if ((fp = fopen(fnam, "r")) != NULL) {
  557.         fclose(fp);
  558.         fp = NULL;
  559.         remove(fnam);
  560.     }
  561.     } else {
  562.     /*
  563.      *  Lynx routine to create a temporary filename
  564.      */
  565. SaveAndExecute_tempname:
  566.     tempname (fnam, NEW_FILE);
  567.     /*
  568.      *  Check for a suffix.
  569.      */
  570.     if (((cp = strrchr(fnam, '.')) != NULL) &&
  571. #ifdef VMS
  572.         NULL == strchr(cp, ']') &&
  573. #endif /* VMS */
  574.         NULL == strchr(cp, '/')) {
  575.         /*
  576.          *    Save the file under a suitably suffixed name.
  577.          */
  578.         *cp = '\0';
  579.         if (!strcasecomp(pres->rep->name, "text/html")) {
  580.         strcat(fnam, HTML_SUFFIX);
  581.         } else if (!strcasecomp(pres->rep->name, "text/plain")) {
  582.         strcat(fnam, ".txt");
  583.         } else if (!strcasecomp(pres->rep->name,
  584.                     "application/octet-stream")) {
  585.         strcat(fnam, ".bin");
  586.         } else if ((suffix = HTFileSuffix(pres->rep, anchor->content_encoding))
  587.                && *suffix == '.') {
  588.         strcat(fnam, suffix);
  589.         /*
  590.          *  It's not one of the suffixes checked for a
  591.          *  spoof in tempname(), so check it now. - FM
  592.          */
  593.         if (strcmp(suffix, HTML_SUFFIX) &&
  594.             strcmp(suffix, ".txt") &&
  595.             strcmp(suffix, ".bin") &&
  596.             (fp = fopen(fnam, "r")) != NULL) {
  597.             fclose(fp);
  598.             fp = NULL;
  599.             goto SaveAndExecute_tempname;
  600.         }
  601.         } else {
  602.         *cp = '.';
  603.         }
  604.     }
  605.     }
  606.  
  607.     me->fp = LYNewBinFile (fnam);
  608.     if (!me->fp) {
  609.     HTAlert(CANNOT_OPEN_TEMP);
  610.     FREE(me);
  611.     return NULL;
  612.     }
  613.  
  614.     StrAllocCopy(me->viewer_command, pres->command);
  615.     /*
  616.      *    Make command to process file.
  617.      */
  618.     me->end_command = (char *)calloc (
  619.             (strlen (pres->command) + 10 + strlen(fnam))
  620.              * sizeof (char),1);
  621.     if (me->end_command == NULL)
  622.     outofmem(__FILE__, "HTSaveAndExecute");
  623.  
  624.     sprintf(me->end_command, pres->command, fnam, "", "", "", "", "", "");
  625.  
  626.     /*
  627.      *    Make command to delete file.
  628.      */
  629.     me->remove_command = (char *)calloc (
  630.             (strlen (REMOVE_COMMAND) + 10 + strlen(fnam))
  631.              * sizeof (char),1);
  632.     if (me->remove_command == NULL)
  633.     outofmem(__FILE__, "HTSaveAndExecute");
  634.  
  635.     sprintf(me->remove_command, REMOVE_COMMAND, fnam);
  636.  
  637.     StrAllocCopy(anchor->FileCache, fnam);
  638.     return me;
  639. }
  640.  
  641.  
  642. /*    Format Converter using system command
  643. **    -------------------------------------
  644. */
  645.  
  646. /* @@@@@@@@@@@@@@@@@@@@@@ */
  647.  
  648. /*    Save to a local file   LJM!!!
  649. **    --------------------
  650. **
  651. **    usually a binary file that can't be displayed
  652. **
  653. **    originally from Ghostview handling by Marc Andreseen.
  654. **    Asks the user if he wants to continue, creates a temporary
  655. **    file, and writes to it.  In HTSaveToFile_Free
  656. **    the user will see a list of choices for download
  657. */
  658. PUBLIC HTStream* HTSaveToFile ARGS3(
  659.     HTPresentation *,    pres,
  660.     HTParentAnchor *,    anchor,
  661.     HTStream *,        sink)
  662. {
  663.     HTStream * ret_obj;
  664.     char fnam[256];
  665.     CONST char * suffix;
  666.     char *cp;
  667.     int c=0;
  668.     BOOL IsBinary = TRUE;
  669.     FILE *fp = NULL;
  670.  
  671.     ret_obj = (HTStream*)calloc(sizeof(* ret_obj),1);
  672.     if (ret_obj == NULL)
  673.     outofmem(__FILE__, "HTSaveToFile");
  674.     ret_obj->isa = &HTFWriter;
  675.     ret_obj->remove_command = NULL;
  676.     ret_obj->end_command = NULL;
  677.     ret_obj->input_format = pres->rep;
  678.     ret_obj->output_format = pres->rep_out;
  679.     ret_obj->anchor = anchor;
  680.     ret_obj->sink = sink;
  681.  
  682.     if (dump_output_immediately) {
  683.     ret_obj->fp = stdout; /* stdout*/
  684.     if (HTOutputFormat == HTAtom_for("www/download"))
  685.         goto Prepend_BASE;
  686.     return ret_obj;
  687.     }
  688.  
  689.     LYCancelDownload = FALSE;
  690.     if (HTOutputFormat != HTAtom_for("www/download")) {
  691.     if (traversal ||
  692.         (no_download && !override_no_download && no_disk_save)) {
  693.         if (!traversal) {
  694.         _statusline(CANNOT_DISPLAY_FILE);
  695.         sleep(AlertSecs);
  696.         }
  697.         LYCancelDownload = TRUE;
  698.         if (traversal)
  699.         LYCancelledFetch = TRUE;
  700.         FREE(ret_obj);
  701.         return(NULL);
  702.     }
  703.  
  704.     if (((cp=strchr((char *)pres->rep->name, ';')) != NULL) &&
  705.         strstr((cp+1), "charset") != NULL) {
  706.         _user_message(WRONG_CHARSET_D_OR_C, (char *)pres->rep->name);
  707.     } else if (*((char *)pres->rep->name) != '\0')    {
  708.         _user_message(UNMAPPED_TYPE_D_OR_C, (char *)pres->rep->name);
  709.     } else {
  710.         _statusline(CANNOT_DISPLAY_FILE_D_OR_C);
  711.     }
  712.  
  713.     while(TOUPPER(c)!='C' && TOUPPER(c)!='D' && c!=7) {
  714.         c=LYgetch();
  715. #ifdef VMS
  716.         /*
  717.          *    'C'ancel on Control-C or Control-Y and
  718.          *    a 'N'o to the "really exit" query. - FM
  719.          */
  720.         if (HadVMSInterrupt) {
  721.         HadVMSInterrupt = FALSE;
  722.         c = 'C';
  723.         }
  724. #endif /* VMS */
  725.     }
  726.  
  727.     /*
  728.      *  Cancel on 'C', 'c' or Control-G or Control-C.
  729.      */
  730.     if (TOUPPER(c)=='C' || c==7 || c==3) {
  731.         _statusline(CANCELLING_FILE);
  732.         LYCancelDownload = TRUE;
  733.         FREE(ret_obj);
  734.         return(NULL);
  735.     }
  736.     }
  737.  
  738.     /*
  739.      *    Set up a 'D'ownload.
  740.      */
  741.     if (anchor->FileCache) {
  742.     strcpy(fnam, anchor->FileCache);
  743.     FREE(anchor->FileCache);
  744.     if ((fp = fopen(fnam, "r")) != NULL) {
  745.         fclose(fp);
  746.         fp = NULL;
  747.         remove(fnam);
  748.     }
  749.     } else {
  750.     /*
  751.      *  Lynx routine to create a temporary filename
  752.      */
  753. SaveToFile_tempname:
  754.     tempname(fnam, NEW_FILE);
  755.     /*
  756.      *  Check for a suffix.
  757.      */
  758.     if (((cp=strrchr(fnam, '.')) != NULL) &&
  759. #ifdef VMS
  760.         NULL == strchr(cp, ']') &&
  761. #endif /* VMS */
  762.         NULL == strchr(cp, '/')) {
  763.         /*
  764.          *    Save the file under a suitably suffixed name.
  765.          */
  766.         *cp = '\0';
  767.         if (!strcasecomp(pres->rep->name, "text/html")) {
  768.         strcat(fnam, HTML_SUFFIX);
  769.         } else if (!strcasecomp(pres->rep->name, "text/plain")) {
  770.         strcat(fnam, ".txt");
  771.         } else if (!strcasecomp(pres->rep->name,
  772.                     "application/octet-stream")) {
  773.         strcat(fnam, ".bin");
  774.         } else if ((suffix = HTFileSuffix(pres->rep,
  775.                           anchor->content_encoding)) && *suffix == '.') {
  776.         strcat(fnam, suffix);
  777.         /*
  778.          *  It's not one of the suffixes checked for a
  779.          *  spoof in tempname(), so check it now. - FM
  780.          */
  781.         if (strcmp(suffix, HTML_SUFFIX) &&
  782.             strcmp(suffix, ".txt") &&
  783.             strcmp(suffix, ".bin") &&
  784.             (fp = fopen(fnam, "r")) != NULL) {
  785.             fclose(fp);
  786.             fp = NULL;
  787.             goto SaveToFile_tempname;
  788.         }
  789.         } else {
  790.         *cp = '.';
  791.         }
  792.     }
  793.     }
  794.  
  795.     if (0==strncasecomp(pres->rep->name, "text/", 5) ||
  796.     0==strcasecomp(pres->rep->name, "application/postscript") ||
  797.     0==strcasecomp(pres->rep->name, "application/x-RUNOFF-MANUAL"))
  798.     /*
  799.      *  It's a text file requested via 'd'ownload.
  800.      *  Keep adding others to the above list, 'til
  801.      *  we add a configurable procedure. - FM
  802.      */
  803.     IsBinary = FALSE;
  804.  
  805.     ret_obj->fp = LYNewBinFile (fnam);
  806.     if (!ret_obj->fp) {
  807.     HTAlert(CANNOT_OPEN_OUTPUT);
  808.     FREE(ret_obj);
  809.     return NULL;
  810.     }
  811.  
  812.     /*
  813.      *    Any "application/foo" or other non-"text/foo" types that
  814.      *    are actually text but not checked, above, will be treated
  815.      *    as binary, so show the type to help sort that out later.
  816.      *    Unix folks don't need to know this, but we'll show it to
  817.      *    them, too. - FM
  818.      */
  819.     user_message("Content-type: %s", pres->rep->name);
  820.     sleep(MessageSecs);
  821.  
  822.     StrAllocCopy(WWW_Download_File,fnam);
  823.  
  824.     /*
  825.      *    Make command to delete file.
  826.      */
  827.     ret_obj->remove_command = (char *)calloc (
  828.             (strlen (REMOVE_COMMAND) + 10+ strlen(fnam))
  829.              * sizeof (char),1);
  830.     if (ret_obj->remove_command == NULL)
  831.     outofmem(__FILE__, "HTSaveToFile");
  832.  
  833.     sprintf(ret_obj->remove_command, REMOVE_COMMAND, fnam);
  834.  
  835. #ifdef VMS
  836.     if (IsBinary && UseFixedRecords) {
  837.     ret_obj->end_command = (char *)calloc (sizeof(char)*20,1);
  838.     if (ret_obj->end_command == NULL)
  839.         outofmem(__FILE__, "HTSaveToFile");
  840.     sprintf(ret_obj->end_command, "SaveVMSBinaryFile");
  841.     FIXED_RECORD_COMMAND = (char *)calloc (
  842.         (strlen (FIXED_RECORD_COMMAND_MASK) + 10 + strlen(fnam))
  843.         * sizeof (char),1);
  844.     if (FIXED_RECORD_COMMAND == NULL)
  845.         outofmem(__FILE__, "HTSaveToFile");
  846.     sprintf(FIXED_RECORD_COMMAND,
  847.         FIXED_RECORD_COMMAND_MASK, fnam, "", "", "", "", "", "");
  848.     } else {
  849. #endif /* VMS */
  850.     ret_obj->end_command = (char *)calloc (sizeof(char)*12,1);
  851.     if (ret_obj->end_command == NULL)
  852.     outofmem(__FILE__, "HTSaveToFile");
  853.     sprintf(ret_obj->end_command, "SaveToFile");
  854. #ifdef VMS
  855.     }
  856. #endif /* VMS */
  857.  
  858.     _statusline(RETRIEVING_FILE);
  859.  
  860.     StrAllocCopy(anchor->FileCache, fnam);
  861. Prepend_BASE:
  862.     if (LYPrependBaseToSource &&
  863.     !strncasecomp(pres->rep->name, "text/html", 9) &&
  864.     !anchor->content_encoding) {
  865.     /*
  866.      *  Add the document's base as a BASE tag at the top of the file,
  867.      *  so that any partial or relative URLs within it will be resolved
  868.      *  relative to that if no BASE tag is present and replaces it.
  869.      *  Note that the markup will be technically invalid if a DOCTYPE
  870.      *  declaration, or HTML or HEAD tags, are present, and thus the
  871.      *  file may need editing for perfection. - FM
  872.      */
  873.     char *temp = NULL;
  874.  
  875.     if (anchor->content_base && *anchor->content_base) {
  876.         StrAllocCopy(temp, anchor->content_base);
  877.     } else if (anchor->content_location && *anchor->content_location) {
  878.         StrAllocCopy(temp, anchor->content_location);
  879.     }
  880.     if (temp) {
  881.         LYRemoveBlanks(temp);
  882.         if (!is_url(temp)) {
  883.         FREE(temp);
  884.         }
  885.     }
  886.  
  887.     fprintf(ret_obj->fp,
  888.         "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
  889.         anchor->address, (temp ? temp : anchor->address));
  890.     FREE(temp);
  891.     }
  892.     if (LYPrependCharsetToSource &&
  893.     !strncasecomp(pres->rep->name, "text/html", 9) &&
  894.     !anchor->content_encoding) {
  895.     /*
  896.      *  Add the document's charset as a META CHARSET tag
  897.      *  at the top of the file, so HTTP charset header
  898.      *  will not be forgotten when a document saved as local file.
  899.      *  We add this line only(!) if HTTP charset present. - LP
  900.      *  Note that the markup will be technically invalid if a DOCTYPE
  901.      *  declaration, or HTML or HEAD tags, are present, and thus the
  902.      *  file may need editing for perfection. - FM
  903.      */
  904.     char *temp = NULL;
  905.  
  906.     if (anchor->charset && *anchor->charset) {
  907.         StrAllocCopy(temp, anchor->charset);
  908.         LYRemoveBlanks(temp);
  909.         fprintf(ret_obj->fp,
  910.         "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=%s\">\n\n",
  911.         temp);
  912.     }
  913.     FREE(temp);
  914.     }
  915.     return ret_obj;
  916. }
  917.  
  918. /*    Set up stream for uncompressing - FM
  919. **    -------------------------------
  920. */
  921. PUBLIC HTStream* HTCompressed ARGS3(
  922.     HTPresentation *,    pres,
  923.     HTParentAnchor *,    anchor,
  924.     HTStream *,        sink)
  925. {
  926.     HTStream* me;
  927.     HTFormat format;
  928.     char *type = NULL;
  929.     HTPresentation *Pres = NULL;
  930.     int n, i;
  931.     BOOL can_present = FALSE;
  932.     char fnam[256];
  933.     CONST char *suffix;
  934.     char *uncompress_mask = NULL;
  935.     char *compress_suffix = "";
  936.     char *cp;
  937.     CONST char *middle;
  938.     FILE *fp = NULL;
  939.  
  940.     /*
  941.      *    Deal with any inappropriate invokations of this function,
  942.      *    or a download request, in which case we won't bother to
  943.      *    uncompress the file. - FM
  944.      */
  945.     if (!(anchor && anchor->content_encoding && anchor->content_type)) {
  946.     /*
  947.      *  We have no idea what we're dealing with,
  948.      *  so treat it as a binary stream. - FM
  949.      */
  950.     format = HTAtom_for("application/octet-stream");
  951.     me = HTStreamStack(format, pres->rep_out, sink, anchor);
  952.     return me;
  953.     }
  954.     n = HTList_count(HTPresentations);
  955.     for (i = 0; i < n; i++) {
  956.     Pres = (HTPresentation *)HTList_objectAt(HTPresentations, i);
  957.     if (!strcasecomp(Pres->rep->name, anchor->content_type) &&
  958.         Pres->rep_out == WWW_PRESENT) {
  959.         /*
  960.          *    We have a presentation mapping for it. - FM
  961.          */
  962.         can_present = TRUE;
  963.         if (!strcasecomp(anchor->content_encoding, "x-gzip") ||
  964.         !strcasecomp(anchor->content_encoding, "gzip")) {
  965.         /*
  966.          *  It's compressed with the modern gzip. - FM
  967.          */
  968.         StrAllocCopy(uncompress_mask, GZIP_PATH);
  969.         StrAllocCat(uncompress_mask, " -d --no-name %s");
  970.         compress_suffix = "gz";
  971.         } else if (!strcasecomp(anchor->content_encoding, "x-compress") ||
  972.                !strcasecomp(anchor->content_encoding, "compress")) {
  973.         /*
  974.          *  It's compressed the old fashioned Unix way. - FM
  975.          */
  976.         StrAllocCopy(uncompress_mask, UNCOMPRESS_PATH);
  977.         StrAllocCat(uncompress_mask, " %s");
  978.         compress_suffix = "Z";
  979.         }
  980.         break;
  981.     }
  982.     }
  983.     if (can_present == FALSE ||          /* no presentation mapping */
  984.     uncompress_mask == NULL ||            /* not gzip or compress */
  985.     strchr(anchor->content_type, ';') ||           /* wrong charset */
  986.     HTOutputFormat == HTAtom_for("www/download") ||     /* download */
  987.     !strcasecomp(pres->rep_out->name, "www/download") ||    /* download */
  988.     (traversal &&       /* only handle html or plain text for traversals */
  989.      strcasecomp(anchor->content_type, "text/html") &&
  990.      strcasecomp(anchor->content_type, "text/plain"))) {
  991.     /*
  992.      *  Cast the Content-Encoding to a Content-Type
  993.      *  and pass it back to be handled as that type. - FM
  994.      */
  995.     if (strchr(anchor->content_encoding, '/') == NULL) {
  996.         StrAllocCopy(type, "application/");
  997.         StrAllocCat(type, anchor->content_encoding);
  998.     } else {
  999.         StrAllocCopy(type, anchor->content_encoding);
  1000.     }
  1001.     format = HTAtom_for(type);
  1002.     FREE(type)
  1003.     FREE(uncompress_mask);
  1004.     me = HTStreamStack(format, pres->rep_out, sink, anchor);
  1005.     return me;
  1006.     }
  1007.  
  1008.     /*
  1009.      *    Set up the stream structure for uncompressing and then
  1010.      *    handling based on the uncompressed Content-Type.- FM
  1011.      */
  1012.     me = (HTStream*)calloc(sizeof(*me),1);
  1013.     if (me == NULL)
  1014.     outofmem(__FILE__, "HTCompressed");
  1015.     me->isa = &HTFWriter;
  1016.     me->input_format = pres->rep;
  1017.     me->output_format = pres->rep_out;
  1018.     me->anchor = anchor;
  1019.     me->sink = sink;
  1020.  
  1021.     /*
  1022.      *    Remove any old versions of the file. - FM
  1023.      */
  1024.     if (anchor->FileCache) {
  1025.     while ((fp = fopen(anchor->FileCache, "r")) != NULL) {
  1026.         fclose(fp);
  1027.         remove(anchor->FileCache);
  1028.     }
  1029.     FREE(anchor->FileCache);
  1030.     }
  1031.  
  1032.     /*
  1033.      *    Get a new temporary filename and substitute a suitable suffix. - FM
  1034.      */
  1035. Compressed_tempname:
  1036.     tempname(fnam, NEW_FILE);
  1037.     if ((cp = strrchr(fnam, '.')) != NULL) {
  1038.     middle = NULL;
  1039.     if (!strcasecomp(anchor->content_type, "text/html")) {
  1040.         middle = HTML_SUFFIX;
  1041.         middle++;        /* point to 'h' of .htm(l) - kw */
  1042.     } else if (!strcasecomp(anchor->content_type, "text/plain")) {
  1043.         middle = "txt";
  1044.     } else if (!strcasecomp(anchor->content_type,
  1045.                 "application/octet-stream")) {
  1046.         middle = "bin";
  1047.     } else if ((suffix =
  1048.             HTFileSuffix(HTAtom_for(anchor->content_type), NULL)) &&
  1049.            *suffix == '.') {
  1050. #if defined(VMS) || defined(FNAMES_8_3)
  1051.         if (strchr(suffix + 1, '.') == NULL)
  1052. #endif
  1053.         middle = suffix + 1;
  1054.     }
  1055.     if (middle) {
  1056.         *cp = '\0';
  1057. #ifdef FNAMES_8_3
  1058.         me->idash = strlen(fnam);      /* remember position of '-'  - kw */
  1059.         strcat(fnam, "-");    /* NAME-htm,  NAME-txt, etc. - hack for DOS */
  1060. #else
  1061.         strcat(fnam, ".");    /* NAME.html, NAME-txt etc. */
  1062. #endif /* FNAMES_8_3 */
  1063.         strcat(fnam, middle);
  1064. #ifdef VMS
  1065.         strcat(fnam, "-");    /* NAME.html-gz, NAME.txt-gz, NAME.txt-Z etc.*/
  1066. #else
  1067.         strcat(fnam, ".");    /* NAME-htm.gz (DOS), NAME.html.gz (UNIX)etc.*/
  1068. #endif /* VMS */
  1069.     } else {
  1070.         *(cp + 1) = '\0';
  1071.     }
  1072.     } else {
  1073.     strcat(fnam, ".");
  1074.     }
  1075.     strcat(fnam, compress_suffix);
  1076.     /*
  1077.      *    It's not one of the suffixes checked for a
  1078.      *    spoof in tempname(), so check it now. - FM
  1079.      */
  1080.     if ((fp = fopen(fnam, "r")) != NULL) {
  1081.     fclose(fp);
  1082.     fp = NULL;
  1083.     goto Compressed_tempname;
  1084.     }
  1085.  
  1086.     /*
  1087.      *    Open the file for receiving the compressed input stream. - FM
  1088.      */
  1089.     me->fp = LYNewBinFile (fnam);
  1090.     if (!me->fp) {
  1091.     HTAlert(CANNOT_OPEN_TEMP);
  1092.     FREE(uncompress_mask);
  1093.     FREE(me);
  1094.     return NULL;
  1095.     }
  1096.  
  1097.     /*
  1098.      *    me->viewer_command will be NULL if the converter Pres found above
  1099.      *    is not for an external viewer but an internal HTStream converter.
  1100.      *    We also don't set it under conditions where HTSaveAndExecute would
  1101.      *    disallow execution of the command. - KW
  1102.      */
  1103.     if (!dump_output_immediately && !traversal
  1104. #if defined(EXEC_LINKS) || defined(EXEC_SCRIPTS)
  1105.     && (Pres->quality != 999.0 ||
  1106.         (!no_exec &&    /* allowed exec link or script ? */
  1107.          (local_exec ||
  1108.           (local_exec_on_local_files &&
  1109.            (LYJumpFileURL ||
  1110.         !strncmp(anchor->address,"file://localhost",16))))))
  1111. #endif /* EXEC_LINKS || EXEC_SCRIPTS */
  1112.     ) {
  1113.     StrAllocCopy(me->viewer_command, Pres->command);
  1114.     }
  1115.  
  1116.     /*
  1117.      *    Make command to process file. - FM
  1118.      */
  1119. #ifdef USE_ZLIB
  1120.     if (compress_suffix[0] == 'g' && /* must be gzip */
  1121.     !me->viewer_command) {
  1122.     /*
  1123.      *  We won't call gzip externally, so we don't need to supply
  1124.      *  a command for it. - kw
  1125.      */
  1126.     StrAllocCopy(me->end_command, "");
  1127.     } else
  1128. #endif /* USE_ZLIB */
  1129.     {
  1130.     me->end_command = (char *)calloc(1, (strlen(uncompress_mask) + 10 +
  1131.                          strlen(fnam)) * sizeof(char));
  1132.     if (me->end_command == NULL)
  1133.         outofmem(__FILE__, "HTCompressed");
  1134.     sprintf(me->end_command, uncompress_mask, fnam, "", "", "", "", "", "");
  1135.     }
  1136.     FREE(uncompress_mask);
  1137.  
  1138.     /*
  1139.      *    Make command to delete file. - FM
  1140.      */
  1141.     me->remove_command = (char *)calloc(1, (strlen(REMOVE_COMMAND) + 10 +
  1142.                         strlen(fnam)) * sizeof(char));
  1143.     if (me->remove_command == NULL)
  1144.     outofmem(__FILE__, "HTCompressed");
  1145.     sprintf(me->remove_command, REMOVE_COMMAND, fnam);
  1146.  
  1147.     /*
  1148.      *    Save the filename and return the structure. - FM
  1149.      */
  1150.     StrAllocCopy(anchor->FileCache, fnam);
  1151.     return me;
  1152. }
  1153.  
  1154. /*    Dump output to stdout - LJM & FM
  1155. **    ---------------------
  1156. **
  1157. */
  1158. PUBLIC HTStream* HTDumpToStdout ARGS3(
  1159.     HTPresentation *,    pres GCC_UNUSED,
  1160.     HTParentAnchor *,    anchor,
  1161.     HTStream *,        sink GCC_UNUSED)
  1162. {
  1163.     HTStream * ret_obj;
  1164.     ret_obj = (HTStream*)calloc(sizeof(* ret_obj),1);
  1165.     if (ret_obj == NULL)
  1166.     outofmem(__FILE__, "HTDumpToStdout");
  1167.     ret_obj->isa = &HTFWriter;
  1168.     ret_obj->remove_command = NULL;
  1169.     ret_obj->end_command = NULL;
  1170.     ret_obj->anchor = anchor;
  1171.  
  1172.     ret_obj->fp = stdout; /* stdout*/
  1173.     return ret_obj;
  1174. }
  1175.  
  1176. #if defined(VMS) && !defined(USE_COMMAND_FILE)
  1177. #include <fab.h>
  1178. #include <rmsdef.h>        /* RMS status codes */
  1179. #include <iodef.h>        /* I/O function codes */
  1180. #include <fibdef.h>        /* file information block defs */
  1181. #include <atrdef.h>        /* attribute request codes */
  1182. #ifdef NOTDEFINED /*** Not all versions of VMS compilers have these.     ***/
  1183. #include <fchdef.h>        /* file characteristics */
  1184. #include <fatdef.h>        /* file attribute defs */
  1185. #else          /*** So we'll define what we need from them ourselves. ***/
  1186. #define FCH$V_CONTIGB    0x005            /* pos of cont best try bit */
  1187. #define FCH$M_CONTIGB    (1 << FCH$V_CONTIGB)    /* contig best try bit mask */
  1188. /* VMS I/O User's Reference Manual: Part I (V5.x doc set) */
  1189. struct fatdef {
  1190.     unsigned char    fat$b_rtype,    fat$b_rattrib;
  1191.     unsigned short    fat$w_rsize;
  1192.     unsigned long    fat$l_hiblk,    fat$l_efblk;
  1193.     unsigned short    fat$w_ffbyte;
  1194.     unsigned char    fat$b_bktsize,    fat$b_vfcsize;
  1195.     unsigned short    fat$w_maxrec,    fat$w_defext,    fat$w_gbc;
  1196.     unsigned    : 16, : 32, : 16;   /* 6 bytes reserved, 2 bytes not used */
  1197.     unsigned short    fat$w_versions;
  1198. };
  1199. #endif /* NOTDEFINED */
  1200.  
  1201. /* arbitrary descriptor without type and class info */
  1202. typedef struct dsc { unsigned short len, mbz; void *adr; } Desc;
  1203.  
  1204. extern unsigned long    sys$open(),  sys$qiow(),  sys$dassgn();
  1205.  
  1206. #define syswork(sts)    ((sts) & 1)
  1207. #define sysfail(sts)    (!syswork(sts))
  1208.  
  1209.  
  1210. /*
  1211.  *  25-Jul-1995 - Pat Rankin (rankin@eql.caltech.edu)
  1212.  *
  1213.  *  Force a file to be marked as having fixed-length, 512 byte records
  1214.  *  without implied carriage control, and with best_try_contiguous set.
  1215.  */
  1216. PUBLIC unsigned long LYVMS_FixedLengthRecords ARGS1(char *, filename)
  1217. {
  1218.     struct FAB        fab;        /* RMS file access block */
  1219.     struct fibdef   fib;        /* XQP file information block */
  1220.     struct fatdef   recattr;        /* XQP file "record" attributes */
  1221.     struct atrdef   attr_rqst_list[3];    /* XQP attribute request itemlist */
  1222.  
  1223.     Desc        fib_dsc;
  1224.     unsigned short  channel,  iosb[4];
  1225.     unsigned long   fchars,  sts,  tmp;
  1226.  
  1227.     /* initialize file access block */
  1228.     fab = cc$rms_fab;
  1229.     fab.fab$l_fna = filename;
  1230.     fab.fab$b_fns = (unsigned char) strlen(filename);
  1231.     fab.fab$l_fop = FAB$M_UFO;    /* user file open; no further RMS processing */
  1232.     fab.fab$b_fac = FAB$M_PUT;    /* need write access */
  1233.     fab.fab$b_shr = FAB$M_NIL;    /* exclusive access */
  1234.  
  1235.     sts = sys$open(&fab);    /* channel in stv; $dassgn to close */
  1236.     if (sts == RMS$_FLK) {
  1237.     /* For MultiNet, at least, if the file was just written by a remote
  1238.        NFS client, the local NFS server might still have it open, and the
  1239.        failed access attempt will provoke it to be closed, so try again. */
  1240.     sts = sys$open(&fab);
  1241.     }
  1242.     if (sysfail(sts)) return sts;
  1243.  
  1244.     /* RMS supplies a user-mode channel (see FAB$L_FOP FAB$V_UFO doc) */
  1245.     channel = (unsigned short) fab.fab$l_stv;
  1246.  
  1247.     /* set up ACP interface strutures */
  1248.     /* file information block, passed by descriptor; it's okay to start with
  1249.        an empty FIB after RMS has accessed the file for us */
  1250.     fib_dsc.len = sizeof fib;
  1251.     fib_dsc.mbz = 0;
  1252.     fib_dsc.adr = &fib;
  1253.     memset((void *)&fib, 0, sizeof fib);
  1254.     /* attribute request list */
  1255.     attr_rqst_list[0].atr$w_size = sizeof recattr;
  1256.     attr_rqst_list[0].atr$w_type = ATR$C_RECATTR;
  1257.     *(void **)&attr_rqst_list[0].atr$l_addr = (void *)&recattr;
  1258.     attr_rqst_list[1].atr$w_size = sizeof fchars;
  1259.     attr_rqst_list[1].atr$w_type = ATR$C_UCHAR;
  1260.     *(void **)&attr_rqst_list[1].atr$l_addr = (void *)&fchars;
  1261.     attr_rqst_list[2].atr$w_size = attr_rqst_list[2].atr$w_type = 0;
  1262.     attr_rqst_list[2].atr$l_addr = 0;
  1263.     /* file "record" attributes */
  1264.     memset((void *)&recattr, 0, sizeof recattr);
  1265.     fchars = 0;     /* file characteristics */
  1266.  
  1267.     /* get current attributes */
  1268.     sts = sys$qiow(0, channel, IO$_ACCESS, iosb, (void(*)())0, 0,
  1269.            &fib_dsc, 0, 0, 0, attr_rqst_list, 0);
  1270.     if (syswork(sts))
  1271.     sts = iosb[0];
  1272.  
  1273.     /* set desired attributes */
  1274.     if (syswork(sts)) {
  1275.     recattr.fat$b_rtype = FAB$C_SEQ | FAB$C_FIX;    /* org=seq, rfm=fix */
  1276.     recattr.fat$w_rsize = recattr.fat$w_maxrec = 512;   /* lrl=mrs=512 */
  1277.     recattr.fat$b_rattrib = 0;            /* rat=none */
  1278.     fchars |= FCH$M_CONTIGB;        /* contiguous-best-try */
  1279.     sts = sys$qiow(0, channel, IO$_DEACCESS, iosb, (void(*)())0, 0,
  1280.                &fib_dsc, 0, 0, 0, attr_rqst_list, 0);
  1281.     if (syswork(sts))
  1282.         sts = iosb[0];
  1283.     }
  1284.  
  1285.     /* all done */
  1286.     tmp = sys$dassgn(channel);
  1287.     if (syswork(sts))
  1288.     sts = tmp;
  1289.     return sts;
  1290. }
  1291. #endif /* VMS && !USE_COMMAND_FILE */
  1292.