/*
 * Pop the specified hist entry, link and line number from the history list but
 * don't actually remove the entry, just return it.
 * (This procedure is badly named :)
 */
void LYpop_num(int number,
	       DocInfo *doc)
{
    if (number >= 0 && (nhist + nhist_extra) > number) {
	doc->link = HDOC(number).link;
	doc->line = HDOC(number).line;
	StrAllocCopy(doc->title, HDOC(number).title);
	StrAllocCopy(doc->address, HDOC(number).address);
	BStrCopy(doc->post_data, HDOC(number).post_data);
	StrAllocCopy(doc->post_content_type, HDOC(number).post_content_type);
	StrAllocCopy(doc->bookmark, HDOC(number).bookmark);
	doc->isHEAD = HDOC(number).isHEAD;
	doc->safe = HDOC(number).safe;
	doc->internal_link = HDOC(number).internal_link;	/* ?? */
#ifdef DISP_PARTIAL
	/* assume we pop the 'doc' to show it soon... */
	LYSetNewline(doc->line);	/* reinitialize */
#endif /* DISP_PARTIAL */
	if (TRACE) {
	    CTRACE((tfp, "LYpop_num(%d)\n", number));
	    CTRACE((tfp, "  link    %d\n", doc->link));
	    CTRACE((tfp, "  line    %d\n", doc->line));
	    CTRACE((tfp, "  title   %s\n", NonNull(doc->title)));
	    CTRACE((tfp, "  address %s\n", NonNull(doc->address)));
	}
    }
}
Exemple #2
0
void LYDownload(char *line)
{
    char *Line = NULL, *method, *file, *sug_file = NULL;
    int method_number;
    int count;
    char *the_command = 0;
    bstring *buffer = NULL;
    bstring *command = NULL;
    char *cp;
    lynx_list_item_type *download_command = 0;
    int ch;
    RecallType recall;
    int FnameTotal;
    int FnameNum;
    BOOLEAN FirstRecall = TRUE;
    BOOLEAN SecondS = FALSE;

#ifdef VMS
    LYDidRename = FALSE;
#endif /* VMS */

    /*
     * Make sure we have a valid download file comparison string loaded via the
     * download options menu.  - FM
     */
    if (LYValidDownloadFile[0] == '\0') {
	goto failed;
    }

    /*
     * Make a copy of the LYNXDOWNLOAD internal URL for parsing.  - FM
     */
    StrAllocCopy(Line, line);

    /*
     * Parse out the File, sug_file, and the Method.
     */
    if ((file = strstr(Line, "/File=")) == NULL)
	goto failed;
    *file = '\0';
    /*
     * Go past "File=".
     */
    file += 6;

    if ((sug_file = strstr(file + 1, "/SugFile=")) != NULL) {
	*sug_file = '\0';
	/*
	 * Go past "SugFile=".
	 */
	sug_file += 9;
	HTUnEscape(sug_file);
    }

    /*
     * Make sure that the file string is the one from the last displayed
     * download options menu.  - FM
     */
    if (strcmp(file, LYValidDownloadFile)) {
	goto failed;
    }
#if defined(DIRED_SUPPORT)
    /* FIXME: use HTLocalName */
    if (!StrNCmp(file, "file://localhost", 16)) {
#ifdef __DJGPP__
	if (!StrNCmp(file + 16, "/dev/", 5))
	    file += 16;
	else {
	    file += 17;
	    file = HTDOS_name(file);
	}
#else
	file += 16;
#endif /* __DJGPP__ */
    } else if (isFILE_URL(file))
	file += LEN_FILE_URL;
    HTUnEscape(file);
#else
#if defined(_WINDOWS)		/* 1997/10/15 (Wed) 16:27:38 */
    if (!StrNCmp(file, "file://localhost/", 17))
	file += 17;
    else if (!StrNCmp(file, "file:/", 6))
	file += 6;
    HTUnEscape(file);
#endif /* _WINDOWS */
#endif /* DIRED_SUPPORT */

    if ((method = strstr(Line, "Method=")) == NULL)
	goto failed;
    /*
     * Go past "Method=".
     */
    method += 7;
    method_number = atoi(method);

    /*
     * Set up the sug_filenames recall buffer.
     */
    FnameTotal = (sug_filenames ? HTList_count(sug_filenames) : 0);
    recall = ((FnameTotal >= 1) ? RECALL_URL : NORECALL);
    FnameNum = FnameTotal;

    if (method_number < 0) {
	/*
	 * Write to local file.
	 */
	_statusline(FILENAME_PROMPT);
      retry:
	if (sug_file) {
	    BStrCopy0(buffer, sug_file);
	} else {
	    BStrCopy0(buffer, "");
	}

      check_recall:
	if ((ch = LYgetBString(&buffer, FALSE, 0, recall)) < 0 ||
	    isBEmpty(buffer) ||
	    ch == UPARROW_KEY ||
	    ch == DNARROW_KEY) {

	    if (recall && ch == UPARROW_KEY) {
		if (FirstRecall) {
		    FirstRecall = FALSE;
		    /*
		     * Use the last Fname in the list.  - FM
		     */
		    FnameNum = 0;
		} else {
		    /*
		     * Go back to the previous Fname in the list.  - FM
		     */
		    FnameNum++;
		}
		if (FnameNum >= FnameTotal) {
		    /*
		     * Reset the FirstRecall flag, and use sug_file or a blank.
		     * - FM
		     */
		    FirstRecall = TRUE;
		    FnameNum = FnameTotal;
		    _statusline(FILENAME_PROMPT);
		    goto retry;
		} else if ((cp = (char *) HTList_objectAt(sug_filenames,
							  FnameNum)) != NULL) {
		    BStrCopy0(buffer, cp);
		    if (FnameTotal == 1) {
			_statusline(EDIT_THE_PREV_FILENAME);
		    } else {
			_statusline(EDIT_A_PREV_FILENAME);
		    }
		    goto check_recall;
		}
	    } else if (recall && ch == DNARROW_KEY) {
		if (FirstRecall) {
		    FirstRecall = FALSE;
		    /*
		     * Use the first Fname in the list.  - FM
		     */
		    FnameNum = FnameTotal - 1;
		} else {
		    /*
		     * Advance to the next Fname in the list.  - FM
		     */
		    FnameNum--;
		}
		if (FnameNum < 0) {
		    /*
		     * Set the FirstRecall flag, and use sug_file or a blank.
		     * - FM
		     */
		    FirstRecall = TRUE;
		    FnameNum = FnameTotal;
		    _statusline(FILENAME_PROMPT);
		    goto retry;
		} else if ((cp = (char *) HTList_objectAt(sug_filenames,
							  FnameNum)) != NULL) {
		    BStrCopy0(buffer, cp);
		    if (FnameTotal == 1) {
			_statusline(EDIT_THE_PREV_FILENAME);
		    } else {
			_statusline(EDIT_A_PREV_FILENAME);
		    }
		    goto check_recall;
		}
	    }

	    /*
	     * Save cancelled.
	     */
	    goto cancelled;
	}

	BStrCopy(command, buffer);
	if (!LYValidateFilename(&buffer, &command))
	    goto cancelled;
#ifdef HAVE_POPEN
	else if (LYIsPipeCommand(buffer->str)) {
	    /* I don't know how to download to a pipe */
	    HTAlert(CANNOT_WRITE_TO_FILE);
	    _statusline(NEW_FILENAME_PROMPT);
	    FirstRecall = TRUE;
	    FnameNum = FnameTotal;
	    goto retry;
	}
#endif

	/*
	 * See if it already exists.
	 */
	switch (LYValidateOutput(buffer->str)) {
	case 'Y':
	    break;
	case 'N':
	    _statusline(NEW_FILENAME_PROMPT);
	    FirstRecall = TRUE;
	    FnameNum = FnameTotal;
	    goto retry;
	default:
	    goto cleanup;
	}

	/*
	 * See if we can write to it.
	 */
	CTRACE((tfp, "LYDownload: filename is %s\n", buffer->str));

	SecondS = TRUE;

	HTInfoMsg(SAVING);
#ifdef VMS
	/*
	 * Try rename() first.  - FM
	 */
	CTRACE((tfp, "command: rename(%s, %s)\n", file, buffer->str));
	if (rename(file, buffer->str)) {
	    /*
	     * Failed.  Use spawned COPY_COMMAND.  - FM
	     */
	    CTRACE((tfp, "         FAILED!\n"));
	    LYCopyFile(file, buffer->str);
	} else {
	    /*
	     * We don't have the temporary file (it was renamed to a permanent
	     * file), so set a flag to pop out of the download menu.  - FM
	     */
	    LYDidRename = TRUE;
	}
	chmod(buffer->str, HIDE_CHMOD);
#else /* Unix: */

	LYCopyFile(file, buffer->str);
	LYRelaxFilePermissions(buffer->str);
#endif /* VMS */

    } else {
	/*
	 * Use configured download commands.
	 */
	BStrCopy0(buffer, "");
	for (count = 0, download_command = downloaders;
	     count < method_number;
	     count++, download_command = download_command->next) ;	/* null body */

	/*
	 * Commands have the form "command %s [etc]" where %s is the filename.
	 */
	if (download_command->command != NULL) {
	    /*
	     * Check for two '%s' and ask for the local filename if there is.
	     */
	    if (HTCountCommandArgs(download_command->command) >= 2) {
		_statusline(FILENAME_PROMPT);

	      again:
		if (sug_file) {
		    BStrCopy0(buffer, sug_file);
		} else {
		    BStrCopy0(buffer, "");
		}

	      check_again:
		if ((ch = LYgetBString(&buffer, FALSE, 0, recall)) < 0 ||
		    isBEmpty(buffer) ||
		    ch == UPARROW_KEY ||
		    ch == DNARROW_KEY) {

		    if (recall && ch == UPARROW_KEY) {
			if (FirstRecall) {
			    FirstRecall = FALSE;
			    /*
			     * Use the last Fname in the list.  - FM
			     */
			    FnameNum = 0;
			} else {
			    /*
			     * Go back to the previous Fname in the list.  - FM
			     */
			    FnameNum++;
			}
			if (FnameNum >= FnameTotal) {
			    /*
			     * Reset the FirstRecall flag, and use sug_file or
			     * a blank.  - FM
			     */
			    FirstRecall = TRUE;
			    FnameNum = FnameTotal;
			    _statusline(FILENAME_PROMPT);
			    goto again;
			} else if ((cp = (char *) HTList_objectAt(sug_filenames,
								  FnameNum))
				   != NULL) {
			    BStrCopy0(buffer, cp);
			    if (FnameTotal == 1) {
				_statusline(EDIT_THE_PREV_FILENAME);
			    } else {
				_statusline(EDIT_A_PREV_FILENAME);
			    }
			    goto check_again;
			}
		    } else if (recall && ch == DNARROW_KEY) {
			if (FirstRecall) {
			    FirstRecall = FALSE;
			    /*
			     * Use the first Fname in the list.  - FM
			     */
			    FnameNum = FnameTotal - 1;
			} else {
			    /*
			     * Advance to the next Fname in the list.  - FM
			     */
			    FnameNum--;
			}
			if (FnameNum < 0) {
			    /*
			     * Set the FirstRecall flag, and use sug_file or a
			     * blank.  - FM
			     */
			    FirstRecall = TRUE;
			    FnameNum = FnameTotal;
			    _statusline(FILENAME_PROMPT);
			    goto again;
			} else if ((cp = (char *) HTList_objectAt(sug_filenames,
								  FnameNum))
				   != NULL) {
			    BStrCopy0(buffer, cp);
			    if (FnameTotal == 1) {
				_statusline(EDIT_THE_PREV_FILENAME);
			    } else {
				_statusline(EDIT_A_PREV_FILENAME);
			    }
			    goto check_again;
			}
		    }

		    /*
		     * Download cancelled.
		     */
		    goto cancelled;
		}

		if (no_dotfiles || !show_dotfiles) {
		    if (*LYPathLeaf(buffer->str) == '.') {
			HTAlert(FILENAME_CANNOT_BE_DOT);
			_statusline(NEW_FILENAME_PROMPT);
			goto again;
		    }
		}
		/*
		 * Cancel if the user entered "/dev/null" on Unix, or an "nl:"
		 * path on VMS.  - FM
		 */
		if (LYIsNullDevice(buffer->str)) {
		    goto cancelled;
		}
		SecondS = TRUE;
	    }

	    /*
	     * The following is considered a bug by the community.  If the
	     * command only takes one argument on the command line, then the
	     * suggested file name is not used.  It actually is not a bug at
	     * all and does as it should, putting both names on the command
	     * line.
	     */
	    count = 1;
	    HTAddParam(&the_command, download_command->command, count, file);
	    if (HTCountCommandArgs(download_command->command) > 1)
		HTAddParam(&the_command, download_command->command, ++count, buffer->str);
	    HTEndParam(&the_command, download_command->command, count);

	} else {
	    HTAlert(MISCONF_DOWNLOAD_COMMAND);
	    goto failed;
	}

	CTRACE((tfp, "command: %s\n", the_command));
	stop_curses();
	LYSystem(the_command);
	FREE(the_command);
	start_curses();
	/* don't remove(file); */
    }

    if (SecondS == TRUE) {
#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 */
    }
    goto cleanup;

  failed:
    HTAlert(CANNOT_DOWNLOAD_FILE);
    goto cleanup;

  cancelled:
    HTInfoMsg(CANCELLING);

  cleanup:
    FREE(Line);
    BStrFree(buffer);
    return;
}
Exemple #3
0
/*  The address has no anchor tag, for sure.
 */
