1 /* $NetBSD: edit.c,v 1.1 2007/02/18 22:13:42 rmind Exp $ */ 2 /* $OpenBSD: edit.c,v 1.14 2006/05/25 03:20:32 ray Exp $ */ 3 4 /* 5 * Written by Raymond Lai <ray@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((int)(*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 ((nwritten = write(fd, text, len)) == -1 || 136 nwritten != len) { 137 warn("error writing to temp file"); 138 cleanup(filename); 139 } 140 } 141 close(fd); 142 143 /* text is no longer used. */ 144 free(text); 145 146 /* Edit temp file. */ 147 edit(filename); 148 149 /* Open temporary file. */ 150 if (!(file = fopen(filename, "r"))) { 151 warn("could not open edited file: %s", filename); 152 cleanup(filename); 153 } 154 155 /* Copy temporary file contents to output file. */ 156 for (nread = sizeof(buf); nread == sizeof(buf);) { 157 nread = fread(buf, sizeof(*buf), sizeof(buf), file); 158 /* Test for error or end of file. */ 159 if (nread != sizeof(buf) && 160 (ferror(file) || !feof(file))) { 161 warnx("error reading edited file: %s", filename); 162 cleanup(filename); 163 } 164 165 /* 166 * If we have nothing to read, break out of loop 167 * instead of writing nothing. 168 */ 169 if (!nread) 170 break; 171 172 /* Write data we just read. */ 173 nwritten = fwrite(buf, sizeof(*buf), nread, outfile); 174 if (nwritten != nread) { 175 warnx("error writing to output file"); 176 cleanup(filename); 177 } 178 } 179 180 /* We've reached the end of the temporary file, so remove it. */ 181 if (unlink(filename)) 182 warn("could not delete: %s", filename); 183 fclose(file); 184 185 free(filename); 186 187 return (0); 188 } 189