xref: /openbsd-src/usr.bin/cvs/logmsg.c (revision fcb086e6f39abf87c8bd29d816b128ab9dabf21c)
1*fcb086e6Stobias /*	$OpenBSD: logmsg.c,v 1.47 2008/03/09 01:02:38 tobias Exp $	*/
2db758d52Sjoris /*
3db758d52Sjoris  * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
4db758d52Sjoris  *
5db758d52Sjoris  * Permission to use, copy, modify, and distribute this software for any
6db758d52Sjoris  * purpose with or without fee is hereby granted, provided that the above
7db758d52Sjoris  * copyright notice and this permission notice appear in all copies.
8db758d52Sjoris  *
9db758d52Sjoris  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10db758d52Sjoris  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11db758d52Sjoris  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12db758d52Sjoris  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13db758d52Sjoris  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14db758d52Sjoris  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15db758d52Sjoris  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16db758d52Sjoris  */
17db758d52Sjoris 
181f8531bdSotto #include <sys/stat.h>
1966510bb6Sxsa #include <sys/types.h>
2066510bb6Sxsa #include <sys/wait.h>
211f8531bdSotto 
221f8531bdSotto #include <errno.h>
231f8531bdSotto #include <fcntl.h>
2466510bb6Sxsa #include <paths.h>
2566510bb6Sxsa #include <signal.h>
26f99068d5Schl #include <stdlib.h>
271f8531bdSotto #include <string.h>
281f8531bdSotto #include <unistd.h>
29db758d52Sjoris 
30db758d52Sjoris #include "cvs.h"
31db758d52Sjoris 
32db758d52Sjoris #define CVS_LOGMSG_PREFIX		"CVS:"
33db758d52Sjoris #define CVS_LOGMSG_LINE		\
34db758d52Sjoris "----------------------------------------------------------------------"
35db758d52Sjoris 
3666510bb6Sxsa int	cvs_logmsg_edit(const char *);
3766510bb6Sxsa 
38db758d52Sjoris char *
39db758d52Sjoris cvs_logmsg_read(const char *path)
40db758d52Sjoris {
41db758d52Sjoris 	int fd;
42db758d52Sjoris 	BUF *bp;
43db758d52Sjoris 	FILE *fp;
44db758d52Sjoris 	size_t len;
45db758d52Sjoris 	struct stat st;
46db758d52Sjoris 	char *buf, *lbuf;
47db758d52Sjoris 
48db758d52Sjoris 	if ((fd = open(path, O_RDONLY)) == -1)
49db758d52Sjoris 		fatal("cvs_logmsg_read: open %s", strerror(errno));
50db758d52Sjoris 
51db758d52Sjoris 	if (fstat(fd, &st) == -1)
52db758d52Sjoris 		fatal("cvs_logmsg_read: fstat %s", strerror(errno));
53db758d52Sjoris 
54db758d52Sjoris 	if (!S_ISREG(st.st_mode))
55db758d52Sjoris 		fatal("cvs_logmsg_read: file is not a regular file");
56db758d52Sjoris 
57db758d52Sjoris 	if ((fp = fdopen(fd, "r")) == NULL)
58db758d52Sjoris 		fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
59db758d52Sjoris 
60*fcb086e6Stobias 	if (st.st_size > SIZE_MAX)
61*fcb086e6Stobias 		fatal("cvs_buf_load_fd: %s: file size too big", path);
62*fcb086e6Stobias 
63db758d52Sjoris 	lbuf = NULL;
649aad96bcStobias 	bp = cvs_buf_alloc(st.st_size);
65db758d52Sjoris 	while ((buf = fgetln(fp, &len))) {
66db758d52Sjoris 		if (buf[len - 1] == '\n') {
67db758d52Sjoris 			buf[len - 1] = '\0';
68db758d52Sjoris 		} else {
69db758d52Sjoris 			lbuf = xmalloc(len + 1);
707347534fSotto 			memcpy(lbuf, buf, len);
717347534fSotto 			lbuf[len] = '\0';
72db758d52Sjoris 			buf = lbuf;
73db758d52Sjoris 		}
74db758d52Sjoris 
75db758d52Sjoris 		len = strlen(buf);
76db758d52Sjoris 
77db758d52Sjoris 		if (!strncmp(buf, CVS_LOGMSG_PREFIX,
78870158f9Stobias 		    sizeof(CVS_LOGMSG_PREFIX) - 1))
79db758d52Sjoris 			continue;
80db758d52Sjoris 
81db758d52Sjoris 		cvs_buf_append(bp, buf, len);
82db758d52Sjoris 		cvs_buf_putc(bp, '\n');
83db758d52Sjoris 	}
84db758d52Sjoris 
85db758d52Sjoris 	if (lbuf != NULL)
86db758d52Sjoris 		xfree(lbuf);
87db758d52Sjoris 
88db758d52Sjoris 	(void)fclose(fp);
89db758d52Sjoris 
90db758d52Sjoris 	cvs_buf_putc(bp, '\0');
91db758d52Sjoris 	return (cvs_buf_release(bp));
92db758d52Sjoris }
93db758d52Sjoris 
94db758d52Sjoris char *
95db758d52Sjoris cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed,
96db758d52Sjoris 	struct cvs_flisthead *modified)
97db758d52Sjoris {
98db758d52Sjoris 	FILE *fp;
9966510bb6Sxsa 	int c, fd, saved_errno;
100db758d52Sjoris 	struct cvs_filelist *cf;
101db758d52Sjoris 	struct stat st1, st2;
10266510bb6Sxsa 	char *fpath, *logmsg;
103db758d52Sjoris 
1048ad0f384Sxsa 	(void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
105db758d52Sjoris 
106f99068d5Schl 	if ((fd = mkstemp(fpath)) == -1)
107db758d52Sjoris 		fatal("cvs_logmsg_create: mkstemp %s", strerror(errno));
108db758d52Sjoris 
10964f70df8Sjoris 	cvs_worklist_add(fpath, &temp_files);
11064f70df8Sjoris 
111db758d52Sjoris 	if ((fp = fdopen(fd, "w")) == NULL) {
112ba617dc1Sxsa 		saved_errno = errno;
113db758d52Sjoris 		(void)unlink(fpath);
114ba617dc1Sxsa 		fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno));
115db758d52Sjoris 	}
116db758d52Sjoris 
117db758d52Sjoris 	fprintf(fp, "\n%s %s\n%s Enter Log.  Lines beginning with `%s' are "
118db758d52Sjoris 	    "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
119db758d52Sjoris 	    CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
120db758d52Sjoris 
1218c49b454Sjoris 	if (added != NULL && !TAILQ_EMPTY(added)) {
122db758d52Sjoris 		fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
123db758d52Sjoris 		TAILQ_FOREACH(cf, added, flist)
124db758d52Sjoris 			fprintf(fp, "\n%s\t%s",
125db758d52Sjoris 			    CVS_LOGMSG_PREFIX, cf->file_path);
126db758d52Sjoris 		fputs("\n", fp);
127db758d52Sjoris 	}
128db758d52Sjoris 
1298c49b454Sjoris 	if (removed != NULL && !TAILQ_EMPTY(removed)) {
130db758d52Sjoris 		fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
131db758d52Sjoris 		TAILQ_FOREACH(cf, removed, flist)
132db758d52Sjoris 			fprintf(fp, "\n%s\t%s",
133db758d52Sjoris 			    CVS_LOGMSG_PREFIX, cf->file_path);
134db758d52Sjoris 		fputs("\n", fp);
135db758d52Sjoris 	}
136db758d52Sjoris 
1378c49b454Sjoris 	if (modified != NULL && !TAILQ_EMPTY(modified)) {
138db758d52Sjoris 		fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
139db758d52Sjoris 		TAILQ_FOREACH(cf, modified, flist)
140db758d52Sjoris 			fprintf(fp, "\n%s\t%s",
141db758d52Sjoris 			    CVS_LOGMSG_PREFIX, cf->file_path);
142db758d52Sjoris 		fputs("\n", fp);
143db758d52Sjoris 	}
144db758d52Sjoris 
145db758d52Sjoris 	fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
146db758d52Sjoris 	(void)fflush(fp);
147db758d52Sjoris 
148db758d52Sjoris 	if (fstat(fd, &st1) == -1) {
149ba617dc1Sxsa 		saved_errno = errno;
150db758d52Sjoris 		(void)unlink(fpath);
151ba617dc1Sxsa 		fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
152db758d52Sjoris 	}
153db758d52Sjoris 
154db758d52Sjoris 	logmsg = NULL;
155db758d52Sjoris 
156db758d52Sjoris 	for (;;) {
157717c55ebSray 		if (cvs_logmsg_edit(fpath) == -1)
158db758d52Sjoris 			break;
159db758d52Sjoris 
160db758d52Sjoris 		if (fstat(fd, &st2) == -1) {
161ba617dc1Sxsa 			saved_errno = errno;
162db758d52Sjoris 			(void)unlink(fpath);
163ba617dc1Sxsa 			fatal("cvs_logmsg_create: fstat %s",
164ba617dc1Sxsa 			    strerror(saved_errno));
165db758d52Sjoris 		}
166db758d52Sjoris 
167db758d52Sjoris 		if (st1.st_mtime != st2.st_mtime) {
168db758d52Sjoris 			logmsg = cvs_logmsg_read(fpath);
169db758d52Sjoris 			break;
170db758d52Sjoris 		}
171db758d52Sjoris 
172db758d52Sjoris 		printf("\nLog message unchanged or not specified\n"
173e93f9b40Sjasper 		    "a)bort, c)ontinue, e)dit\nAction: (continue) ");
174db758d52Sjoris 		(void)fflush(stdout);
175db758d52Sjoris 
176db758d52Sjoris 		c = getc(stdin);
1777a2038a8Stobias 		if (c == EOF || c == 'a') {
178db758d52Sjoris 			fatal("Aborted by user");
179db758d52Sjoris 		} else if (c == '\n' || c == 'c') {
180db758d52Sjoris 			logmsg = xstrdup("");
181db758d52Sjoris 			break;
182db758d52Sjoris 		} else if (c == 'e') {
183db758d52Sjoris 			continue;
184db758d52Sjoris 		} else {
185db758d52Sjoris 			cvs_log(LP_ERR, "invalid input");
186db758d52Sjoris 			continue;
187db758d52Sjoris 		}
188db758d52Sjoris 	}
189db758d52Sjoris 
190db758d52Sjoris 	(void)fclose(fp);
191db758d52Sjoris 	(void)unlink(fpath);
192db758d52Sjoris 	xfree(fpath);
193db758d52Sjoris 
194db758d52Sjoris 	return (logmsg);
195db758d52Sjoris }
19666510bb6Sxsa 
197717c55ebSray /*
198717c55ebSray  * Execute an editor on the specified pathname, which is interpreted
199717c55ebSray  * from the shell.  This means flags may be included.
200717c55ebSray  *
201717c55ebSray  * Returns -1 on error, or the exit value on success.
202717c55ebSray  */
20366510bb6Sxsa int
20466510bb6Sxsa cvs_logmsg_edit(const char *pathname)
20566510bb6Sxsa {
20666510bb6Sxsa 	char *argp[] = {"sh", "-c", NULL, NULL}, *p;
20766510bb6Sxsa 	sig_t sighup, sigint, sigquit;
20866510bb6Sxsa 	pid_t pid;
20906381711Sray 	int saved_errno, st;
21066510bb6Sxsa 
21166510bb6Sxsa 	(void)xasprintf(&p, "%s %s", cvs_editor, pathname);
21266510bb6Sxsa 	argp[2] = p;
21366510bb6Sxsa 
21466510bb6Sxsa 	sighup = signal(SIGHUP, SIG_IGN);
21566510bb6Sxsa 	sigint = signal(SIGINT, SIG_IGN);
21666510bb6Sxsa 	sigquit = signal(SIGQUIT, SIG_IGN);
21706381711Sray 	if ((pid = fork()) == -1)
21806381711Sray 		goto fail;
21966510bb6Sxsa 	if (pid == 0) {
22066510bb6Sxsa 		execv(_PATH_BSHELL, argp);
22166510bb6Sxsa 		_exit(127);
22266510bb6Sxsa 	}
22306381711Sray 	while (waitpid(pid, &st, 0) == -1)
22466510bb6Sxsa 		if (errno != EINTR)
22506381711Sray 			goto fail;
22606381711Sray 	xfree(p);
22766510bb6Sxsa 	(void)signal(SIGHUP, sighup);
22866510bb6Sxsa 	(void)signal(SIGINT, sigint);
22966510bb6Sxsa 	(void)signal(SIGQUIT, sigquit);
23006381711Sray 	if (!WIFEXITED(st)) {
23106381711Sray 		errno = EINTR;
23266510bb6Sxsa 		return (-1);
23366510bb6Sxsa 	}
23406381711Sray 	return (WEXITSTATUS(st));
23506381711Sray 
23606381711Sray  fail:
23706381711Sray 	saved_errno = errno;
23806381711Sray 	(void)signal(SIGHUP, sighup);
23906381711Sray 	(void)signal(SIGINT, sigint);
24006381711Sray 	(void)signal(SIGQUIT, sigquit);
24106381711Sray 	xfree(p);
24206381711Sray 	errno = saved_errno;
24306381711Sray 	return (-1);
24466510bb6Sxsa }
245