Annotation of src/usr.bin/cvs/logmsg.c, Revision 1.27
1.27 ! xsa 1: /* $OpenBSD: logmsg.c,v 1.26 2005/12/30 02:03:28 joris Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.7 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.7 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.7 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.7 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.7 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.27 ! xsa 27: #include "includes.h"
1.1 jfb 28:
1.14 xsa 29: #include "buf.h"
1.1 jfb 30: #include "cvs.h"
31: #include "log.h"
1.2 jfb 32: #include "proto.h"
1.1 jfb 33:
34:
1.18 xsa 35: #define CVS_LOGMSG_BIGMSG 32000
36: #define CVS_LOGMSG_PREFIX "CVS:"
1.8 jfb 37: #define CVS_LOGMSG_LINE \
1.1 jfb 38: "----------------------------------------------------------------------"
39:
40:
1.8 jfb 41: static const char *cvs_logmsg_ops[3] = {
42: "Added", "Modified", "Removed",
43: };
44:
45:
1.1 jfb 46: /*
47: * cvs_logmsg_open()
48: *
49: * Open the file specified by <path> and allocate a buffer large enough to
50: * hold all of the file's contents. Lines starting with the log prefix
51: * are not included in the result.
52: * The returned value must later be free()d.
53: * Returns a pointer to the allocated buffer on success, or NULL on failure.
54: */
1.17 xsa 55: char *
1.1 jfb 56: cvs_logmsg_open(const char *path)
57: {
58: int lcont;
59: size_t len;
60: char lbuf[256], *msg;
61: struct stat st;
62: FILE *fp;
63: BUF *bp;
64:
1.24 xsa 65: if (stat(path, &st) == -1)
66: fatal("cvs_logmsg_open: stat: `%s': %s", path, strerror(errno));
1.1 jfb 67:
1.24 xsa 68: if (!S_ISREG(st.st_mode))
69: fatal("cvs_logmsg_open: message file must be a regular file");
1.1 jfb 70:
71: if (st.st_size > CVS_LOGMSG_BIGMSG) {
72: do {
73: fprintf(stderr,
74: "The specified message file seems big. "
75: "Proceed anyways? (y/n) ");
1.24 xsa 76: if (fgets(lbuf, (int)sizeof(lbuf), stdin) == NULL)
77: fatal("cvs_logmsg_open: fgets failed");
1.1 jfb 78:
79: len = strlen(lbuf);
80: if ((len == 0) || (len > 2) ||
81: ((lbuf[0] != 'y') && (lbuf[0] != 'n'))) {
82: fprintf(stderr, "invalid input\n");
83: continue;
1.7 tedu 84: } else if (lbuf[0] == 'y')
1.1 jfb 85: break;
1.24 xsa 86: else if (lbuf[0] == 'n')
87: fatal("aborted by user");
1.1 jfb 88:
89: } while (1);
90: }
91:
1.24 xsa 92: if ((fp = fopen(path, "r")) == NULL)
93: fatal("cvs_logmsg_open: fopen: `%s': %s",
94: path, strerror(errno));
1.1 jfb 95:
1.21 xsa 96: bp = cvs_buf_alloc((size_t)128, BUF_AUTOEXT);
1.1 jfb 97:
98: /* lcont is used to tell if a buffer returned by fgets is a start
99: * of line or just line continuation because the buffer isn't
100: * large enough to hold the entire line.
101: */
102: lcont = 0;
103:
1.20 xsa 104: while (fgets(lbuf, (int)sizeof(lbuf), fp) != NULL) {
1.1 jfb 105: len = strlen(lbuf);
106: if (len == 0)
107: continue;
1.3 jfb 108: else if ((lcont == 0) && (strncmp(lbuf, CVS_LOGMSG_PREFIX,
109: strlen(CVS_LOGMSG_PREFIX)) == 0))
1.1 jfb 110: /* skip lines starting with the prefix */
111: continue;
112:
1.24 xsa 113: cvs_buf_append(bp, lbuf, strlen(lbuf));
1.1 jfb 114:
115: lcont = (lbuf[len - 1] == '\n') ? 0 : 1;
116: }
1.11 jfb 117: (void)fclose(fp);
118:
1.23 xsa 119: cvs_buf_putc(bp, '\0');
1.1 jfb 120:
121: msg = (char *)cvs_buf_release(bp);
122:
123: return (msg);
124: }
125:
126:
127: /*
128: * cvs_logmsg_get()
129: *
1.8 jfb 130: * Get a log message by forking and executing the user's editor. The <dir>
131: * argument is a relative path to the directory for which the log message
1.12 david 132: * applies, and the 3 tail queue arguments contains all the files for which the
1.8 jfb 133: * log message will apply. Any of these arguments can be set to NULL in the
134: * case where there is no information to display.
1.1 jfb 135: * Returns the message in a dynamically allocated string on success, NULL on
136: * failure.
137: */
1.17 xsa 138: char *
1.8 jfb 139: cvs_logmsg_get(const char *dir, struct cvs_flist *added,
140: struct cvs_flist *modified, struct cvs_flist *removed)
1.1 jfb 141: {
1.8 jfb 142: int i, fd, argc, fds[3], nl;
1.3 jfb 143: size_t len, tlen;
144: char *argv[4], buf[16], path[MAXPATHLEN], fpath[MAXPATHLEN], *msg;
1.1 jfb 145: FILE *fp;
1.3 jfb 146: CVSFILE *cvsfp;
1.1 jfb 147: struct stat st1, st2;
1.8 jfb 148: struct cvs_flist *files[3];
149:
150: files[0] = added;
151: files[1] = modified;
152: files[2] = removed;
1.1 jfb 153:
154: msg = NULL;
155: fds[0] = -1;
156: fds[1] = -1;
157: fds[2] = -1;
1.19 xsa 158: strlcpy(path, cvs_tmpdir, sizeof(path));
159: strlcat(path, "/cvsXXXXXXXXXX", sizeof(path));
1.1 jfb 160: argc = 0;
161: argv[argc++] = cvs_editor;
162: argv[argc++] = path;
163: argv[argc] = NULL;
1.25 moritz 164: tlen = 0;
1.1 jfb 165:
1.24 xsa 166: if ((fd = mkstemp(path)) == -1)
167: fatal("cvs_logmsg_get: mkstemp: `%s': %s",
168: path, strerror(errno));
1.1 jfb 169:
1.24 xsa 170: if ((fp = fdopen(fd, "w")) == NULL) {
1.3 jfb 171: if (unlink(path) == -1)
172: cvs_log(LP_ERRNO, "failed to unlink temporary file");
1.24 xsa 173: fatal("cvs_logmsg_get: fdopen failed");
1.8 jfb 174: }
1.1 jfb 175:
1.8 jfb 176: fprintf(fp, "\n%s %s\n%s Enter Log. Lines beginning with `%s' are "
177: "removed automatically\n%s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE,
178: CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX, CVS_LOGMSG_PREFIX);
179:
180: if (dir != NULL)
181: fprintf(fp, "%s Commiting in %s\n%s\n", CVS_LOGMSG_PREFIX, dir,
182: CVS_LOGMSG_PREFIX);
183:
184: for (i = 0; i < 3; i++) {
185: if (files[i] == NULL)
1.16 joris 186: continue;
187:
188: if (SIMPLEQ_EMPTY(files[i]))
1.8 jfb 189: continue;
190:
191: fprintf(fp, "%s %s Files:", CVS_LOGMSG_PREFIX,
192: cvs_logmsg_ops[i]);
1.3 jfb 193: nl = 1;
1.13 jfb 194: SIMPLEQ_FOREACH(cvsfp, files[i], cf_list) {
1.3 jfb 195: /* take the space into account */
196: cvs_file_getpath(cvsfp, fpath, sizeof(fpath));
197: len = strlen(fpath) + 1;
198: if (tlen + len >= 72)
199: nl = 1;
200:
201: if (nl) {
202: fprintf(fp, "\n%s\t", CVS_LOGMSG_PREFIX);
203: tlen = 8;
204: nl = 0;
205: }
206:
207: fprintf(fp, " %s", fpath);
208: tlen += len;
209: }
1.9 jfb 210: fputc('\n', fp);
1.1 jfb 211:
212: }
1.9 jfb 213: fprintf(fp, "%s %s\n", CVS_LOGMSG_PREFIX, CVS_LOGMSG_LINE);
1.1 jfb 214: (void)fflush(fp);
215:
216: if (fstat(fd, &st1) == -1) {
217: if (unlink(path) == -1)
218: cvs_log(LP_ERRNO, "failed to unlink log file %s", path);
1.24 xsa 219: fatal("cvs_logmsg_get: fstat failed");
1.1 jfb 220: }
221:
222: for (;;) {
1.8 jfb 223: if (cvs_exec(argc, argv, fds) < 0)
1.1 jfb 224: break;
1.8 jfb 225:
1.1 jfb 226: if (fstat(fd, &st2) == -1) {
227: cvs_log(LP_ERRNO, "failed to stat log message file");
228: break;
229: }
230:
231: if (st2.st_mtime != st1.st_mtime) {
232: msg = cvs_logmsg_open(path);
233: break;
234: }
235:
236: /* nothing was entered */
237: fprintf(stderr,
1.15 xsa 238: "\nLog message unchanged or not specified\na)bort, "
1.1 jfb 239: "c)ontinue, e)dit, !)reuse this message unchanged "
240: "for remaining dirs\nAction: (continue) ");
241:
1.20 xsa 242: if (fgets(buf, (int)sizeof(buf), stdin) == NULL) {
1.1 jfb 243: cvs_log(LP_ERRNO, "failed to read from standard input");
244: break;
245: }
246:
247: len = strlen(buf);
248: if ((len == 0) || (len > 2)) {
249: fprintf(stderr, "invalid input\n");
250: continue;
1.7 tedu 251: } else if (buf[0] == 'a') {
1.15 xsa 252: cvs_log(LP_ABORT, "aborted by user");
1.1 jfb 253: break;
254: } else if ((buf[0] == '\n') || (buf[0] == 'c')) {
255: /* empty message */
1.22 joris 256: msg = xstrdup("");
1.1 jfb 257: break;
1.8 jfb 258: } else if (buf[0] == 'e')
1.1 jfb 259: continue;
1.8 jfb 260: else if (buf[0] == '!') {
1.1 jfb 261: /* XXX do something */
262: }
263: }
264:
265: (void)fclose(fp);
266: (void)close(fd);
267:
268: if (unlink(path) == -1)
269: cvs_log(LP_ERRNO, "failed to unlink log file %s", path);
270:
271: return (msg);
1.2 jfb 272: }
273:
274:
275: /*
276: * cvs_logmsg_send()
277: *
278: */
1.26 joris 279: void
1.2 jfb 280: cvs_logmsg_send(struct cvsroot *root, const char *msg)
281: {
1.5 jfb 282: const char *mp;
283: char *np, buf[256];
1.2 jfb 284:
1.26 joris 285: cvs_sendarg(root, "-m", 0);
1.2 jfb 286:
1.5 jfb 287: for (mp = msg; mp != NULL; mp = strchr(mp, '\n')) {
288: if (*mp == '\n')
289: mp++;
1.4 jfb 290:
291: /* XXX ghetto */
1.5 jfb 292: strlcpy(buf, mp, sizeof(buf));
293: np = strchr(buf, '\n');
294: if (np != NULL)
295: *np = '\0';
1.26 joris 296: cvs_sendarg(root, buf, (mp == msg) ? 0 : 1);
1.2 jfb 297: }
1.1 jfb 298: }