<commit_before>/*-
 * Copyright (c) 1992, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
 *	<NAME>.  All rights reserved.
 *
 * See the LICENSE file for redistribution information.
 */

#include "config.h"

#ifndef lint
static const char sccsid[] = "@(#)exf.c	10.49 (Berkeley) 10/10/96";
#endif /* not lint */

#include <sys/param.h>
#include <sys/types.h>		/* XXX: param.h may not have included types.h */
#include <sys/queue.h>
#include <sys/stat.h>

/*
 * We include <sys/file.h>, because the flock(2) and open(2) #defines
 * were found there on historical systems.  We also include <fcntl.h>
 * because the open(2) #defines are found there on newer systems.
 */
#include <sys/file.h>

#include <bitstring.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "common.h"

static int	file_backup __P((SCR *, char *, char *));
static void	file_cinit __P((SCR *));
static void	file_comment __P((SCR *));
static int	file_spath __P((SCR *, FREF *, struct stat *, int *));

/*
 * file_add --
 *	Insert a file name into the FREF list, if it doesn't already
 *	appear in it.
 *
 * !!!
 * The "if it doesn't already appear" changes vi's semantics slightly.  If
 * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
 * will reflect the line/column of the previous edit session.  Historic nvi
 * did not do this.  The change is a logical extension of the change where
 * vi now remembers the last location in any file that it has ever edited,
 * not just the previously edited file.
 *
 * PUBLIC: FREF *file_add __P((SCR *, CHAR_T *));
 */
FREF *
file_add(sp, name)
	SCR *sp;
	CHAR_T *name;
{
	GS *gp;
	FREF *frp, *tfrp;

	/*
	 * Return it if it already exists.  Note that we test against the
	 * user's name, whatever that happens to be, including if it's a
	 * temporary file.
	 *
	 * If the user added a file but was unable to initialize it, there
	 * can be file list entries where the name field is NULL.  Discard
	 * them the next time we see them.
	 */
	gp = sp->gp;
	if (name != NULL)
		for (frp = gp->frefq.cqh_first;
		    frp != (FREF *)&gp->frefq; frp = frp->q.cqe_next) {
			if (frp->name == NULL) {
				tfrp = frp->q.cqe_next;
				CIRCLEQ_REMOVE(&gp->frefq, frp, q);
				if (frp->name != NULL)
					free(frp->name);
				free(frp);
				frp = tfrp;
				continue;
			}
			if (!strcmp(frp->name, name))
				return (frp);
		}

	/* Allocate and initialize the FREF structure. */
	CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
	if (frp == NULL)
		return (NULL);

	/*
	 * If no file name specified, or if the file name is a request
	 * for something temporary, file_init() will allocate the file
	 * name.  Temporary files are always ignored.
	 */
	if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
	    (frp->name = strdup(name)) == NULL) {
		free(frp);
		msgq(sp, M_SYSERR, NULL);
		return (NULL);
	}

	/* Append into the chain of file names. */
	CIRCLEQ_INSERT_TAIL(&gp->frefq, frp, q);

	return (frp);
}

/*
 * file_init --
 *	Start editing a file, based on the FREF structure.  If successsful,
 *	let go of any previous file.  Don't release the previous file until
 *	absolutely sure we have the new one.
 *
 * PUBLIC: int file_init __P((SCR *, FREF *, char *, int));
 */