PRIVATE HTParentAnchor0 * HTAnchor_findAddress_in_adult_table ARGS1(
	CONST DocAddress *,	newdoc)
{
    /*
    **  Check whether we have this node.
    */
    int hash;
    HTList * adults;
    HTList *grownups;
    HTParentAnchor0 * foundAnchor;
    BOOL need_extra_info = (newdoc->post_data || newdoc->post_content_type ||
		newdoc->bookmark || newdoc->isHEAD || newdoc->safe);

    /*
     *  We need not free adult_table[] atexit -
     *  it should be perfectly empty after free'ing all HText's.
     *  (There is an error if it is not empty at exit). -LP
     */

    /*
    **  Select list from hash table,
    */
    hash = HASH_FUNCTION(newdoc->address);
    adults = &(adult_table[hash]);

    /*
    **  Search list for anchor.
    */
    grownups = adults;
    while (NULL != (foundAnchor =
		    (HTParentAnchor0 *)HTList_nextObject(grownups))) {
	if (HTSEquivalent(foundAnchor->address, newdoc->address) &&

	    ((!foundAnchor->info && !need_extra_info) ||
	     (foundAnchor->info &&
	      HTBEquivalent(foundAnchor->info->post_data, newdoc->post_data) &&
	      foundAnchor->info->isHEAD == newdoc->isHEAD)))
	{
	    CTRACE((tfp, "Anchor %p with address `%s' already exists.\n",
			(void *)foundAnchor, newdoc->address));
	    return foundAnchor;
	}
    }

    /*
    **  Node not found: create new anchor.
    */
    foundAnchor = HTParentAnchor0_new(newdoc->address, hash);
    CTRACE((tfp, "New anchor %p has hash %d and address `%s'\n",
		(void *)foundAnchor, hash, newdoc->address));

    if (need_extra_info) {
	/* rare case, create a big structure */
	HTParentAnchor *p = HTParentAnchor_new(foundAnchor);

	if (newdoc->post_data)
	    BStrCopy(p->post_data, newdoc->post_data);
	if (newdoc->post_content_type)
	    StrAllocCopy(p->post_content_type,
		     newdoc->post_content_type);
	if (newdoc->bookmark)
	    StrAllocCopy(p->bookmark, newdoc->bookmark);
	p->isHEAD = newdoc->isHEAD;
	p->safe = newdoc->safe;
    }
    HTList_linkObject(adults, foundAnchor, &foundAnchor->_add_adult);

    return foundAnchor;
}
/*
 * Push the current filename, link and line number onto the history list.
 */
