xref: /netbsd-src/usr.bin/sdiff/edit.c (revision 3b6b08b599974eb758be548fcb1beb970687fbfc)
1*3b6b08b5Srillig /*	$NetBSD: edit.c,v 1.5 2021/08/27 17:36:37 rillig Exp $	*/
25533f1ebSrmind /*	$OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */
35533f1ebSrmind 
45533f1ebSrmind /*
55533f1ebSrmind  * Written by Raymond Lai <ray@cyth.net>.
65533f1ebSrmind  * Public domain.
75533f1ebSrmind  */
85533f1ebSrmind 
95533f1ebSrmind #include <sys/types.h>
105533f1ebSrmind #include <sys/wait.h>
115533f1ebSrmind 
125533f1ebSrmind #include <ctype.h>
135533f1ebSrmind #include <err.h>
145533f1ebSrmind #include <stdio.h>
155533f1ebSrmind #include <stdlib.h>
165533f1ebSrmind #include <string.h>
175533f1ebSrmind #include <unistd.h>
185533f1ebSrmind 
195533f1ebSrmind #include "common.h"
205533f1ebSrmind #include "extern.h"
215533f1ebSrmind 
225533f1ebSrmind static void edit(const char *);
235533f1ebSrmind 
245533f1ebSrmind /*
255533f1ebSrmind  * Takes the name of a file and opens it with an editor.
265533f1ebSrmind  */
275533f1ebSrmind static void
edit(const char * filename)285533f1ebSrmind edit(const char *filename)
295533f1ebSrmind {
305533f1ebSrmind 	int status;
315533f1ebSrmind 	pid_t pid;
325533f1ebSrmind 	const char *editor;
335533f1ebSrmind 
345533f1ebSrmind 	editor = getenv("VISUAL");
355533f1ebSrmind 	if (editor == NULL)
365533f1ebSrmind 		editor = getenv("EDITOR");
375533f1ebSrmind 	if (editor == NULL)
385533f1ebSrmind 		editor = "vi";
395533f1ebSrmind 
405533f1ebSrmind 	/* Start editor on temporary file. */
415533f1ebSrmind 	switch (pid = fork()) {
425533f1ebSrmind 	case 0:
435533f1ebSrmind 		/* child */
44acae6852Splunky 		execlp(editor, editor, filename, (void *)NULL);
455533f1ebSrmind 		warn("could not execute editor: %s", editor);
465533f1ebSrmind 		cleanup(filename);
475533f1ebSrmind 	case -1:
485533f1ebSrmind 		warn("could not fork");
495533f1ebSrmind 		cleanup(filename);
505533f1ebSrmind 	}
515533f1ebSrmind 
525533f1ebSrmind 	/* parent */
535533f1ebSrmind 	/* Wait for editor to exit. */
545533f1ebSrmind 	if (waitpid(pid, &status, 0) == -1) {
555533f1ebSrmind 		warn("waitpid");
565533f1ebSrmind 		cleanup(filename);
575533f1ebSrmind 	}
585533f1ebSrmind 
595533f1ebSrmind 	/* Check that editor terminated normally. */
605533f1ebSrmind 	if (!WIFEXITED(status)) {
615533f1ebSrmind 		warn("%s terminated abnormally", editor);
625533f1ebSrmind 		cleanup(filename);
635533f1ebSrmind 	}
645533f1ebSrmind }
655533f1ebSrmind 
665533f1ebSrmind /*
675533f1ebSrmind  * Parse edit command.  Returns 0 on success, -1 on error.
685533f1ebSrmind  */
695533f1ebSrmind int
eparse(const char * cmd,const char * left,const char * right)705533f1ebSrmind eparse(const char *cmd, const char *left, const char *right)
715533f1ebSrmind {
725533f1ebSrmind 	FILE *file;
735533f1ebSrmind 	size_t nread, nwritten;
745533f1ebSrmind 	int fd;
755533f1ebSrmind 	char *filename;
765533f1ebSrmind 	char buf[BUFSIZ], *text;
775533f1ebSrmind 
785533f1ebSrmind 	/* Skip whitespace. */
79*3b6b08b5Srillig 	while (isspace((unsigned char)(*cmd)))
805533f1ebSrmind 		++cmd;
815533f1ebSrmind 
825533f1ebSrmind 	text = NULL;
835533f1ebSrmind 	switch (*cmd) {
845533f1ebSrmind 	case '\0':
855533f1ebSrmind 		/* Edit empty file. */
865533f1ebSrmind 		break;
875533f1ebSrmind 
885533f1ebSrmind 	case 'b':
895533f1ebSrmind 		/* Both strings. */
905533f1ebSrmind 		if (left == NULL)
915533f1ebSrmind 			goto RIGHT;
925533f1ebSrmind 		if (right == NULL)
935533f1ebSrmind 			goto LEFT;
945533f1ebSrmind 
955533f1ebSrmind 		/* Neither column is blank, so print both. */
965533f1ebSrmind 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
975533f1ebSrmind 			err(2, "could not allocate memory");
985533f1ebSrmind 		break;
995533f1ebSrmind 
1005533f1ebSrmind 	case 'l':
1015533f1ebSrmind LEFT:
1025533f1ebSrmind 		/* Skip if there is no left column. */
1035533f1ebSrmind 		if (left == NULL)
1045533f1ebSrmind 			break;
1055533f1ebSrmind 
1065533f1ebSrmind 		if (asprintf(&text, "%s\n", left) == -1)
1075533f1ebSrmind 			err(2, "could not allocate memory");
1085533f1ebSrmind 
1095533f1ebSrmind 		break;
1105533f1ebSrmind 
1115533f1ebSrmind 	case 'r':
1125533f1ebSrmind RIGHT:
1135533f1ebSrmind 		/* Skip if there is no right column. */
1145533f1ebSrmind 		if (right == NULL)
1155533f1ebSrmind 			break;
1165533f1ebSrmind 
1175533f1ebSrmind 		if (asprintf(&text, "%s\n", right) == -1)
1185533f1ebSrmind 			err(2, "could not allocate memory");
1195533f1ebSrmind 
1205533f1ebSrmind 		break;
1215533f1ebSrmind 
1225533f1ebSrmind 	default:
1235533f1ebSrmind 		return (-1);
1245533f1ebSrmind 	}
1255533f1ebSrmind 
1265533f1ebSrmind 	/* Create temp file. */
1275533f1ebSrmind 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
1285533f1ebSrmind 		err(2, "asprintf");
1295533f1ebSrmind 	if ((fd = mkstemp(filename)) == -1)
1305533f1ebSrmind 		err(2, "mkstemp");
1315533f1ebSrmind 	if (text != NULL) {
1325533f1ebSrmind 		size_t len;
1335533f1ebSrmind 
1345533f1ebSrmind 		len = strlen(text);
13562a63750Slukem 		if ((size_t)write(fd, text, len) != len) {
1365533f1ebSrmind 			warn("error writing to temp file");
1375533f1ebSrmind 			cleanup(filename);
1385533f1ebSrmind 		}
1395533f1ebSrmind 	}
1405533f1ebSrmind 	close(fd);
1415533f1ebSrmind 
1425533f1ebSrmind 	/* text is no longer used. */
1435533f1ebSrmind 	free(text);
1445533f1ebSrmind 
1455533f1ebSrmind 	/* Edit temp file. */
1465533f1ebSrmind 	edit(filename);
1475533f1ebSrmind 
1485533f1ebSrmind 	/* Open temporary file. */
1495533f1ebSrmind 	if (!(file = fopen(filename, "r"))) {
1505533f1ebSrmind 		warn("could not open edited file: %s", filename);
1515533f1ebSrmind 		cleanup(filename);
1525533f1ebSrmind 	}
1535533f1ebSrmind 
1545533f1ebSrmind 	/* Copy temporary file contents to output file. */
1555533f1ebSrmind 	for (nread = sizeof(buf); nread == sizeof(buf);) {
1565533f1ebSrmind 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
1575533f1ebSrmind 		/* Test for error or end of file. */
1585533f1ebSrmind 		if (nread != sizeof(buf) &&
1595533f1ebSrmind 		    (ferror(file) || !feof(file))) {
1605533f1ebSrmind 			warnx("error reading edited file: %s", filename);
1615533f1ebSrmind 			cleanup(filename);
1625533f1ebSrmind 		}
1635533f1ebSrmind 
1645533f1ebSrmind 		/*
1655533f1ebSrmind 		 * If we have nothing to read, break out of loop
1665533f1ebSrmind 		 * instead of writing nothing.
1675533f1ebSrmind 		 */
1685533f1ebSrmind 		if (!nread)
1695533f1ebSrmind 			break;
1705533f1ebSrmind 
1715533f1ebSrmind 		/* Write data we just read. */
1725533f1ebSrmind 		nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
1735533f1ebSrmind 		if (nwritten != nread) {
1745533f1ebSrmind 			warnx("error writing to output file");
1755533f1ebSrmind 			cleanup(filename);
1765533f1ebSrmind 		}
1775533f1ebSrmind 	}
1785533f1ebSrmind 
1795533f1ebSrmind 	/* We've reached the end of the temporary file, so remove it. */
1805533f1ebSrmind 	if (unlink(filename))
1815533f1ebSrmind 		warn("could not delete: %s", filename);
1825533f1ebSrmind 	fclose(file);
1835533f1ebSrmind 
1845533f1ebSrmind 	free(filename);
1855533f1ebSrmind 
1865533f1ebSrmind 	return (0);
1875533f1ebSrmind }
188