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
cvs_edit(int argc,char ** argv)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
cvs_editors(int argc,char ** argv)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
cvs_unedit(int argc,char ** argv)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
cvs_edit_local(struct cvs_file * cf)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
cvs_editors_local(struct cvs_file * cf)320 cvs_editors_local(struct cvs_file *cf)
321 {
322 }
323
324 static void
cvs_unedit_local(struct cvs_file * cf)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 *
cvs_base_handle(struct cvs_file * cf,int flags)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