int
file_init(sp, frp, rcv_name, flags)
	SCR *sp;
	FREF *frp;
	char *rcv_name;
	int flags;
{
	EXF *ep;
	RECNOINFO oinfo;
	struct stat sb;
	size_t psize;
	int fd, exists, open_err, readonly;
	char *oname, tname[MAXPATHLEN];

	open_err = readonly = 0;

	/*
	 * If the file is a recovery file, let the recovery code handle it.
	 * Clear the FR_RECOVER flag first -- the recovery code does set up,
	 * and then calls us!  If the recovery call fails, it's probably
	 * because the named file doesn't exist.  So, move boldly forward,
	 * presuming that there's an error message the user will get to see.
	 */
	if (F_ISSET(frp, FR_RECOVER)) {
		F_CLR(frp, FR_RECOVER);
		return (rcv_read(sp, frp));
	}

	/*
	 * Required FRP initialization; the only flag we keep is the
	 * cursor information.
	 */
	F_CLR(frp, ~FR_CURSORSET);

	/*
	 * Required EXF initialization:
	 *	Flush the line caches.
	 *	Default recover mail file fd to -1.
	 *	Set initial EXF flag bits.
	 */
	CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
	ep->c_lno = ep->c_nlines = OOBLNO;
	ep->rcv_fd = ep->fcntl_fd = -1;
	F_SET(ep, F_FIRSTMODIFY);

	/*
	 * Scan the user's path to find the file that we're going to
	 * try and open.
	 */
	if (file_spath(sp, frp, &sb, &exists))
		return (1);

	/*
	 * If no name or backing file, for whatever reason, create a backing
	 * temporary file, saving the temp file name so we can later unlink
	 * it.  If the user never named this file, copy the temporary file name
	 * to the real name (we display that until the user renames it).
	 */
	oname = frp->name;
	if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
		if (opts_empty(sp, O_DIRECTORY, 0))
			goto err;
		(void)snprintf(tname, sizeof(tname),
		    "%s/vi.XXXXXX", O_STR(sp, O_DIRECTORY));
		if ((fd = mkstemp(tname)) == -1) {
			msgq(sp, M_SYSERR,
			    "237|Unable to create temporary file");
			goto err;
		}
		(void)close(fd);

		if (frp->name == NULL)
			F_SET(frp, FR_TMPFILE);
		if ((frp->tname = strdup(tname)) == NULL ||
		    frp->name == NULL && (frp->name = strdup(tname)) == NULL) {
			if (frp->tname != NULL)
				free(frp->tname);
			msgq(sp, M_SYSERR, NULL);
			(void)unlink(tname);
			goto err;
		}
		oname = frp->tname;
		psize = 1024;
		if (!LF_ISSET(FS_OPENERR))
			F_SET(frp, FR_NEWFILE);

		time(&ep->mtime);
	} else {
		/*
		 * XXX
		 * A seat of the pants calculation: try to keep the file in
		 * 15 pages or less.  Don't use a page size larger than 10K<commit_msg>10 X's in mkstemp().<commit_after>/*-
 * Copyright (c) 1992, 1993, 1994
 *	The Regents of the University of California.  All rights reserved.
 * Copyright (c) 1992, 1993, 1994, 1995, 1996
 *	<NAME>.  All rights reserved.
 *
 * See the LICENSE file for redistribution information.
 *
 */

#include "config.h"

#ifndef lint
#if 0
static const char sccsid[] = "@(#)exf.c	10.49 (Berkeley) 10/10/96";
#endif
static const char rcsid[] = 
  "$FreeBSD$";
#endif /* not lint */

#include <sys/param.h>
#include <sys/types.h>		/* XXX: param.h may not have included types.h */
#include <sys/queue.h>
#include <sys/stat.h>

/*
 * We include <sys/file.h>, because the flock(2) and open(2) #defines
 * were found there on historical systems.  We also include <fcntl.h>
 * because the open(2) #defines are found there on newer systems.
 */
#include <sys/file.h>

#include <bitstring.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "common.h"

static int	file_backup __P((SCR *, char *, char *));
static void	file_cinit __P((SCR *));
static void	file_comment __P((SCR *));
static int	file_spath __P((SCR *, FREF *, struct stat *, int *));

/*
 * file_add --
 *	Insert a file name into the FREF list, if it doesn't already
 *	appear in it.
 *
 * !!!
 * The "if it doesn't already appear" changes vi's semantics slightly.  If
 * you do a "vi foo bar", and then execute "next bar baz", the edit of bar
 * will reflect the line/column of the previous edit session.  Historic nvi
 * did not do this.  The change is a logical extension of the change where
 * vi now remembers the last location in any file that it has ever edited,
 * not just the previously edited file.
 *
 * PUBLIC: FREF *file_add __P((SCR *, CHAR_T *));
 */
FREF *
file_add(sp, name)
	SCR *sp;
	CHAR_T *name;
{
	GS *gp;
	FREF *frp, *tfrp;

	/*
	 * Return it if it already exists.  Note that we test against the
	 * user's name, whatever that happens to be, including if it's a
	 * temporary file.
	 *
	 * If the user added a file but was unable to initialize it, there
	 * can be file list entries where the name field is NULL.  Discard
	 * them the next time we see them.
	 */
	gp = sp->gp;
	if (name != NULL)
		for (frp = gp->frefq.cqh_first;
		    frp != (FREF *)&gp->frefq; frp = frp->q.cqe_next) {
			if (frp->name == NULL) {
				tfrp = frp->q.cqe_next;
				CIRCLEQ_REMOVE(&gp->frefq, frp, q);
				if (frp->name != NULL)
					free(frp->name);
				free(frp);
				frp = tfrp;
				continue;
			}
			if (!strcmp(frp->name, name))
				return (frp);
		}

	/* Allocate and initialize the FREF structure. */
	CALLOC(sp, frp, FREF *, 1, sizeof(FREF));
	if (frp == NULL)
		return (NULL);

	/*
	 * If no file name specified, or if the file name is a request
	 * for something temporary, file_init() will allocate the file
	 * name.  Temporary files are always ignored.
	 */
	if (name != NULL && strcmp(name, TEMPORARY_FILE_STRING) &&
	    (frp->name = strdup(name)) == NULL) {
		free(frp);
		msgq(sp, M_SYSERR, NULL);
		return (NULL);
	}

	/* Append into the chain of file names. */
	CIRCLEQ_INSERT_TAIL(&gp->frefq, frp, q);

	return (frp);
}

/*
 * file_init --
 *	Start editing a file, based on the FREF structure.  If successsful,
 *	let go of any previous file.  Don't release the previous file until
 *	absolutely sure we have the new one.
 *
 * PUBLIC: int file_init __P((SCR *, FREF *, char *, int));
 */
int
file_init(sp, frp, rcv_name, flags)
	SCR *sp;
	FREF *frp;
	char *rcv_name;
	int flags;
{
	EXF *ep;
	RECNOINFO oinfo;
	struct stat sb;
	size_t psize;
	int fd, exists, open_err, readonly;
	char *oname, tname[MAXPATHLEN];

	open_err = readonly = 0;

	/*
	 * If the file is a recovery file, let the recovery code handle it.
	 * Clear the FR_RECOVER flag first -- the recovery code does set up,
	 * and then calls us!  If the recovery call fails, it's probably
	 * because the named file doesn't exist.  So, move boldly forward,
	 * presuming that there's an error message the user will get to see.
	 */
	if (F_ISSET(frp, FR_RECOVER)) {
		F_CLR(frp, FR_RECOVER);
		return (rcv_read(sp, frp));
	}

	/*
	 * Required FRP initialization; the only flag we keep is the
	 * cursor information.
	 */
	F_CLR(frp, ~FR_CURSORSET);

	/*
	 * Required EXF initialization:
	 *	Flush the line caches.
	 *	Default recover mail file fd to -1.
	 *	Set initial EXF flag bits.
	 */
	CALLOC_RET(sp, ep, EXF *, 1, sizeof(EXF));
	ep->c_lno = ep->c_nlines = OOBLNO;
	ep->rcv_fd = ep->fcntl_fd = -1;
	F_SET(ep, F_FIRSTMODIFY);

	/*
	 * Scan the user's path to find the file that we're going to
	 * try and open.
	 */
	if (file_spath(sp, frp, &sb, &exists))
		return (1);

	/*
	 * If no name or backing file, for whatever reason, create a backing
	 * temporary file, saving the temp file name so we can later unlink
	 * it.  If the user never named this file, copy the temporary file name
	 * to the real name (we display that until the user renames it).
	 */
	oname = frp->name;
	if (LF_ISSET(FS_OPENERR) || oname == NULL || !exists) {
		if (opts_empty(sp, O_DIRECTORY, 0))
			goto err;
		(void)snprintf(tname, sizeof(tname),
		    "%s/vi.XXXXXXXXXX", O_STR(sp, O_DIRECTORY));
		if ((fd = mkstemp(tname)) == -1) {
			msgq(sp, M_SYSERR,
			    "237|Unable to create temporary file");
			goto err;
		}
		(void)close(fd);

		if (frp->name == NULL)
			F_SET(frp, FR_TMPFILE);
		if ((frp->tname = strdup(tname)) == NULL ||
		    frp->name == NULL && (frp->name = strdup(tname)) == NULL) {
			if (frp->tname != NULL)
				free(frp->tname);
			msgq(sp, M_SYSERR, NULL);
			(void)unlink(tname);
			goto err;
		}
		oname = frp->tname;
		psize = 1024;
		if (!LF_ISSET(FS_OPENERR))
			F_SET(frp, FR_NEWFILE);

		time(&ep->mtime);
	} else {
		/*
		 * XXX
		 * A seat of the pants calculation: try to keep the file in
		 * 15 pages or less.  Don't use a page size larger than 10K
{'fraction_non_alphanumeric': 0.13566385982818746, 'fraction_numerical': 0.011142298205324487, 'mean_word_length': 3.2188733405095085, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6403557', 'n_tokens_mistral': 4729, 'n_tokens_neox': 4296, 'n_words': 1502}