Annotation of src/usr.bin/cvs/edit.c, Revision 1.45
1.45 ! deraadt 1: /* $OpenBSD: edit.c,v 1.44 2008/02/06 12:42:46 tobias 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 = {
1.42 tobias 45: CVS_OP_EDIT, CVS_USE_WDIR, "edit",
1.18 xsa 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.42 tobias 55: CVS_OP_EDITORS, CVS_USE_WDIR, "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 = {
1.42 tobias 65: CVS_OP_UNEDIT, CVS_USE_WDIR, "unedit",
1.15 xsa 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':
1.41 tobias 103: flags |= CR_RECURSE_DIRS;
1.18 xsa 104: break;
105: default:
106: fatal("%s", cvs_cmd_edit.cmd_synopsis);
107: }
108: }
109:
110: argc -= optind;
111: argv += optind;
112:
113: if (argc == 0)
114: fatal("%s", cvs_cmd_edit.cmd_synopsis);
115:
116: if (edit_aflags == 0)
117: edit_aflags |= E_ALL;
118:
119: cr.enterdir = NULL;
120: cr.leavedir = NULL;
121:
122: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.27 joris 123: cvs_client_connect_to_server();
1.18 xsa 124: cr.fileproc = cvs_client_sendfile;
125:
126: if (!(flags & CR_RECURSE_DIRS))
127: cvs_client_send_request("Argument -l");
128: } else {
129: cr.fileproc = cvs_edit_local;
130: }
131:
132: cr.flags = flags;
133:
134: cvs_file_run(argc, argv, &cr);
135:
136: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
137: cvs_client_send_files(argv, argc);
138: cvs_client_senddir(".");
139: cvs_client_send_request("edit");
140: cvs_client_get_responses();
141: }
142:
143: return (0);
144: }
145:
146: int
1.14 xsa 147: cvs_editors(int argc, char **argv)
1.1 jfb 148: {
1.12 xsa 149: int ch;
1.14 xsa 150: int flags;
151: struct cvs_recursion cr;
152:
153: flags = CR_RECURSE_DIRS;
1.1 jfb 154:
1.14 xsa 155: while ((ch = getopt(argc, argv, cvs_cmd_editors.cmd_opts)) != -1) {
1.1 jfb 156: switch (ch) {
157: case 'l':
1.14 xsa 158: flags &= ~CR_RECURSE_DIRS;
1.1 jfb 159: break;
160: case 'R':
1.41 tobias 161: flags |= CR_RECURSE_DIRS;
1.1 jfb 162: break;
163: default:
1.14 xsa 164: fatal("%s", cvs_cmd_editors.cmd_synopsis);
1.1 jfb 165: }
166: }
167:
1.14 xsa 168: argc -= optind;
169: argv += optind;
1.1 jfb 170:
1.14 xsa 171: if (argc == 0)
172: fatal("%s", cvs_cmd_editors.cmd_synopsis);
1.1 jfb 173:
1.14 xsa 174: cr.enterdir = NULL;
175: cr.leavedir = NULL;
1.1 jfb 176:
1.14 xsa 177: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.27 joris 178: cvs_client_connect_to_server();
1.14 xsa 179: cr.fileproc = cvs_client_sendfile;
180:
181: if (!(flags & CR_RECURSE_DIRS))
182: cvs_client_send_request("Argument -l");
183: } else {
184: cr.fileproc = cvs_editors_local;
185: }
1.1 jfb 186:
1.14 xsa 187: cr.flags = flags;
1.9 xsa 188:
1.14 xsa 189: cvs_file_run(argc, argv, &cr);
1.9 xsa 190:
1.14 xsa 191: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
192: cvs_client_send_files(argv, argc);
193: cvs_client_senddir(".");
194: cvs_client_send_request("editors");
195: cvs_client_get_responses();
1.9 xsa 196: }
197:
1.14 xsa 198: return (0);
199: }
1.9 xsa 200:
1.15 xsa 201: int
202: cvs_unedit(int argc, char **argv)
203: {
204: int ch;
205: int flags;
206: struct cvs_recursion cr;
207:
208: flags = CR_RECURSE_DIRS;
209:
210: while ((ch = getopt(argc, argv, cvs_cmd_unedit.cmd_opts)) != -1) {
211: switch (ch) {
212: case 'l':
213: flags &= ~CR_RECURSE_DIRS;
214: break;
215: case 'R':
1.41 tobias 216: flags |= CR_RECURSE_DIRS;
1.15 xsa 217: break;
218: default:
219: fatal("%s", cvs_cmd_unedit.cmd_synopsis);
220: }
221: }
222:
223: argc -= optind;
224: argv += optind;
225:
226: if (argc == 0)
227: fatal("%s", cvs_cmd_unedit.cmd_synopsis);
228:
229: cr.enterdir = NULL;
230: cr.leavedir = NULL;
231:
232: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
1.28 xsa 233: cvs_client_connect_to_server();
1.15 xsa 234: cr.fileproc = cvs_client_sendfile;
235:
236: if (!(flags & CR_RECURSE_DIRS))
237: cvs_client_send_request("Argument -l");
238: } else {
239: cr.fileproc = cvs_unedit_local;
240: }
241:
242: cr.flags = flags;
243:
244: cvs_file_run(argc, argv, &cr);
245:
246: if (current_cvsroot->cr_method != CVS_METHOD_LOCAL) {
247: cvs_client_send_files(argv, argc);
248: cvs_client_senddir(".");
249: cvs_client_send_request("unedit");
250: cvs_client_get_responses();
251: }
252:
253: return (0);
1.18 xsa 254: }
255:
256: static void
257: cvs_edit_local(struct cvs_file *cf)
258: {
259: FILE *fp;
260: struct tm *t;
261: time_t now;
1.36 xsa 262: char timebuf[CVS_TIME_BUFSZ], thishost[MAXHOSTNAMELEN];
263: char bfpath[MAXPATHLEN], wdir[MAXPATHLEN];
1.18 xsa 264:
265: if (cvs_noexec == 1)
266: return;
267:
1.24 xsa 268: cvs_log(LP_TRACE, "cvs_edit_local(%s)", cf->file_path);
269:
1.37 joris 270: cvs_file_classify(cf, cvs_directory_tag);
1.23 xsa 271:
1.18 xsa 272: if ((fp = fopen(CVS_PATH_NOTIFY, "a")) == NULL)
273: fatal("cvs_edit_local: fopen: `%s': %s",
274: CVS_PATH_NOTIFY, strerror(errno));
275:
276: (void)time(&now);
277: if ((t = gmtime(&now)) == NULL)
278: fatal("gmtime failed");
279:
1.20 xsa 280: asctime_r(t, timebuf);
1.40 tobias 281: timebuf[strcspn(timebuf, "\n")] = '\0';
1.18 xsa 282:
1.19 xsa 283: if (gethostname(thishost, sizeof(thishost)) == -1)
284: fatal("gethostname failed");
285:
1.21 xsa 286: if (getcwd(wdir, sizeof(wdir)) == NULL)
287: fatal("getcwd failed");
288:
1.25 xsa 289: (void)fprintf(fp, "E%s\t%s GMT\t%s\t%s\t",
1.21 xsa 290: cf->file_name, timebuf, thishost, wdir);
1.18 xsa 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:
1.33 xsa 306: (void)xsnprintf(bfpath, MAXPATHLEN, "%s/%s",
307: CVS_PATH_BASEDIR, cf->file_name);
1.22 xsa 308:
309: if (mkdir(CVS_PATH_BASEDIR, 0755) == -1 && errno != EEXIST)
310: fatal("cvs_edit_local: `%s': %s", CVS_PATH_BASEDIR,
311: strerror(errno));
1.18 xsa 312:
1.26 xsa 313: if (cvs_file_copy(cf->file_path, bfpath) == -1)
314: fatal("cvs_edit_local: cvs_file_copy failed");
1.18 xsa 315:
1.23 xsa 316: (void)cvs_base_handle(cf, BASE_ADD);
1.15 xsa 317: }
318:
1.14 xsa 319: static void
320: cvs_editors_local(struct cvs_file *cf)
321: {
1.15 xsa 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;
1.31 otto 331: char bfpath[MAXPATHLEN], timebuf[64], thishost[MAXHOSTNAMELEN];
1.44 tobias 332: char wdir[MAXPATHLEN], sticky[CVS_ENT_MAXLINELEN];
1.24 xsa 333: RCSNUM *ba_rev;
1.15 xsa 334:
335: if (cvs_noexec == 1)
336: return;
337:
1.24 xsa 338: cvs_log(LP_TRACE, "cvs_unedit_local(%s)", cf->file_path);
339:
1.38 joris 340: cvs_file_classify(cf, cvs_directory_tag);
1.23 xsa 341:
1.33 xsa 342: (void)xsnprintf(bfpath, MAXPATHLEN, "%s/%s",
343: CVS_PATH_BASEDIR, cf->file_name);
1.15 xsa 344:
1.31 otto 345: if (stat(bfpath, &st) == -1)
1.15 xsa 346: return;
347:
1.17 xsa 348: if (cvs_file_cmp(cf->file_path, bfpath) != 0) {
349: cvs_printf("%s has been modified; revert changes? ",
350: cf->file_name);
351:
1.31 otto 352: if (cvs_yesno() == -1)
1.17 xsa 353: return;
354: }
1.15 xsa 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: if ((t = gmtime(&now)) == NULL)
364: fatal("gmtime failed");
365:
1.20 xsa 366: asctime_r(t, timebuf);
1.40 tobias 367: timebuf[strcspn(timebuf, "\n")] = '\0';
1.15 xsa 368:
1.19 xsa 369: if (gethostname(thishost, sizeof(thishost)) == -1)
370: fatal("gethostname failed");
371:
1.21 xsa 372: if (getcwd(wdir, sizeof(wdir)) == NULL)
1.23 xsa 373: fatal("getcwd failed");
1.21 xsa 374:
1.15 xsa 375: (void)fprintf(fp, "U%s\t%s GMT\t%s\t%s\t\n",
1.21 xsa 376: cf->file_name, timebuf, thishost, wdir);
1.15 xsa 377:
378: (void)fclose(fp);
379:
1.29 xsa 380: if ((ba_rev = cvs_base_handle(cf, BASE_GET)) == NULL) {
381: cvs_log(LP_ERR, "%s not mentioned in %s",
382: cf->file_name, CVS_PATH_BASEREV);
383: return;
384: }
385:
1.24 xsa 386: if (cf->file_ent != NULL) {
1.29 xsa 387: CVSENTRIES *entlist;
388: struct cvs_ent *ent;
1.35 xsa 389: char *entry, rbuf[CVS_REV_BUFSZ];
1.29 xsa 390:
391: entlist = cvs_ent_open(cf->file_wd);
392:
393: if ((ent = cvs_ent_get(entlist, cf->file_name)) == NULL)
1.30 xsa 394: fatal("cvs_unedit_local: cvs_ent_get failed");
1.29 xsa 395:
396: (void)rcsnum_tostr(ba_rev, rbuf, sizeof(rbuf));
397:
398: memset(timebuf, 0, sizeof(timebuf));
399: ctime_r(&cf->file_ent->ce_mtime, timebuf);
1.40 tobias 400: timebuf[strcspn(timebuf, "\n")] = '\0';
1.29 xsa 401:
1.44 tobias 402: sticky[0] = '\0';
403: if (cf->file_ent->ce_tag != NULL)
404: (void)xsnprintf(sticky, sizeof(sticky), "T%s",
405: cf->file_ent->ce_tag);
406:
1.29 xsa 407: (void)xasprintf(&entry, "/%s/%s/%s/%s/%s",
1.43 tobias 408: cf->file_name, rbuf, timebuf, cf->file_ent->ce_opts ? : "",
1.44 tobias 409: sticky);
1.29 xsa 410:
411: cvs_ent_add(entlist, entry);
412:
413: cvs_ent_free(ent);
414: cvs_ent_close(entlist, ENT_SYNC);
415:
416: xfree(entry);
1.24 xsa 417: }
1.29 xsa 418:
419: rcsnum_free(ba_rev);
1.24 xsa 420:
421: (void)cvs_base_handle(cf, BASE_REMOVE);
1.16 xsa 422:
423: if (fchmod(cf->fd, 0644) == -1)
424: fatal("cvs_unedit_local: fchmod %s", strerror(errno));
1.23 xsa 425: }
426:
427: static RCSNUM *
428: cvs_base_handle(struct cvs_file *cf, int flags)
429: {
430: FILE *fp, *tfp;
431: RCSNUM *ba_rev;
1.24 xsa 432: int i;
433: char *dp, *sp;
1.35 xsa 434: char buf[MAXPATHLEN], *fields[2], rbuf[CVS_REV_BUFSZ];
1.23 xsa 435:
436: cvs_log(LP_TRACE, "cvs_base_handle(%s)", cf->file_path);
437:
438: tfp = NULL;
439: ba_rev = NULL;
440:
1.24 xsa 441: if (((fp = fopen(CVS_PATH_BASEREV, "r")) == NULL) && errno != ENOENT) {
1.23 xsa 442: cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREV);
443: goto out;
444: }
445:
446: if (flags & (BASE_ADD|BASE_REMOVE)) {
447: if ((tfp = fopen(CVS_PATH_BASEREVTMP, "w")) == NULL) {
448: cvs_log(LP_ERRNO, "%s", CVS_PATH_BASEREVTMP);
449: goto out;
450: }
451: }
452:
1.24 xsa 453: if (fp != NULL) {
1.45 ! deraadt 454: while (fgets(buf, sizeof(buf), fp)) {
1.39 gilles 455: buf[strcspn(buf, "\n")] = '\0';
1.24 xsa 456:
457: if (buf[0] != 'B')
458: continue;
459:
460: sp = buf + 1;
461: i = 0;
462: do {
463: if ((dp = strchr(sp, '/')) != NULL)
464: *(dp++) = '\0';
465: fields[i++] = sp;
466: sp = dp;
467: } while (dp != NULL && i < 2);
468:
469: if (cvs_file_cmpname(fields[0], cf->file_path) == 0) {
470: if (flags & BASE_GET) {
471: ba_rev = rcsnum_parse(fields[1]);
472: if (ba_rev == NULL)
473: fatal("cvs_base_handle: "
474: "rcsnum_parse");
475: goto got_rev;
476: }
477: } else {
478: if (flags & (BASE_ADD|BASE_REMOVE))
479: (void)fprintf(tfp, "%s\n", buf);
1.23 xsa 480: }
481: }
482: }
483:
484: got_rev:
485: if (flags & (BASE_ADD)) {
486: (void)rcsnum_tostr(cf->file_ent->ce_rev, rbuf, sizeof(rbuf));
487: (void)fprintf(tfp, "B%s/%s/\n", cf->file_path, rbuf);
488: }
489:
490: out:
491: if (fp != NULL)
492: (void)fclose(fp);
493:
494: if (tfp != NULL) {
495: (void)fclose(tfp);
496: (void)cvs_rename(CVS_PATH_BASEREVTMP, CVS_PATH_BASEREV);
497: }
498:
499: return (ba_rev);
1.1 jfb 500: }