[BACK]Return to edit.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sdiff

File: [local] / src / usr.bin / sdiff / edit.c (download)

Revision 1.8, Tue Dec 27 05:00:28 2005 UTC (18 years, 5 months ago) by deraadt
Branch: MAIN
Changes since 1.7: +3 -3 lines

goto labels after case statements make lint happier

/*	$OpenBSD: edit.c,v 1.8 2005/12/27 05:00:28 deraadt Exp $ */

/*
 * Written by Raymond Lai <ray@cyth.net>.
 * Public domain.
 */

#include <sys/types.h>
#include <sys/wait.h>

#include <ctype.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include "extern.h"

__dead static void cleanup(const char *);
static void edit(const char *);
static char *xmktemp(const char *);

static void
cleanup(const char *filename)
{
	if (unlink(filename))
		err(2, "could not delete: %s", filename);
	exit(2);
}

/*
 * Takes the name of a file and opens it with an editor.
 */
static void
edit(const char *filename)
{
	int status;
	pid_t pid;
	const char *editor;

	editor = getenv("VISUAL");
	if (editor == NULL)
		editor = getenv("EDITOR");
	if (editor == NULL)
		editor = "vi";

	/* Start editor on temporary file. */
	switch (pid = fork()) {
	case 0:
		/* child */
		execlp(editor, editor, filename, (void *)NULL);
		warn("could not execute editor: %s", editor);
		cleanup(filename);
		/* NOTREACHED */
	case -1:
		warn("could not fork");
		cleanup(filename);
		/* NOTREACHED */
	}

	/* parent */
	/* Wait for editor to exit. */
	if (waitpid(pid, &status, 0) == -1) {
		warn("waitpid");
		cleanup(filename);
		/* NOTREACHED */
	}

	/* Check that editor terminated normally. */
	if (!WIFEXITED(status)) {
		warn("%s terminated abnormally", editor);
		cleanup(filename);
		/* NOTREACHED */
	}
}

/*
 * Creates and returns the name of a temporary file.  Takes a string
 * (or NULL) is written to the temporary file.  The returned string
 * needs to be freed.
 */
static char *
xmktemp(const char *s)
{
	FILE *file;
	int fd;
	const char *tmpdir;
	char *filename;

	/* If TMPDIR is set, use it; otherwise use /tmp. */
	if (!(tmpdir = getenv("TMPDIR")))
		tmpdir = "/tmp";
	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
		err(2, "xmktemp");

	/* Create temp file. */
	if ((fd = mkstemp(filename)) == -1)
		err(2, "could not create temporary file");

	/* If we don't write anything to the file, just close. */
	if (s == NULL) {
		close(fd);

		return (filename);
	}

	/* Open temp file for writing. */
	if ((file = fdopen(fd, "w")) == NULL) {
		warn("could not open %s", filename);
		cleanup(filename);
		/* NOTREACHED */
	}

	/* Write to file. */
	if (fputs(s, file)) {
		warn("could not write to %s", filename);
		cleanup(filename);
		/* NOTREACHED */
	}

	/* Close temp file. */
	fclose(file);

	return (filename);
}

/*
 * Parse edit command.  Returns 0 on success, -1 on error.
 */
int
eparse(const char *cmd, const char *left, const char *right)
{
	FILE *file;
	size_t nread, nwritten;
	const char *filename;
	char buf[BUFSIZ], *text;

	/* Skip whitespace. */
	while (isspace(*cmd))
		++cmd;

	text = NULL;
	switch (*cmd) {
	case '\0':
		/* Edit empty file. */
		break;

	case 'b':
		/* Both strings. */
		if (left == NULL)
			goto RIGHT;
		if (right == NULL) 
			goto LEFT;

		/* Neither column is blank, so print both. */
		if (asprintf(&text, "%s%s\n", left, right) == -1)
			err(2, "could not allocate memory");
		break;

	case 'l':
LEFT:
		/* Skip if there is no left column. */
		if (left == NULL)
			break;

		if (asprintf(&text, "%s\n", left) == -1)
			err(2, "could not allocate memory");

		break;

	case 'r':
RIGHT:
		/* Skip if there is no right column. */
		if (right == NULL)
			break;

		if (asprintf(&text, "%s\n", right) == -1)
			err(2, "could not allocate memory");

		break;

	default:
		return (-1);
	}

	/* Create temp file. */
	filename = xmktemp(text);

	/* text is no longer used. */
	free(text);

	/* Edit temp file. */
	edit(filename);

	/* Open temporary file. */
	if (!(file = fopen(filename, "r"))) {
		warn("could not open edited file: %s", filename);
		cleanup(filename);
		/* NOTREACHED */
	}

	/* Copy temporary file contents to output file. */
	for (nread = sizeof(buf); nread == sizeof(buf);) {
		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
		/* Test for error or end of file. */
		if (nread != sizeof(buf) &&
		    (ferror(file) || !feof(file))) {
			warnx("error reading edited file: %s", filename);
			cleanup(filename);
			/* NOTREACHED */
		}

		/*
		 * If we have nothing to read, break out of loop
		 * instead of writing nothing.
		 */
		if (!nread)
			break;

		/* Write data we just read. */
		nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
		if (nwritten != nread) {
			warnx("error writing to output file");
			cleanup(filename);
			/* NOTREACHED */
		}
	}

	/* We've reached the end of the temporary file, so remove it. */
	if (unlink(filename))
		warn("could not delete: %s", filename);
	fclose(file);

	/* filename was malloc()ed in xmktemp(). */
	free((void *)filename);

	return (0);
}