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