1 /*
2 * t_editor.c
3 *
4 * editor routines for tagutil.
5 */
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <sys/wait.h>
9 #include <sys/time.h>
10 #include <sys/resource.h>
11
12 #include "t_config.h"
13 #include "t_format.h"
14 #include "t_editor.h"
15 #include "t_loader.h"
16
17
18 int
t_edit(struct t_tune * tune)19 t_edit(struct t_tune *tune)
20 {
21 FILE *fp = NULL;
22 struct t_taglist *tlist = NULL;
23 char *tmp = NULL, *fmtdata = NULL;
24 const char *editor, *tmpdir;
25 pid_t editpid; /* child process */
26 int status, success = 0;
27 struct stat before, after;
28 extern const struct t_format *Fflag;
29
30 assert(tune != NULL);
31
32 /* convert the tags into the requested format */
33 tlist = t_tune_tags(tune);
34 if (tlist == NULL)
35 goto out;
36 fmtdata = Fflag->tags2fmt(tlist, t_tune_path(tune));
37 t_taglist_delete(tlist);
38 tlist = NULL;
39 if (fmtdata == NULL)
40 goto out;
41
42 tmpdir = getenv("TMPDIR");
43 if (tmpdir == NULL)
44 tmpdir = "/tmp";
45 /* print the format data into a temp file */
46 if (asprintf(&tmp, "%s/%s-XXXXXX.%s", tmpdir, getprogname(),
47 Fflag->fileext) < 0) {
48 goto out;
49 }
50 if (mkstemps(tmp, strlen(Fflag->fileext) + 1) == -1) {
51 warn("mkstemps");
52 goto out;
53 }
54 fp = fopen(tmp, "w");
55 if (fp == NULL) {
56 warn("%s: fopen", tmp);
57 goto out;
58 }
59 if (fprintf(fp, "%s", fmtdata) < 0) {
60 warn("%s: fprintf", tmp);
61 goto out;
62 }
63 if (fclose(fp) != 0) {
64 warn("%s: fclose", tmp);
65 goto out;
66 }
67 fp = NULL;
68
69 /* call the user's editor to edit the temp file */
70 editor = getenv("EDITOR");
71 if (editor == NULL) {
72 warnx("please set the $EDITOR environment variable.");
73 goto out;
74 }
75
76 /* save the current mtime so we know later if the file has been
77 modified */
78 if (stat(tmp, &before) != 0)
79 goto out;
80
81 /* launch the editor */
82 switch (editpid = fork()) {
83 case -1: /* error */
84 warn("fork");
85 goto out;
86 /* NOTREACHED */
87 case 0: /* child (edit process) */
88 execlp(editor, /* argv[0] */editor, /* argv[1] */tmp, NULL);
89 /* if we reach here, execlp(3) has failed */
90 err(EXIT_FAILURE, "execlp");
91 /* NOTREACHED */
92 default: /* parent (tagutil process) */
93 waitpid(editpid, &status, 0);
94 }
95
96 /* get the mtime now that the editor has been run */
97 if (stat(tmp, &after) != 0)
98 goto out;
99
100 int modified = (after.st_mtim.tv_sec > before.st_mtim.tv_sec ||
101 after.st_mtim.tv_nsec > before.st_mtim.tv_nsec);
102
103 /* we perform the load iff the file has been modified by the edit
104 process and that process exited with success */
105 if (modified && WIFEXITED(status) && WEXITSTATUS(status) == 0) {
106 if (t_load(tune, tmp) == -1)
107 goto out;
108 }
109
110 success = 1;
111 /* FALLTHROUGH */
112 out:
113 if (fp != NULL)
114 (void)fclose(fp);
115 if (tmp != NULL)
116 (void)unlink(tmp);
117 free(tmp);
118 free(fmtdata);
119 return (success ? 0 : -1);
120 }
121