1 /*
2 * $LynxId: LYPrint.c,v 1.109 2021/07/29 20:38:35 tom Exp $
3 */
4 #include <HTUtils.h>
5 #include <HTAccess.h>
6 #include <HTList.h>
7 #include <HTAlert.h>
8 #include <HTFile.h>
9 #include <LYCurses.h>
10 #include <GridText.h>
11 #include <LYUtils.h>
12 #include <LYPrint.h>
13 #include <LYGlobalDefs.h>
14 #include <LYSignal.h>
15 #include <LYStrings.h>
16 #include <LYClean.h>
17 #include <LYGetFile.h>
18 #include <LYHistory.h>
19 #include <LYList.h>
20 #include <LYCharSets.h> /* To get current charset for mail header. */
21
22 #include <LYLeaks.h>
23
24 #define CancelPrint(msg) HTInfoMsg(msg); goto done
25 #define CannotPrint(msg) HTAlert(msg); goto done
26
27 /*
28 * printfile prints out the current file minus the links and targets to a
29 * variety of places
30 */
31
32 /* it parses an incoming link that looks like
33 *
34 * LYNXPRINT://LOCAL_FILE/lines=##
35 * LYNXPRINT://MAIL_FILE/lines=##
36 * LYNXPRINT://TO_SCREEN/lines=##
37 * LYNXPRINT://LPANSI/lines=##
38 * LYNXPRINT://PRINTER/lines=##/number=#
39 */
40
41 #define TO_FILE 1
42 #define TO_SCREEN 2
43 /*
44 * "lpansi.c"
45 * Original author: Gary Day (gday@comp.uark.edu), 11/30/93
46 * Current version: 2.1 by Noel Hunter (noel@wfu.edu), 10/20/94
47 *
48 * Basic structure based on print -- format files for printing from
49 * _Practical_C_Programming by Steve Oualline, O'Reilly & Associates
50 *
51 * adapted from the README for lpansi.c v2.1, dated 10/20/1994:
52 * Print to ANSI printer on local terminal
53 * The VT100 standard defines printer on and off escape sequences,
54 * esc[5i is printer on, and esc[4i is printer off.
55 *
56 * incorporate the idea of "lpansi" directly into LYPrint.c - HN
57 */
58 #define LPANSI 3
59 #define MAIL 4
60 #define PRINTER 5
61
62 #if USE_VMS_MAILER
63 static int remove_quotes(char *string);
64 #endif /* USE_VMS_MAILER */
65
66 static char *subject_translate8bit(char *source);
67
68 #define LYNX_PRINT_TITLE 0
69 #define LYNX_PRINT_URL 1
70 #define LYNX_PRINT_DATE 2
71 #define LYNX_PRINT_LASTMOD 3
72
73 #define MAX_PUTENV 4
74
set_environ(int name,const char * value,const char * no_value)75 static void set_environ(int name,
76 const char *value,
77 const char *no_value)
78 {
79 static const char *names[MAX_PUTENV] =
80 {
81 "LYNX_PRINT_TITLE",
82 "LYNX_PRINT_URL",
83 "LYNX_PRINT_DATE",
84 "LYNX_PRINT_LASTMOD",
85 };
86 static char *pointers[MAX_PUTENV];
87 char *envbuffer = 0;
88
89 #ifdef VMS
90 #define SET_ENVIRON(name, value, no_value) set_environ(name, value, no_value)
91 char temp[80];
92
93 StrAllocCopy(envbuffer, value);
94 if (isEmpty(envbuffer))
95 StrAllocCopy(envbuffer, no_value);
96 Define_VMSLogical(strcpy(temp, names[name]), envbuffer);
97 FREE(envbuffer);
98 #else
99 #define SET_ENVIRON(name, value, no_value) set_environ(name, value, "")
100 /*
101 * Once we've given a string to 'putenv()', we must not free it until we
102 * give it a string to replace it.
103 */
104 StrAllocCopy(envbuffer, names[name]);
105 StrAllocCat(envbuffer, "=");
106 StrAllocCat(envbuffer, value ? value : no_value);
107 putenv(envbuffer);
108 FREE(pointers[name]);
109 pointers[name] = envbuffer;
110 #endif
111 }
112
suggested_filename(DocInfo * newdoc)113 static char *suggested_filename(DocInfo *newdoc)
114 {
115 char *sug_filename = 0;
116 int rootlen;
117
118 /*
119 * Load the suggested filename string. - FM
120 */
121 if (HText_getSugFname() != 0)
122 StrAllocCopy(sug_filename, HText_getSugFname()); /* must be freed */
123 else
124 StrAllocCopy(sug_filename, newdoc->address); /* must be freed */
125 /*
126 * Strip suffix for compressed-files, if present.
127 */
128 if (HTCompressFileType(sug_filename, ".", &rootlen) != cftNone)
129 sug_filename[rootlen] = '\0';
130
131 CTRACE((tfp, "suggest %s\n", sug_filename));
132 return sug_filename;
133 }
134
SetupFilename(bstring ** filename,const char * sug_filename)135 static void SetupFilename(bstring **filename,
136 const char *sug_filename)
137 {
138 HTFormat format;
139 HTAtom *encoding;
140 char *cp;
141
142 BStrCopy0(*filename, sug_filename); /* add suggestion info */
143 /*
144 * FIXME: the history-recall still uses fixed-size buffers
145 */
146 if ((*filename)->len >= LY_MAXPATH) {
147 (*filename)->str[LY_MAXPATH - 1] = '\0';
148 } else {
149 BStrAlloc(*filename, LY_MAXPATH);
150 }
151 change_sug_filename((*filename)->str);
152 if (!(HTisDocumentSource())
153 && (cp = strrchr((*filename)->str, '.')) != NULL) {
154 format = HTFileFormat((*filename)->str, &encoding, NULL);
155 CTRACE((tfp, "... format %s\n", format->name));
156 if (!strcasecomp(format->name, STR_HTML) ||
157 !IsUnityEnc(encoding)) {
158 (*filename)->len = (int) (cp - (*filename)->str);
159 BStrCat0(*filename, TEXT_SUFFIX);
160 }
161 }
162 CTRACE((tfp, "... result %s\n", (*filename)->str));
163 }
164
165 #define FN_INIT 0
166 #define FN_READ 1
167 #define FN_DONE 2
168 #define FN_QUIT 3
169
170 #define PRINT_FLAG 0
171 #define GENERIC_FLAG 1
172
RecallFilename(bstring ** filename,BOOLEAN * first,int * now,int * total,int flag)173 static int RecallFilename(bstring **filename,
174 BOOLEAN *first,
175 int *now,
176 int *total,
177 int flag)
178 {
179 int ch;
180 char *cp;
181 RecallType recall;
182
183 /*
184 * Set up the sug_filenames recall buffer.
185 */
186 if (*now < 0) {
187 *total = (sug_filenames ? HTList_count(sug_filenames) : 0);
188 *now = *total;
189 }
190 recall = ((*total >= 1) ? RECALL_URL : NORECALL);
191
192 if ((ch = LYgetBString(filename, FALSE, 0, recall)) < 0 ||
193 isBEmpty(*filename) || ch == UPARROW_KEY || ch == DNARROW_KEY) {
194 if (recall && ch == UPARROW_KEY) {
195 if (*first) {
196 *first = FALSE;
197 /*
198 * Use the last Fname in the list. - FM
199 */
200 *now = 0;
201 } else {
202 /*
203 * Go back to the previous Fname in the list. - FM
204 */
205 *now += 1;
206 }
207 if (*now >= *total) {
208 /*
209 * Reset the *first flag, and use sug_file or a blank. -
210 * FM
211 */
212 *first = TRUE;
213 *now = *total;
214 _statusline(FILENAME_PROMPT);
215 return FN_INIT;
216 } else if ((cp = (char *) HTList_objectAt(sug_filenames,
217 *now)) != NULL) {
218 BStrCopy0(*filename, cp);
219 if (*total == 1) {
220 _statusline(EDIT_THE_PREV_FILENAME);
221 } else {
222 _statusline(EDIT_A_PREV_FILENAME);
223 }
224 return FN_READ;
225 }
226 } else if (recall && ch == DNARROW_KEY) {
227 if (*first) {
228 *first = FALSE;
229 /*
230 * Use the first Fname in the list. - FM
231 */
232 *now = *total - 1;
233 } else {
234 /*
235 * Advance to the next Fname in the list. - FM
236 */
237 *now -= 1;
238 }
239 if (*now < 0) {
240 /*
241 * Set the *first flag, and use sug_file or a blank. - FM
242 */
243 *first = TRUE;
244 *now = *total;
245 _statusline(FILENAME_PROMPT);
246 return FN_INIT;
247 } else if ((cp = (char *) HTList_objectAt(sug_filenames,
248 *now)) != NULL) {
249 BStrCopy0(*filename, cp);
250 if (*total == 1) {
251 _statusline(EDIT_THE_PREV_FILENAME);
252 } else {
253 _statusline(EDIT_A_PREV_FILENAME);
254 }
255 return FN_READ;
256 }
257 }
258
259 /*
260 * Operation cancelled.
261 */
262 if (flag == PRINT_FLAG)
263 HTInfoMsg(SAVE_REQUEST_CANCELLED);
264 else if (flag == GENERIC_FLAG)
265 return FN_QUIT;
266
267 return FN_QUIT;
268 }
269 return FN_DONE;
270 }
271
confirm_by_pages(const char * prompt,int lines_in_file,int lines_per_page)272 static BOOLEAN confirm_by_pages(const char *prompt,
273 int lines_in_file,
274 int lines_per_page)
275 {
276 int pages = lines_in_file / (lines_per_page + 1);
277 int c;
278
279 /* count fractional pages ! */
280 if ((lines_in_file % (LYlines + 1)) > 0)
281 pages++;
282
283 if (pages > 4) {
284 char *msg = 0;
285
286 HTSprintf0(&msg, prompt, pages);
287 c = HTConfirmDefault(msg, YES);
288 FREE(msg);
289
290 if (c == YES) {
291 LYaddstr(" Ok...");
292 } else {
293 HTInfoMsg(PRINT_REQUEST_CANCELLED);
294 return FALSE;
295 }
296 }
297 return TRUE;
298 }
299
send_file_to_file(DocInfo * newdoc,char * content_base,char * sug_filename)300 static void send_file_to_file(DocInfo *newdoc,
301 char *content_base,
302 char *sug_filename)
303 {
304 BOOLEAN FirstRecall = TRUE;
305 BOOLEAN use_cte;
306 const char *disp_charset;
307 FILE *outfile_fp;
308 bstring *buffer = NULL;
309 bstring *filename = NULL;
310 int FnameNum = -1;
311 int FnameTotal;
312 int c = 0;
313
314 _statusline(FILENAME_PROMPT);
315
316 retry:
317 SetupFilename(&filename, sug_filename);
318 if (non_empty(lynx_save_space)) {
319 BStrCopy0(buffer, lynx_save_space);
320 BStrCat(buffer, filename);
321 BStrCopy(filename, buffer);
322 } else {
323 BStrCopy0(buffer, "");
324 }
325
326 check_recall:
327 switch (RecallFilename(&filename, &FirstRecall, &FnameNum,
328 &FnameTotal, PRINT_FLAG)) {
329 case FN_INIT:
330 goto retry;
331 case FN_READ:
332 goto check_recall;
333 case FN_QUIT:
334 goto done;
335 default:
336 break;
337 }
338
339 if (!LYValidateFilename(&buffer, &filename)) {
340 CancelPrint(SAVE_REQUEST_CANCELLED);
341 }
342
343 /*
344 * See if it already exists.
345 */
346 switch (c = LYValidateOutput(buffer->str)) {
347 case 'Y':
348 break;
349 case 'N':
350 _statusline(NEW_FILENAME_PROMPT);
351 FirstRecall = TRUE;
352 FnameNum = FnameTotal;
353 goto retry;
354 default:
355 goto done;
356 }
357
358 /*
359 * See if we can write to it.
360 */
361 CTRACE((tfp, "LYPrint: filename is %s, action is `%c'\n", buffer->str, c));
362
363 #ifdef HAVE_POPEN
364 if (buffer->str[0] == '|') {
365 if (no_shell) {
366 HTUserMsg(SPAWNING_DISABLED);
367 FirstRecall = TRUE;
368 FnameNum = FnameTotal;
369 goto retry;
370 } else if ((outfile_fp = popen(buffer->str + 1, "w")) == NULL) {
371 CTRACE((tfp, "LYPrint: errno is %d\n", errno));
372 HTAlert(CANNOT_WRITE_TO_FILE);
373 _statusline(NEW_FILENAME_PROMPT);
374 FirstRecall = TRUE;
375 FnameNum = FnameTotal;
376 goto retry;
377 }
378 } else
379 #endif
380 if ((outfile_fp = (TOUPPER(c) == 'A'
381 ? LYAppendToTxtFile(buffer->str)
382 : LYNewTxtFile(buffer->str))) == NULL) {
383 CTRACE((tfp, "LYPrint: errno is %d\n", errno));
384 HTAlert(CANNOT_WRITE_TO_FILE);
385 _statusline(NEW_FILENAME_PROMPT);
386 FirstRecall = TRUE;
387 FnameNum = FnameTotal;
388 goto retry;
389 }
390
391 if (LYPrependBaseToSource && HTisDocumentSource()) {
392 /*
393 * Added the document's base as a BASE tag to the top of the file. May
394 * create technically invalid HTML, but will help get any partial or
395 * relative URLs resolved properly if no BASE tag is present to replace
396 * it. - FM
397 *
398 * Add timestamp (last reload).
399 */
400
401 fprintf(outfile_fp,
402 "<!-- X-URL: %s -->\n", newdoc->address);
403 if (HText_getDate() != NULL) {
404 fprintf(outfile_fp,
405 "<!-- Date: %s -->\n", HText_getDate());
406 if (HText_getLastModified() != NULL
407 && strcmp(HText_getLastModified(), HText_getDate())
408 && strcmp(HText_getLastModified(),
409 "Thu, 01 Jan 1970 00:00:01 GMT")) {
410 fprintf(outfile_fp,
411 "<!-- Last-Modified: %s -->\n", HText_getLastModified());
412 }
413 }
414
415 fprintf(outfile_fp,
416 "<BASE HREF=\"%s\">\n", content_base);
417 }
418
419 if (LYPrependCharsetToSource && HTisDocumentSource()) {
420 /*
421 * Added the document's charset as a META CHARSET tag to the top of the
422 * file. May create technically invalid HTML, but will help to resolve
423 * properly the document converted via chartrans: printed document
424 * correspond to a display charset and we *should* override both
425 * assume_local_charset and original document's META CHARSET (if any).
426 *
427 * Currently, if several META CHARSETs are found Lynx uses the first
428 * only, and it is opposite to BASE where the original BASE in the
429 * <HEAD> overrides ones from the top.
430 *
431 * As in print-to-email we write charset only if the document has 8-bit
432 * characters, and we have no CJK or an unofficial "x-" charset.
433 */
434 use_cte = HTLoadedDocumentEightbit();
435 disp_charset = LYCharSet_UC[current_char_set].MIMEname;
436 if (!use_cte || LYHaveCJKCharacterSet ||
437 strncasecomp(disp_charset, "x-", 2) == 0) {
438 } else {
439 fprintf(outfile_fp,
440 "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"" STR_HTML
441 "; charset=%s\">\n\n",
442 disp_charset);
443 }
444 }
445
446 print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* FILE */
447 if (keypad_mode)
448 printlist(outfile_fp, FALSE);
449
450 #ifdef HAVE_POPEN
451 if (LYIsPipeCommand(buffer->str))
452 pclose(outfile_fp);
453 else
454 #endif
455 LYCloseOutput(outfile_fp);
456
457 #ifdef VMS
458 if (0 == strncasecomp(buffer->str, "sys$disk:", 9)) {
459 if (0 == StrNCmp((buffer->str + 9), "[]", 2)) {
460 HTAddSugFilename(buffer->str + 11);
461 } else {
462 HTAddSugFilename(buffer->str + 9);
463 }
464 } else {
465 HTAddSugFilename(buffer->str);
466 }
467 #else
468 HTAddSugFilename(buffer->str);
469 #endif /* VMS */
470
471 done:
472 BStrFree(buffer);
473 BStrFree(filename);
474 return;
475 }
476
send_file_to_mail(DocInfo * newdoc,char * content_base,char * content_location)477 static void send_file_to_mail(DocInfo *newdoc,
478 char *content_base,
479 char *content_location)
480 {
481 static BOOLEAN first_mail_preparsed = TRUE;
482
483 #if USE_VMS_MAILER
484 BOOLEAN isPMDF = LYMailPMDF();
485 FILE *hfd;
486 char hdrfile[LY_MAXPATH];
487 #endif
488 BOOL use_mime;
489
490 #if !CAN_PIPE_TO_MAILER
491 char my_temp[LY_MAXPATH];
492 #endif
493
494 BOOL use_cte;
495 BOOL use_type;
496 const char *disp_charset;
497 FILE *outfile_fp;
498 char *buffer = NULL;
499 char *subject = NULL;
500 bstring *user_response = NULL;
501
502 if (!LYSystemMail())
503 return;
504
505 if (LYPreparsedSource && first_mail_preparsed &&
506 HTisDocumentSource()) {
507 if (HTConfirmDefault(CONFIRM_MAIL_SOURCE_PREPARSED, NO) == YES) {
508 LYaddstr(" Ok...");
509 first_mail_preparsed = FALSE;
510 } else {
511 CancelPrint(MAIL_REQUEST_CANCELLED);
512 }
513 }
514
515 _statusline(MAIL_ADDRESS_PROMPT);
516 BStrCopy0(user_response, NonNull(personal_mail_address));
517 if (LYgetBString(&user_response, FALSE, 0, RECALL_MAIL) < 0 ||
518 isBEmpty(user_response)) {
519 CancelPrint(MAIL_REQUEST_CANCELLED);
520 }
521
522 /*
523 * Determine which mail headers should be sent. Use Content-Type and
524 * MIME-Version headers only if needed. We need them if we are mailing
525 * HTML source, or if we have 8-bit characters and will be sending
526 * Content-Transfer-Encoding to indicate this. We will append a charset
527 * parameter to the Content-Type if we do not have an "x-" charset, and we
528 * will include the Content-Transfer-Encoding only if we are appending the
529 * charset parameter, because indicating an 8-bit transfer without also
530 * indicating the charset can cause problems with many mailers. - FM & KW
531 */
532 disp_charset = LYCharSet_UC[current_char_set].MIMEname;
533 use_cte = HTLoadedDocumentEightbit();
534 if (!(use_cte && strncasecomp(disp_charset, "x-", 2))) {
535 disp_charset = NULL;
536 #if USE_VMS_MAILER
537 use_cte = FALSE;
538 #endif
539 }
540 #if USE_VMS_MAILER
541 use_type = (BOOL) (disp_charset || HTisDocumentSource());
542 #endif
543
544 /*
545 * Use newdoc->title as a subject instead of sug_filename: MORE readable
546 * and 8-bit letters shouldn't be a problem - LP
547 */
548 /* change_sug_filename(sug_filename); */
549 subject = subject_translate8bit(newdoc->title);
550
551 if (newdoc->isHEAD) {
552 /*
553 * Special case for mailing HEAD response: this is rather technical
554 * information, show URL.
555 */
556 FREE(subject);
557 StrAllocCopy(subject, "HEAD ");
558 StrAllocCat(subject, newdoc->address);
559 }
560 #if USE_VMS_MAILER
561 if (StrChr(user_response->str, '@') &&
562 !StrChr(user_response->str, ':') &&
563 !StrChr(user_response->str, '%') &&
564 !StrChr(user_response->str, '"')) {
565 char *temp = 0;
566
567 HTSprintf0(&temp, mail_adrs, user_response->str);
568 BStrCopy0(user_response, temp);
569 FREE(temp);
570 }
571
572 outfile_fp = LYOpenTemp(my_temp,
573 (HTisDocumentSource())
574 ? HTML_SUFFIX
575 : TEXT_SUFFIX,
576 "w");
577 if (outfile_fp == NULL) {
578 CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
579 }
580
581 if (isPMDF) {
582 if ((hfd = LYOpenTemp(hdrfile, TEXT_SUFFIX, "w")) == NULL) {
583 CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
584 }
585 if (use_type) {
586 fprintf(hfd, "Mime-Version: 1.0\n");
587 if (use_cte) {
588 fprintf(hfd, "Content-Transfer-Encoding: 8bit\n");
589 }
590 }
591 if (HTisDocumentSource()) {
592 /*
593 * Add Content-Type, Content-Location, and Content-Base headers for
594 * HTML source. - FM
595 */
596 fprintf(hfd, "Content-Type: " STR_HTML);
597 if (disp_charset != NULL) {
598 fprintf(hfd, "; charset=%s\n", disp_charset);
599 } else {
600 fprintf(hfd, "\n");
601 }
602 fprintf(hfd, "Content-Base: %s\n", content_base);
603 fprintf(hfd, "Content-Location: %s\n", content_location);
604 } else {
605 /*
606 * Add Content-Type: text/plain if we have 8-bit characters and a
607 * valid charset for non-source documents. - FM
608 */
609 if (disp_charset != NULL) {
610 fprintf(hfd,
611 "Content-Type: " STR_PLAINTEXT "; charset=%s\n",
612 disp_charset);
613 }
614 }
615 /*
616 * X-URL header. - FM
617 */
618 fprintf(hfd, "X-URL: %s\n", newdoc->address);
619 /*
620 * For PMDF, put the subject in the header file and close it. - FM
621 */
622 fprintf(hfd, "Subject: %.70s\n\n", subject);
623 LYCloseTempFP(hfd);
624 }
625
626 /*
627 * Write the contents to a temp file.
628 */
629 if (LYPrependBaseToSource && HTisDocumentSource()) {
630 /*
631 * Added the document's base as a BASE tag to the top of the message
632 * body. May create technically invalid HTML, but will help get any
633 * partial or relative URLs resolved properly if no BASE tag is present
634 * to replace it. - FM
635 */
636 fprintf(outfile_fp,
637 "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
638 newdoc->address, content_base);
639 } else if (!isPMDF) {
640 fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
641 }
642 print_wwwfile_to_fd(outfile_fp, TRUE, FALSE); /* MAIL */
643 if (keypad_mode)
644 printlist(outfile_fp, FALSE);
645 LYCloseTempFP(outfile_fp);
646
647 buffer = NULL;
648 if (isPMDF) {
649 /*
650 * Now set up the command. - FM
651 */
652 HTSprintf0(&buffer,
653 "%s %s %s,%s %s",
654 system_mail,
655 system_mail_flags,
656 hdrfile,
657 my_temp,
658 user_response->str);
659 } else {
660 /*
661 * For "generic" VMS MAIL, include the subject in the command. - FM
662 */
663 remove_quotes(subject);
664 HTSprintf0(&buffer,
665 "%s %s/subject=\"%.70s\" %s %s",
666 system_mail,
667 system_mail_flags,
668 subject,
669 my_temp,
670 user_response->str);
671 }
672
673 stop_curses();
674 SetOutputMode(O_TEXT);
675 printf(MAILING_FILE);
676 LYSystem(buffer);
677 LYSleepAlert();
678 start_curses();
679 SetOutputMode(O_BINARY);
680
681 if (isPMDF)
682 (void) LYRemoveTemp(hdrfile);
683 (void) LYRemoveTemp(my_temp);
684 #else /* !VMS (Unix or DOS) */
685
686 #if CAN_PIPE_TO_MAILER
687 outfile_fp = LYPipeToMailer();
688 #else
689 outfile_fp = LYOpenTemp(my_temp, TEXT_SUFFIX, "w");
690 #endif
691 if (outfile_fp == NULL) {
692 CannotPrint(MAIL_REQUEST_FAILED);
693 }
694
695 /*
696 * Determine which mail headers should be sent. Use Content-Type and
697 * MIME-Version headers only if needed. We need them if we are mailing
698 * HTML source, or if we have 8-bit characters and will be sending
699 * Content-Transfer-Encoding to indicate this.
700 *
701 * Send Content-Transfer-Encoding only if the document has 8-bit
702 * characters. Send a charset parameter only if the document has 8-bit
703 * characters and we seem to have a valid charset. - kw
704 */
705 use_cte = HTLoadedDocumentEightbit();
706 disp_charset = LYCharSet_UC[current_char_set].MIMEname;
707 /*
708 * Don't send a charset if we have a CJK character set selected, since it
709 * may not be appropriate for mail... Also don't use an unofficial "x-"
710 * charset. - kw
711 */
712 if (!use_cte || LYHaveCJKCharacterSet ||
713 strncasecomp(disp_charset, "x-", 2) == 0) {
714 disp_charset = NULL;
715 }
716 #ifdef NOTDEFINED
717 /* Enable this if indicating an 8-bit transfer without also indicating the
718 * charset causes problems. - kw */
719 if (use_cte && !disp_charset)
720 use_cte = FALSE;
721 #endif /* NOTDEFINED */
722 use_type = (BOOL) (disp_charset || HTisDocumentSource());
723 use_mime = (BOOL) (use_cte || use_type);
724
725 if (use_mime) {
726 fprintf(outfile_fp, "Mime-Version: 1.0\n");
727 if (use_cte) {
728 fprintf(outfile_fp, "Content-Transfer-Encoding: 8bit\n");
729 }
730 }
731
732 if (HTisDocumentSource()) {
733 /*
734 * Add Content-Type, Content-Location, and Content-Base headers for
735 * HTML source. - FM
736 */
737 fprintf(outfile_fp, "Content-Type: " STR_HTML);
738 if (disp_charset != NULL) {
739 fprintf(outfile_fp, "; charset=%s\n", disp_charset);
740 } else {
741 fprintf(outfile_fp, "\n");
742 }
743 } else {
744 /*
745 * Add Content-Type: text/plain if we have 8-bit characters and a
746 * valid charset for non-source documents. - KW
747 */
748 if (disp_charset != NULL) {
749 fprintf(outfile_fp,
750 "Content-Type: " STR_PLAINTEXT "; charset=%s\n",
751 disp_charset);
752 }
753 }
754 /*
755 * If we are using MIME headers, add content-base and content-location if
756 * we have them. This will always be the case if the document is source.
757 * - kw
758 */
759 if (use_mime) {
760 if (content_base)
761 fprintf(outfile_fp, "Content-Base: %s\n", content_base);
762 if (content_location)
763 fprintf(outfile_fp, "Content-Location: %s\n", content_location);
764 }
765
766 /*
767 * Add the To, Subject, and X-URL headers. - FM
768 */
769 fprintf(outfile_fp, "To: %s\nSubject: %s\n", user_response->str, subject);
770 fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
771
772 if (LYPrependBaseToSource && HTisDocumentSource()) {
773 /*
774 * Added the document's base as a BASE tag to the top of the message
775 * body. May create technically invalid HTML, but will help get any
776 * partial or relative URLs resolved properly if no BASE tag is present
777 * to replace it. - FM
778 */
779 fprintf(outfile_fp,
780 "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
781 newdoc->address, content_base);
782 }
783 print_wwwfile_to_fd(outfile_fp, TRUE, FALSE); /* MAIL */
784 if (keypad_mode)
785 printlist(outfile_fp, FALSE);
786
787 #if CAN_PIPE_TO_MAILER
788 pclose(outfile_fp);
789 #else
790 LYCloseOutput(outfile_fp);
791 LYSendMailFile(user_response->str,
792 my_temp,
793 subject,
794 "",
795 "");
796 (void) LYRemoveTemp(my_temp); /* Delete the tmpfile. */
797 #endif /* CAN_PIPE_TO_MAILER */
798 #endif /* USE_VMS_MAILER */
799
800 done: /* send_file_to_mail() */
801 BStrFree(user_response);
802 FREE(buffer);
803 FREE(subject);
804 return;
805 }
806
send_file_to_printer(DocInfo * newdoc,char * content_base,char * sug_filename,int printer_number)807 static void send_file_to_printer(DocInfo *newdoc,
808 char *content_base,
809 char *sug_filename,
810 int printer_number)
811 {
812 BOOLEAN FirstRecall = TRUE;
813 FILE *outfile_fp;
814 char *the_command = 0;
815 bstring *my_file = NULL;
816 char my_temp[LY_MAXPATH];
817 int FnameTotal, FnameNum = -1;
818 lynx_list_item_type *cur_printer;
819
820 outfile_fp = LYOpenTemp(my_temp,
821 (HTisDocumentSource())
822 ? HTML_SUFFIX
823 : TEXT_SUFFIX,
824 "w");
825 if (outfile_fp == NULL) {
826 CannotPrint(FILE_ALLOC_FAILED);
827 }
828
829 if (LYPrependBaseToSource && HTisDocumentSource()) {
830 /*
831 * Added the document's base as a BASE tag to the top of the file. May
832 * create technically invalid HTML, but will help get any partial or
833 * relative URLs resolved properly if no BASE tag is present to replace
834 * it. - FM
835 */
836 fprintf(outfile_fp,
837 "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
838 newdoc->address, content_base);
839 }
840 print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* PRINTER */
841 if (keypad_mode)
842 printlist(outfile_fp, FALSE);
843
844 LYCloseTempFP(outfile_fp);
845
846 /* find the right printer number */
847 {
848 int count = 0;
849
850 for (cur_printer = printers;
851 count < printer_number;
852 count++, cur_printer = cur_printer->next) ; /* null body */
853 }
854
855 /*
856 * Commands have the form "command %s [%s] [etc]" where %s is the filename
857 * and the second optional %s is the suggested filename.
858 */
859 if (cur_printer->command == NULL) {
860 CannotPrint(PRINTER_MISCONF_ERROR);
861 }
862
863 /*
864 * Check for two '%s' and ask for the second filename argument if there
865 * is.
866 */
867 BStrCopy0(my_file, "");
868 if (HTCountCommandArgs(cur_printer->command) >= 2) {
869 _statusline(FILENAME_PROMPT);
870 again:
871 SetupFilename(&my_file, sug_filename);
872 check_again:
873 switch (RecallFilename(&my_file, &FirstRecall, &FnameNum,
874 &FnameTotal, PRINT_FLAG)) {
875 case FN_INIT:
876 goto again;
877 case FN_READ:
878 goto check_again;
879 case FN_QUIT:
880 goto done;
881 default:
882 break;
883 }
884
885 if (no_dotfiles || !show_dotfiles) {
886 if (*LYPathLeaf(my_file->str) == '.') {
887 HTAlert(FILENAME_CANNOT_BE_DOT);
888 _statusline(NEW_FILENAME_PROMPT);
889 FirstRecall = TRUE;
890 FnameNum = FnameTotal;
891 goto again;
892 }
893 }
894 /*
895 * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path
896 * on VMS. - FM
897 */
898 if (LYIsNullDevice(my_file->str)) {
899 CancelPrint(PRINT_REQUEST_CANCELLED);
900 }
901 HTAddSugFilename(my_file->str);
902 }
903 #ifdef SH_EX /* 1999/01/04 (Mon) 09:37:03 */
904 HTAddParam(&the_command, cur_printer->command, 1, my_temp);
905 if (!isBEmpty(my_file)) {
906 HTAddParam(&the_command, cur_printer->command, 2, my_file->str);
907 HTEndParam(&the_command, cur_printer->command, 3);
908 } else {
909 HTEndParam(&the_command, cur_printer->command, 2);
910 }
911 #else
912 HTAddParam(&the_command, cur_printer->command, 1, my_temp);
913 HTAddParam(&the_command, cur_printer->command, 2, my_file->str);
914 HTEndParam(&the_command, cur_printer->command, 2);
915 #endif
916
917 /*
918 * Move the cursor to the top of the screen so that output from system'd
919 * commands don't scroll up the screen.
920 */
921 LYmove(1, 1);
922
923 stop_curses();
924 CTRACE((tfp, "command: %s\n", the_command));
925 SetOutputMode(O_TEXT);
926 printf(PRINTING_FILE);
927 /*
928 * Set various bits of document information as environment variables, for
929 * use by external print scripts/etc. On UNIX, We assume there are values,
930 * and leave NULL value checking up to the external PRINTER: cmd/script -
931 * KED
932 */
933 SET_ENVIRON(LYNX_PRINT_TITLE, HText_getTitle(), "No Title");
934 SET_ENVIRON(LYNX_PRINT_URL, newdoc->address, "No URL");
935 SET_ENVIRON(LYNX_PRINT_DATE, HText_getDate(), "No Date");
936 SET_ENVIRON(LYNX_PRINT_LASTMOD, HText_getLastModified(), "No LastMod");
937
938 LYSystem(the_command);
939 FREE(the_command);
940 (void) LYRemoveTemp(my_temp);
941
942 /*
943 * Remove the various LYNX_PRINT_xxxx logicals. - KED
944 * [could use unsetenv(), but it's not portable]
945 */
946 SET_ENVIRON(LYNX_PRINT_TITLE, "", "");
947 SET_ENVIRON(LYNX_PRINT_URL, "", "");
948 SET_ENVIRON(LYNX_PRINT_DATE, "", "");
949 SET_ENVIRON(LYNX_PRINT_LASTMOD, "", "");
950
951 fflush(stdout);
952 #ifndef VMS
953 signal(SIGINT, cleanup_sig);
954 #endif /* !VMS */
955 #ifdef SH_EX
956 fprintf(stdout, gettext(" Print job complete.\n"));
957 fflush(stdout);
958 #endif
959 SetOutputMode(O_BINARY);
960 LYSleepMsg();
961 start_curses();
962
963 done: /* send_file_to_printer() */
964 BStrFree(my_file);
965 return;
966 }
967
send_file_to_screen(DocInfo * newdoc,char * content_base,int Lpansi)968 static void send_file_to_screen(DocInfo *newdoc,
969 char *content_base,
970 int Lpansi)
971 {
972 FILE *outfile_fp;
973 bstring *prompt = NULL;
974
975 if (Lpansi) {
976 _statusline(CHECK_PRINTER);
977 } else {
978 _statusline(PRESS_RETURN_TO_BEGIN);
979 }
980
981 BStrCopy0(prompt, "");
982 if (LYgetBString(&prompt, FALSE, 0, NORECALL) < 0) {
983 CancelPrint(PRINT_REQUEST_CANCELLED);
984 } else {
985 outfile_fp = stdout;
986
987 stop_curses();
988 SetOutputMode(O_TEXT);
989
990 #ifndef VMS
991 signal(SIGINT, SIG_IGN);
992 #endif /* !VMS */
993
994 if (LYPrependBaseToSource && HTisDocumentSource()) {
995 /*
996 * Added the document's base as a BASE tag to the top of the file. May
997 * create technically invalid HTML, but will help get any partial or
998 * relative URLs resolved properly if no BASE tag is present to replace
999 * it. - FM
1000 */
1001 fprintf(outfile_fp,
1002 "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
1003 newdoc->address, content_base);
1004 }
1005 if (Lpansi)
1006 printf("\033[5i");
1007 print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* SCREEN */
1008 if (keypad_mode)
1009 printlist(outfile_fp, FALSE);
1010
1011 #ifdef VMS
1012 if (HadVMSInterrupt) {
1013 HadVMSInterrupt = FALSE;
1014 start_curses();
1015 CancelPrint(PRINT_REQUEST_CANCELLED);
1016 }
1017 #endif /* VMS */
1018 if (Lpansi) {
1019 printf("\n\014"); /* Form feed */
1020 printf("\033[4i");
1021 fflush(stdout); /* refresh to screen */
1022 } else {
1023 fprintf(stdout, "\n\n%s", PRESS_RETURN_TO_FINISH);
1024 fflush(stdout); /* refresh to screen */
1025 (void) LYgetch(); /* grab some user input to pause */
1026 #ifdef VMS
1027 HadVMSInterrupt = FALSE;
1028 #endif /* VMS */
1029 }
1030 #ifdef SH_EX
1031 fprintf(stdout, "\n");
1032 #endif
1033 SetOutputMode(O_BINARY);
1034 start_curses();
1035 }
1036
1037 done: /* send_file_to_screen() */
1038 BStrFree(prompt);
1039 return;
1040 }
1041
printfile(DocInfo * newdoc)1042 int printfile(DocInfo *newdoc)
1043 {
1044 BOOLEAN Lpansi = FALSE;
1045 DocAddress WWWDoc;
1046 char *content_base = NULL;
1047 char *content_location = NULL;
1048 char *cp = NULL;
1049 char *link_info = NULL;
1050 char *sug_filename = NULL;
1051 int lines_in_file = 0;
1052 int pagelen = 0;
1053 int printer_number = 0;
1054 int type = 0;
1055
1056 /*
1057 * Extract useful info from URL.
1058 */
1059 StrAllocCopy(link_info, newdoc->address + 12);
1060
1061 /*
1062 * Reload the file we want to print into memory.
1063 */
1064 LYpop(newdoc);
1065 WWWDoc.address = newdoc->address;
1066 WWWDoc.post_data = newdoc->post_data;
1067 WWWDoc.post_content_type = newdoc->post_content_type;
1068 WWWDoc.bookmark = newdoc->bookmark;
1069 WWWDoc.isHEAD = newdoc->isHEAD;
1070 WWWDoc.safe = newdoc->safe;
1071 if (!HTLoadAbsolute(&WWWDoc))
1072 return (NOT_FOUND);
1073
1074 /*
1075 * If we have an explicit content-base, we may use it even if not in source
1076 * mode. - kw
1077 */
1078 if (HText_getContentBase()) {
1079 StrAllocCopy(content_base, HText_getContentBase());
1080 LYRemoveBlanks(content_base);
1081 if (isEmpty(content_base)) {
1082 FREE(content_base);
1083 }
1084 }
1085 /*
1086 * If document is source, load the content_base and content_location
1087 * strings. - FM
1088 */
1089 if (HTisDocumentSource()) {
1090 if (HText_getContentLocation()) {
1091 StrAllocCopy(content_location, HText_getContentLocation());
1092 LYRemoveBlanks(content_location);
1093 if (isEmpty(content_location)) {
1094 FREE(content_location);
1095 }
1096 }
1097 if (!content_base) {
1098 if ((content_location) && is_url(content_location)) {
1099 StrAllocCopy(content_base, content_location);
1100 } else {
1101 StrAllocCopy(content_base, newdoc->address);
1102 }
1103 }
1104 if (!content_location) {
1105 StrAllocCopy(content_location, newdoc->address);
1106 }
1107 }
1108
1109 sug_filename = suggested_filename(newdoc);
1110
1111 /*
1112 * Get the number of lines in the file.
1113 */
1114 if ((cp = LYstrstr(link_info, "lines=")) != NULL) {
1115 /*
1116 * Terminate prev string here.
1117 */
1118 *cp = '\0';
1119 /*
1120 * Number of characters in "lines=".
1121 */
1122 cp += 6;
1123
1124 lines_in_file = atoi(cp);
1125 }
1126
1127 /*
1128 * Determine the type.
1129 */
1130 if (LYstrstr(link_info, "LOCAL_FILE")) {
1131 type = TO_FILE;
1132 } else if (LYstrstr(link_info, "TO_SCREEN")) {
1133 type = TO_SCREEN;
1134 } else if (LYstrstr(link_info, "LPANSI")) {
1135 Lpansi = TRUE;
1136 type = TO_SCREEN;
1137 } else if (LYstrstr(link_info, "MAIL_FILE")) {
1138 type = MAIL;
1139 } else if (LYstrstr(link_info, "PRINTER")) {
1140 type = PRINTER;
1141
1142 if ((cp = LYstrstr(link_info, "number=")) != NULL) {
1143 /* number of characters in "number=" */
1144 cp += 7;
1145 printer_number = atoi(cp);
1146 }
1147 if ((cp = LYstrstr(link_info, "pagelen=")) != NULL) {
1148 /* number of characters in "pagelen=" */
1149 cp += 8;
1150 pagelen = atoi(cp);
1151 } else {
1152 /* default to 66 lines */
1153 pagelen = 66;
1154 }
1155 }
1156
1157 /*
1158 * Act on the request. - FM
1159 */
1160 switch (type) {
1161
1162 case TO_FILE:
1163 send_file_to_file(newdoc, content_base, sug_filename);
1164 break;
1165
1166 case MAIL:
1167 send_file_to_mail(newdoc, content_base, content_location);
1168 break;
1169
1170 case TO_SCREEN:
1171 if (confirm_by_pages(CONFIRM_LONG_SCREEN_PRINT, lines_in_file, LYlines))
1172 send_file_to_screen(newdoc, content_base, Lpansi);
1173 break;
1174
1175 case PRINTER:
1176 if (confirm_by_pages(CONFIRM_LONG_PAGE_PRINT, lines_in_file, pagelen))
1177 send_file_to_printer(newdoc, content_base, sug_filename, printer_number);
1178 break;
1179
1180 } /* end switch */
1181
1182 FREE(link_info);
1183 FREE(sug_filename);
1184 FREE(content_base);
1185 FREE(content_location);
1186 return (NORMAL);
1187 }
1188
1189 #if USE_VMS_MAILER
remove_quotes(char * string)1190 static int remove_quotes(char *string)
1191 {
1192 int i;
1193
1194 for (i = 0; string[i] != '\0'; i++)
1195 if (string[i] == '"')
1196 string[i] = ' ';
1197 else if (string[i] == '&')
1198 string[i] = ' ';
1199 else if (string[i] == '|')
1200 string[i] = ' ';
1201
1202 return (0);
1203 }
1204 #endif /* USE_VMS_MAILER */
1205
1206 /*
1207 * Mail subject may have 8-bit characters and they are in display charset.
1208 * There is no stable practice for 8-bit subject encodings: MIME defines
1209 * "quoted-printable" which holds charset info but most mailers still don't
1210 * support it. On the other hand many mailers send open 8-bit subjects without
1211 * charset info and use local assumption for certain countries. Besides that,
1212 * obsolete SMTP software is not 8bit clean but still in use, it strips the
1213 * characters in 128-160 range from subjects which may be a fault outside
1214 * iso-8859-XX.
1215 *
1216 * We translate subject to "outgoing_mail_charset" (defined in lynx.cfg) it may
1217 * correspond to US-ASCII as the safest value or any other lynx character
1218 * handler, -1 for no translation (so display charset).
1219 *
1220 * Always returns a new allocated string which has to be freed.
1221 */
1222 #include <LYCharUtils.h>
subject_translate8bit(char * source)1223 static char *subject_translate8bit(char *source)
1224 {
1225 char *target = NULL;
1226
1227 int charset_in, charset_out;
1228
1229 int i = outgoing_mail_charset; /* from lynx.cfg, -1 by default */
1230
1231 StrAllocCopy(target, source);
1232 if (i < 0
1233 || i == current_char_set
1234 || LYCharSet_UC[current_char_set].enc == UCT_ENC_CJK
1235 || LYCharSet_UC[i].enc == UCT_ENC_CJK) {
1236 return (target); /* OK */
1237 } else {
1238 charset_out = i;
1239 charset_in = current_char_set;
1240 }
1241
1242 LYUCTranslateBackHeaderText(&target, charset_in, charset_out, YES);
1243
1244 return (target);
1245 }
1246
1247 /*
1248 * print_options writes out the current printer choices to a file
1249 * so that the user can select printers in the same way that
1250 * they select all other links
1251 * printer links look like
1252 *
1253 * LYNXPRINT://LOCAL_FILE/lines=# print to a local file
1254 * LYNXPRINT://TO_SCREEN/lines=# print to the screen
1255 * LYNXPRINT://LPANSI/lines=# print to the local terminal
1256 * LYNXPRINT://MAIL_FILE/lines=# mail the file
1257 * LYNXPRINT://PRINTER/lines=#/number=# print to printer number #
1258 */
print_options(char ** newfile,const char * printed_url,int lines_in_file)1259 int print_options(char **newfile,
1260 const char *printed_url,
1261 int lines_in_file)
1262 {
1263 static char my_temp[LY_MAXPATH] = "\0";
1264 char *buffer = 0;
1265 int count;
1266 int pages;
1267 FILE *fp0;
1268 lynx_list_item_type *cur_printer;
1269
1270 if ((fp0 = InternalPageFP(my_temp, TRUE)) == 0)
1271 return (-1);
1272
1273 LYLocalFileToURL(newfile, my_temp);
1274
1275 BeginInternalPage(fp0, PRINT_OPTIONS_TITLE, PRINT_OPTIONS_HELP);
1276
1277 fprintf(fp0, "<pre>\n");
1278
1279 /* pages = lines_in_file/66 + 1; */
1280 pages = (lines_in_file + 65) / 66;
1281 HTSprintf0(&buffer,
1282 " <em>%s</em> %s\n <em>%s</em> %d\n <em>%s</em> %d %s %s\n",
1283 gettext("Document:"), printed_url,
1284 gettext("Number of lines:"), lines_in_file,
1285 gettext("Number of pages:"), pages,
1286 (pages > 1 ? gettext("pages") : gettext("page")),
1287 gettext("(approximately)"));
1288 fputs(buffer, fp0);
1289 FREE(buffer);
1290
1291 if (no_print || no_disk_save || no_mail)
1292 fprintf(fp0,
1293 " <em>%s</em>\n",
1294 gettext("Some print functions have been disabled!"));
1295
1296 fprintf(fp0, "\n%s\n",
1297 (user_mode == NOVICE_MODE)
1298 ? gettext("Standard print options:")
1299 : gettext("Print options:"));
1300
1301 if (no_disk_save == FALSE && no_print == FALSE) {
1302 fprintf(fp0,
1303 " <a href=\"%s//LOCAL_FILE/lines=%d\">%s</a>\n",
1304 STR_LYNXPRINT,
1305 lines_in_file,
1306 gettext("Save to a local file"));
1307 } else {
1308 fprintf(fp0, " <em>%s</em>\n", gettext("Save to disk disabled"));
1309 }
1310 if (no_mail == FALSE && local_host_only == FALSE)
1311 fprintf(fp0,
1312 " <a href=\"%s//MAIL_FILE/lines=%d\">%s</a>\n",
1313 STR_LYNXPRINT,
1314 lines_in_file,
1315 gettext("Mail the file"));
1316
1317 #if defined(UNIX) || defined(VMS)
1318 fprintf(fp0,
1319 " <a href=\"%s//TO_SCREEN/lines=%d\">%s</a>\n",
1320 STR_LYNXPRINT,
1321 lines_in_file,
1322 gettext("Print to the screen"));
1323 fprintf(fp0,
1324 " <a href=\"%s//LPANSI/lines=%d\">%s</a>\n",
1325 STR_LYNXPRINT,
1326 lines_in_file,
1327 gettext("Print out on a printer attached to your vt100 terminal"));
1328 #endif
1329
1330 if (user_mode == NOVICE_MODE)
1331 fprintf(fp0, "\n%s\n", gettext("Local additions:"));
1332
1333 for (count = 0, cur_printer = printers; cur_printer != NULL;
1334 cur_printer = cur_printer->next, count++)
1335 if (no_print == FALSE || cur_printer->always_enabled) {
1336 fprintf(fp0,
1337 " <a href=\"%s//PRINTER/number=%d/pagelen=%d/lines=%d\">",
1338 STR_LYNXPRINT,
1339 count, cur_printer->pagelen, lines_in_file);
1340 fprintf(fp0, "%s", (cur_printer->name ?
1341 cur_printer->name : "No Name Given"));
1342 fprintf(fp0, "</a>\n");
1343 }
1344 fprintf(fp0, "</pre>\n");
1345 EndInternalPage(fp0);
1346 LYCloseTempFP(fp0);
1347
1348 LYforce_no_cache = TRUE;
1349 return (0);
1350 }
1351
1352 /*
1353 * General purpose filename getter.
1354 *
1355 * Returns a pointer to an absolute filename string, if the input filename
1356 * exists, and is readable. Returns NULL if the input was cancelled (^G, or CR
1357 * on empty input).
1358 *
1359 * The pointer to the filename string needs to be free()'d by the caller (when
1360 * non-NULL).
1361 *
1362 * --KED 02/21/99
1363 */
GetFileName(void)1364 char *GetFileName(void)
1365 {
1366 struct stat stat_info;
1367
1368 bstring *fbuf = NULL;
1369 bstring *tbuf = NULL;
1370 char *result = NULL;
1371
1372 BOOLEAN FirstRecall = TRUE;
1373 int FnameNum = -1;
1374 int FnameTotal;
1375
1376 _statusline(FILENAME_PROMPT);
1377
1378 retry:
1379 /*
1380 * No initial filename.
1381 */
1382 SetupFilename(&fbuf, "");
1383
1384 check_recall:
1385 /*
1386 * Go get a filename (it would be nice to do TAB == filename-completion as
1387 * the name is entered, but we'll save doing that for another time.
1388 */
1389 switch (RecallFilename(&fbuf, &FirstRecall, &FnameNum,
1390 &FnameTotal, GENERIC_FLAG)) {
1391 case FN_INIT:
1392 goto retry;
1393 case FN_READ:
1394 goto check_recall;
1395 case FN_QUIT:
1396 goto cleanup;
1397 default:
1398 break;
1399 }
1400
1401 /*
1402 * Add raw input form to list ... we may want to reuse/edit it on a
1403 * subsequent call, etc.
1404 */
1405 #ifdef VMS
1406 if (0 == strncasecomp(fbuf->str, "sys$disk:", 9)) {
1407 if (0 == StrNCmp((fbuf->str + 9), "[]", 2)) {
1408 HTAddSugFilename(fbuf->str + 11);
1409 } else {
1410 HTAddSugFilename(fbuf->str + 9);
1411 }
1412 } else {
1413 HTAddSugFilename(fbuf->str);
1414 }
1415 #else
1416 HTAddSugFilename(fbuf->str);
1417 #endif /* VMS */
1418
1419 /*
1420 * Expand tilde's, make filename absolute, etc.
1421 */
1422 BStrCopy0(tbuf, "");
1423 if (!LYValidateFilename(&tbuf, &fbuf))
1424 goto cleanup;
1425
1426 /*
1427 * Check for file existence; readability.
1428 */
1429 if ((stat(tbuf->str, &stat_info) < 0) ||
1430 (!(S_ISREG(stat_info.st_mode)
1431 #ifdef S_IFLNK
1432 || S_ISLNK(stat_info.st_mode)
1433 #endif /* S_IFLNK */
1434 ))) {
1435 HTInfoMsg(FILE_DOES_NOT_EXIST);
1436 _statusline(FILE_DOES_NOT_EXIST_RE);
1437 FirstRecall = TRUE;
1438 FnameNum = FnameTotal;
1439 goto retry;
1440 }
1441
1442 if (!LYCanReadFile(tbuf->str)) {
1443 HTInfoMsg(FILE_NOT_READABLE);
1444 _statusline(FILE_NOT_READABLE_RE);
1445 FirstRecall = TRUE;
1446 FnameNum = FnameTotal;
1447 goto retry;
1448 }
1449
1450 /*
1451 * We have a valid filename, and readable file. Return it to the caller.
1452 *
1453 * The returned pointer should be free()'d by the caller.
1454 */
1455 StrAllocCopy(result, tbuf->str);
1456
1457 cleanup:
1458 BStrFree(fbuf);
1459 BStrFree(tbuf);
1460 return (result);
1461 }
1462