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