Annotation of src/usr.bin/cvs/commit.c, Revision 1.4
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.2 jfb 40: #include "proto.h"
1.1 jfb 41:
42:
1.3 krapht 43: #define CVS_COMMIT_BIGMSG 8000
44: #define CVS_COMMIT_FTMPL "/tmp/cvsXXXXXXXXXX"
45: #define CVS_COMMIT_LOGPREFIX "CVS:"
46: #define CVS_COMMIT_LOGLINE \
47: "----------------------------------------------------------------------"
48:
1.1 jfb 49:
50:
51: static char* cvs_commit_openmsg (const char *);
1.3 krapht 52: static char* cvs_commit_getmsg (const char *);
1.1 jfb 53:
54:
55: /*
56: * cvs_commit()
57: *
58: * Handler for the `cvs commit' command.
59: */
60:
61: int
62: cvs_commit(int argc, char **argv)
63: {
64: int ch, recurse;
65: char *msg, *mfile;
66:
67: recurse = 1;
68: mfile = NULL;
69: msg = NULL;
70:
1.3 krapht 71: #if 0
72: cvs_commit_getmsg(".");
73: #endif
74:
1.1 jfb 75: while ((ch = getopt(argc, argv, "F:flm:R")) != -1) {
76: switch (ch) {
77: case 'F':
78: mfile = optarg;
79: break;
80: case 'f':
81: recurse = 0;
82: break;
83: case 'l':
84: recurse = 0;
85: break;
86: case 'm':
87: msg = optarg;
88: break;
89: case 'R':
90: recurse = 1;
91: break;
92: default:
93: return (EX_USAGE);
94: }
95: }
96:
97: if ((msg != NULL) && (mfile != NULL)) {
98: cvs_log(LP_ERR, "the -F and -m flags are mutually exclusive");
99: return (EX_USAGE);
100: }
101:
102: if ((mfile != NULL) && (msg = cvs_commit_openmsg(mfile)) == NULL)
103: return (EX_DATAERR);
104:
105: argc -= optind;
106: argv += optind;
107:
108: return (0);
109: }
110:
111:
112: /*
113: * cvs_commit_openmsg()
114: *
115: * Open the file specified by <path> and allocate a buffer large enough to
116: * hold all of the file's contents. The returned value must later be freed
117: * using the free() function.
118: * Returns a pointer to the allocated buffer on success, or NULL on failure.
119: */
120:
121: static char*
122: cvs_commit_openmsg(const char *path)
123: {
1.3 krapht 124: int fd, ch;
1.1 jfb 125: size_t sz;
1.3 krapht 126: char buf[32], *msg;
1.1 jfb 127: struct stat st;
128:
129: if (stat(path, &st) == -1) {
130: cvs_log(LP_ERRNO, "failed to stat `%s'", path);
131: return (NULL);
132: }
133:
1.3 krapht 134: if (!S_ISREG(st.st_mode)) {
135: cvs_log(LP_ERR, "message file must be a regular file");
136: return (NULL);
137: }
138:
139: if (st.st_size > CVS_COMMIT_BIGMSG) {
140: do {
141: fprintf(stderr,
142: "The specified message file seems big. "
143: "Proceed anyways? (y/n) ");
144: if (fgets(buf, sizeof(buf), stdin) == NULL) {
145: cvs_log(LP_ERRNO,
146: "failed to read from standard input");
147: return (NULL);
148: }
149:
150: sz = strlen(buf);
1.4 ! krapht 151: if ((sz == 0) || (sz > 2) ||
! 152: ((buf[sz] != 'y') && (buf[sz] != 'n'))) {
! 153: fprintf(stderr, "invalid input\n");
1.3 krapht 154: continue;
155: }
1.4 ! krapht 156: else if (buf[sz] == 'y')
! 157: break;
! 158: else if (buf[sz] == 'n') {
1.3 krapht 159: cvs_log(LP_ERR, "aborted by user");
160: return (NULL);
161: }
162:
163: } while (1);
164: }
165:
1.1 jfb 166: sz = st.st_size + 1;
167:
168: msg = (char *)malloc(sz);
169: if (msg == NULL) {
170: cvs_log(LP_ERRNO, "failed to allocate message buffer");
171: return (NULL);
172: }
173:
174: fd = open(path, O_RDONLY, 0);
175: if (fd == -1) {
176: cvs_log(LP_ERRNO, "failed to open message file `%s'", path);
177: return (NULL);
178: }
179:
180: if (read(fd, msg, sz - 1) == -1) {
181: cvs_log(LP_ERRNO, "failed to read CVS commit message");
182: return (NULL);
183: }
184: msg[sz - 1] = '\0';
185:
186: return (msg);
1.3 krapht 187: }
188:
189:
190: /*
191: * cvs_commit_getmsg()
192: *
193: * Get a commit log message by forking the user's editor.
194: * Returns the message in a dynamically allocated string on success, NULL on
195: * failure.
196: */
197:
198: static char*
199: cvs_commit_getmsg(const char *dir)
200: {
201: int ret, fd, argc, fds[3];
202: char *argv[4], path[MAXPATHLEN], *msg;
203: FILE *fp;
204:
205: fds[0] = -1;
206: fds[1] = -1;
207: fds[2] = -1;
208: strlcpy(path, CVS_COMMIT_FTMPL, sizeof(path));
209: argc = 0;
210: argv[argc++] = cvs_editor;
211: argv[argc++] = path;
212: argv[argc] = NULL;
213:
214: if ((fd = mkstemp(path)) == -1) {
215: cvs_log(LP_ERRNO, "failed to create temporary file");
216: return (NULL);
217: }
218:
219: fp = fdopen(fd, "w");
220: if (fp == NULL) {
221: cvs_log(LP_ERRNO, "failed to fdopen");
222: exit(1);
223: } else {
224: fprintf(fp,
225: "\n%s %s\n%s Enter Log. Lines beginning with `%s' are "
226: "removed automatically\n%s\n%s Commiting in %s\n"
227: "%s\n%s Modified Files:\n",
228: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGLINE,
229: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX,
230: CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX,
231: dir, CVS_COMMIT_LOGPREFIX, CVS_COMMIT_LOGPREFIX);
232:
233: /* XXX list files here */
234:
235: fprintf(fp, "%s %s\n", CVS_COMMIT_LOGPREFIX,
236: CVS_COMMIT_LOGLINE);
237: }
238: (void)fflush(fp);
239: (void)fclose(fp);
240:
241: do {
242: ret = cvs_exec(argc, argv, fds);
243: if (ret == -1) {
244: fprintf(stderr,
245: "Log message unchanged or not specified\n"
246: "a)bort, c)ontinue, e)dit, !)reuse this message "
247: "unchanged for remaining dirs\nAction: () ");
248:
249: ret = getchar();
250: if (ret == 'a') {
251: cvs_log(LP_ERR, "aborted by user");
252: break;
253: } else if (ret == 'c') {
254: } else if (ret == 'e') {
255: } else if (ret == '!') {
256: }
257:
258: }
259: } while (0);
260:
261: (void)close(fd);
262:
263: return (msg);
264: }
265:
266:
267: /*
268: * cvs_commit_gettmpl()
269: *
270: * Get the template to display when invoking the editor to get a commit
271: * message.
272: */
273:
274: cvs_commit_gettmpl(void)
275: {
276:
1.1 jfb 277: }