Annotation of src/usr.bin/cvs/edit.c, Revision 1.30
1.30 ! xsa 1: /* $OpenBSD: edit.c,v 1.29 2007/01/11 15:45:02 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.11 xsa 18: #include "includes.h"
1.1 jfb 19:
20: #include "cvs.h"
21: #include "log.h"
1.14 xsa 22: #include "remote.h"
1.1 jfb 23:
1.18 xsa 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:
1.23 xsa 29: #define BASE_ADD 0x01
30: #define BASE_GET 0x02
31: #define BASE_REMOVE 0x04
32:
1.18 xsa 33: static void cvs_edit_local(struct cvs_file *);
1.14 xsa 34: static void cvs_editors_local(struct cvs_file *);
1.15 xsa 35: static void cvs_unedit_local(struct cvs_file *);
1.1 jfb 36:
1.23 xsa 37: static RCSNUM *cvs_base_handle(struct cvs_file *, int);
38:
1.18 xsa 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:
1.1 jfb 51: struct cvs_cmd cvs_cmd_editors = {
1.14 xsa 52: CVS_OP_EDITORS, 0, "editors",
1.1 jfb 53: { },
1.14 xsa 54: "See who is editing a watched file",
1.3 xsa 55: "[-lR] [file ...]",
1.1 jfb 56: "lR",
57: NULL,
1.14 xsa 58: cvs_editors
1.1 jfb 59: };
60:
1.15 xsa 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:
1.14 xsa 71: int
1.18 xsa 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) {
1.27 joris 119: cvs_client_connect_to_server();
1.18 xsa 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
1.14 xsa 143: cvs_editors(int argc, char **argv)
1.1 jfb 144: {
1.12 xsa 145: int ch;
1.14 xsa 146: int flags;
147: struct cvs_recursion cr;
148:
149: flags = CR_RECURSE_DIRS;
1.1 jfb 150:
1.14 xsa 151: while ((ch = getopt(argc, argv, cvs_cmd_editors.cmd_opts)) != -1) {
1.1 jfb 152: switch (ch) {
153: case 'l':
1.14 xsa 154: flags &= ~CR_RECURSE_DIRS;
1.1 jfb 155: break;
156: case 'R':
157: break;
158: default:
1.14 xsa 159: fatal("%s", cvs_cmd_editors.cmd_synopsis);
1.1 jfb 160: }
161: }
162:
1.14 xsa 163: argc -= optind;
164: argv += optind;
1.1 jfb 165:
1.14 xsa 166: if (argc == 0)
167: fatal("%s", cvs_cmd_editors.cmd_synopsis);
1.1 jfb 168:
1.14 xsa 169: cr.enterdir = NULL;
170: cr.leavedir = NULL;
1.1 jfb 171:
1.14 xsa 172: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.27 joris 173: cvs_client_connect_to_server();
1.14 xsa 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: }
1.1 jfb 181:
1.14 xsa 182: cr.flags = flags;
1.9 xsa 183:
1.14 xsa 184: cvs_file_run(argc, argv, &cr);
1.9 xsa 185:
1.14 xsa 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();
1.9 xsa 191: }
192:
1.14 xsa 193: return (0);
194: }
1.9 xsa 195:
1.15 xsa 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) {
1.28 xsa 227: cvs_client_connect_to_server();
1.15 xsa 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);
1.18 xsa 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;
1.21 xsa 256: char *bfpath, timebuf[64], thishost[MAXHOSTNAMELEN], wdir[MAXPATHLEN];
1.18 xsa 257:
258: if (cvs_noexec == 1)
259: return;
260:
1.24 xsa 261: cvs_log(LP_TRACE, "cvs_edit_local(%s)", cf->file_path);
262:
1.23 xsa 263: cvs_file_classify(cf, NULL, 0);
264:
1.18 xsa 265: if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
266: fatal("cvs_edit_local: fopen: `%s': %s",
267: CVS_PATH_NOTIFY, strerror(errno));
268:
269: (void)time(&now);
270: if ((t = gmtime(&now)) == NULL)
271: fatal("gmtime failed");
272:
1.20 xsa 273: asctime_r(t, timebuf);
274: if (timebuf[strlen(timebuf) - 1] == '\n')
275: timebuf[strlen(timebuf) - 1] = '\0';
1.18 xsa 276:
1.19 xsa 277: if (gethostname(thishost, sizeof(thishost)) == -1)
278: fatal("gethostname failed");
279:
1.21 xsa 280: if (getcwd(wdir, sizeof(wdir)) == NULL)
281: fatal("getcwd failed");
282:
1.25 xsa 283: (void)fprintf(fp, "E%s\t%s GMT\t%s\t%s\t",
1.21 xsa 284: cf->file_name, timebuf, thishost, wdir);
1.18 xsa 285:
286: if (edit_aflags & E_EDIT)
287: (void)fprintf(fp, "E");
288: if (edit_aflags & E_UNEDIT)
289: (void)fprintf(fp, "U");
290: if (edit_aflags & E_COMMIT)
291: (void)fprintf(fp, "C");
292:
293: (void)fprintf(fp, "\n");
294:
295: (void)fclose(fp);
296:
297: if (fchmod(cf->fd, 0644) == -1)
298: fatal("cvs_edit_local: fchmod %s", strerror(errno));
299:
300: bfpath = xmalloc(MAXPATHLEN);
301: if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath,
302: MAXPATHLEN) >= MAXPATHLEN)
303: fatal("cvs_edit_local: truncation");
1.22 xsa 304:
305: if (mkdir(CVS_PATH_BASEDIR, 0755) == -1 && errno != EEXIST)
306: fatal("cvs_edit_local: `%s': %s", CVS_PATH_BASEDIR,
307: strerror(errno));
1.18 xsa 308:
1.26 xsa 309: if (cvs_file_copy(cf->file_path, bfpath) == -1)
310: fatal("cvs_edit_local: cvs_file_copy failed");
1.18 xsa 311:
312: xfree(bfpath);
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.21 xsa 329: char *bfpath, timebuf[64], thishost[MAXHOSTNAMELEN], wdir[MAXPATHLEN];
1.24 xsa 330: RCSNUM *ba_rev;
1.15 xsa 331:
332: if (cvs_noexec == 1)
333: return;
334:
1.24 xsa 335: cvs_log(LP_TRACE, "cvs_unedit_local(%s)", cf->file_path);
336:
1.23 xsa 337: cvs_file_classify(cf, NULL, 0);
338:
1.15 xsa 339: bfpath = xmalloc(MAXPATHLEN);
340: if (cvs_path_cat(CVS_PATH_BASEDIR, cf->file_name, bfpath,
341: MAXPATHLEN) >= MAXPATHLEN)
342: fatal("cvs_unedit_local: truncation");
343:
344: if (stat(bfpath, &st) == -1) {
345: xfree(bfpath);
346: return;
347: }
348:
1.17 xsa 349: if (cvs_file_cmp(cf->file_path, bfpath) != 0) {
350: cvs_printf("%s has been modified; revert changes? ",
351: cf->file_name);
352:
353: if (cvs_yesno() == -1) {
354: xfree(bfpath);
355: return;
356: }
357: }
1.15 xsa 358:
359: cvs_rename(bfpath, cf->file_path);
360: xfree(bfpath);
361:
362: if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
363: fatal("cvs_unedit_local: fopen: `%s': %s",
364: CVS_PATH_NOTIFY, strerror(errno));
365:
366: (void)time(&now);
367: if ((t = gmtime(&now)) == NULL)
368: fatal("gmtime failed");
369:
1.20 xsa 370: asctime_r(t, timebuf);
371: if (timebuf[strlen(timebuf) - 1] == '\n')
372: timebuf[strlen(timebuf) - 1] = '\0';
1.15 xsa 373:
1.19 xsa 374: if (gethostname(thishost, sizeof(thishost)) == -1)
375: fatal("gethostname failed");
376:
1.21 xsa 377: if (getcwd(wdir, sizeof(wdir)) == NULL)
1.23 xsa 378: fatal("getcwd failed");
1.21 xsa 379:
1.15 xsa 380: (void)fprintf(fp, "U%s\t%s GMT\t%s\t%s\t\n",
1.21 xsa 381: cf->file_name, timebuf, thishost, wdir);
1.15 xsa 382:
383: (void)fclose(fp);
384:
1.29 xsa 385: if ((ba_rev = cvs_base_handle(cf, BASE_GET)) == NULL) {
386: cvs_log(LP_ERR, "%s not mentioned in %s",
387: cf->file_name, CVS_PATH_BASEREV);
388: return;
389: }
390:
1.24 xsa 391: if (cf->file_ent != NULL) {
1.29 xsa 392: CVSENTRIES *entlist;
393: struct cvs_ent *ent;
394: char *entry, rbuf[16];
395:
396: entlist = cvs_ent_open(cf->file_wd);
397:
398: if ((ent = cvs_ent_get(entlist, cf->file_name)) == NULL)
1.30 ! xsa 399: fatal("cvs_unedit_local: cvs_ent_get failed");
1.29 xsa 400:
401: (void)rcsnum_tostr(ba_rev, rbuf, sizeof(rbuf));
402:
403: memset(timebuf, 0, sizeof(timebuf));
404: ctime_r(&cf->file_ent->ce_mtime, timebuf);
405: if (timebuf[strlen(timebuf) - 1] == '\n')
406: timebuf[strlen(timebuf) - 1] = '\0';
407:
408: (void)xasprintf(&entry, "/%s/%s/%s/%s/%s",
409: cf->file_name, rbuf, timebuf,
410: (cf->file_ent->ce_tag) ? cf->file_ent->ce_tag : "",
411: (cf->file_ent->ce_opts) ? cf->file_ent->ce_opts : "");
412:
413: cvs_ent_add(entlist, entry);
414:
415: cvs_ent_free(ent);
416: cvs_ent_close(entlist, ENT_SYNC);
417:
418: xfree(entry);
1.24 xsa 419: }
1.29 xsa 420:
421: rcsnum_free(ba_rev);
1.24 xsa 422:
423: (void)cvs_base_handle(cf, BASE_REMOVE);
1.16 xsa 424:
425: if (fchmod(cf->fd, 0644) == -1)
426: fatal("cvs_unedit_local: fchmod %s", strerror(errno));
1.23 xsa 427: }
428:
429: static RCSNUM *
430: cvs_base_handle(struct cvs_file *cf, int flags)
431: {
432: FILE *fp, *tfp;
433: RCSNUM *ba_rev;
434: size_t len;
1.24 xsa 435: int i;
436: char *dp, *sp;
437: char buf[MAXPATHLEN], *fields[2], rbuf[16];
1.23 xsa 438:
439: cvs_log(LP_TRACE, "cvs_base_handle(%s)", cf->file_path);
440:
441: tfp = NULL;
442: ba_rev = NULL;
443:
1.24 xsa 444: if (((fp = fopen(CVS_PATH_BASEREV, "r")) == NULL) && errno != ENOENT) {
1.23 xsa 445: cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREV);
446: goto out;
447: }
448:
449: if (flags & (BASE_ADD|BASE_REMOVE)) {
450: if ((tfp = fopen(CVS_PATH_BASEREVTMP, "w")) == NULL) {
451: cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREVTMP);
452: goto out;
453: }
454: }
455:
1.24 xsa 456: if (fp != NULL) {
457: while(fgets(buf, sizeof(buf), fp)) {
458: len = strlen(buf);
459: if (len > 0 && buf[len - 1] == '\n')
460: buf[len - 1] = '\0';
461:
462: if (buf[0] != 'B')
463: continue;
464:
465: sp = buf + 1;
466: i = 0;
467: do {
468: if ((dp = strchr(sp, '/')) != NULL)
469: *(dp++) = '\0';
470: fields[i++] = sp;
471: sp = dp;
472: } while (dp != NULL && i < 2);
473:
474: if (cvs_file_cmpname(fields[0], cf->file_path) == 0) {
475: if (flags & BASE_GET) {
476: ba_rev = rcsnum_parse(fields[1]);
477: if (ba_rev == NULL)
478: fatal("cvs_base_handle: "
479: "rcsnum_parse");
480: goto got_rev;
481: }
482: } else {
483: if (flags & (BASE_ADD|BASE_REMOVE))
484: (void)fprintf(tfp, "%s\n", buf);
1.23 xsa 485: }
486: }
487: }
488:
489: got_rev:
490: if (flags & (BASE_ADD)) {
491: (void)rcsnum_tostr(cf->file_ent->ce_rev, rbuf, sizeof(rbuf));
492: (void)fprintf(tfp, "B%s/%s/\n", cf->file_path, rbuf);
493: }
494:
495: out:
496: if (fp != NULL)
497: (void)fclose(fp);
498:
499: if (tfp != NULL) {
500: (void)fclose(tfp);
501: (void)cvs_rename(CVS_PATH_BASEREVTMP, CVS_PATH_BASEREV);
502: }
503:
504: return (ba_rev);
1.1 jfb 505: }