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