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