dvtm

Fork of dvtm, a minimal terminal multiplexer
git clone git://git.laack.co/dvtm.git
Log | Files | Refs | README | LICENSE

dvtm-editor.c (4485B)


      1 /* Invoke $EDITOR as a filter.
      2  *
      3  * Copyright (c) 2016 Dmitry Bogatov <KAction@gnu.org>
      4  * Copyright (c) 2017 Marc André Tanner <mat@brain-dump.org>
      5  *
      6  * Permission to use, copy, modify, and distribute this software for any
      7  * purpose with or without fee is hereby granted, provided that the above
      8  * copyright notice and this permission notice appear in all copies.
      9  *
     10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     17  */
     18 #include <sys/stat.h>
     19 #include <sys/types.h>
     20 #include <sys/wait.h>
     21 #include <errno.h>
     22 #include <fcntl.h>
     23 #include <stdarg.h>
     24 #include <stdio.h>
     25 #include <stdlib.h>
     26 #include <string.h>
     27 #include <unistd.h>
     28 
     29 static void error(const char *msg, ...) {
     30 	va_list ap;
     31 	va_start(ap, msg);
     32 	vfprintf(stderr, msg, ap);
     33 	va_end(ap);
     34 	if (errno)
     35 		fprintf(stderr, ": %s", strerror(errno));
     36 	fprintf(stderr, "\n");
     37 }
     38 
     39 int main(int argc, char *argv[])
     40 {
     41 	int exit_status = EXIT_FAILURE, tmp_write = -1;
     42 
     43 	const char *editor = getenv("DVTM_EDITOR");
     44 	if (!editor)
     45 		editor = getenv("VISUAL");
     46 	if (!editor)
     47 		editor = getenv("EDITOR");
     48 	if (!editor)
     49 		editor = "vi";
     50 
     51 	char tempname[] = "/tmp/dvtm-editor.XXXXXX";
     52 	if ((tmp_write = mkstemp(tempname)) == -1) {
     53 		error("failed to open temporary file `%s'", tempname);
     54 		goto err;
     55 	}
     56 
     57 	/* POSIX does not mandates modes of temporary file. */
     58 	if (fchmod(tmp_write, 0600) == -1) {
     59 		error("failed to change mode of temporary file `%s'", tempname);
     60 		goto err;
     61 	}
     62 
     63 	char buffer[2048];
     64 	ssize_t bytes;
     65 	while ((bytes = read(STDIN_FILENO, buffer, sizeof(buffer))) > 0) {
     66 		do {
     67 			ssize_t written = write(tmp_write, buffer, bytes);
     68 			if (written == -1) {
     69 				error("failed to write data to temporary file `%s'",
     70 				      tempname);
     71 				goto err;
     72 			}
     73 			bytes -= written;
     74 		} while (bytes > 0);
     75 	}
     76 
     77 	if (fsync(tmp_write) == -1) {
     78 		error("failed to fsync temporary file `%s'", tempname);
     79 		goto err;
     80 	}
     81 
     82 	struct stat stat_before;
     83 	if (fstat(tmp_write, &stat_before) == -1) {
     84 		error("failed to stat newly created temporary file `%s'", tempname);
     85 		goto err;
     86 	}
     87 
     88 	if (close(tmp_write) == -1) {
     89 		error("failed to close temporary file `%s'", tempname);
     90 		goto err;
     91 	}
     92 
     93 	pid_t pid = fork();
     94 	if (pid == -1) {
     95 		error("failed to fork editor process");
     96 		goto err;
     97 	} else if (pid == 0) {
     98 		int tty = open("/dev/tty", O_RDWR);
     99 		if (tty == -1) {
    100 			error("failed to open /dev/tty");
    101 			_exit(1);
    102 		}
    103 
    104 		if (dup2(tty, STDIN_FILENO) == -1) {
    105 			error("failed to set tty as stdin");
    106 			_exit(1);
    107 		}
    108 
    109 		if (dup2(tty, STDOUT_FILENO) == -1) {
    110 			error("failed to set tty as stdout");
    111 			_exit(1);
    112 		}
    113 
    114 		if (dup2(tty, STDERR_FILENO) == -1) {
    115 			error("failed to set tty as stderr");
    116 			_exit(1);
    117 		}
    118 
    119 		close(tty);
    120 
    121 		const char *editor_argv[argc+2];
    122 		editor_argv[0] = editor;
    123 		for (int i = 1; i < argc; i++)
    124 			editor_argv[i] = argv[i];
    125 		editor_argv[argc] = tempname;
    126 		editor_argv[argc+1] = NULL;
    127 
    128 		execvp(editor, (char* const*)editor_argv);
    129 		error("failed to exec editor process `%s'", editor);
    130 		_exit(127);
    131 	}
    132 
    133 	int status;
    134 	if (waitpid(pid, &status, 0) == -1) {
    135 		error("waitpid failed");
    136 		goto err;
    137 	}
    138 	if (!WIFEXITED(status)) {
    139 		error("editor invocation failed");
    140 		goto err;
    141 	}
    142 	if ((status = WEXITSTATUS(status)) != 0) {
    143 		error("editor terminated with exit status: %d", status);
    144 		goto err;
    145 	}
    146 
    147 	int tmp_read = open(tempname, O_RDONLY);
    148 	if (tmp_read == -1) {
    149 		error("failed to open for reading of edited temporary file `%s'",
    150 		      tempname);
    151 		goto err;
    152 	}
    153 
    154 	struct stat stat_after;
    155 	if (fstat(tmp_read, &stat_after) == -1) {
    156 		error("failed to stat edited temporary file `%s'", tempname);
    157 		goto err;
    158 	}
    159 
    160 	if (stat_before.st_mtime == stat_after.st_mtime)
    161 		goto ok; /* no modifications */
    162 
    163 	while ((bytes = read(tmp_read, buffer, sizeof(buffer))) > 0) {
    164 		do {
    165 			ssize_t written = write(STDOUT_FILENO, buffer, bytes);
    166 			if (written == -1) {
    167 				error("failed to write data to stdout");
    168 				goto err;
    169 			}
    170 			bytes -= written;
    171 		} while (bytes > 0);
    172 	}
    173 
    174 ok:
    175 	exit_status = EXIT_SUCCESS;
    176 err:
    177 	if (tmp_write != -1)
    178 		unlink(tempname);
    179 	return exit_status;
    180 }