Annotation of src/usr.bin/rcs/ci.c, Revision 1.59
1.59 ! niallo 1: /* $OpenBSD: ci.c,v 1.58 2005/11/16 19:06:41 niallo 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:
27: #include <sys/param.h>
28: #include <sys/types.h>
29: #include <sys/stat.h>
30: #include <sys/wait.h>
31:
32: #include <err.h>
33: #include <pwd.h>
34: #include <errno.h>
35: #include <stdio.h>
36: #include <ctype.h>
37: #include <stdlib.h>
38: #include <unistd.h>
39: #include <string.h>
1.20 niallo 40: #include <time.h>
1.1 niallo 41:
42: #include "log.h"
43: #include "rcs.h"
1.4 niallo 44: #include "diff.h"
1.1 niallo 45: #include "rcsprog.h"
1.9 niallo 46:
1.58 niallo 47: #define CI_OPTSTRING "d::f::i::j::k:l::m:M::N:n:qr::s:u::Vw:"
1.25 niallo 48: #define DATE_NOW -1
49: #define DATE_MTIME -2
50:
1.58 niallo 51: #define LOG_INIT "Initial revision"
52: #define LOG_PROMPT "enter log message, terminated with a single '.' " \
53: "or end of file:\n>> "
54: #define DESC_PROMPT "enter description, terminated with single '.' " \
55: "or end of file:\nNOTE: This is NOT the log message!\n"
56:
57: struct checkin_params {
58: int flags, openflags;
59: mode_t fmode;
60: time_t date;
61: RCSFILE *file;
62: RCSNUM *frev, *newrev;
63: char fpath[MAXPATHLEN], *rcs_msg, *username, *deltatext, *filename;
64: const char *symbol, *state;
65: };
66:
67: static int checkin_attach_symbol(struct checkin_params *pb);
68: static int checkin_checklock(struct checkin_params *pb);
69: static char * checkin_diff_file(struct checkin_params *);
70: static char * checkin_getdesc(void);
71: static char * checkin_getinput(const char *);
1.34 niallo 72: static char * checkin_getlogmsg(RCSNUM *, RCSNUM *);
1.58 niallo 73: static void checkin_init(struct checkin_params *);
74: static void checkin_revert(struct checkin_params *pb);
1.4 niallo 75:
1.1 niallo 76: void
77: checkin_usage(void)
78: {
79: fprintf(stderr,
1.58 niallo 80: "usage: ci [-MNqV] [-d[date]] [-f[rev]] [-i[rev]] [-j[rev]]\n"
81: " [-kmode] [-l[rev]] [-M[rev]] [-mmsg] [-Nsymbol]\n"
82: " [-nsymbol] [-r[rev]] [-sstate] [-u[rev]] [-wusername]\n"
83: " file ...\n");
84: }
85:
1.55 niallo 86:
1.1 niallo 87:
88: /*
89: * checkin_main()
90: *
91: * Handler for the `ci' program.
92: * Returns 0 on success, or >0 on error.
93: */
94: int
95: checkin_main(int argc, char **argv)
96: {
1.58 niallo 97: int i, ch, status;
98: char *filec;
99: struct stat sb;
100: struct checkin_params pb;
1.4 niallo 101: BUF *bp;
1.1 niallo 102:
1.58 niallo 103: pb.date = DATE_NOW;
104: pb.file = NULL;
105: pb.rcs_msg = pb.username = NULL;
106: pb.state = pb.symbol = NULL;
107: pb.newrev = NULL;
108: pb.fmode = pb.flags = status = 0;
1.1 niallo 109:
1.58 niallo 110: pb.flags = INTERACTIVE;
111: pb.openflags = RCS_RDWR|RCS_CREATE;
1.9 niallo 112:
1.58 niallo 113: while ((ch = rcs_getopt(argc, argv, CI_OPTSTRING)) != -1) {
1.1 niallo 114: switch (ch) {
1.20 niallo 115: case 'd':
1.25 niallo 116: if (rcs_optarg == NULL)
1.58 niallo 117: pb.date = DATE_MTIME;
118: else if ((pb.date = cvs_date_parse(rcs_optarg)) <= 0) {
1.20 niallo 119: cvs_log(LP_ERR, "invalide date");
120: exit(1);
121: }
122: break;
1.29 niallo 123: case 'f':
1.58 niallo 124: rcs_set_rev(rcs_optarg, &pb.newrev);
125: pb.flags |= FORCE;
1.29 niallo 126: break;
1.1 niallo 127: case 'h':
128: (usage)();
129: exit(0);
1.58 niallo 130: case 'i':
131: rcs_set_rev(rcs_optarg, &pb.newrev);
132: pb.openflags |= RCS_CREATE;
133: break;
134: case 'j':
135: rcs_set_rev(rcs_optarg, &pb.newrev);
136: pb.openflags &= ~RCS_CREATE;
137: break;
1.30 niallo 138: case 'l':
1.58 niallo 139: rcs_set_rev(rcs_optarg, &pb.newrev);
140: pb.flags |= CO_LOCK;
1.54 niallo 141: break;
142: case 'M':
1.58 niallo 143: rcs_set_rev(rcs_optarg, &pb.newrev);
144: pb.flags |= CO_REVDATE;
1.30 niallo 145: break;
1.1 niallo 146: case 'm':
1.58 niallo 147: pb.rcs_msg = rcs_optarg;
148: if (pb.rcs_msg == NULL) {
149: cvs_log(LP_ERR,
150: "missing message for -m option");
151: exit(1);
152: }
153: pb.flags &= ~INTERACTIVE;
1.3 joris 154: break;
1.42 niallo 155: case 'N':
1.58 niallo 156: if ((pb.symbol = strdup(rcs_optarg)) == NULL) {
1.42 niallo 157: cvs_log(LP_ERRNO, "out of memory");
158: exit(1);
159: }
1.58 niallo 160: if (rcs_sym_check(pb.symbol) != 1) {
161: cvs_log(LP_ERR, "invalid symbol `%s'",
162: pb.symbol);
1.42 niallo 163: exit(1);
164: }
1.58 niallo 165: pb.flags |= CI_SYMFORCE;
1.42 niallo 166: break;
1.38 niallo 167: case 'n':
1.58 niallo 168: if ((pb.symbol = strdup(rcs_optarg)) == NULL) {
1.38 niallo 169: cvs_log(LP_ERRNO, "out of memory");
170: exit(1);
171: }
1.58 niallo 172: if (rcs_sym_check(pb.symbol) != 1) {
173: cvs_log(LP_ERR, "invalid symbol `%s'",
174: pb.symbol);
1.38 niallo 175: exit(1);
176: }
177: break;
1.3 joris 178: case 'q':
179: verbose = 0;
1.1 niallo 180: break;
1.30 niallo 181: case 'r':
1.58 niallo 182: rcs_set_rev(rcs_optarg, &pb.newrev);
183: pb.flags |= CI_DEFAULT;
1.9 niallo 184: break;
1.51 niallo 185: case 's':
1.58 niallo 186: pb.state = rcs_optarg;
187: if (rcs_state_check(pb.state) < 0) {
188: cvs_log(LP_ERR, "invalid state `%s'",
189: pb.state);
1.51 niallo 190: exit(1);
191: }
192: break;
1.9 niallo 193: case 'u':
1.58 niallo 194: rcs_set_rev(rcs_optarg, &pb.newrev);
195: pb.flags |= CO_UNLOCK;
1.9 niallo 196: break;
1.30 niallo 197: case 'V':
198: printf("%s\n", rcs_version);
199: exit(0);
1.31 niallo 200: case 'w':
1.58 niallo 201: pb.username = rcs_optarg;
1.31 niallo 202: break;
1.1 niallo 203: default:
204: (usage)();
205: exit(1);
206: }
207: }
208:
1.24 joris 209: argc -= rcs_optind;
210: argv += rcs_optind;
211:
1.1 niallo 212: if (argc == 0) {
213: cvs_log(LP_ERR, "no input file");
214: (usage)();
215: exit(1);
216: }
217:
1.58 niallo 218: if ((pb.username == NULL) && (pb.username = getlogin()) == NULL) {
1.31 niallo 219: cvs_log(LP_ERRNO, "failed to get username");
220: exit(1);
221: }
222:
223:
1.1 niallo 224: for (i = 0; i < argc; i++) {
1.58 niallo 225: pb.filename = argv[i];
226: if (rcs_statfile(pb.filename, pb.fpath, sizeof(pb.fpath)) < 0)
1.1 niallo 227: continue;
228:
1.58 niallo 229: /*
230: * Test for existence of ,v file. If we are expected to
231: * create one, set NEWFILE flag.
232: */
233: if ((pb.openflags & RCS_CREATE) && (stat(pb.fpath, &sb) < 0))
234: pb.flags |= NEWFILE;
235: else
236: pb.openflags &= ~RCS_CREATE;
237:
238: pb.file = rcs_open(pb.fpath, pb.openflags, pb.fmode);
239:
240: if (pb.file == NULL) {
241: cvs_log(LP_ERR, "failed to open rcsfile '%s'", pb.fpath);
1.1 niallo 242: exit(1);
243: }
1.29 niallo 244:
1.58 niallo 245: if (verbose == 1)
246: printf("%s <-- %s\n", pb.fpath, pb.filename);
1.36 niallo 247:
1.58 niallo 248: pb.frev = pb.file->rf_head;
1.29 niallo 249:
1.17 niallo 250: /*
251: * If revision passed on command line is less than HEAD, bail.
252: */
1.58 niallo 253: if ((pb.newrev != NULL)
254: && (rcsnum_cmp(pb.newrev, pb.frev, 0) > 0)) {
1.17 niallo 255: cvs_log(LP_ERR, "revision is too low!");
256: status = 1;
1.58 niallo 257: rcs_close(pb.file);
1.17 niallo 258: continue;
259: }
1.4 niallo 260:
261: /*
262: * Load file contents
263: */
264: if ((bp = cvs_buf_load(argv[i], BUF_AUTOEXT)) == NULL) {
1.58 niallo 265: cvs_log(LP_ERR, "failed to load '%s'", pb.filename);
1.4 niallo 266: exit(1);
267: }
268:
1.12 niallo 269: if (cvs_buf_putc(bp, '\0') < 0)
270: exit(1);
271:
1.23 niallo 272: filec = (char *)cvs_buf_release(bp);
1.13 joris 273:
1.6 niallo 274: /*
1.29 niallo 275: * Get RCS patch
276: */
1.58 niallo 277: if ((pb.deltatext = checkin_diff_file(&pb)) == NULL) {
1.29 niallo 278: cvs_log(LP_ERR, "failed to get diff");
279: exit(1);
280: }
281:
282: /*
1.58 niallo 283: * If -f is not specified and there are no differences, tell
284: * the user and revert to latest version.
1.29 niallo 285: */
1.58 niallo 286: if (!(pb.flags & FORCE) && (strlen(pb.deltatext) < 1)) {
287: checkin_revert(&pb);
1.29 niallo 288: continue;
289: }
290:
291: /*
1.16 niallo 292: * Check for a lock belonging to this user. If none,
293: * abort check-in.
294: */
1.58 niallo 295: if (checkin_checklock(&pb) < 0) {
1.16 niallo 296: status = 1;
297: continue;
298: }
299:
300: /*
1.6 niallo 301: * If no log message specified, get it interactively.
302: */
1.58 niallo 303: if (pb.flags & INTERACTIVE)
304: pb.rcs_msg = checkin_getlogmsg(pb.frev, pb.newrev);
1.4 niallo 305:
306: /*
307: * Remove the lock
308: */
1.58 niallo 309: if (rcs_lock_remove(pb.file, pb.frev) < 0) {
1.4 niallo 310: if (rcs_errno != RCS_ERR_NOENT)
311: cvs_log(LP_WARN, "failed to remove lock");
312: }
313:
314: /*
315: * Current head revision gets the RCS patch as rd_text
316: */
1.58 niallo 317: if (rcs_deltatext_set(pb.file, pb.frev, pb.deltatext) == -1) {
1.7 niallo 318: cvs_log(LP_ERR,
319: "failed to set new rd_text for head rev");
1.4 niallo 320: exit (1);
1.25 niallo 321: }
1.32 joris 322:
1.25 niallo 323: /*
1.58 niallo 324: * Set the date of the revision to be the last modification
325: * time of the working file if -d has no argument.
1.25 niallo 326: */
1.58 niallo 327: if (pb.date == DATE_MTIME) {
328: if (stat(pb.filename, &sb) != 0) {
1.33 niallo 329: cvs_log(LP_ERRNO, "failed to stat: `%s'",
1.58 niallo 330: pb.filename);
331: rcs_close(pb.file);
1.25 niallo 332: continue;
333: }
1.58 niallo 334: pb.date = (time_t)sb.st_mtimespec.tv_sec;
1.4 niallo 335: }
1.32 joris 336:
1.4 niallo 337: /*
338: * Now add our new revision
339: */
1.58 niallo 340: if (rcs_rev_add(pb.file,
341: (pb.newrev == NULL ? RCS_HEAD_REV : pb.newrev),
342: pb.rcs_msg, pb.date, pb.username) != 0) {
1.4 niallo 343: cvs_log(LP_ERR, "failed to add new revision");
344: exit(1);
345: }
346:
347: /*
1.12 niallo 348: * If we are checking in to a non-default (ie user-specified)
349: * revision, set head to this revision.
350: */
1.58 niallo 351: if (pb.newrev != NULL)
352: rcs_head_set(pb.file, pb.newrev);
1.15 niallo 353: else
1.58 niallo 354: pb.newrev = pb.file->rf_head;
1.32 joris 355:
1.12 niallo 356: /*
1.4 niallo 357: * New head revision has to contain entire file;
358: */
1.58 niallo 359: if (rcs_deltatext_set(pb.file, pb.frev, filec) == -1) {
1.4 niallo 360: cvs_log(LP_ERR, "failed to set new head revision");
361: exit(1);
1.38 niallo 362: }
363:
364: /*
365: * Attach a symbolic name to this revision if specified.
366: */
1.58 niallo 367: if (pb.symbol != NULL
368: && (checkin_attach_symbol(&pb) < 0)) {
369: status = 1;
370: continue;
1.4 niallo 371: }
1.51 niallo 372:
373: /*
374: * Set the state of this revision if specified.
375: */
1.58 niallo 376: if (pb.state != NULL)
377: (void)rcs_state_set(pb.file, pb.newrev, pb.state);
1.4 niallo 378:
1.58 niallo 379: free(pb.deltatext);
1.4 niallo 380: free(filec);
1.58 niallo 381: (void)unlink(pb.filename);
1.4 niallo 382:
1.9 niallo 383: /*
384: * Do checkout if -u or -l are specified.
385: */
1.58 niallo 386: if (((pb.flags & CO_LOCK) || (pb.flags & CO_UNLOCK))
387: && !(pb.flags & CI_DEFAULT))
388: checkout_rev(pb.file, pb.newrev, pb.filename, pb.flags,
389: pb.username);
1.28 niallo 390:
1.4 niallo 391: /* File will NOW be synced */
1.58 niallo 392: rcs_close(pb.file);
1.4 niallo 393:
1.58 niallo 394: if (pb.flags & INTERACTIVE) {
395: free(pb.rcs_msg);
396: pb.rcs_msg = NULL;
1.6 niallo 397: }
1.4 niallo 398: }
399:
1.16 niallo 400: return (status);
1.4 niallo 401: }
402:
1.59 ! niallo 403: /*
! 404: * checkin_diff_file()
! 405: *
! 406: * Generate the diff between the working file and a revision.
! 407: * Returns pointer to a char array on success, NULL on failure.
! 408: */
1.4 niallo 409: static char *
1.58 niallo 410: checkin_diff_file(struct checkin_params *pb)
1.4 niallo 411: {
412: char path1[MAXPATHLEN], path2[MAXPATHLEN];
413: BUF *b1, *b2, *b3;
414: char rbuf[64], *deltatext;
415:
1.58 niallo 416: rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
1.4 niallo 417:
1.58 niallo 418: if ((b1 = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
419: cvs_log(LP_ERR, "failed to load file: '%s'", pb->filename);
1.4 niallo 420: return (NULL);
1.1 niallo 421: }
422:
1.58 niallo 423: if ((b2 = rcs_getrev(pb->file, pb->frev)) == NULL) {
1.4 niallo 424: cvs_log(LP_ERR, "failed to load revision");
425: cvs_buf_free(b1);
426: return (NULL);
427: }
428:
1.57 xsa 429: if ((b3 = cvs_buf_alloc((size_t)128, BUF_AUTOEXT)) == NULL) {
1.4 niallo 430: cvs_log(LP_ERR, "failed to allocated buffer for diff");
431: cvs_buf_free(b1);
432: cvs_buf_free(b2);
433: return (NULL);
434: }
435:
1.50 xsa 436: strlcpy(path1, rcs_tmpdir, sizeof(path1));
437: strlcat(path1, "/diff1.XXXXXXXXXX", sizeof(path1));
1.4 niallo 438: if (cvs_buf_write_stmp(b1, path1, 0600) == -1) {
439: cvs_log(LP_ERRNO, "could not write temporary file");
440: cvs_buf_free(b1);
441: cvs_buf_free(b2);
442: return (NULL);
443: }
444: cvs_buf_free(b1);
445:
1.50 xsa 446: strlcpy(path2, rcs_tmpdir, sizeof(path2));
447: strlcat(path2, "/diff2.XXXXXXXXXX", sizeof(path2));
1.4 niallo 448: if (cvs_buf_write_stmp(b2, path2, 0600) == -1) {
449: cvs_buf_free(b2);
450: (void)unlink(path1);
451: return (NULL);
452: }
453: cvs_buf_free(b2);
454:
1.5 niallo 455: diff_format = D_RCSDIFF;
1.4 niallo 456: cvs_diffreg(path1, path2, b3);
457: (void)unlink(path1);
458: (void)unlink(path2);
459:
460: cvs_buf_putc(b3, '\0');
461: deltatext = (char *)cvs_buf_release(b3);
462:
463: return (deltatext);
1.6 niallo 464: }
465:
466: /*
1.59 ! niallo 467: * checkin_getlogmsg()
! 468: *
1.6 niallo 469: * Get log message from user interactively.
1.59 ! niallo 470: * Returns pointer to a char array on success, NULL on failure.
1.6 niallo 471: */
472: static char *
1.34 niallo 473: checkin_getlogmsg(RCSNUM *rev, RCSNUM *rev2)
1.6 niallo 474: {
1.58 niallo 475: char *rcs_msg, nrev[16], prev[16];
1.6 niallo 476: RCSNUM *tmprev;
477:
1.7 niallo 478: rcs_msg = NULL;
1.6 niallo 479: tmprev = rcsnum_alloc();
480: rcsnum_cpy(rev, tmprev, 16);
1.9 niallo 481: rcsnum_tostr(tmprev, prev, sizeof(prev));
1.12 niallo 482: if (rev2 == NULL)
483: rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev));
484: else
485: rcsnum_tostr(rev2, nrev, sizeof(nrev));
1.6 niallo 486: rcsnum_free(tmprev);
487:
1.47 niallo 488: if (verbose == 1)
489: printf("new revision: %s; previous revision: %s\n", nrev,
490: prev);
1.32 joris 491:
1.58 niallo 492: rcs_msg = checkin_getinput(LOG_PROMPT);
493: return (rcs_msg);
494: }
495:
496:
497: /*
498: * checkin_getdesc()
499: *
500: * Get file description interactively.
1.59 ! niallo 501: * Returns pointer to a char array on success, NULL on failure.
1.58 niallo 502: */
503: static char *
504: checkin_getdesc()
505: {
506: char *description;
507:
508: description = checkin_getinput(DESC_PROMPT);
509: return (description);
510: }
511:
512: /*
513: * checkin_getinput()
514: *
1.59 ! niallo 515: * Get some input from the user, in RCS style, prompting with message <prompt>.
! 516: * Returns pointer to a char array on success, NULL on failure.
1.58 niallo 517: */
518: static char *
519: checkin_getinput(const char *prompt)
520: {
521: BUF *inputbuf;
522: char *input, buf[128];
523:
524: if ((inputbuf = cvs_buf_alloc((size_t)64, BUF_AUTOEXT)) == NULL) {
525: cvs_log(LP_ERR, "failed to allocate input buffer");
526: return (NULL);
527: }
528:
529: printf(prompt);
1.6 niallo 530: for (;;) {
531: fgets(buf, (int)sizeof(buf), stdin);
1.9 niallo 532: if (feof(stdin) || ferror(stdin) || buf[0] == '.')
1.6 niallo 533: break;
1.58 niallo 534: cvs_buf_append(inputbuf, buf, strlen(buf));
1.46 joris 535: printf(">> ");
1.6 niallo 536: }
1.32 joris 537:
1.58 niallo 538: cvs_buf_putc(inputbuf, '\0');
539: input = (char *)cvs_buf_release(inputbuf);
540:
541: return (input);
542: }
543:
544: /*
545: * checkin_init()
546: *
547: * Does an initial check in, just enough to create the new ,v file
1.59 ! niallo 548: * XXX not full implemented yet.
1.58 niallo 549: */
550: static void
551: checkin_init(struct checkin_params *pb)
552: {
553: BUF *bp;
554: char *rcs_desc, *filec;
555:
556: /*
557: * Load file contents
558: */
559: if ((bp = cvs_buf_load(pb->filename, BUF_AUTOEXT)) == NULL) {
560: cvs_log(LP_ERR, "failed to load '%s'", pb->filename);
561: exit(1);
562: }
563:
564: if (cvs_buf_putc(bp, '\0') < 0)
565: exit(1);
566:
567: filec = (char *)cvs_buf_release(bp);
568:
569: /*
570: * Get description from user
571: */
572: rcs_desc = checkin_getdesc();
573: rcs_desc_set(pb->file, rcs_desc);
574:
575: /*
576: * Now add our new revision
577: */
578: if (rcs_rev_add(pb->file, RCS_HEAD_REV, LOG_INIT,
579: -1, pb->username) != 0) {
580: cvs_log(LP_ERR, "failed to add new revision");
581: exit(1);
582: }
583: }
584:
1.59 ! niallo 585: /*
! 586: * checkin_attach_symbol()
! 587: *
! 588: * Attempt to attach the specified symbol to the revision.
! 589: * On success, return 0. On error return -1.
! 590: */
1.58 niallo 591: static int
592: checkin_attach_symbol(struct checkin_params *pb)
593: {
594: char rbuf[16];
595: int ret;
596: if (verbose == 1)
597: printf("symbol: %s\n", pb->symbol);
598: if (pb->flags & CI_SYMFORCE)
599: rcs_sym_remove(pb->file, pb->symbol);
600: if ((ret = rcs_sym_add(pb->file, pb->symbol, pb->newrev) == -1)
601: && (rcs_errno == RCS_ERR_DUPENT)) {
602: rcsnum_tostr(rcs_sym_getrev(pb->file, pb->symbol),
603: rbuf, sizeof(rbuf));
604: cvs_log(LP_ERR,
605: "symbolic name %s already bound to %s",
606: pb->symbol, rbuf);
607: rcs_close(pb->file);
608: return (-1);
609: } else if (ret == -1) {
610: cvs_log(LP_ERR, "problem adding symbol: %s",
611: pb->symbol);
612: rcs_close(pb->file);
613: return (-1);
614: }
615: return (0);
616: }
617:
1.59 ! niallo 618: /*
! 619: * checkin_revert()
! 620: *
! 621: * If there are no differences between the working file and the latest revision
! 622: * and the -f flag is not specified, simply revert to the latest version and
! 623: * warn the user.
! 624: *
! 625: */
1.58 niallo 626: static void
627: checkin_revert(struct checkin_params *pb)
628: {
629: char rbuf[16];
630:
631: rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf));
632: cvs_log(LP_WARN,
633: "file is unchanged; reverting to previous revision %s",
634: rbuf);
635: (void)unlink(pb->filename);
636: if ((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK))
637: checkout_rev(pb->file, pb->frev, pb->filename,
638: pb->flags, pb->username);
639: rcs_lock_remove(pb->file, pb->frev);
640: rcs_close(pb->file);
641: if (verbose == 1)
642: printf("done\n");
643: }
644:
1.59 ! niallo 645: /*
! 646: * checkin_checklock()
! 647: *
! 648: * Check for the existence of a lock on the file. If there are no locks, or it
! 649: * is not locked by the correct user, return -1. Otherwise, return 0.
! 650: */
1.58 niallo 651: static int
652: checkin_checklock(struct checkin_params *pb)
653: {
654: int found = 0, notlocked = 1;
655: struct rcs_lock *lkp;
656:
657: if (!TAILQ_EMPTY(&(pb->file->rf_locks))) {
658: TAILQ_FOREACH(lkp, &(pb->file->rf_locks), rl_list) {
659: if (!strcmp(lkp->rl_name, pb->username))
660: notlocked = 0;
661:
662: if (!strcmp(lkp->rl_name, pb->username) &&
663: !rcsnum_cmp(lkp->rl_num, pb->frev, 0)) {
664: found = 1;
665: return (0);
666: }
667: }
668: }
1.32 joris 669:
1.58 niallo 670: if ((found == 0) && (notlocked == 0)) {
671: cvs_log(LP_ERR, "no locks set for '%s'", pb->username);
672: rcs_close(pb->file);
673: return (-1);
674: }
675: return (0);
1.1 niallo 676: }