xref: /openbsd-src/usr.bin/cvs/edit.c (revision daf88648c0e349d5c02e1504293082072c981640)
1 /*	$OpenBSD: edit.c,v 1.31 2007/01/25 18:56:33 otto Exp $	*/
2 /*
3  * Copyright (c) 2006, 2007 Xavier Santolaria <xsa@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 "log.h"
22 #include "remote.h"
23 
24 #define E_COMMIT	0x01
25 #define E_EDIT		0x02
26 #define E_UNEDIT	0x04
27 #define E_ALL		(E_EDIT|E_COMMIT|E_UNEDIT)
28 
29 #define BASE_ADD	0x01
30 #define BASE_GET	0x02
31 #define BASE_REMOVE	0x04
32 
33 static void	cvs_edit_local(struct cvs_file *);
34 static void	cvs_editors_local(struct cvs_file *);
35 static void	cvs_unedit_local(struct cvs_file *);
36 
37 static RCSNUM	*cvs_base_handle(struct cvs_file *, int);
38 
39 static int	edit_aflags = 0;
40 
41 struct cvs_cmd cvs_cmd_edit = {
42 	CVS_OP_EDIT, 0, "edit",
43 	{ },
44 	"Get ready to edit a watched file",
45 	"[-lR] [-a action] [file ...]",
46 	"a:lR",
47 	NULL,
48 	cvs_edit
49 };
50 
51 struct cvs_cmd cvs_cmd_editors = {
52 	CVS_OP_EDITORS, 0, "editors",
53 	{ },
54 	"See who is editing a watched file",
55 	"[-lR] [file ...]",
56 	"lR",
57 	NULL,
58 	cvs_editors
59 };
60 
61 struct cvs_cmd cvs_cmd_unedit = {
62 	CVS_OP_UNEDIT, 0, "unedit",
63 	{ },
64 	"Undo an edit command",
65 	"[-lR] [file ...]",
66 	"lR",
67 	NULL,
68 	cvs_unedit
69 };
70 
71 int
72 cvs_edit(int argc, char **argv)
73 {
74 	int ch;
75 	int flags;
76 	struct cvs_recursion cr;
77 
78 	flags = CR_RECURSE_DIRS;
79 
80 	while ((ch = getopt(argc, argv, cvs_cmd_edit.cmd_opts)) != -1) {
81 		switch (ch) {
82 		case 'a':
83 			if (strcmp(optarg, "edit") == 0)
84 				edit_aflags |= E_EDIT;
85 			else if (strcmp(optarg, "unedit") == 0)
86 				edit_aflags |= E_UNEDIT;
87 			else if (strcmp(optarg, "commit") == 0)
88 				edit_aflags |= E_COMMIT;
89 			else if (strcmp(optarg, "all") == 0)
90 				edit_aflags |= E_ALL;
91 			else if (strcmp(optarg, "none") == 0)
92 				edit_aflags &= ~E_ALL;
93 			else
94 				fatal("%s", cvs_cmd_edit.cmd_synopsis);
95 			break;
96 		case 'l':
97 			flags &= ~CR_RECURSE_DIRS;
98 			break;
99 		case 'R':
100 			break;
101 		default:
102 			fatal("%s", cvs_cmd_edit.cmd_synopsis);
103 		}
104 	}
105 
106 	argc -= optind;
107 	argv += optind;
108 
109 	if (argc == 0)
110 		fatal("%s", cvs_cmd_edit.cmd_synopsis);
111 
112 	if (edit_aflags == 0)
113 		edit_aflags |= E_ALL;
114 
115 	cr.enterdir = NULL;
116 	cr.leavedir = NULL;
117 
118 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
119 		cvs_client_connect_to_server();
120 		cr.fileproc = cvs_client_sendfile;
121 
122 		if (!(flags & CR_RECURSE_DIRS))
123 			cvs_client_send_request("Argument -l");
124 	} else {
125 		cr.fileproc = cvs_edit_local;
126 	}
127 
128 	cr.flags = flags;
129 
130 	cvs_file_run(argc, argv, &cr);
131 
132 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
133 		cvs_client_send_files(argv, argc);
134 		cvs_client_senddir(".");
135 		cvs_client_send_request("edit");
136 		cvs_client_get_responses();
137 	}
138 
139 	return (0);
140 }
141 
142 int
143 cvs_editors(int argc, char **argv)
144 {
145 	int ch;
146 	int flags;
147 	struct cvs_recursion cr;
148 
149 	flags = CR_RECURSE_DIRS;
150 
151 	while ((ch = getopt(argc, argv, cvs_cmd_editors.cmd_opts)) != -1) {
152 		switch (ch) {
153 		case 'l':
154 			flags &= ~CR_RECURSE_DIRS;
155 			break;
156 		case 'R':
157 			break;
158 		default:
159 			fatal("%s", cvs_cmd_editors.cmd_synopsis);
160 		}
161 	}
162 
163 	argc -= optind;
164 	argv += optind;
165 
166 	if (argc == 0)
167 		fatal("%s", cvs_cmd_editors.cmd_synopsis);
168 
169 	cr.enterdir = NULL;
170 	cr.leavedir = NULL;
171 
172 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
173 		cvs_client_connect_to_server();
174 		cr.fileproc = cvs_client_sendfile;
175 
176 		if (!(flags & CR_RECURSE_DIRS))
177 			cvs_client_send_request("Argument -l");
178 	} else {
179 		cr.fileproc = cvs_editors_local;
180 	}
181 
182 	cr.flags = flags;
183 
184 	cvs_file_run(argc, argv, &cr);
185 
186 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
187 		cvs_client_send_files(argv, argc);
188 		cvs_client_senddir(".");
189 		cvs_client_send_request("editors");
190 		cvs_client_get_responses();
191 	}
192 
193 	return (0);
194 }
195 
196 int
197 cvs_unedit(int argc, char **argv)
198 {
199 	int ch;
200 	int flags;
201 	struct cvs_recursion cr;
202 
203 	flags = CR_RECURSE_DIRS;
204 
205 	while ((ch = getopt(argc, argv, cvs_cmd_unedit.cmd_opts)) != -1) {
206 		switch (ch) {
207 		case 'l':
208 			flags &= ~CR_RECURSE_DIRS;
209 			break;
210 		case 'R':
211 			break;
212 		default:
213 			fatal("%s", cvs_cmd_unedit.cmd_synopsis);
214 		}
215 	}
216 
217 	argc -= optind;
218 	argv += optind;
219 
220 	if (argc == 0)
221 		fatal("%s", cvs_cmd_unedit.cmd_synopsis);
222 
223 	cr.enterdir = NULL;
224 	cr.leavedir = NULL;
225 
226 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
227 		cvs_client_connect_to_server();
228 		cr.fileproc = cvs_client_sendfile;
229 
230 		if (!(flags & CR_RECURSE_DIRS))
231 			cvs_client_send_request("Argument -l");
232 	} else {
233 		cr.fileproc = cvs_unedit_local;
234 	}
235 
236 	cr.flags = flags;
237 
238 	cvs_file_run(argc, argv, &cr);
239 
240 	if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
241 		cvs_client_send_files(argv, argc);
242 		cvs_client_senddir(".");
243 		cvs_client_send_request("unedit");
244 		cvs_client_get_responses();
245 	}
246 
247 	return (0);
248 }
249 
250 static void
251 cvs_edit_local(struct cvs_file *cf)
252 {
253 	FILE *fp;
254 	struct tm *t;
255 	time_t now;
256 	char bfpath[MAXPATHLEN], timebuf[64], thishost[MAXHOSTNAMELEN];
257 	char wdir[MAXPATHLEN];
258 
259 	if (cvs_noexec == 1)
260 		return;
261 
262 	cvs_log(LP_TRACE, "cvs_edit_local(%s)", cf->file_path);
263 
264 	cvs_file_classify(cf, NULL, 0);
265 
266 	if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
267 		fatal("cvs_edit_local: fopen: `%s': %s",
268 		    CVS_PATH_NOTIFY, strerror(errno));
269 
270 	(void)time(&now);
271 	if ((t = gmtime(&now)) == NULL)
272 		fatal("gmtime failed");
273 
274 	asctime_r(t, timebuf);
275 	if (timebuf[strlen(timebuf) - 1] == '\n')
276                 timebuf[strlen(timebuf) - 1] = '\0';
277 
278 	if (gethostname(thishost, sizeof(thishost)) == -1)
279 		fatal("gethostname failed");
280 
281 	if (getcwd(wdir, sizeof(wdir)) == NULL)
282 		fatal("getcwd failed");
283 
284 	(void)fprintf(fp, "E%s\t%s GMT\t%s\t%s\t",
285 	    cf->file_name, timebuf, thishost, wdir);
286 
287 	if (edit_aflags & E_EDIT)
288 		(void)fprintf(fp, "E");
289 	if (edit_aflags & E_UNEDIT)
290 		(void)fprintf(fp, "U");
291 	if (edit_aflags & E_COMMIT)
292 		(void)fprintf(fp, "C");
293 
294 	(void)fprintf(fp, "\n");
295 
296 	(void)fclose(fp);
297 
298 	if (fchmod(cf->fd, 0644) == -1)
299 		fatal("cvs_edit_local: fchmod %s", strerror(errno));
300 
301 	if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath,
302 	    MAXPATHLEN) >= MAXPATHLEN)
303 		fatal("cvs_edit_local: truncation");
304 
305 	if (mkdir(CVS_PATH_BASEDIR, 0755) == -1 && errno != EEXIST)
306 		fatal("cvs_edit_local: `%s': %s", CVS_PATH_BASEDIR,
307 		    strerror(errno));
308 
309 	if (cvs_file_copy(cf->file_path, bfpath) == -1)
310 		fatal("cvs_edit_local: cvs_file_copy failed");
311 
312 	(void)cvs_base_handle(cf, BASE_ADD);
313 }
314 
315 static void
316 cvs_editors_local(struct cvs_file *cf)
317 {
318 }
319 
320 static void
321 cvs_unedit_local(struct cvs_file *cf)
322 {
323 	FILE *fp;
324 	struct stat st;
325 	struct tm *t;
326 	time_t now;
327 	char bfpath[MAXPATHLEN], timebuf[64], thishost[MAXHOSTNAMELEN];
328 	char wdir[MAXPATHLEN];
329 	RCSNUM *ba_rev;
330 
331 	if (cvs_noexec == 1)
332 		return;
333 
334 	cvs_log(LP_TRACE, "cvs_unedit_local(%s)", cf->file_path);
335 
336 	cvs_file_classify(cf, NULL, 0);
337 
338 	if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath,
339 	    MAXPATHLEN) >= MAXPATHLEN)
340 		fatal("cvs_unedit_local: truncation");
341 
342 	if (stat(bfpath, &st) == -1)
343 		return;
344 
345 	if (cvs_file_cmp(cf->file_path, bfpath) != 0) {
346 		cvs_printf("%s has been modified; revert changes? ",
347 		    cf->file_name);
348 
349 		if (cvs_yesno() == -1)
350 			return;
351 	}
352 
353 	cvs_rename(bfpath, cf->file_path);
354 
355 	if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
356 		fatal("cvs_unedit_local: fopen: `%s': %s",
357 		    CVS_PATH_NOTIFY, strerror(errno));
358 
359 	(void)time(&now);
360 	if ((t = gmtime(&now)) == NULL)
361 		fatal("gmtime failed");
362 
363 	asctime_r(t, timebuf);
364 	if (timebuf[strlen(timebuf) - 1] == '\n')
365                 timebuf[strlen(timebuf) - 1] = '\0';
366 
367 	if (gethostname(thishost, sizeof(thishost)) == -1)
368 		fatal("gethostname failed");
369 
370 	if (getcwd(wdir, sizeof(wdir)) == NULL)
371 		fatal("getcwd failed");
372 
373 	(void)fprintf(fp, "U%s\t%s GMT\t%s\t%s\t\n",
374 	    cf->file_name, timebuf, thishost, wdir);
375 
376 	(void)fclose(fp);
377 
378 	if ((ba_rev = cvs_base_handle(cf, BASE_GET)) == NULL) {
379 		cvs_log(LP_ERR, "%s not mentioned in %s",
380 		    cf->file_name, CVS_PATH_BASEREV);
381 		return;
382 	}
383 
384 	if (cf->file_ent != NULL) {
385 		CVSENTRIES *entlist;
386 		struct cvs_ent *ent;
387 		char *entry, rbuf[16];
388 
389 		entlist = cvs_ent_open(cf->file_wd);
390 
391 		if ((ent = cvs_ent_get(entlist, cf->file_name)) == NULL)
392 			fatal("cvs_unedit_local: cvs_ent_get failed");
393 
394 		(void)rcsnum_tostr(ba_rev, rbuf, sizeof(rbuf));
395 
396 		memset(timebuf, 0, sizeof(timebuf));
397 		ctime_r(&cf->file_ent->ce_mtime, timebuf);
398 		if (timebuf[strlen(timebuf) - 1] == '\n')
399 			timebuf[strlen(timebuf) - 1] = '\0';
400 
401 		(void)xasprintf(&entry, "/%s/%s/%s/%s/%s",
402 		    cf->file_name, rbuf, timebuf,
403 		    (cf->file_ent->ce_tag) ? cf->file_ent->ce_tag : "",
404 		    (cf->file_ent->ce_opts) ? cf->file_ent->ce_opts : "");
405 
406 		cvs_ent_add(entlist, entry);
407 
408 		cvs_ent_free(ent);
409 		cvs_ent_close(entlist, ENT_SYNC);
410 
411 		xfree(entry);
412 	}
413 
414 	rcsnum_free(ba_rev);
415 
416 	(void)cvs_base_handle(cf, BASE_REMOVE);
417 
418 	if (fchmod(cf->fd, 0644) == -1)
419 		fatal("cvs_unedit_local: fchmod %s", strerror(errno));
420 }
421 
422 static RCSNUM *
423 cvs_base_handle(struct cvs_file *cf, int flags)
424 {
425 	FILE *fp, *tfp;
426 	RCSNUM *ba_rev;
427 	size_t len;
428 	int i;
429 	char *dp, *sp;
430 	char buf[MAXPATHLEN], *fields[2], rbuf[16];
431 
432 	cvs_log(LP_TRACE, "cvs_base_handle(%s)", cf->file_path);
433 
434 	tfp = NULL;
435 	ba_rev = NULL;
436 
437 	if (((fp = fopen(CVS_PATH_BASEREV, "r")) == NULL) && errno != ENOENT) {
438 		cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREV);
439 		goto out;
440 	}
441 
442 	if (flags & (BASE_ADD|BASE_REMOVE)) {
443 		if ((tfp = fopen(CVS_PATH_BASEREVTMP, "w")) == NULL) {
444 			cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREVTMP);
445 			goto out;
446 		}
447 	}
448 
449 	if (fp != NULL) {
450 		while(fgets(buf, sizeof(buf), fp)) {
451 			len = strlen(buf);
452 			if (len > 0 && buf[len - 1] == '\n')
453 				buf[len - 1] = '\0';
454 
455 			if (buf[0] != 'B')
456 				continue;
457 
458 			sp = buf + 1;
459 			i = 0;
460 			do {
461 				if ((dp = strchr(sp, '/')) != NULL)
462 					*(dp++) = '\0';
463 				fields[i++] = sp;
464 				sp = dp;
465 			} while (dp != NULL && i < 2);
466 
467 			if (cvs_file_cmpname(fields[0], cf->file_path) == 0) {
468 				if (flags & BASE_GET) {
469 					ba_rev = rcsnum_parse(fields[1]);
470 					if (ba_rev == NULL)
471 						fatal("cvs_base_handle: "
472 						    "rcsnum_parse");
473 					goto got_rev;
474 				}
475 			} else {
476 				if (flags & (BASE_ADD|BASE_REMOVE))
477 					(void)fprintf(tfp, "%s\n", buf);
478 			}
479 		}
480 	}
481 
482 got_rev:
483 	if (flags & (BASE_ADD)) {
484 		(void)rcsnum_tostr(cf->file_ent->ce_rev, rbuf, sizeof(rbuf));
485 		(void)fprintf(tfp, "B%s/%s/\n", cf->file_path, rbuf);
486 	}
487 
488 out:
489 	if (fp != NULL)
490 		(void)fclose(fp);
491 
492 	if (tfp != NULL) {
493 		(void)fclose(tfp);
494 		(void)cvs_rename(CVS_PATH_BASEREVTMP, CVS_PATH_BASEREV);
495 	}
496 
497 	return (ba_rev);
498 }
499