Home | History | Annotate | Line # | Download | only in sdiff
      1 /*	$NetBSD: edit.c,v 1.5 2021/08/27 17:36:37 rillig Exp $	*/
      2 /*	$OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */
      3 
      4 /*
      5  * Written by Raymond Lai <ray (at) cyth.net>.
      6  * Public domain.
      7  */
      8 
      9 #include <sys/types.h>
     10 #include <sys/wait.h>
     11 
     12 #include <ctype.h>
     13 #include <err.h>
     14 #include <stdio.h>
     15 #include <stdlib.h>
     16 #include <string.h>
     17 #include <unistd.h>
     18 
     19 #include "common.h"
     20 #include "extern.h"
     21 
     22 static void edit(const char *);
     23 
     24 /*
     25  * Takes the name of a file and opens it with an editor.
     26  */
     27 static void
     28 edit(const char *filename)
     29 {
     30 	int status;
     31 	pid_t pid;
     32 	const char *editor;
     33 
     34 	editor = getenv("VISUAL");
     35 	if (editor == NULL)
     36 		editor = getenv("EDITOR");
     37 	if (editor == NULL)
     38 		editor = "vi";
     39 
     40 	/* Start editor on temporary file. */
     41 	switch (pid = fork()) {
     42 	case 0:
     43 		/* child */
     44 		execlp(editor, editor, filename, (void *)NULL);
     45 		warn("could not execute editor: %s", editor);
     46 		cleanup(filename);
     47 	case -1:
     48 		warn("could not fork");
     49 		cleanup(filename);
     50 	}
     51 
     52 	/* parent */
     53 	/* Wait for editor to exit. */
     54 	if (waitpid(pid, &status, 0) == -1) {
     55 		warn("waitpid");
     56 		cleanup(filename);
     57 	}
     58 
     59 	/* Check that editor terminated normally. */
     60 	if (!WIFEXITED(status)) {
     61 		warn("%s terminated abnormally", editor);
     62 		cleanup(filename);
     63 	}
     64 }
     65 
     66 /*
     67  * Parse edit command.  Returns 0 on success, -1 on error.
     68  */
     69 int
     70 eparse(const char *cmd, const char *left, const char *right)
     71 {
     72 	FILE *file;
     73 	size_t nread, nwritten;
     74 	int fd;
     75 	char *filename;
     76 	char buf[BUFSIZ], *text;
     77 
     78 	/* Skip whitespace. */
     79 	while (isspace((unsigned char)(*cmd)))
     80 		++cmd;
     81 
     82 	text = NULL;
     83 	switch (*cmd) {
     84 	case '\0':
     85 		/* Edit empty file. */
     86 		break;
     87 
     88 	case 'b':
     89 		/* Both strings. */
     90 		if (left == NULL)
     91 			goto RIGHT;
     92 		if (right == NULL)
     93 			goto LEFT;
     94 
     95 		/* Neither column is blank, so print both. */
     96 		if (asprintf(&text, "%s\n%s\n", left, right) == -1)
     97 			err(2, "could not allocate memory");
     98 		break;
     99 
    100 	case 'l':
    101 LEFT:
    102 		/* Skip if there is no left column. */
    103 		if (left == NULL)
    104 			break;
    105 
    106 		if (asprintf(&text, "%s\n", left) == -1)
    107 			err(2, "could not allocate memory");
    108 
    109 		break;
    110 
    111 	case 'r':
    112 RIGHT:
    113 		/* Skip if there is no right column. */
    114 		if (right == NULL)
    115 			break;
    116 
    117 		if (asprintf(&text, "%s\n", right) == -1)
    118 			err(2, "could not allocate memory");
    119 
    120 		break;
    121 
    122 	default:
    123 		return (-1);
    124 	}
    125 
    126 	/* Create temp file. */
    127 	if (asprintf(&filename, "%s/sdiff.XXXXXXXXXX", tmpdir) == -1)
    128 		err(2, "asprintf");
    129 	if ((fd = mkstemp(filename)) == -1)
    130 		err(2, "mkstemp");
    131 	if (text != NULL) {
    132 		size_t len;
    133 
    134 		len = strlen(text);
    135 		if ((size_t)write(fd, text, len) != len) {
    136 			warn("error writing to temp file");
    137 			cleanup(filename);
    138 		}
    139 	}
    140 	close(fd);
    141 
    142 	/* text is no longer used. */
    143 	free(text);
    144 
    145 	/* Edit temp file. */
    146 	edit(filename);
    147 
    148 	/* Open temporary file. */
    149 	if (!(file = fopen(filename, "r"))) {
    150 		warn("could not open edited file: %s", filename);
    151 		cleanup(filename);
    152 	}
    153 
    154 	/* Copy temporary file contents to output file. */
    155 	for (nread = sizeof(buf); nread == sizeof(buf);) {
    156 		nread = fread(buf, sizeof(*buf), sizeof(buf), file);
    157 		/* Test for error or end of file. */
    158 		if (nread != sizeof(buf) &&
    159 		    (ferror(file) || !feof(file))) {
    160 			warnx("error reading edited file: %s", filename);
    161 			cleanup(filename);
    162 		}
    163 
    164 		/*
    165 		 * If we have nothing to read, break out of loop
    166 		 * instead of writing nothing.
    167 		 */
    168 		if (!nread)
    169 			break;
    170 
    171 		/* Write data we just read. */
    172 		nwritten = fwrite(buf, sizeof(*buf), nread, outfile);
    173 		if (nwritten != nread) {
    174 			warnx("error writing to output file");
    175 			cleanup(filename);
    176 		}
    177 	}
    178 
    179 	/* We've reached the end of the temporary file, so remove it. */
    180 	if (unlink(filename))
    181 		warn("could not delete: %s", filename);
    182 	fclose(file);
    183 
    184 	free(filename);
    185 
    186 	return (0);
    187 }
    188