int LYpush(DocInfo *doc, int force_push)
{
    /*
     * Don't push NULL file names.
     */
    if (*doc->address == '\0')
	return 0;

    /*
     * Check whether this is a document we don't push unless forced.  - FM
     */
    if (!force_push) {
	/*
	 * Don't push the history, printer, or download lists.
	 */
	if (!LYwouldPush(doc->title, doc->address)) {
	    if (!LYforce_no_cache)
		LYoverride_no_cache = TRUE;
	    return 0;
	}
    }

    /*
     * If file is identical to one before it, don't push it.
     * But do not duplicate it if there is only one on the stack,
     * note that HDOC() starts from 0, so nhist should be > 0.
     */
    if (nhist >= 1 && are_identical(&(history[nhist - 1]), doc)) {
	if (HDOC(nhist - 1).internal_link == doc->internal_link) {
	    /* But it is nice to have the last position remembered!
	       - kw */
	    HDOC(nhist - 1).link = doc->link;
	    HDOC(nhist - 1).line = doc->line;
	    return 0;
	}
    }

    /*
     * If file is identical to the current document, just move the pointer.
     */
    if (nhist_extra >= 1 && are_identical(&(history[nhist]), doc)) {
	HDOC(nhist).link = doc->link;
	HDOC(nhist).line = doc->line;
	nhist_extra--;
	LYAllocHistory(nhist);
	nhist++;
	trace_history("LYpush: just move the cursor");
	return 1;
    }

    clean_extra_history();
#ifdef LY_FIND_LEAKS
    if (!already_registered_clean_all_history) {
	already_registered_clean_all_history = 1;
	atexit(clean_all_history);
    }
#endif

    /*
     * OK, push it...
     */
    LYAllocHistory(nhist);
    HDOC(nhist).link = doc->link;
    HDOC(nhist).line = doc->line;

    HDOC(nhist).title = NULL;
    LYformTitle(&(HDOC(nhist).title), doc->title);

    HDOC(nhist).address = NULL;
    StrAllocCopy(HDOC(nhist).address, doc->address);

    HDOC(nhist).post_data = NULL;
    BStrCopy(HDOC(nhist).post_data, doc->post_data);

    HDOC(nhist).post_content_type = NULL;
    StrAllocCopy(HDOC(nhist).post_content_type, doc->post_content_type);

    HDOC(nhist).bookmark = NULL;
    StrAllocCopy(HDOC(nhist).bookmark, doc->bookmark);

    HDOC(nhist).isHEAD = doc->isHEAD;
    HDOC(nhist).safe = doc->safe;

    HDOC(nhist).internal_link = FALSE;	/* by default */
    history[nhist].intern_seq_start = -1;	/* by default */
    if (doc->internal_link) {
	/* Now some tricky stuff: if the caller thinks that the doc
	   to push was the result of following an internal
	   (fragment) link, we check whether we believe it.
	   It is only accepted as valid if the immediately preceding
	   item on the history stack is actually the same document
	   except for fragment and location info.  I.e. the Parent
	   Anchors are the same.
	   Also of course this requires that this is not the first
	   history item. - kw */
	if (nhist > 0) {
	    DocAddress WWWDoc;
	    HTParentAnchor *thisparent, *thatparent = NULL;

	    WWWDoc.address = doc->address;
	    WWWDoc.post_data = doc->post_data;
	    WWWDoc.post_content_type = doc->post_content_type;
	    WWWDoc.bookmark = doc->bookmark;
	    WWWDoc.isHEAD = doc->isHEAD;
	    WWWDoc.safe = doc->safe;
	    thisparent =
		HTAnchor_findAddress(&WWWDoc);
	    /* Now find the ParentAnchor for the previous history
	     * item - kw
	     */
	    if (thisparent) {
		/* If the last-pushed item is a LYNXIMGMAP but THIS one
		 * isn't, compare the physical URLs instead. - kw
		 */
		if (isLYNXIMGMAP(HDOC(nhist - 1).address) &&
		    !isLYNXIMGMAP(doc->address)) {
		    WWWDoc.address = HDOC(nhist - 1).address + LEN_LYNXIMGMAP;
		    /*
		     * If THIS item is a LYNXIMGMAP but the last-pushed one
		     * isn't, fake it by using THIS item's address for
		     * thatparent... - kw
		     */
		} else if (isLYNXIMGMAP(doc->address) &&
			   !isLYNXIMGMAP(HDOC(nhist - 1).address)) {
		    char *temp = NULL;

		    StrAllocCopy(temp, STR_LYNXIMGMAP);
		    StrAllocCat(temp, doc->address + LEN_LYNXIMGMAP);
		    WWWDoc.address = temp;
		    WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type;
		    WWWDoc.bookmark = HDOC(nhist - 1).bookmark;
		    WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD;
		    WWWDoc.safe = HDOC(nhist - 1).safe;
		    thatparent =
			HTAnchor_findAddress(&WWWDoc);
		    FREE(temp);
		} else {
		    WWWDoc.address = HDOC(nhist - 1).address;
		}
		if (!thatparent) {	/* if not yet done */
		    WWWDoc.post_data = HDOC(nhist - 1).post_data;
		    WWWDoc.post_content_type = HDOC(nhist - 1).post_content_type;
		    WWWDoc.bookmark = HDOC(nhist - 1).bookmark;
		    WWWDoc.isHEAD = HDOC(nhist - 1).isHEAD;
		    WWWDoc.safe = HDOC(nhist - 1).safe;
		    thatparent =
			HTAnchor_findAddress(&WWWDoc);
		}
		/* In addition to equality of the ParentAnchors, require
		 * that IF we have a HTMainText (i.e., it wasn't just
		 * HTuncache'd by mainloop), THEN it has to be consistent
		 * with what we are trying to push.
		 *
		 * This may be overkill...  - kw
		 */
		if (thatparent == thisparent &&
		    (!HTMainText || HTMainAnchor == thisparent)
		    ) {
		    HDOC(nhist).internal_link = TRUE;
		    history[nhist].intern_seq_start =
			history[nhist - 1].intern_seq_start >= 0 ?
			history[nhist - 1].intern_seq_start : nhist - 1;
		    CTRACE((tfp, "\nLYpush: pushed as internal link, OK\n"));
		}
	    }
	}
	if (!HDOC(nhist).internal_link) {
	    CTRACE((tfp, "\nLYpush: push as internal link requested, %s\n",
		    "but didn't check out!"));
	}
    }
    CTRACE((tfp, "\nLYpush[%d]: address:%s\n        title:%s\n",
	    nhist, doc->address, doc->title));
    nhist++;
    return 1;
}
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 (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=\"text/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;
}