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