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