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