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