Annotation of src/usr.bin/cvs/commit.c, Revision 1.5
1.1 jfb 1: /* $OpenBSD$ */
2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
9: *
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
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
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
27: #include <sys/types.h>
28: #include <sys/stat.h>
29:
30: #include <errno.h>
31: #include <stdio.h>
32: #include <fcntl.h>
33: #include <stdlib.h>
34: #include <unistd.h>
35: #include <string.h>
36: #include <sysexits.h>
37:
38: #include "cvs.h"
39: #include "log.h"
1.5 ! krapht 40: #include "buf.h"
1.2 jfb 41: #include "proto.h"
1.1 jfb 42:
43:
1.5 ! krapht 44: #define CVS_COMMIT_BIGMSG 32000
1.3 krapht 45: #define CVS_COMMIT_FTMPL "/tmp/cvsXXXXXXXXXX"
46: #define CVS_COMMIT_LOGPREFIX "CVS:"
47: #define CVS_COMMIT_LOGLINE \
48: "----------------------------------------------------------------------"
49:
1.1 jfb 50:
51:
52: static char* cvs_commit_openmsg (const char *);
1.3 krapht 53: static char* cvs_commit_getmsg (const char *);
1.1 jfb 54:
55:
56: /*
57: * cvs_commit()
58: *
59: * Handler for the `cvs commit' command.
60: */
61:
62: int
63: cvs_commit(int argc, char **argv)
64: {
65: int ch, recurse;
66: char *msg, *mfile;
67:
68: recurse = 1;
69: mfile = NULL;
70: msg = NULL;
71:
1.3 krapht 72: cvs_commit_getmsg(".");
73:
1.1 jfb 74: while ((ch = getopt(argc, argv, "F:flm:R")) != -1) {
75: switch (ch) {
76: case 'F':
77: mfile = optarg;
78: break;
79: case 'f':
80: recurse = 0;
81: break;
82: case 'l':
83: recurse = 0;
84: break;
85: case 'm':
86: msg = optarg;
87: break;
88: case 'R':
89: recurse = 1;
90: break;
91: default:
92: return (EX_USAGE);
93: }
94: }
95:
96: if ((msg != NULL) && (mfile != NULL)) {
97: cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive");
98: return (EX_USAGE);
99: }
100:
101: if ((mfile != NULL) && (msg = cvs_commit_openmsg(mfile)) == NULL)
102: return (EX_DATAERR);
103:
104: argc -= optind;
105: argv += optind;
106:
107: return (0);
108: }
109:
110:
111: /*
112: * cvs_commit_openmsg()
113: *
114: * Open the file specified by <path> and allocate a buffer large enough to
115: * hold all of the file's contents. The returned value must later be freed
116: * using the free() function.
117: * Returns a pointer to the allocated buffer on success, or NULL on failure.
118: */
119:
120: static char*
121: cvs_commit_openmsg(const char *path)
122: {
1.5 ! krapht 123: int ch;
! 124: size_t len;
! 125: char lbuf[256], *msg;
1.1 jfb 126: struct stat st;
1.5 ! krapht 127: FILE *fp;
! 128: BUF *bp;
1.1 jfb 129:
130: if (stat(path, &st) == -1) {
131: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
132: return (NULL);
133: }
134:
1.3 krapht 135: if (!S_ISREG(st.st_mode)) {
136: cvs_log(LP_ERR, "message file must be a regular file");
137: return (NULL);
138: }
139:
140: if (st.st_size > CVS_COMMIT_BIGMSG) {
141: do {
142: fprintf(stderr,
143: "The specified message file seems big. "
144: "Proceed anyways? (y/n) ");
1.5 ! krapht 145: if (fgets(lbuf, sizeof(lbuf), stdin) == NULL) {
1.3 krapht 146: cvs_log(LP_ERRNO,
147: "failed to read from standard input");
148: return (NULL);
149: }
150:
1.5 ! krapht 151: len = strlen(lbuf);
! 152: if ((len == 0) || (len > 2) ||
! 153: ((lbuf[0] != 'y') && (lbuf[0] != 'n'))) {
1.4 krapht 154: fprintf(stderr, "invalid input\n");
1.3 krapht 155: continue;
156: }
1.5 ! krapht 157: else if (lbuf[0] == 'y')
1.4 krapht 158: break;
1.5 ! krapht 159: else if (lbuf[0] == 'n') {
1.3 krapht 160: cvs_log(LP_ERR, "aborted by user");
161: return (NULL);
162: }
163:
164: } while (1);
165: }
166:
1.5 ! krapht 167: if ((fp = fopen(path, "r")) == NULL) {
! 168: cvs_log(LP_ERRNO, "failed to open message file `%s'", path);
1.1 jfb 169: return (NULL);
170: }
171:
1.5 ! krapht 172: bp = cvs_buf_alloc(128, BUF_AUTOEXT);
! 173: if (bp == NULL) {
1.1 jfb 174: return (NULL);
175: }
176:
1.5 ! krapht 177: while (fgets(lbuf, sizeof(lbuf), fp) != NULL) {
! 178: len = strlen(lbuf);
! 179: if (len == 0)
! 180: continue;
! 181:
! 182: /* skip lines starting with the prefix */
! 183: if (strncmp(lbuf, CVS_COMMIT_LOGPREFIX,
! 184: strlen(CVS_COMMIT_LOGPREFIX)) == 0)
! 185: continue;
! 186:
! 187: cvs_buf_append(bp, lbuf, strlen(lbuf));
1.1 jfb 188: }
1.5 ! krapht 189: cvs_buf_putc(bp, '\0');
! 190:
! 191: msg = (char *)cvs_buf_release(bp);
1.1 jfb 192:
193: return (msg);
1.3 krapht 194: }
195:
196:
197: /*
198: * cvs_commit_getmsg()
199: *
200: * Get a commit log message by forking the user's editor.
201: * Returns the message in a dynamically allocated string on success, NULL on
202: * failure.
203: */
204:
205: static char*
206: cvs_commit_getmsg(const char *dir)
207: {
208: int ret, fd, argc, fds[3];
1.5 ! krapht 209: size_t len;
! 210: char *argv[4], buf[16], path[MAXPATHLEN], *msg;
1.3 krapht 211: FILE *fp;
1.5 ! krapht 212: struct stat st1, st2;
1.3 krapht 213:
1.5 ! krapht 214: msg = NULL;
1.3 krapht 215: fds[0] = -1;
216: fds[1] = -1;
217: fds[2] = -1;
218: strlcpy(path, CVS_COMMIT_FTMPL, sizeof(path));
219: argc = 0;
220: argv[argc++] = cvs_editor;
221: argv[argc++] = path;
222: argv[argc] = NULL;
223:
224: if ((fd = mkstemp(path)) == -1) {
225: cvs_log(LP_ERRNO, "failed to create temporary file");
226: return (NULL);
227: }
228:
229: fp = fdopen(fd, "w");
230: if (fp == NULL) {
231: cvs_log(LP_ERRNO, "failed to fdopen");
232: } else {
233: fprintf(fp,
234: "\n%s %s\n%s Enter Log. Lines beginning with `%s' are "
235: "removed automatically\n%s\n%s Commiting in %s\n"
236: "%s\n%s Modified Files:\n",
237: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGLINE,
238: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX,
239: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX,
240: dir, CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX);
241:
242: /* XXX list files here */
243:
244: fprintf(fp, "%s %s\n", CVS_COMMIT_LOGPREFIX,
245: CVS_COMMIT_LOGLINE);
246: }
247: (void)fflush(fp);
248:
1.5 ! krapht 249: if (fstat(fd, &st1) == -1) {
! 250: cvs_log(LP_ERRNO, "failed to stat log message file");
! 251:
! 252: (void)fclose(fp);
! 253: if (unlink(path) == -1)
! 254: cvs_log(LP_ERRNO, "failed to unlink log file %s", path);
! 255: return (NULL);
! 256: }
! 257:
! 258: for (;;) {
1.3 krapht 259: ret = cvs_exec(argc, argv, fds);
1.5 ! krapht 260: if (ret == -1)
! 261: break;
! 262: if (fstat(fd, &st2) == -1) {
! 263: cvs_log(LP_ERRNO, "failed to stat log message file");
! 264: break;
! 265: }
1.3 krapht 266:
1.5 ! krapht 267: if (st2.st_mtime != st1.st_mtime)
! 268: break;
1.3 krapht 269:
1.5 ! krapht 270: /* nothing was entered */
! 271: fprintf(stderr,
! 272: "Log message unchanged or not specified\na)bort, "
! 273: "c)ontinue, e)dit, !)reuse this message unchanged "
! 274: "for remaining dirs\nAction: (continue) ");
1.3 krapht 275:
1.5 ! krapht 276: if (fgets(buf, sizeof(buf), stdin) == NULL) {
! 277: cvs_log(LP_ERRNO, "failed to read from standard input");
! 278: break;
! 279: }
1.3 krapht 280:
1.5 ! krapht 281: len = strlen(buf);
! 282: if ((len == 0) || (len > 2)) {
! 283: fprintf(stderr, "invalid input\n");
! 284: continue;
! 285: }
! 286: else if (buf[0] == 'a') {
! 287: cvs_log(LP_ERR, "aborted by user");
! 288: break;
! 289: } else if ((buf[0] == '\n') || (buf[0] == 'c')) {
! 290: /* empty message */
! 291: msg = strdup("");
! 292: break;
! 293: } else if (ret == 'e')
! 294: continue;
! 295: else if (ret == '!') {
! 296: /* XXX do something */
! 297: }
! 298: }
1.3 krapht 299:
1.5 ! krapht 300: (void)fclose(fp);
! 301: (void)close(fd);
1.3 krapht 302:
1.5 ! krapht 303: if (unlink(path) == -1)
! 304: cvs_log(LP_ERRNO, "failed to unlink log file %s", path);
1.3 krapht 305:
1.5 ! krapht 306: return (msg);
1.1 jfb 307: }