xref: /openbsd-src/usr.bin/cvs/logmsg.c (revision 799f675f6700f14e59124f9825c723e9f2ce19dc)
1 /*	$OpenBSD: logmsg.c,v 1.37 2007/01/25 22:49:39 xsa Exp $	*/
2 /*
3  * Copyright (c) 2007 Joris Vink <joris@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include "includes.h"
19 
20 #include "cvs.h"
21 #include "file.h"
22 #include "log.h"
23 #include "worklist.h"
24 
25 #define CVS_LOGMSG_PREFIX		"CVS:"
26 #define CVS_LOGMSG_LINE		\
27 "----------------------------------------------------------------------"
28 
29 char *
30 cvs_logmsg_read(const char *path)
31 {
32 	int fd;
33 	BUF *bp;
34 	FILE *fp;
35 	size_t len;
36 	struct stat st;
37 	char *buf, *lbuf;
38 
39 	if ((fd = open(path, O_RDONLY)) == -1)
40 		fatal("cvs_logmsg_read: open %s", strerror(errno));
41 
42 	if (fstat(fd, &st) == -1)
43 		fatal("cvs_logmsg_read: fstat %s", strerror(errno));
44 
45 	if (!S_ISREG(st.st_mode))
46 		fatal("cvs_logmsg_read: file is not a regular file");
47 
48 	if ((fp = fdopen(fd, "r")) == NULL)
49 		fatal("cvs_logmsg_read: fdopen %s", strerror(errno));
50 
51 	lbuf = NULL;
52 	bp = cvs_buf_alloc(st.st_size, BUF_AUTOEXT);
53 	while ((buf = fgetln(fp, &len))) {
54 		if (buf[len - 1] == '\n') {
55 			buf[len - 1] = '\0';
56 		} else {
57 			lbuf = xmalloc(len + 1);
58 			memcpy(lbuf, buf, len);
59 			lbuf[len] = '\0';
60 			buf = lbuf;
61 		}
62 
63 		len = strlen(buf);
64 		if (len == 0)
65 			continue;
66 
67 		if (!strncmp(buf, CVS_LOGMSG_PREFIX,
68 		    strlen(CVS_LOGMSG_PREFIX)))
69 			continue;
70 
71 		cvs_buf_append(bp, buf, len);
72 		cvs_buf_putc(bp, '\n');
73 	}
74 
75 	if (lbuf != NULL)
76 		xfree(lbuf);
77 
78 	(void)fclose(fp);
79 
80 	cvs_buf_putc(bp, '\0');
81 	return (cvs_buf_release(bp));
82 }
83 
84 char *
85 cvs_logmsg_create(struct cvs_flisthead *added, struct cvs_flisthead *removed,
86 	struct cvs_flisthead *modified)
87 {
88 	FILE *fp;
89 	int c, fd, argc, saved_errno;
90 	struct cvs_filelist *cf;
91 	struct stat st1, st2;
92 	char *fpath, *logmsg, *argv[4];
93 
94 	(void)xasprintf(&fpath, "%s/cvsXXXXXXXXXX", cvs_tmpdir);
95 
96 	if ((fd = mkstemp(fpath)) == NULL)
97 		fatal("cvs_logmsg_create: mkstemp %s", strerror(errno));
98 
99 	cvs_worklist_add(fpath, &temp_files);
100 
101 	if ((fp = fdopen(fd, "w")) == NULL) {
102 		saved_errno = errno;
103 		(void)unlink(fpath);
104 		fatal("cvs_logmsg_create: fdopen %s", strerror(saved_errno));
105 	}
106 
107 	fprintf(fp, "\n%s %s\n%s Enter Log.  Lines beginning with `%s' are "
108 	    "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
109 	    CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
110 
111 	if (added != NULL && !TAILQ_EMPTY(added)) {
112 		fprintf(fp, "%s Added Files:", CVS_LOGMSG_PREFIX);
113 		TAILQ_FOREACH(cf, added, flist)
114 			fprintf(fp, "\n%s\t%s",
115 			    CVS_LOGMSG_PREFIX, cf->file_path);
116 		fputs("\n", fp);
117 	}
118 
119 	if (removed != NULL && !TAILQ_EMPTY(removed)) {
120 		fprintf(fp, "%s Removed Files:", CVS_LOGMSG_PREFIX);
121 		TAILQ_FOREACH(cf, removed, flist)
122 			fprintf(fp, "\n%s\t%s",
123 			    CVS_LOGMSG_PREFIX, cf->file_path);
124 		fputs("\n", fp);
125 	}
126 
127 	if (modified != NULL && !TAILQ_EMPTY(modified)) {
128 		fprintf(fp, "%s Modified Files:", CVS_LOGMSG_PREFIX);
129 		TAILQ_FOREACH(cf, modified, flist)
130 			fprintf(fp, "\n%s\t%s",
131 			    CVS_LOGMSG_PREFIX, cf->file_path);
132 		fputs("\n", fp);
133 	}
134 
135 	fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
136 	(void)fflush(fp);
137 
138 	if (fstat(fd, &st1) == -1) {
139 		saved_errno = errno;
140 		(void)unlink(fpath);
141 		fatal("cvs_logmsg_create: fstat %s", strerror(saved_errno));
142 	}
143 
144 	argc = 0;
145 	argv[argc++] = cvs_editor;
146 	argv[argc++] = fpath;
147 	argv[argc] = NULL;
148 
149 	logmsg = NULL;
150 
151 	for (;;) {
152 		if (cvs_exec(argc, argv) < 0)
153 			break;
154 
155 		if (fstat(fd, &st2) == -1) {
156 			saved_errno = errno;
157 			(void)unlink(fpath);
158 			fatal("cvs_logmsg_create: fstat %s",
159 			    strerror(saved_errno));
160 		}
161 
162 		if (st1.st_mtime != st2.st_mtime) {
163 			logmsg = cvs_logmsg_read(fpath);
164 			break;
165 		}
166 
167 		printf("\nLog message unchanged or not specified\n"
168 		    "a)bort, c)ontinue, e)dit\nAction: (continue) ");
169 		(void)fflush(stdout);
170 
171 		c = getc(stdin);
172 		if (c == 'a') {
173 			fatal("Aborted by user");
174 		} else if (c == '\n' || c == 'c') {
175 			logmsg = xstrdup("");
176 			break;
177 		} else if (c == 'e') {
178 			continue;
179 		} else {
180 			cvs_log(LP_ERR, "invalid input");
181 			continue;
182 		}
183 	}
184 
185 	(void)fclose(fp);
186 	(void)unlink(fpath);
187 	xfree(fpath);
188 
189 	return (logmsg);
190 }
191