Annotation of src/usr.bin/rcs/ci.c, Revision 1.92
1.92 ! xsa 1: /* $OpenBSD: ci.c,v 1.91 2006/01/02 08:13:28 xsa Exp $ */
1.1 niallo 2: /*
3: * Copyright (c) 2005 Niall O'Higgins <niallo@openbsd.org>
4: * All rights reserved.
5: *
6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following cinditions
8: * are met:
9: *
10: * 1. Redistributions of source cide must retain the above cipyright
11: * notice, this list of cinditions and the following disclaimer.
12: * 2. The name of the author may not be used to endorse or promote products
13: * derived from this software without specific prior written permission.
14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25: */
26:
1.91 xsa 27: #include "includes.h"
1.1 niallo 28:
1.92 ! xsa 29: #include "rcsprog.h"
1.4 niallo 30: #include "diff.h"
1.9 niallo 31:
1.80 niallo 32: #define CI_OPTSTRING "d::f::i::j::k:l::m:M::N:n:qr::s:Tt:u::Vw:x:"
1.25 niallo 33: #define DATE_NOW -1
34: #define DATE_MTIME -2
35:
1.58 niallo 36: #define LOG_INIT "Initial revision"
1.63 niallo 37: #define LOG_PROMPT "enter log message, terminated with a single '.' " \
1.58 niallo 38: "or end of file:\n>> "
1.63 niallo 39: #define DESC_PROMPT "enter description, terminated with single '.' " \
40: "or end of file:\nNOTE: This is NOT the log message!" \
41: "\n>> "
1.58 niallo 42:
43: struct checkin_params {
44: int flags, openflags;
45: mode_t fmode;
46: time_t date;
47: RCSFILE *file;
48: RCSNUM *frev, *newrev;
49: char fpath[MAXPATHLEN], *rcs_msg, *username, *deltatext, *filename;
1.82 joris 50: char *author;
1.80 niallo 51: const char *symbol, *state, *description;
1.58 niallo 52: };
53:
1.68 xsa 54: static int checkin_attach_symbol(struct checkin_params *pb);
55: static int checkin_checklock(struct checkin_params *pb);
56: static char *checkin_choose_rcsfile(const char *);
57: static char *checkin_diff_file(struct checkin_params *);
58: static char *checkin_getdesc(void);
59: static char *checkin_getinput(const char *);
60: static char *checkin_getlogmsg(RCSNUM *, RCSNUM *);
61: static int checkin_init(struct checkin_params *);
62: static int checkin_mtimedate(struct checkin_params *pb);
63: static int checkin_update(struct checkin_params *pb);
64: static void checkin_revert(struct checkin_params *pb);
1.4 niallo 65:
1.1 niallo 66: void
67: checkin_usage(void)
68: {
69: fprintf(stderr,
1.67 xsa 70: "usage: ci [-MNqTV] [-d[date]] [-f[rev]] [-i[rev]] [-j[rev]]\n"
1.74 xsa 71: " [-kmode] [-l[rev]] [-M[rev]] [-mmsg] [-Nsymbol]\n"
1.85 xsa 72: " [-nsymbol] [-r[rev]] [-sstate] [-tfile|str] [-u[rev]]\n"
1.81 niallo 73: " [-wusername] [-xsuffixes] file ...\n");
1.58 niallo 74: }
75:
1.55 niallo 76:
1.1 niallo 77:
78: /*
79: * checkin_main()
80: *
81: * Handler for the `ci' program.
82: * Returns 0 on success, or >0 on error.
83: */
84: int
85: checkin_main(int argc, char **argv)
86: {
1.58 niallo 87: int i, ch, status;
88: struct checkin_params pb;
1.1 niallo 89:
1.58 niallo 90: pb.date = DATE_NOW;
91: pb.file = NULL;
1.82 joris 92: pb.rcs_msg = pb.username = pb.author = NULL;
1.80 niallo 93: pb.state = pb.symbol = pb.description = NULL;
1.58 niallo 94: pb.newrev = NULL;
95: pb.fmode = pb.flags = status = 0;
1.1 niallo 96:
1.58 niallo 97: pb.flags = INTERACTIVE;
1.90 niallo 98: pb.openflags = RCS_RDWR|RCS_CREATE|RCS_PARSE_FULLY;
1.9 niallo 99:
1.58 niallo 100: while ((ch = rcs_getopt(argc, argv, CI_OPTSTRING)) != -1) {
1.1 niallo 101: switch (ch) {
1.20 niallo 102: case 'd':
1.25 niallo 103: if (rcs_optarg == NULL)
1.58 niallo 104: pb.date = DATE_MTIME;
1.87 xsa 105: else if ((pb.date = cvs_date_parse(rcs_optarg)) <= 0)
106: fatal("invalid date");
1.20 niallo 107: break;
1.29 niallo 108: case 'f':
1.58 niallo 109: rcs_set_rev(rcs_optarg, &pb.newrev);
110: pb.flags |= FORCE;
1.29 niallo 111: break;
1.1 niallo 112: case 'h':
113: (usage)();
114: exit(0);
1.58 niallo 115: case 'i':
116: rcs_set_rev(rcs_optarg, &pb.newrev);
117: pb.openflags |= RCS_CREATE;
1.66 niallo 118: pb.flags |= CI_INIT;
1.58 niallo 119: break;
120: case 'j':
121: rcs_set_rev(rcs_optarg, &pb.newrev);
122: pb.openflags &= ~RCS_CREATE;
1.66 niallo 123: pb.flags &= ~CI_INIT;
1.58 niallo 124: break;
1.30 niallo 125: case 'l':
1.58 niallo 126: rcs_set_rev(rcs_optarg, &pb.newrev);
127: pb.flags |= CO_LOCK;
1.54 niallo 128: break;
129: case 'M':
1.58 niallo 130: rcs_set_rev(rcs_optarg, &pb.newrev);
131: pb.flags |= CO_REVDATE;
1.30 niallo 132: break;
1.1 niallo 133: case 'm':
1.58 niallo 134: pb.rcs_msg = rcs_optarg;
1.87 xsa 135: if (pb.rcs_msg == NULL)
136: fatal("missing message for -m option");
1.58 niallo 137: pb.flags &= ~INTERACTIVE;
1.3 joris 138: break;
1.42 niallo 139: case 'N':
1.84 joris 140: pb.symbol = xstrdup(rcs_optarg);
1.87 xsa 141: if (rcs_sym_check(pb.symbol) != 1)
142: fatal("invalid symbol `%s'", pb.symbol);
1.58 niallo 143: pb.flags |= CI_SYMFORCE;
1.42 niallo 144: break;
1.38 niallo 145: case 'n':
1.84 joris 146: pb.symbol = xstrdup(rcs_optarg);
1.87 xsa 147: if (rcs_sym_check(pb.symbol) != 1)
148: fatal("invalid symbol `%s'", pb.symbol);
1.38 niallo 149: break;
1.3 joris 150: case 'q':
151: verbose = 0;
1.1 niallo 152: break;
1.30 niallo 153: case 'r':
1.58 niallo 154: rcs_set_rev(rcs_optarg, &pb.newrev);
155: pb.flags |= CI_DEFAULT;
1.9 niallo 156: break;
1.51 niallo 157: case 's':
1.58 niallo 158: pb.state = rcs_optarg;
1.87 xsa 159: if (rcs_state_check(pb.state) < 0)
160: fatal("invalid state `%s'", pb.state);
1.67 xsa 161: break;
162: case 'T':
163: pb.flags |= PRESERVETIME;
1.51 niallo 164: break;
1.80 niallo 165: case 't':
1.84 joris 166: pb.description = xstrdup(rcs_optarg);
1.80 niallo 167: break;
1.9 niallo 168: case 'u':
1.58 niallo 169: rcs_set_rev(rcs_optarg, &pb.newrev);
170: pb.flags |= CO_UNLOCK;
1.9 niallo 171: break;
1.30 niallo 172: case 'V':
173: printf("%s\n", rcs_version);
174: exit(0);
1.31 niallo 175: case 'w':
1.84 joris 176: pb.author = xstrdup(rcs_optarg);
1.64 xsa 177: break;
178: case 'x':
179: rcs_suffixes = rcs_optarg;
1.31 niallo 180: break;
1.1 niallo 181: default:
182: (usage)();
183: exit(1);
184: }
185: }
186:
1.24 joris 187: argc -= rcs_optind;
188: argv += rcs_optind;
189:
1.1 niallo 190: if (argc == 0) {
191: cvs_log(LP_ERR, "no input file");
192: (usage)();
193: exit(1);
194: }
195:
1.86 xsa 196: if ((pb.username = getlogin()) == NULL)
197: fatal("getlogin failed");
1.31 niallo 198:
199:
1.1 niallo 200: for (i = 0; i < argc; i++) {
1.58 niallo 201: pb.filename = argv[i];
1.1 niallo 202:
1.58 niallo 203: /*
204: * Test for existence of ,v file. If we are expected to
205: * create one, set NEWFILE flag.
206: */
1.71 niallo 207: if (rcs_statfile(pb.filename, pb.fpath, sizeof(pb.fpath)) < 0) {
208: if (pb.openflags & RCS_CREATE)
209: pb.flags |= NEWFILE;
210: else {
211: cvs_log(LP_ERR, "No existing RCS file");
212: status = 1;
213: continue;
214: }
1.66 niallo 215: } else {
216: if (pb.flags & CI_INIT) {
217: cvs_log(LP_ERR, "%s already exists", pb.fpath);
218: status = 1;
219: continue;
220: }
1.58 niallo 221: pb.openflags &= ~RCS_CREATE;
1.66 niallo 222: }
1.63 niallo 223: /*
224: * If we are to create a new ,v file, we must decide where it
225: * should go.
226: */
227: if (pb.flags & NEWFILE) {
228: char *fpath = checkin_choose_rcsfile(pb.filename);
229: if (fpath == NULL) {
230: status = 1;
231: continue;
232: }
233: strlcpy(pb.fpath, fpath, sizeof(pb.fpath));
1.84 joris 234: xfree(fpath);
1.63 niallo 235: }
236:
1.58 niallo 237: pb.file = rcs_open(pb.fpath, pb.openflags, pb.fmode);
238:
1.87 xsa 239: if (pb.file == NULL)
240: fatal("failed to open rcsfile '%s'", pb.fpath);
1.29 niallo 241:
1.58 niallo 242: if (verbose == 1)
243: printf("%s <-- %s\n", pb.fpath, pb.filename);
1.61 niallo 244:
1.63 niallo 245: if (pb.flags & NEWFILE)
246: status = checkin_init(&pb);
1.15 niallo 247: else
1.63 niallo 248: status = checkin_update(&pb);
1.4 niallo 249: }
250:
1.16 niallo 251: return (status);
1.4 niallo 252: }
253:
1.59 niallo 254: /*
255: * checkin_diff_file()
256: *
1.65 xsa 257: * Generate the diff between the working file and a revision.
1.59 niallo 258: * Returns pointer to a char array on success, NULL on failure.
259: */
1.4 niallo 260: static char *
1.58 niallo 261: checkin_diff_file(struct checkin_params *pb)
1.4 niallo 262: {
263: char path1[MAXPATHLEN], path2[MAXPATHLEN];
264: BUF *b1, *b2, *b3;
265: char rbuf[64], *deltatext;
266:
1.58 niallo 267: rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
1.4 niallo 268:
1.58 niallo 269: if ((b1 = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
270: cvs_log(LP_ERR, "failed to load file: '%s'", pb->filename);
1.4 niallo 271: return (NULL);
1.1 niallo 272: }
273:
1.58 niallo 274: if ((b2 = rcs_getrev(pb->file, pb->frev)) == NULL) {
1.4 niallo 275: cvs_log(LP_ERR, "failed to load revision");
276: cvs_buf_free(b1);
277: return (NULL);
278: }
279:
1.57 xsa 280: if ((b3 = cvs_buf_alloc((size_t)128, BUF_AUTOEXT)) == NULL) {
1.4 niallo 281: cvs_log(LP_ERR, "failed to allocated buffer for diff");
282: cvs_buf_free(b1);
283: cvs_buf_free(b2);
284: return (NULL);
285: }
286:
1.50 xsa 287: strlcpy(path1, rcs_tmpdir, sizeof(path1));
288: strlcat(path1, "/diff1.XXXXXXXXXX", sizeof(path1));
1.4 niallo 289: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
290: cvs_log(LP_ERRNO, "could not write temporary file");
291: cvs_buf_free(b1);
292: cvs_buf_free(b2);
293: return (NULL);
294: }
295: cvs_buf_free(b1);
296:
1.50 xsa 297: strlcpy(path2, rcs_tmpdir, sizeof(path2));
298: strlcat(path2, "/diff2.XXXXXXXXXX", sizeof(path2));
1.4 niallo 299: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
300: cvs_buf_free(b2);
301: (void)unlink(path1);
302: return (NULL);
303: }
304: cvs_buf_free(b2);
305:
1.5 niallo 306: diff_format = D_RCSDIFF;
1.4 niallo 307: cvs_diffreg(path1, path2, b3);
308: (void)unlink(path1);
309: (void)unlink(path2);
310:
311: cvs_buf_putc(b3, '\0');
312: deltatext = (char *)cvs_buf_release(b3);
313:
314: return (deltatext);
1.6 niallo 315: }
316:
317: /*
1.65 xsa 318: * checkin_getlogmsg()
1.59 niallo 319: *
1.6 niallo 320: * Get log message from user interactively.
1.59 niallo 321: * Returns pointer to a char array on success, NULL on failure.
1.6 niallo 322: */
323: static char *
1.34 niallo 324: checkin_getlogmsg(RCSNUM *rev, RCSNUM *rev2)
1.6 niallo 325: {
1.58 niallo 326: char *rcs_msg, nrev[16], prev[16];
1.6 niallo 327: RCSNUM *tmprev;
328:
1.7 niallo 329: rcs_msg = NULL;
1.6 niallo 330: tmprev = rcsnum_alloc();
331: rcsnum_cpy(rev, tmprev, 16);
1.9 niallo 332: rcsnum_tostr(tmprev, prev, sizeof(prev));
1.12 niallo 333: if (rev2 == NULL)
334: rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
335: else
336: rcsnum_tostr(rev2, nrev, sizeof(nrev));
1.6 niallo 337: rcsnum_free(tmprev);
338:
1.47 niallo 339: if (verbose == 1)
340: printf("new revision: %s; previous revision: %s\n", nrev,
341: prev);
1.32 joris 342:
1.58 niallo 343: rcs_msg = checkin_getinput(LOG_PROMPT);
344: return (rcs_msg);
345: }
346:
347:
348: /*
349: * checkin_getdesc()
350: *
351: * Get file description interactively.
1.59 niallo 352: * Returns pointer to a char array on success, NULL on failure.
1.58 niallo 353: */
354: static char *
355: checkin_getdesc()
356: {
357: char *description;
358:
359: description = checkin_getinput(DESC_PROMPT);
360: return (description);
361: }
362:
363: /*
364: * checkin_getinput()
365: *
1.59 niallo 366: * Get some input from the user, in RCS style, prompting with message <prompt>.
367: * Returns pointer to a char array on success, NULL on failure.
1.58 niallo 368: */
369: static char *
370: checkin_getinput(const char *prompt)
371: {
372: BUF *inputbuf;
373: char *input, buf[128];
374:
375: if ((inputbuf = cvs_buf_alloc((size_t)64, BUF_AUTOEXT)) == NULL) {
376: cvs_log(LP_ERR, "failed to allocate input buffer");
377: return (NULL);
378: }
379:
380: printf(prompt);
1.6 niallo 381: for (;;) {
382: fgets(buf, (int)sizeof(buf), stdin);
1.9 niallo 383: if (feof(stdin) || ferror(stdin) || buf[0] == '.')
1.6 niallo 384: break;
1.58 niallo 385: cvs_buf_append(inputbuf, buf, strlen(buf));
1.46 joris 386: printf(">> ");
1.6 niallo 387: }
1.32 joris 388:
1.58 niallo 389: cvs_buf_putc(inputbuf, '\0');
390: input = (char *)cvs_buf_release(inputbuf);
391:
392: return (input);
393: }
394:
395: /*
1.63 niallo 396: * checkin_update()
397: *
398: * Do a checkin to an existing RCS file.
399: *
400: * On success, return 0. On error return -1.
401: */
402: static int
403: checkin_update(struct checkin_params *pb)
404: {
1.77 xsa 405: char *filec, numb1[64], numb2[64];
1.63 niallo 406: BUF *bp;
407:
1.82 joris 408: /*
409: * XXX this is wrong, we need to get the revision the user
1.85 xsa 410: * has the lock for. So we can decide if we want to create a
1.82 joris 411: * branch or not. (if it's not current HEAD we need to branch).
412: */
1.63 niallo 413: pb->frev = pb->file->rf_head;
414:
1.82 joris 415: if (checkin_checklock(pb) < 0)
416: return (-1);
417:
418: /* If revision passed on command line is less than HEAD, bail.
419: * XXX only applies to ci -r1.2 foo for example if HEAD is > 1.2 and
420: * there is no lock set for the user.
421: */
1.63 niallo 422: if ((pb->newrev != NULL)
423: && (rcsnum_cmp(pb->newrev, pb->frev, 0) > 0)) {
1.77 xsa 424: cvs_log(LP_ERR,
425: "%s: revision %s too low; must be higher than %s",
426: pb->file->rf_path,
427: rcsnum_tostr(pb->newrev, numb1, sizeof(numb1)),
428: rcsnum_tostr(pb->frev, numb2, sizeof(numb2)));
1.63 niallo 429: rcs_close(pb->file);
430: return (-1);
431: }
432:
1.73 xsa 433: /* Load file contents */
1.63 niallo 434: if ((bp = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
435: cvs_log(LP_ERR, "failed to load '%s'", pb->filename);
436: return (-1);
437: }
438:
439: if (cvs_buf_putc(bp, '\0') < 0)
440: return (-1);
441:
442: filec = (char *)cvs_buf_release(bp);
443:
1.73 xsa 444: /* Get RCS patch */
1.63 niallo 445: if ((pb->deltatext = checkin_diff_file(pb)) == NULL) {
446: cvs_log(LP_ERR, "failed to get diff");
447: return (-1);
448: }
449:
450: /*
451: * If -f is not specified and there are no differences, tell
452: * the user and revert to latest version.
453: */
454: if (!(pb->flags & FORCE) && (strlen(pb->deltatext) < 1)) {
455: checkin_revert(pb);
456: return (0);
457: }
458:
1.73 xsa 459: /* If no log message specified, get it interactively. */
1.63 niallo 460: if (pb->flags & INTERACTIVE)
461: pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev);
462:
1.82 joris 463: if (rcs_lock_remove(pb->file, pb->username, pb->frev) < 0) {
1.63 niallo 464: if (rcs_errno != RCS_ERR_NOENT)
1.82 joris 465: cvs_log(LP_WARN, "failed to remove lock");
466: else if (!(pb->flags & CO_LOCK))
467: cvs_log(LP_WARN, "previous revision was not locked; "
468: "ignoring -l option");
1.63 niallo 469: }
470:
1.73 xsa 471: /* Current head revision gets the RCS patch as rd_text */
1.87 xsa 472: if (rcs_deltatext_set(pb->file, pb->frev, pb->deltatext) == -1)
473: fatal("failed to set new rd_text for head rev");
1.63 niallo 474:
475: /*
476: * Set the date of the revision to be the last modification
477: * time of the working file if -d has no argument.
478: */
479: if (pb->date == DATE_MTIME
480: && (checkin_mtimedate(pb) < 0))
481: return (-1);
482:
1.73 xsa 483: /* Now add our new revision */
1.63 niallo 484: if (rcs_rev_add(pb->file,
485: (pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev),
1.82 joris 486: pb->rcs_msg, pb->date, pb->author) != 0) {
1.63 niallo 487: cvs_log(LP_ERR, "failed to add new revision");
488: return (-1);
489: }
490:
491: /*
492: * If we are checking in to a non-default (ie user-specified)
493: * revision, set head to this revision.
494: */
495: if (pb->newrev != NULL)
496: rcs_head_set(pb->file, pb->newrev);
497: else
498: pb->newrev = pb->file->rf_head;
499:
1.73 xsa 500: /* New head revision has to contain entire file; */
1.87 xsa 501: if (rcs_deltatext_set(pb->file, pb->frev, filec) == -1)
502: fatal("failed to set new head revision");
1.63 niallo 503:
1.73 xsa 504: /* Attach a symbolic name to this revision if specified. */
1.63 niallo 505: if (pb->symbol != NULL
506: && (checkin_attach_symbol(pb) < 0))
507: return (-1);
508:
1.73 xsa 509: /* Set the state of this revision if specified. */
1.63 niallo 510: if (pb->state != NULL)
511: (void)rcs_state_set(pb->file, pb->newrev, pb->state);
512:
1.84 joris 513: xfree(pb->deltatext);
514: xfree(filec);
1.63 niallo 515: (void)unlink(pb->filename);
516:
1.73 xsa 517: /* Do checkout if -u or -l are specified. */
1.63 niallo 518: if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
519: && !(pb->flags & CI_DEFAULT))
520: checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags,
1.89 joris 521: pb->username, pb->author, NULL, NULL);
1.63 niallo 522:
523: /* File will NOW be synced */
524: rcs_close(pb->file);
525:
526: if (pb->flags & INTERACTIVE) {
1.84 joris 527: xfree(pb->rcs_msg);
1.63 niallo 528: pb->rcs_msg = NULL;
529: }
530: return (0);
531: }
532:
533: /*
1.58 niallo 534: * checkin_init()
1.65 xsa 535: *
1.58 niallo 536: * Does an initial check in, just enough to create the new ,v file
1.63 niallo 537: * On success, return 0. On error return -1.
1.58 niallo 538: */
1.63 niallo 539: static int
1.58 niallo 540: checkin_init(struct checkin_params *pb)
541: {
1.80 niallo 542: BUF *bp, *dp;
543: char *filec;
544: const char *rcs_desc;
1.58 niallo 545:
1.73 xsa 546: /* Load file contents */
1.58 niallo 547: if ((bp = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
548: cvs_log(LP_ERR, "failed to load '%s'", pb->filename);
1.63 niallo 549: return (-1);
1.58 niallo 550: }
551:
552: if (cvs_buf_putc(bp, '\0') < 0)
1.63 niallo 553: return (-1);
1.58 niallo 554:
555: filec = (char *)cvs_buf_release(bp);
556:
1.73 xsa 557: /* Get description from user */
1.80 niallo 558: if (pb->description == NULL)
559: rcs_desc = (const char *)checkin_getdesc();
560: else {
561: if (*pb->description == '-') {
562: pb->description++;
1.81 niallo 563: rcs_desc = (const char *)pb->description;
564: } else {
1.80 niallo 565: dp = cvs_buf_load(pb->description, BUF_AUTOEXT);
566: if (dp == NULL) {
567: cvs_log(LP_ERR,
568: "failed to load description file '%s'",
569: pb->description);
1.84 joris 570: xfree(filec);
1.80 niallo 571: return (-1);
572: }
573: if (cvs_buf_putc(dp, '\0') < 0) {
1.84 joris 574: xfree(filec);
1.80 niallo 575: return (-1);
576: }
577: rcs_desc = (const char *)cvs_buf_release(dp);
578: }
579: }
1.58 niallo 580: rcs_desc_set(pb->file, rcs_desc);
581:
1.73 xsa 582: /* Now add our new revision */
1.82 joris 583: if (rcs_rev_add(pb->file, RCS_HEAD_REV, LOG_INIT, -1, pb->author) != 0) {
1.58 niallo 584: cvs_log(LP_ERR, "failed to add new revision");
1.63 niallo 585: return (-1);
586: }
587: /*
588: * If we are checking in to a non-default (ie user-specified)
589: * revision, set head to this revision.
590: */
591: if (pb->newrev != NULL)
592: rcs_head_set(pb->file, pb->newrev);
593: else
594: pb->newrev = pb->file->rf_head;
595:
1.73 xsa 596: /* New head revision has to contain entire file; */
1.66 niallo 597: if (rcs_deltatext_set(pb->file, pb->file->rf_head, filec) == -1) {
1.63 niallo 598: cvs_log(LP_ERR, "failed to set new head revision");
599: return (-1);
1.58 niallo 600: }
1.73 xsa 601: /* Attach a symbolic name to this revision if specified. */
1.66 niallo 602: if (pb->symbol != NULL
603: && (checkin_attach_symbol(pb) < 0))
604: return (-1);
605:
1.73 xsa 606: /* Set the state of this revision if specified. */
1.66 niallo 607: if (pb->state != NULL)
608: (void)rcs_state_set(pb->file, pb->newrev, pb->state);
609:
1.84 joris 610: xfree(filec);
1.66 niallo 611: (void)unlink(pb->filename);
612:
1.73 xsa 613: /* Do checkout if -u or -l are specified. */
1.66 niallo 614: if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
615: && !(pb->flags & CI_DEFAULT))
616: checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags,
1.89 joris 617: pb->username, pb->author, NULL, NULL);
1.63 niallo 618:
1.66 niallo 619: /* File will NOW be synced */
620: rcs_close(pb->file);
1.63 niallo 621: return (0);
1.58 niallo 622: }
623:
1.59 niallo 624: /*
625: * checkin_attach_symbol()
626: *
627: * Attempt to attach the specified symbol to the revision.
628: * On success, return 0. On error return -1.
629: */
1.58 niallo 630: static int
631: checkin_attach_symbol(struct checkin_params *pb)
632: {
633: char rbuf[16];
634: int ret;
635: if (verbose == 1)
636: printf("symbol: %s\n", pb->symbol);
637: if (pb->flags & CI_SYMFORCE)
638: rcs_sym_remove(pb->file, pb->symbol);
639: if ((ret = rcs_sym_add(pb->file, pb->symbol, pb->newrev) == -1)
640: && (rcs_errno == RCS_ERR_DUPENT)) {
641: rcsnum_tostr(rcs_sym_getrev(pb->file, pb->symbol),
642: rbuf, sizeof(rbuf));
643: cvs_log(LP_ERR,
644: "symbolic name %s already bound to %s",
645: pb->symbol, rbuf);
646: rcs_close(pb->file);
647: return (-1);
648: } else if (ret == -1) {
649: cvs_log(LP_ERR, "problem adding symbol: %s",
650: pb->symbol);
651: rcs_close(pb->file);
652: return (-1);
653: }
654: return (0);
655: }
656:
1.59 niallo 657: /*
658: * checkin_revert()
659: *
660: * If there are no differences between the working file and the latest revision
661: * and the -f flag is not specified, simply revert to the latest version and
662: * warn the user.
663: *
664: */
1.58 niallo 665: static void
666: checkin_revert(struct checkin_params *pb)
667: {
668: char rbuf[16];
669:
670: rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
671: cvs_log(LP_WARN,
672: "file is unchanged; reverting to previous revision %s",
673: rbuf);
674: (void)unlink(pb->filename);
675: if ((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
676: checkout_rev(pb->file, pb->frev, pb->filename,
1.89 joris 677: pb->flags, pb->username, pb->author, NULL, NULL);
1.82 joris 678: rcs_lock_remove(pb->file, pb->username, pb->frev);
1.58 niallo 679: rcs_close(pb->file);
680: if (verbose == 1)
681: printf("done\n");
682: }
683:
1.59 niallo 684: /*
685: * checkin_checklock()
686: *
687: * Check for the existence of a lock on the file. If there are no locks, or it
688: * is not locked by the correct user, return -1. Otherwise, return 0.
689: */
1.58 niallo 690: static int
691: checkin_checklock(struct checkin_params *pb)
692: {
693: struct rcs_lock *lkp;
694:
1.82 joris 695: TAILQ_FOREACH(lkp, &(pb->file->rf_locks), rl_list) {
696: if ((!strcmp(lkp->rl_name, pb->username)) &&
697: (!rcsnum_cmp(lkp->rl_num, pb->frev, 0)))
698: return (0);
1.58 niallo 699: }
1.32 joris 700:
1.82 joris 701: cvs_log(LP_ERR,
702: "%s: no lock set by %s", pb->file->rf_path, pb->username);
703: rcs_close(pb->file);
704: return (-1);
1.61 niallo 705: }
706:
707: /*
1.62 niallo 708: * checkin_mtimedate()
1.61 niallo 709: *
710: * Set the date of the revision to be the last modification
1.62 niallo 711: * time of the working file.
1.61 niallo 712: *
713: * On success, return 0. On error return -1.
714: */
715: static int
1.62 niallo 716: checkin_mtimedate(struct checkin_params *pb)
1.61 niallo 717: {
718: struct stat sb;
719: if (stat(pb->filename, &sb) != 0) {
1.72 xsa 720: cvs_log(LP_ERRNO, "failed to stat `%s'", pb->filename);
1.61 niallo 721: rcs_close(pb->file);
722: return (-1);
723: }
724: pb->date = (time_t)sb.st_mtimespec.tv_sec;
1.58 niallo 725: return (0);
1.63 niallo 726: }
727:
728: /*
1.75 niallo 729: * checkin_choose_rcsfile()
1.63 niallo 730: *
731: * Given a relative filename, decide where the corresponding ,v file
1.65 xsa 732: * should be.
1.63 niallo 733: *
734: * Returns pointer to a char array on success, NULL on failure.
735: */
736: static char *
737: checkin_choose_rcsfile(const char *filename)
738: {
1.76 niallo 739: char name[MAXPATHLEN], *basepath;
740: const char *ptr;
1.63 niallo 741: size_t len;
742: struct stat sb;
743:
1.84 joris 744: basepath = xmalloc(MAXPATHLEN);
1.88 alek 745: basepath[0] = '\0';
1.76 niallo 746: if (strchr(filename, '/') == NULL) {
747: strlcat(basepath, RCSDIR"/", MAXPATHLEN);
748: if ((stat(basepath, &sb) == 0) && (sb.st_mode & S_IFDIR)) {
749: /* <path>/RCS/<filename>,v */
750: strlcat(basepath, filename, MAXPATHLEN);
751: strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
752: } else {
753: /* <path>/<filename>,v */
754: strlcpy(basepath, filename, MAXPATHLEN);
755: strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
756: }
757: } else {
758: ptr = filename;
759: /* Walk backwards till we find the base directory */
760: len = strlen(filename);
761: ptr += len + 1;
762: while (filename[len] != '/') {
763: len--;
764: ptr--;
765: }
1.63 niallo 766: /*
767: * Need two bytes extra for trailing slash and
768: * NUL-termination.
769: */
770: len += 2;
1.76 niallo 771: if (len > MAXPATHLEN) {
1.84 joris 772: xfree(basepath);
1.63 niallo 773: return (NULL);
774: }
1.76 niallo 775: strlcpy(basepath, filename, len);
776: strlcpy(name, ptr, MAXPATHLEN);
1.63 niallo 777: strlcat(basepath, RCSDIR"/", MAXPATHLEN);
778: if ((stat(basepath, &sb) == 0) && (sb.st_mode & S_IFDIR)) {
779: /* <path>/RCS/<filename>,v */
1.76 niallo 780: strlcat(basepath, name, MAXPATHLEN);
1.63 niallo 781: strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
782: } else {
783: /* <path>/<filename>,v */
1.76 niallo 784: strlcpy(basepath, filename, MAXPATHLEN);
1.63 niallo 785: strlcat(basepath, RCS_FILE_EXT, MAXPATHLEN);
786: }
1.76 niallo 787: }
788: return (basepath);
1.1 niallo 789: }