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