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