xref: /openbsd-src/usr.bin/cvs/commit.c (revision 52b730553a50a0154adc5e4c06e1c5b959a9bb5f)
1 /*	$OpenBSD: commit.c,v 1.4 2004/11/09 21:11:37 krapht Exp $	*/
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"
40 #include "proto.h"
41 
42 
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 
49 
50 
51 static char*  cvs_commit_openmsg   (const char *);
52 static char*  cvs_commit_getmsg   (const char *);
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 
71 #if 0
72 	cvs_commit_getmsg(".");
73 #endif
74 
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 {
124 	int fd, ch;
125 	size_t sz;
126 	char buf[32], *msg;
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 
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);
151 			if ((sz == 0) || (sz > 2) ||
152 			    ((buf[sz] != 'y') && (buf[sz] != 'n'))) {
153 				fprintf(stderr, "invalid input\n");
154 				continue;
155 			}
156 			else if (buf[sz] == 'y')
157 				break;
158 			else if (buf[sz] == 'n') {
159 				cvs_log(LP_ERR, "aborted by user");
160 				return (NULL);
161 			}
162 
163 		} while (1);
164 	}
165 
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);
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 
277 }
278