summaryrefslogtreecommitdiffstats
path: root/src/LYPrint.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:37:15 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:37:15 +0000
commitae5d181b854d3ccb373b6bc01b4869e44ff4d87a (patch)
tree91f59efb48c56a84cc798e012fccb667b63d3fee /src/LYPrint.c
parentInitial commit. (diff)
downloadlynx-ae5d181b854d3ccb373b6bc01b4869e44ff4d87a.tar.xz
lynx-ae5d181b854d3ccb373b6bc01b4869e44ff4d87a.zip
Adding upstream version 2.9.0dev.12.upstream/2.9.0dev.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/LYPrint.c')
-rw-r--r--src/LYPrint.c1461
1 files changed, 1461 insertions, 0 deletions
diff --git a/src/LYPrint.c b/src/LYPrint.c
new file mode 100644
index 0000000..58b81a6
--- /dev/null
+++ b/src/LYPrint.c
@@ -0,0 +1,1461 @@
+/*
+ * $LynxId: LYPrint.c,v 1.109 2021/07/29 20:38:35 tom Exp $
+ */
+#include <HTUtils.h>
+#include <HTAccess.h>
+#include <HTList.h>
+#include <HTAlert.h>
+#include <HTFile.h>
+#include <LYCurses.h>
+#include <GridText.h>
+#include <LYUtils.h>
+#include <LYPrint.h>
+#include <LYGlobalDefs.h>
+#include <LYSignal.h>
+#include <LYStrings.h>
+#include <LYClean.h>
+#include <LYGetFile.h>
+#include <LYHistory.h>
+#include <LYList.h>
+#include <LYCharSets.h> /* To get current charset for mail header. */
+
+#include <LYLeaks.h>
+
+#define CancelPrint(msg) HTInfoMsg(msg); goto done
+#define CannotPrint(msg) HTAlert(msg); goto done
+
+/*
+ * printfile prints out the current file minus the links and targets to a
+ * variety of places
+ */
+
+/* it parses an incoming link that looks like
+ *
+ * LYNXPRINT://LOCAL_FILE/lines=##
+ * LYNXPRINT://MAIL_FILE/lines=##
+ * LYNXPRINT://TO_SCREEN/lines=##
+ * LYNXPRINT://LPANSI/lines=##
+ * LYNXPRINT://PRINTER/lines=##/number=#
+ */
+
+#define TO_FILE 1
+#define TO_SCREEN 2
+/*
+ * "lpansi.c"
+ * Original author: Gary Day (gday@comp.uark.edu), 11/30/93
+ * Current version: 2.1 by Noel Hunter (noel@wfu.edu), 10/20/94
+ *
+ * Basic structure based on print -- format files for printing from
+ * _Practical_C_Programming by Steve Oualline, O'Reilly & Associates
+ *
+ * adapted from the README for lpansi.c v2.1, dated 10/20/1994:
+ * Print to ANSI printer on local terminal
+ * The VT100 standard defines printer on and off escape sequences,
+ * esc[5i is printer on, and esc[4i is printer off.
+ *
+ * incorporate the idea of "lpansi" directly into LYPrint.c - HN
+ */
+#define LPANSI 3
+#define MAIL 4
+#define PRINTER 5
+
+#if USE_VMS_MAILER
+static int remove_quotes(char *string);
+#endif /* USE_VMS_MAILER */
+
+static char *subject_translate8bit(char *source);
+
+#define LYNX_PRINT_TITLE 0
+#define LYNX_PRINT_URL 1
+#define LYNX_PRINT_DATE 2
+#define LYNX_PRINT_LASTMOD 3
+
+#define MAX_PUTENV 4
+
+static void set_environ(int name,
+ const char *value,
+ const char *no_value)
+{
+ static const char *names[MAX_PUTENV] =
+ {
+ "LYNX_PRINT_TITLE",
+ "LYNX_PRINT_URL",
+ "LYNX_PRINT_DATE",
+ "LYNX_PRINT_LASTMOD",
+ };
+ static char *pointers[MAX_PUTENV];
+ char *envbuffer = 0;
+
+#ifdef VMS
+#define SET_ENVIRON(name, value, no_value) set_environ(name, value, no_value)
+ char temp[80];
+
+ StrAllocCopy(envbuffer, value);
+ if (isEmpty(envbuffer))
+ StrAllocCopy(envbuffer, no_value);
+ Define_VMSLogical(strcpy(temp, names[name]), envbuffer);
+ FREE(envbuffer);
+#else
+#define SET_ENVIRON(name, value, no_value) set_environ(name, value, "")
+ /*
+ * Once we've given a string to 'putenv()', we must not free it until we
+ * give it a string to replace it.
+ */
+ StrAllocCopy(envbuffer, names[name]);
+ StrAllocCat(envbuffer, "=");
+ StrAllocCat(envbuffer, value ? value : no_value);
+ putenv(envbuffer);
+ FREE(pointers[name]);
+ pointers[name] = envbuffer;
+#endif
+}
+
+static char *suggested_filename(DocInfo *newdoc)
+{
+ char *sug_filename = 0;
+ int rootlen;
+
+ /*
+ * Load the suggested filename string. - FM
+ */
+ if (HText_getSugFname() != 0)
+ StrAllocCopy(sug_filename, HText_getSugFname()); /* must be freed */
+ else
+ StrAllocCopy(sug_filename, newdoc->address); /* must be freed */
+ /*
+ * Strip suffix for compressed-files, if present.
+ */
+ if (HTCompressFileType(sug_filename, ".", &rootlen) != cftNone)
+ sug_filename[rootlen] = '\0';
+
+ CTRACE((tfp, "suggest %s\n", sug_filename));
+ return sug_filename;
+}
+
+static void SetupFilename(bstring **filename,
+ const char *sug_filename)
+{
+ HTFormat format;
+ HTAtom *encoding;
+ char *cp;
+
+ BStrCopy0(*filename, sug_filename); /* add suggestion info */
+ /*
+ * FIXME: the history-recall still uses fixed-size buffers
+ */
+ if ((*filename)->len >= LY_MAXPATH) {
+ (*filename)->str[LY_MAXPATH - 1] = '\0';
+ } else {
+ BStrAlloc(*filename, LY_MAXPATH);
+ }
+ change_sug_filename((*filename)->str);
+ if (!(HTisDocumentSource())
+ && (cp = strrchr((*filename)->str, '.')) != NULL) {
+ format = HTFileFormat((*filename)->str, &encoding, NULL);
+ CTRACE((tfp, "... format %s\n", format->name));
+ if (!strcasecomp(format->name, STR_HTML) ||
+ !IsUnityEnc(encoding)) {
+ (*filename)->len = (int) (cp - (*filename)->str);
+ BStrCat0(*filename, TEXT_SUFFIX);
+ }
+ }
+ CTRACE((tfp, "... result %s\n", (*filename)->str));
+}
+
+#define FN_INIT 0
+#define FN_READ 1
+#define FN_DONE 2
+#define FN_QUIT 3
+
+#define PRINT_FLAG 0
+#define GENERIC_FLAG 1
+
+static int RecallFilename(bstring **filename,
+ BOOLEAN *first,
+ int *now,
+ int *total,
+ int flag)
+{
+ int ch;
+ char *cp;
+ RecallType recall;
+
+ /*
+ * Set up the sug_filenames recall buffer.
+ */
+ if (*now < 0) {
+ *total = (sug_filenames ? HTList_count(sug_filenames) : 0);
+ *now = *total;
+ }
+ recall = ((*total >= 1) ? RECALL_URL : NORECALL);
+
+ if ((ch = LYgetBString(filename, FALSE, 0, recall)) < 0 ||
+ isBEmpty(*filename) || ch == UPARROW_KEY || ch == DNARROW_KEY) {
+ if (recall && ch == UPARROW_KEY) {
+ if (*first) {
+ *first = FALSE;
+ /*
+ * Use the last Fname in the list. - FM
+ */
+ *now = 0;
+ } else {
+ /*
+ * Go back to the previous Fname in the list. - FM
+ */
+ *now += 1;
+ }
+ if (*now >= *total) {
+ /*
+ * Reset the *first flag, and use sug_file or a blank. -
+ * FM
+ */
+ *first = TRUE;
+ *now = *total;
+ _statusline(FILENAME_PROMPT);
+ return FN_INIT;
+ } else if ((cp = (char *) HTList_objectAt(sug_filenames,
+ *now)) != NULL) {
+ BStrCopy0(*filename, cp);
+ if (*total == 1) {
+ _statusline(EDIT_THE_PREV_FILENAME);
+ } else {
+ _statusline(EDIT_A_PREV_FILENAME);
+ }
+ return FN_READ;
+ }
+ } else if (recall && ch == DNARROW_KEY) {
+ if (*first) {
+ *first = FALSE;
+ /*
+ * Use the first Fname in the list. - FM
+ */
+ *now = *total - 1;
+ } else {
+ /*
+ * Advance to the next Fname in the list. - FM
+ */
+ *now -= 1;
+ }
+ if (*now < 0) {
+ /*
+ * Set the *first flag, and use sug_file or a blank. - FM
+ */
+ *first = TRUE;
+ *now = *total;
+ _statusline(FILENAME_PROMPT);
+ return FN_INIT;
+ } else if ((cp = (char *) HTList_objectAt(sug_filenames,
+ *now)) != NULL) {
+ BStrCopy0(*filename, cp);
+ if (*total == 1) {
+ _statusline(EDIT_THE_PREV_FILENAME);
+ } else {
+ _statusline(EDIT_A_PREV_FILENAME);
+ }
+ return FN_READ;
+ }
+ }
+
+ /*
+ * Operation cancelled.
+ */
+ if (flag == PRINT_FLAG)
+ HTInfoMsg(SAVE_REQUEST_CANCELLED);
+ else if (flag == GENERIC_FLAG)
+ return FN_QUIT;
+
+ return FN_QUIT;
+ }
+ return FN_DONE;
+}
+
+static BOOLEAN confirm_by_pages(const char *prompt,
+ int lines_in_file,
+ int lines_per_page)
+{
+ int pages = lines_in_file / (lines_per_page + 1);
+ int c;
+
+ /* count fractional pages ! */
+ if ((lines_in_file % (LYlines + 1)) > 0)
+ pages++;
+
+ if (pages > 4) {
+ char *msg = 0;
+
+ HTSprintf0(&msg, prompt, pages);
+ c = HTConfirmDefault(msg, YES);
+ FREE(msg);
+
+ if (c == YES) {
+ LYaddstr(" Ok...");
+ } else {
+ HTInfoMsg(PRINT_REQUEST_CANCELLED);
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static void send_file_to_file(DocInfo *newdoc,
+ char *content_base,
+ char *sug_filename)
+{
+ BOOLEAN FirstRecall = TRUE;
+ BOOLEAN use_cte;
+ const char *disp_charset;
+ FILE *outfile_fp;
+ bstring *buffer = NULL;
+ bstring *filename = NULL;
+ int FnameNum = -1;
+ int FnameTotal;
+ int c = 0;
+
+ _statusline(FILENAME_PROMPT);
+
+ retry:
+ SetupFilename(&filename, sug_filename);
+ if (non_empty(lynx_save_space)) {
+ BStrCopy0(buffer, lynx_save_space);
+ BStrCat(buffer, filename);
+ BStrCopy(filename, buffer);
+ } else {
+ BStrCopy0(buffer, "");
+ }
+
+ check_recall:
+ switch (RecallFilename(&filename, &FirstRecall, &FnameNum,
+ &FnameTotal, PRINT_FLAG)) {
+ case FN_INIT:
+ goto retry;
+ case FN_READ:
+ goto check_recall;
+ case FN_QUIT:
+ goto done;
+ default:
+ break;
+ }
+
+ if (!LYValidateFilename(&buffer, &filename)) {
+ CancelPrint(SAVE_REQUEST_CANCELLED);
+ }
+
+ /*
+ * See if it already exists.
+ */
+ switch (c = LYValidateOutput(buffer->str)) {
+ case 'Y':
+ break;
+ case 'N':
+ _statusline(NEW_FILENAME_PROMPT);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ default:
+ goto done;
+ }
+
+ /*
+ * See if we can write to it.
+ */
+ CTRACE((tfp, "LYPrint: filename is %s, action is `%c'\n", buffer->str, c));
+
+#ifdef HAVE_POPEN
+ if (buffer->str[0] == '|') {
+ if (no_shell) {
+ HTUserMsg(SPAWNING_DISABLED);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ } else if ((outfile_fp = popen(buffer->str + 1, "w")) == NULL) {
+ CTRACE((tfp, "LYPrint: errno is %d\n", errno));
+ HTAlert(CANNOT_WRITE_TO_FILE);
+ _statusline(NEW_FILENAME_PROMPT);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ }
+ } else
+#endif
+ if ((outfile_fp = (TOUPPER(c) == 'A'
+ ? LYAppendToTxtFile(buffer->str)
+ : LYNewTxtFile(buffer->str))) == NULL) {
+ CTRACE((tfp, "LYPrint: errno is %d\n", errno));
+ HTAlert(CANNOT_WRITE_TO_FILE);
+ _statusline(NEW_FILENAME_PROMPT);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ }
+
+ if (LYPrependBaseToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's base as a BASE tag to the top of the file. May
+ * create technically invalid HTML, but will help get any partial or
+ * relative URLs resolved properly if no BASE tag is present to replace
+ * it. - FM
+ *
+ * Add timestamp (last reload).
+ */
+
+ fprintf(outfile_fp,
+ "<!-- X-URL: %s -->\n", newdoc->address);
+ if (HText_getDate() != NULL) {
+ fprintf(outfile_fp,
+ "<!-- Date: %s -->\n", HText_getDate());
+ if (HText_getLastModified() != NULL
+ && strcmp(HText_getLastModified(), HText_getDate())
+ && strcmp(HText_getLastModified(),
+ "Thu, 01 Jan 1970 00:00:01 GMT")) {
+ fprintf(outfile_fp,
+ "<!-- Last-Modified: %s -->\n", HText_getLastModified());
+ }
+ }
+
+ fprintf(outfile_fp,
+ "<BASE HREF=\"%s\">\n", content_base);
+ }
+
+ if (LYPrependCharsetToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's charset as a META CHARSET tag to the top of the
+ * file. May create technically invalid HTML, but will help to resolve
+ * properly the document converted via chartrans: printed document
+ * correspond to a display charset and we *should* override both
+ * assume_local_charset and original document's META CHARSET (if any).
+ *
+ * Currently, if several META CHARSETs are found Lynx uses the first
+ * only, and it is opposite to BASE where the original BASE in the
+ * <HEAD> overrides ones from the top.
+ *
+ * As in print-to-email we write charset only if the document has 8-bit
+ * characters, and we have no CJK or an unofficial "x-" charset.
+ */
+ use_cte = HTLoadedDocumentEightbit();
+ disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+ if (!use_cte || LYHaveCJKCharacterSet ||
+ strncasecomp(disp_charset, "x-", 2) == 0) {
+ } else {
+ fprintf(outfile_fp,
+ "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"" STR_HTML
+ "; charset=%s\">\n\n",
+ disp_charset);
+ }
+ }
+
+ print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* FILE */
+ if (keypad_mode)
+ printlist(outfile_fp, FALSE);
+
+#ifdef HAVE_POPEN
+ if (LYIsPipeCommand(buffer->str))
+ pclose(outfile_fp);
+ else
+#endif
+ LYCloseOutput(outfile_fp);
+
+#ifdef VMS
+ if (0 == strncasecomp(buffer->str, "sys$disk:", 9)) {
+ if (0 == StrNCmp((buffer->str + 9), "[]", 2)) {
+ HTAddSugFilename(buffer->str + 11);
+ } else {
+ HTAddSugFilename(buffer->str + 9);
+ }
+ } else {
+ HTAddSugFilename(buffer->str);
+ }
+#else
+ HTAddSugFilename(buffer->str);
+#endif /* VMS */
+
+ done:
+ BStrFree(buffer);
+ BStrFree(filename);
+ return;
+}
+
+static void send_file_to_mail(DocInfo *newdoc,
+ char *content_base,
+ char *content_location)
+{
+ static BOOLEAN first_mail_preparsed = TRUE;
+
+#if USE_VMS_MAILER
+ BOOLEAN isPMDF = LYMailPMDF();
+ FILE *hfd;
+ char hdrfile[LY_MAXPATH];
+#endif
+ BOOL use_mime;
+
+#if !CAN_PIPE_TO_MAILER
+ char my_temp[LY_MAXPATH];
+#endif
+
+ BOOL use_cte;
+ BOOL use_type;
+ const char *disp_charset;
+ FILE *outfile_fp;
+ char *buffer = NULL;
+ char *subject = NULL;
+ bstring *user_response = NULL;
+
+ if (!LYSystemMail())
+ return;
+
+ if (LYPreparsedSource && first_mail_preparsed &&
+ HTisDocumentSource()) {
+ if (HTConfirmDefault(CONFIRM_MAIL_SOURCE_PREPARSED, NO) == YES) {
+ LYaddstr(" Ok...");
+ first_mail_preparsed = FALSE;
+ } else {
+ CancelPrint(MAIL_REQUEST_CANCELLED);
+ }
+ }
+
+ _statusline(MAIL_ADDRESS_PROMPT);
+ BStrCopy0(user_response, NonNull(personal_mail_address));
+ if (LYgetBString(&user_response, FALSE, 0, RECALL_MAIL) < 0 ||
+ isBEmpty(user_response)) {
+ CancelPrint(MAIL_REQUEST_CANCELLED);
+ }
+
+ /*
+ * Determine which mail headers should be sent. Use Content-Type and
+ * MIME-Version headers only if needed. We need them if we are mailing
+ * HTML source, or if we have 8-bit characters and will be sending
+ * Content-Transfer-Encoding to indicate this. We will append a charset
+ * parameter to the Content-Type if we do not have an "x-" charset, and we
+ * will include the Content-Transfer-Encoding only if we are appending the
+ * charset parameter, because indicating an 8-bit transfer without also
+ * indicating the charset can cause problems with many mailers. - FM & KW
+ */
+ disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+ use_cte = HTLoadedDocumentEightbit();
+ if (!(use_cte && strncasecomp(disp_charset, "x-", 2))) {
+ disp_charset = NULL;
+#if USE_VMS_MAILER
+ use_cte = FALSE;
+#endif
+ }
+#if USE_VMS_MAILER
+ use_type = (BOOL) (disp_charset || HTisDocumentSource());
+#endif
+
+ /*
+ * Use newdoc->title as a subject instead of sug_filename: MORE readable
+ * and 8-bit letters shouldn't be a problem - LP
+ */
+ /* change_sug_filename(sug_filename); */
+ subject = subject_translate8bit(newdoc->title);
+
+ if (newdoc->isHEAD) {
+ /*
+ * Special case for mailing HEAD response: this is rather technical
+ * information, show URL.
+ */
+ FREE(subject);
+ StrAllocCopy(subject, "HEAD ");
+ StrAllocCat(subject, newdoc->address);
+ }
+#if USE_VMS_MAILER
+ if (StrChr(user_response->str, '@') &&
+ !StrChr(user_response->str, ':') &&
+ !StrChr(user_response->str, '%') &&
+ !StrChr(user_response->str, '"')) {
+ char *temp = 0;
+
+ HTSprintf0(&temp, mail_adrs, user_response->str);
+ BStrCopy0(user_response, temp);
+ FREE(temp);
+ }
+
+ outfile_fp = LYOpenTemp(my_temp,
+ (HTisDocumentSource())
+ ? HTML_SUFFIX
+ : TEXT_SUFFIX,
+ "w");
+ if (outfile_fp == NULL) {
+ CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
+ }
+
+ if (isPMDF) {
+ if ((hfd = LYOpenTemp(hdrfile, TEXT_SUFFIX, "w")) == NULL) {
+ CannotPrint(UNABLE_TO_OPEN_TEMPFILE);
+ }
+ if (use_type) {
+ fprintf(hfd, "Mime-Version: 1.0\n");
+ if (use_cte) {
+ fprintf(hfd, "Content-Transfer-Encoding: 8bit\n");
+ }
+ }
+ if (HTisDocumentSource()) {
+ /*
+ * Add Content-Type, Content-Location, and Content-Base headers for
+ * HTML source. - FM
+ */
+ fprintf(hfd, "Content-Type: " STR_HTML);
+ if (disp_charset != NULL) {
+ fprintf(hfd, "; charset=%s\n", disp_charset);
+ } else {
+ fprintf(hfd, "\n");
+ }
+ fprintf(hfd, "Content-Base: %s\n", content_base);
+ fprintf(hfd, "Content-Location: %s\n", content_location);
+ } else {
+ /*
+ * Add Content-Type: text/plain if we have 8-bit characters and a
+ * valid charset for non-source documents. - FM
+ */
+ if (disp_charset != NULL) {
+ fprintf(hfd,
+ "Content-Type: " STR_PLAINTEXT "; charset=%s\n",
+ disp_charset);
+ }
+ }
+ /*
+ * X-URL header. - FM
+ */
+ fprintf(hfd, "X-URL: %s\n", newdoc->address);
+ /*
+ * For PMDF, put the subject in the header file and close it. - FM
+ */
+ fprintf(hfd, "Subject: %.70s\n\n", subject);
+ LYCloseTempFP(hfd);
+ }
+
+ /*
+ * Write the contents to a temp file.
+ */
+ if (LYPrependBaseToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's base as a BASE tag to the top of the message
+ * body. May create technically invalid HTML, but will help get any
+ * partial or relative URLs resolved properly if no BASE tag is present
+ * to replace it. - FM
+ */
+ fprintf(outfile_fp,
+ "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+ newdoc->address, content_base);
+ } else if (!isPMDF) {
+ fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
+ }
+ print_wwwfile_to_fd(outfile_fp, TRUE, FALSE); /* MAIL */
+ if (keypad_mode)
+ printlist(outfile_fp, FALSE);
+ LYCloseTempFP(outfile_fp);
+
+ buffer = NULL;
+ if (isPMDF) {
+ /*
+ * Now set up the command. - FM
+ */
+ HTSprintf0(&buffer,
+ "%s %s %s,%s %s",
+ system_mail,
+ system_mail_flags,
+ hdrfile,
+ my_temp,
+ user_response->str);
+ } else {
+ /*
+ * For "generic" VMS MAIL, include the subject in the command. - FM
+ */
+ remove_quotes(subject);
+ HTSprintf0(&buffer,
+ "%s %s/subject=\"%.70s\" %s %s",
+ system_mail,
+ system_mail_flags,
+ subject,
+ my_temp,
+ user_response->str);
+ }
+
+ stop_curses();
+ SetOutputMode(O_TEXT);
+ printf(MAILING_FILE);
+ LYSystem(buffer);
+ LYSleepAlert();
+ start_curses();
+ SetOutputMode(O_BINARY);
+
+ if (isPMDF)
+ (void) LYRemoveTemp(hdrfile);
+ (void) LYRemoveTemp(my_temp);
+#else /* !VMS (Unix or DOS) */
+
+#if CAN_PIPE_TO_MAILER
+ outfile_fp = LYPipeToMailer();
+#else
+ outfile_fp = LYOpenTemp(my_temp, TEXT_SUFFIX, "w");
+#endif
+ if (outfile_fp == NULL) {
+ CannotPrint(MAIL_REQUEST_FAILED);
+ }
+
+ /*
+ * Determine which mail headers should be sent. Use Content-Type and
+ * MIME-Version headers only if needed. We need them if we are mailing
+ * HTML source, or if we have 8-bit characters and will be sending
+ * Content-Transfer-Encoding to indicate this.
+ *
+ * Send Content-Transfer-Encoding only if the document has 8-bit
+ * characters. Send a charset parameter only if the document has 8-bit
+ * characters and we seem to have a valid charset. - kw
+ */
+ use_cte = HTLoadedDocumentEightbit();
+ disp_charset = LYCharSet_UC[current_char_set].MIMEname;
+ /*
+ * Don't send a charset if we have a CJK character set selected, since it
+ * may not be appropriate for mail... Also don't use an unofficial "x-"
+ * charset. - kw
+ */
+ if (!use_cte || LYHaveCJKCharacterSet ||
+ strncasecomp(disp_charset, "x-", 2) == 0) {
+ disp_charset = NULL;
+ }
+#ifdef NOTDEFINED
+ /* Enable this if indicating an 8-bit transfer without also indicating the
+ * charset causes problems. - kw */
+ if (use_cte && !disp_charset)
+ use_cte = FALSE;
+#endif /* NOTDEFINED */
+ use_type = (BOOL) (disp_charset || HTisDocumentSource());
+ use_mime = (BOOL) (use_cte || use_type);
+
+ if (use_mime) {
+ fprintf(outfile_fp, "Mime-Version: 1.0\n");
+ if (use_cte) {
+ fprintf(outfile_fp, "Content-Transfer-Encoding: 8bit\n");
+ }
+ }
+
+ if (HTisDocumentSource()) {
+ /*
+ * Add Content-Type, Content-Location, and Content-Base headers for
+ * HTML source. - FM
+ */
+ fprintf(outfile_fp, "Content-Type: " STR_HTML);
+ if (disp_charset != NULL) {
+ fprintf(outfile_fp, "; charset=%s\n", disp_charset);
+ } else {
+ fprintf(outfile_fp, "\n");
+ }
+ } else {
+ /*
+ * Add Content-Type: text/plain if we have 8-bit characters and a
+ * valid charset for non-source documents. - KW
+ */
+ if (disp_charset != NULL) {
+ fprintf(outfile_fp,
+ "Content-Type: " STR_PLAINTEXT "; charset=%s\n",
+ disp_charset);
+ }
+ }
+ /*
+ * If we are using MIME headers, add content-base and content-location if
+ * we have them. This will always be the case if the document is source.
+ * - kw
+ */
+ if (use_mime) {
+ if (content_base)
+ fprintf(outfile_fp, "Content-Base: %s\n", content_base);
+ if (content_location)
+ fprintf(outfile_fp, "Content-Location: %s\n", content_location);
+ }
+
+ /*
+ * Add the To, Subject, and X-URL headers. - FM
+ */
+ fprintf(outfile_fp, "To: %s\nSubject: %s\n", user_response->str, subject);
+ fprintf(outfile_fp, "X-URL: %s\n\n", newdoc->address);
+
+ if (LYPrependBaseToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's base as a BASE tag to the top of the message
+ * body. May create technically invalid HTML, but will help get any
+ * partial or relative URLs resolved properly if no BASE tag is present
+ * to replace it. - FM
+ */
+ fprintf(outfile_fp,
+ "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+ newdoc->address, content_base);
+ }
+ print_wwwfile_to_fd(outfile_fp, TRUE, FALSE); /* MAIL */
+ if (keypad_mode)
+ printlist(outfile_fp, FALSE);
+
+#if CAN_PIPE_TO_MAILER
+ pclose(outfile_fp);
+#else
+ LYCloseOutput(outfile_fp);
+ LYSendMailFile(user_response->str,
+ my_temp,
+ subject,
+ "",
+ "");
+ (void) LYRemoveTemp(my_temp); /* Delete the tmpfile. */
+#endif /* CAN_PIPE_TO_MAILER */
+#endif /* USE_VMS_MAILER */
+
+ done: /* send_file_to_mail() */
+ BStrFree(user_response);
+ FREE(buffer);
+ FREE(subject);
+ return;
+}
+
+static void send_file_to_printer(DocInfo *newdoc,
+ char *content_base,
+ char *sug_filename,
+ int printer_number)
+{
+ BOOLEAN FirstRecall = TRUE;
+ FILE *outfile_fp;
+ char *the_command = 0;
+ bstring *my_file = NULL;
+ char my_temp[LY_MAXPATH];
+ int FnameTotal, FnameNum = -1;
+ lynx_list_item_type *cur_printer;
+
+ outfile_fp = LYOpenTemp(my_temp,
+ (HTisDocumentSource())
+ ? HTML_SUFFIX
+ : TEXT_SUFFIX,
+ "w");
+ if (outfile_fp == NULL) {
+ CannotPrint(FILE_ALLOC_FAILED);
+ }
+
+ if (LYPrependBaseToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's base as a BASE tag to the top of the file. May
+ * create technically invalid HTML, but will help get any partial or
+ * relative URLs resolved properly if no BASE tag is present to replace
+ * it. - FM
+ */
+ fprintf(outfile_fp,
+ "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+ newdoc->address, content_base);
+ }
+ print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* PRINTER */
+ if (keypad_mode)
+ printlist(outfile_fp, FALSE);
+
+ LYCloseTempFP(outfile_fp);
+
+ /* find the right printer number */
+ {
+ int count = 0;
+
+ for (cur_printer = printers;
+ count < printer_number;
+ count++, cur_printer = cur_printer->next) ; /* null body */
+ }
+
+ /*
+ * Commands have the form "command %s [%s] [etc]" where %s is the filename
+ * and the second optional %s is the suggested filename.
+ */
+ if (cur_printer->command == NULL) {
+ CannotPrint(PRINTER_MISCONF_ERROR);
+ }
+
+ /*
+ * Check for two '%s' and ask for the second filename argument if there
+ * is.
+ */
+ BStrCopy0(my_file, "");
+ if (HTCountCommandArgs(cur_printer->command) >= 2) {
+ _statusline(FILENAME_PROMPT);
+ again:
+ SetupFilename(&my_file, sug_filename);
+ check_again:
+ switch (RecallFilename(&my_file, &FirstRecall, &FnameNum,
+ &FnameTotal, PRINT_FLAG)) {
+ case FN_INIT:
+ goto again;
+ case FN_READ:
+ goto check_again;
+ case FN_QUIT:
+ goto done;
+ default:
+ break;
+ }
+
+ if (no_dotfiles || !show_dotfiles) {
+ if (*LYPathLeaf(my_file->str) == '.') {
+ HTAlert(FILENAME_CANNOT_BE_DOT);
+ _statusline(NEW_FILENAME_PROMPT);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto again;
+ }
+ }
+ /*
+ * Cancel if the user entered "/dev/null" on Unix, or an "nl:" path
+ * on VMS. - FM
+ */
+ if (LYIsNullDevice(my_file->str)) {
+ CancelPrint(PRINT_REQUEST_CANCELLED);
+ }
+ HTAddSugFilename(my_file->str);
+ }
+#ifdef SH_EX /* 1999/01/04 (Mon) 09:37:03 */
+ HTAddParam(&the_command, cur_printer->command, 1, my_temp);
+ if (!isBEmpty(my_file)) {
+ HTAddParam(&the_command, cur_printer->command, 2, my_file->str);
+ HTEndParam(&the_command, cur_printer->command, 3);
+ } else {
+ HTEndParam(&the_command, cur_printer->command, 2);
+ }
+#else
+ HTAddParam(&the_command, cur_printer->command, 1, my_temp);
+ HTAddParam(&the_command, cur_printer->command, 2, my_file->str);
+ HTEndParam(&the_command, cur_printer->command, 2);
+#endif
+
+ /*
+ * Move the cursor to the top of the screen so that output from system'd
+ * commands don't scroll up the screen.
+ */
+ LYmove(1, 1);
+
+ stop_curses();
+ CTRACE((tfp, "command: %s\n", the_command));
+ SetOutputMode(O_TEXT);
+ printf(PRINTING_FILE);
+ /*
+ * Set various bits of document information as environment variables, for
+ * use by external print scripts/etc. On UNIX, We assume there are values,
+ * and leave NULL value checking up to the external PRINTER: cmd/script -
+ * KED
+ */
+ SET_ENVIRON(LYNX_PRINT_TITLE, HText_getTitle(), "No Title");
+ SET_ENVIRON(LYNX_PRINT_URL, newdoc->address, "No URL");
+ SET_ENVIRON(LYNX_PRINT_DATE, HText_getDate(), "No Date");
+ SET_ENVIRON(LYNX_PRINT_LASTMOD, HText_getLastModified(), "No LastMod");
+
+ LYSystem(the_command);
+ FREE(the_command);
+ (void) LYRemoveTemp(my_temp);
+
+ /*
+ * Remove the various LYNX_PRINT_xxxx logicals. - KED
+ * [could use unsetenv(), but it's not portable]
+ */
+ SET_ENVIRON(LYNX_PRINT_TITLE, "", "");
+ SET_ENVIRON(LYNX_PRINT_URL, "", "");
+ SET_ENVIRON(LYNX_PRINT_DATE, "", "");
+ SET_ENVIRON(LYNX_PRINT_LASTMOD, "", "");
+
+ fflush(stdout);
+#ifndef VMS
+ signal(SIGINT, cleanup_sig);
+#endif /* !VMS */
+#ifdef SH_EX
+ fprintf(stdout, gettext(" Print job complete.\n"));
+ fflush(stdout);
+#endif
+ SetOutputMode(O_BINARY);
+ LYSleepMsg();
+ start_curses();
+
+ done: /* send_file_to_printer() */
+ BStrFree(my_file);
+ return;
+}
+
+static void send_file_to_screen(DocInfo *newdoc,
+ char *content_base,
+ int Lpansi)
+{
+ FILE *outfile_fp;
+ bstring *prompt = NULL;
+
+ if (Lpansi) {
+ _statusline(CHECK_PRINTER);
+ } else {
+ _statusline(PRESS_RETURN_TO_BEGIN);
+ }
+
+ BStrCopy0(prompt, "");
+ if (LYgetBString(&prompt, FALSE, 0, NORECALL) < 0) {
+ CancelPrint(PRINT_REQUEST_CANCELLED);
+ } else {
+ outfile_fp = stdout;
+
+ stop_curses();
+ SetOutputMode(O_TEXT);
+
+#ifndef VMS
+ signal(SIGINT, SIG_IGN);
+#endif /* !VMS */
+
+ if (LYPrependBaseToSource && HTisDocumentSource()) {
+ /*
+ * Added the document's base as a BASE tag to the top of the file. May
+ * create technically invalid HTML, but will help get any partial or
+ * relative URLs resolved properly if no BASE tag is present to replace
+ * it. - FM
+ */
+ fprintf(outfile_fp,
+ "<!-- X-URL: %s -->\n<BASE HREF=\"%s\">\n\n",
+ newdoc->address, content_base);
+ }
+ if (Lpansi)
+ printf("\033[5i");
+ print_wwwfile_to_fd(outfile_fp, FALSE, FALSE); /* SCREEN */
+ if (keypad_mode)
+ printlist(outfile_fp, FALSE);
+
+#ifdef VMS
+ if (HadVMSInterrupt) {
+ HadVMSInterrupt = FALSE;
+ start_curses();
+ CancelPrint(PRINT_REQUEST_CANCELLED);
+ }
+#endif /* VMS */
+ if (Lpansi) {
+ printf("\n\014"); /* Form feed */
+ printf("\033[4i");
+ fflush(stdout); /* refresh to screen */
+ } else {
+ fprintf(stdout, "\n\n%s", PRESS_RETURN_TO_FINISH);
+ fflush(stdout); /* refresh to screen */
+ (void) LYgetch(); /* grab some user input to pause */
+#ifdef VMS
+ HadVMSInterrupt = FALSE;
+#endif /* VMS */
+ }
+#ifdef SH_EX
+ fprintf(stdout, "\n");
+#endif
+ SetOutputMode(O_BINARY);
+ start_curses();
+ }
+
+ done: /* send_file_to_screen() */
+ BStrFree(prompt);
+ return;
+}
+
+int printfile(DocInfo *newdoc)
+{
+ BOOLEAN Lpansi = FALSE;
+ DocAddress WWWDoc;
+ char *content_base = NULL;
+ char *content_location = NULL;
+ char *cp = NULL;
+ char *link_info = NULL;
+ char *sug_filename = NULL;
+ int lines_in_file = 0;
+ int pagelen = 0;
+ int printer_number = 0;
+ int type = 0;
+
+ /*
+ * Extract useful info from URL.
+ */
+ StrAllocCopy(link_info, newdoc->address + 12);
+
+ /*
+ * Reload the file we want to print into memory.
+ */
+ LYpop(newdoc);
+ WWWDoc.address = newdoc->address;
+ WWWDoc.post_data = newdoc->post_data;
+ WWWDoc.post_content_type = newdoc->post_content_type;
+ WWWDoc.bookmark = newdoc->bookmark;
+ WWWDoc.isHEAD = newdoc->isHEAD;
+ WWWDoc.safe = newdoc->safe;
+ if (!HTLoadAbsolute(&WWWDoc))
+ return (NOT_FOUND);
+
+ /*
+ * If we have an explicit content-base, we may use it even if not in source
+ * mode. - kw
+ */
+ if (HText_getContentBase()) {
+ StrAllocCopy(content_base, HText_getContentBase());
+ LYRemoveBlanks(content_base);
+ if (isEmpty(content_base)) {
+ FREE(content_base);
+ }
+ }
+ /*
+ * If document is source, load the content_base and content_location
+ * strings. - FM
+ */
+ if (HTisDocumentSource()) {
+ if (HText_getContentLocation()) {
+ StrAllocCopy(content_location, HText_getContentLocation());
+ LYRemoveBlanks(content_location);
+ if (isEmpty(content_location)) {
+ FREE(content_location);
+ }
+ }
+ if (!content_base) {
+ if ((content_location) && is_url(content_location)) {
+ StrAllocCopy(content_base, content_location);
+ } else {
+ StrAllocCopy(content_base, newdoc->address);
+ }
+ }
+ if (!content_location) {
+ StrAllocCopy(content_location, newdoc->address);
+ }
+ }
+
+ sug_filename = suggested_filename(newdoc);
+
+ /*
+ * Get the number of lines in the file.
+ */
+ if ((cp = LYstrstr(link_info, "lines=")) != NULL) {
+ /*
+ * Terminate prev string here.
+ */
+ *cp = '\0';
+ /*
+ * Number of characters in "lines=".
+ */
+ cp += 6;
+
+ lines_in_file = atoi(cp);
+ }
+
+ /*
+ * Determine the type.
+ */
+ if (LYstrstr(link_info, "LOCAL_FILE")) {
+ type = TO_FILE;
+ } else if (LYstrstr(link_info, "TO_SCREEN")) {
+ type = TO_SCREEN;
+ } else if (LYstrstr(link_info, "LPANSI")) {
+ Lpansi = TRUE;
+ type = TO_SCREEN;
+ } else if (LYstrstr(link_info, "MAIL_FILE")) {
+ type = MAIL;
+ } else if (LYstrstr(link_info, "PRINTER")) {
+ type = PRINTER;
+
+ if ((cp = LYstrstr(link_info, "number=")) != NULL) {
+ /* number of characters in "number=" */
+ cp += 7;
+ printer_number = atoi(cp);
+ }
+ if ((cp = LYstrstr(link_info, "pagelen=")) != NULL) {
+ /* number of characters in "pagelen=" */
+ cp += 8;
+ pagelen = atoi(cp);
+ } else {
+ /* default to 66 lines */
+ pagelen = 66;
+ }
+ }
+
+ /*
+ * Act on the request. - FM
+ */
+ switch (type) {
+
+ case TO_FILE:
+ send_file_to_file(newdoc, content_base, sug_filename);
+ break;
+
+ case MAIL:
+ send_file_to_mail(newdoc, content_base, content_location);
+ break;
+
+ case TO_SCREEN:
+ if (confirm_by_pages(CONFIRM_LONG_SCREEN_PRINT, lines_in_file, LYlines))
+ send_file_to_screen(newdoc, content_base, Lpansi);
+ break;
+
+ case PRINTER:
+ if (confirm_by_pages(CONFIRM_LONG_PAGE_PRINT, lines_in_file, pagelen))
+ send_file_to_printer(newdoc, content_base, sug_filename, printer_number);
+ break;
+
+ } /* end switch */
+
+ FREE(link_info);
+ FREE(sug_filename);
+ FREE(content_base);
+ FREE(content_location);
+ return (NORMAL);
+}
+
+#if USE_VMS_MAILER
+static int remove_quotes(char *string)
+{
+ int i;
+
+ for (i = 0; string[i] != '\0'; i++)
+ if (string[i] == '"')
+ string[i] = ' ';
+ else if (string[i] == '&')
+ string[i] = ' ';
+ else if (string[i] == '|')
+ string[i] = ' ';
+
+ return (0);
+}
+#endif /* USE_VMS_MAILER */
+
+/*
+ * Mail subject may have 8-bit characters and they are in display charset.
+ * There is no stable practice for 8-bit subject encodings: MIME defines
+ * "quoted-printable" which holds charset info but most mailers still don't
+ * support it. On the other hand many mailers send open 8-bit subjects without
+ * charset info and use local assumption for certain countries. Besides that,
+ * obsolete SMTP software is not 8bit clean but still in use, it strips the
+ * characters in 128-160 range from subjects which may be a fault outside
+ * iso-8859-XX.
+ *
+ * We translate subject to "outgoing_mail_charset" (defined in lynx.cfg) it may
+ * correspond to US-ASCII as the safest value or any other lynx character
+ * handler, -1 for no translation (so display charset).
+ *
+ * Always returns a new allocated string which has to be freed.
+ */
+#include <LYCharUtils.h>
+static char *subject_translate8bit(char *source)
+{
+ char *target = NULL;
+
+ int charset_in, charset_out;
+
+ int i = outgoing_mail_charset; /* from lynx.cfg, -1 by default */
+
+ StrAllocCopy(target, source);
+ if (i < 0
+ || i == current_char_set
+ || LYCharSet_UC[current_char_set].enc == UCT_ENC_CJK
+ || LYCharSet_UC[i].enc == UCT_ENC_CJK) {
+ return (target); /* OK */
+ } else {
+ charset_out = i;
+ charset_in = current_char_set;
+ }
+
+ LYUCTranslateBackHeaderText(&target, charset_in, charset_out, YES);
+
+ return (target);
+}
+
+/*
+ * print_options writes out the current printer choices to a file
+ * so that the user can select printers in the same way that
+ * they select all other links
+ * printer links look like
+ *
+ * LYNXPRINT://LOCAL_FILE/lines=# print to a local file
+ * LYNXPRINT://TO_SCREEN/lines=# print to the screen
+ * LYNXPRINT://LPANSI/lines=# print to the local terminal
+ * LYNXPRINT://MAIL_FILE/lines=# mail the file
+ * LYNXPRINT://PRINTER/lines=#/number=# print to printer number #
+ */
+int print_options(char **newfile,
+ const char *printed_url,
+ int lines_in_file)
+{
+ static char my_temp[LY_MAXPATH] = "\0";
+ char *buffer = 0;
+ int count;
+ int pages;
+ FILE *fp0;
+ lynx_list_item_type *cur_printer;
+
+ if ((fp0 = InternalPageFP(my_temp, TRUE)) == 0)
+ return (-1);
+
+ LYLocalFileToURL(newfile, my_temp);
+
+ BeginInternalPage(fp0, PRINT_OPTIONS_TITLE, PRINT_OPTIONS_HELP);
+
+ fprintf(fp0, "<pre>\n");
+
+ /* pages = lines_in_file/66 + 1; */
+ pages = (lines_in_file + 65) / 66;
+ HTSprintf0(&buffer,
+ " <em>%s</em> %s\n <em>%s</em> %d\n <em>%s</em> %d %s %s\n",
+ gettext("Document:"), printed_url,
+ gettext("Number of lines:"), lines_in_file,
+ gettext("Number of pages:"), pages,
+ (pages > 1 ? gettext("pages") : gettext("page")),
+ gettext("(approximately)"));
+ fputs(buffer, fp0);
+ FREE(buffer);
+
+ if (no_print || no_disk_save || no_mail)
+ fprintf(fp0,
+ " <em>%s</em>\n",
+ gettext("Some print functions have been disabled!"));
+
+ fprintf(fp0, "\n%s\n",
+ (user_mode == NOVICE_MODE)
+ ? gettext("Standard print options:")
+ : gettext("Print options:"));
+
+ if (no_disk_save == FALSE && no_print == FALSE) {
+ fprintf(fp0,
+ " <a href=\"%s//LOCAL_FILE/lines=%d\">%s</a>\n",
+ STR_LYNXPRINT,
+ lines_in_file,
+ gettext("Save to a local file"));
+ } else {
+ fprintf(fp0, " <em>%s</em>\n", gettext("Save to disk disabled"));
+ }
+ if (no_mail == FALSE && local_host_only == FALSE)
+ fprintf(fp0,
+ " <a href=\"%s//MAIL_FILE/lines=%d\">%s</a>\n",
+ STR_LYNXPRINT,
+ lines_in_file,
+ gettext("Mail the file"));
+
+#if defined(UNIX) || defined(VMS)
+ fprintf(fp0,
+ " <a href=\"%s//TO_SCREEN/lines=%d\">%s</a>\n",
+ STR_LYNXPRINT,
+ lines_in_file,
+ gettext("Print to the screen"));
+ fprintf(fp0,
+ " <a href=\"%s//LPANSI/lines=%d\">%s</a>\n",
+ STR_LYNXPRINT,
+ lines_in_file,
+ gettext("Print out on a printer attached to your vt100 terminal"));
+#endif
+
+ if (user_mode == NOVICE_MODE)
+ fprintf(fp0, "\n%s\n", gettext("Local additions:"));
+
+ for (count = 0, cur_printer = printers; cur_printer != NULL;
+ cur_printer = cur_printer->next, count++)
+ if (no_print == FALSE || cur_printer->always_enabled) {
+ fprintf(fp0,
+ " <a href=\"%s//PRINTER/number=%d/pagelen=%d/lines=%d\">",
+ STR_LYNXPRINT,
+ count, cur_printer->pagelen, lines_in_file);
+ fprintf(fp0, "%s", (cur_printer->name ?
+ cur_printer->name : "No Name Given"));
+ fprintf(fp0, "</a>\n");
+ }
+ fprintf(fp0, "</pre>\n");
+ EndInternalPage(fp0);
+ LYCloseTempFP(fp0);
+
+ LYforce_no_cache = TRUE;
+ return (0);
+}
+
+/*
+ * General purpose filename getter.
+ *
+ * Returns a pointer to an absolute filename string, if the input filename
+ * exists, and is readable. Returns NULL if the input was cancelled (^G, or CR
+ * on empty input).
+ *
+ * The pointer to the filename string needs to be free()'d by the caller (when
+ * non-NULL).
+ *
+ * --KED 02/21/99
+ */
+char *GetFileName(void)
+{
+ struct stat stat_info;
+
+ bstring *fbuf = NULL;
+ bstring *tbuf = NULL;
+ char *result = NULL;
+
+ BOOLEAN FirstRecall = TRUE;
+ int FnameNum = -1;
+ int FnameTotal;
+
+ _statusline(FILENAME_PROMPT);
+
+ retry:
+ /*
+ * No initial filename.
+ */
+ SetupFilename(&fbuf, "");
+
+ check_recall:
+ /*
+ * Go get a filename (it would be nice to do TAB == filename-completion as
+ * the name is entered, but we'll save doing that for another time.
+ */
+ switch (RecallFilename(&fbuf, &FirstRecall, &FnameNum,
+ &FnameTotal, GENERIC_FLAG)) {
+ case FN_INIT:
+ goto retry;
+ case FN_READ:
+ goto check_recall;
+ case FN_QUIT:
+ goto cleanup;
+ default:
+ break;
+ }
+
+ /*
+ * Add raw input form to list ... we may want to reuse/edit it on a
+ * subsequent call, etc.
+ */
+#ifdef VMS
+ if (0 == strncasecomp(fbuf->str, "sys$disk:", 9)) {
+ if (0 == StrNCmp((fbuf->str + 9), "[]", 2)) {
+ HTAddSugFilename(fbuf->str + 11);
+ } else {
+ HTAddSugFilename(fbuf->str + 9);
+ }
+ } else {
+ HTAddSugFilename(fbuf->str);
+ }
+#else
+ HTAddSugFilename(fbuf->str);
+#endif /* VMS */
+
+ /*
+ * Expand tilde's, make filename absolute, etc.
+ */
+ BStrCopy0(tbuf, "");
+ if (!LYValidateFilename(&tbuf, &fbuf))
+ goto cleanup;
+
+ /*
+ * Check for file existence; readability.
+ */
+ if ((stat(tbuf->str, &stat_info) < 0) ||
+ (!(S_ISREG(stat_info.st_mode)
+#ifdef S_IFLNK
+ || S_ISLNK(stat_info.st_mode)
+#endif /* S_IFLNK */
+ ))) {
+ HTInfoMsg(FILE_DOES_NOT_EXIST);
+ _statusline(FILE_DOES_NOT_EXIST_RE);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ }
+
+ if (!LYCanReadFile(tbuf->str)) {
+ HTInfoMsg(FILE_NOT_READABLE);
+ _statusline(FILE_NOT_READABLE_RE);
+ FirstRecall = TRUE;
+ FnameNum = FnameTotal;
+ goto retry;
+ }
+
+ /*
+ * We have a valid filename, and readable file. Return it to the caller.
+ *
+ * The returned pointer should be free()'d by the caller.
+ */
+ StrAllocCopy(result, tbuf->str);
+
+ cleanup:
+ BStrFree(fbuf);
+ BStrFree(tbuf);
+ return (result);
+}