1 /* $OpenBSD: ci.c,v 1.217 2014/05/19 19:42:24 jca Exp $ */ 2 /* 3 * Copyright (c) 2005, 2006 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 conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions 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/stat.h> 28 29 #include <ctype.h> 30 #include <err.h> 31 #include <fcntl.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <unistd.h> 36 37 #include "rcsprog.h" 38 #include "diff.h" 39 40 #define CI_OPTSTRING "d::f::I::i::j::k::l::M::m::N:n:qr::s:Tt::u::Vw:x::z::" 41 #define DATE_NOW -1 42 #define DATE_MTIME -2 43 44 #define KW_ID "Id" 45 #define KW_OPENBSD "OpenBSD" 46 #define KW_AUTHOR "Author" 47 #define KW_DATE "Date" 48 #define KW_STATE "State" 49 #define KW_REVISION "Revision" 50 51 #define KW_TYPE_ID 1 52 #define KW_TYPE_AUTHOR 2 53 #define KW_TYPE_DATE 3 54 #define KW_TYPE_STATE 4 55 #define KW_TYPE_REVISION 5 56 57 /* Maximum number of tokens in a keyword. */ 58 #define KW_NUMTOKS_MAX 10 59 60 #define RCSNUM_ZERO_ENDING(x) (x->rn_id[x->rn_len - 1] == 0) 61 62 extern struct rcs_kw rcs_expkw[]; 63 64 static int workfile_fd; 65 66 struct checkin_params { 67 int flags, openflags; 68 mode_t fmode; 69 time_t date; 70 RCSFILE *file; 71 RCSNUM *frev, *newrev; 72 const char *description, *symbol; 73 char fpath[MAXPATHLEN], *rcs_msg, *username, *filename; 74 char *author, *state; 75 BUF *deltatext; 76 }; 77 78 static int checkin_attach_symbol(struct checkin_params *); 79 static int checkin_checklock(struct checkin_params *); 80 static BUF *checkin_diff_file(struct checkin_params *); 81 static char *checkin_getlogmsg(RCSNUM *, RCSNUM *, int); 82 static int checkin_init(struct checkin_params *); 83 static int checkin_keywordscan(BUF *, RCSNUM **, time_t *, char **, 84 char **); 85 static int checkin_keywordtype(char *); 86 static void checkin_mtimedate(struct checkin_params *); 87 static void checkin_parsekeyword(char *, RCSNUM **, time_t *, char **, 88 char **); 89 static int checkin_update(struct checkin_params *); 90 static int checkin_revert(struct checkin_params *); 91 92 void 93 checkin_usage(void) 94 { 95 fprintf(stderr, 96 "usage: ci [-qV] [-d[date]] [-f[rev]] [-I[rev]] [-i[rev]]\n" 97 " [-j[rev]] [-k[rev]] [-l[rev]] [-M[rev]] [-mmsg]\n" 98 " [-Nsymbol] [-nsymbol] [-r[rev]] [-sstate] [-t[str]]\n" 99 " [-u[rev]] [-wusername] [-xsuffixes] [-ztz] file ...\n"); 100 } 101 102 /* 103 * checkin_main() 104 * 105 * Handler for the `ci' program. 106 * Returns 0 on success, or >0 on error. 107 */ 108 int 109 checkin_main(int argc, char **argv) 110 { 111 int fd; 112 int i, ch, status; 113 int base_flags, base_openflags; 114 char *rev_str; 115 struct checkin_params pb; 116 117 pb.date = DATE_NOW; 118 pb.file = NULL; 119 pb.rcs_msg = pb.username = pb.author = pb.state = NULL; 120 pb.description = pb.symbol = NULL; 121 pb.deltatext = NULL; 122 pb.newrev = NULL; 123 pb.fmode = S_IRUSR|S_IRGRP|S_IROTH; 124 status = 0; 125 base_flags = INTERACTIVE; 126 base_openflags = RCS_RDWR|RCS_CREATE|RCS_PARSE_FULLY; 127 rev_str = NULL; 128 129 while ((ch = rcs_getopt(argc, argv, CI_OPTSTRING)) != -1) { 130 switch (ch) { 131 case 'd': 132 if (rcs_optarg == NULL) 133 pb.date = DATE_MTIME; 134 else if ((pb.date = date_parse(rcs_optarg)) == -1) 135 errx(1, "invalid date"); 136 break; 137 case 'f': 138 rcs_setrevstr(&rev_str, rcs_optarg); 139 base_flags |= FORCE; 140 break; 141 case 'I': 142 rcs_setrevstr(&rev_str, rcs_optarg); 143 base_flags |= INTERACTIVE; 144 break; 145 case 'i': 146 rcs_setrevstr(&rev_str, rcs_optarg); 147 base_openflags |= RCS_CREATE; 148 base_flags |= CI_INIT; 149 break; 150 case 'j': 151 rcs_setrevstr(&rev_str, rcs_optarg); 152 base_openflags &= ~RCS_CREATE; 153 base_flags &= ~CI_INIT; 154 break; 155 case 'k': 156 rcs_setrevstr(&rev_str, rcs_optarg); 157 base_flags |= CI_KEYWORDSCAN; 158 break; 159 case 'l': 160 rcs_setrevstr(&rev_str, rcs_optarg); 161 base_flags |= CO_LOCK; 162 break; 163 case 'M': 164 rcs_setrevstr(&rev_str, rcs_optarg); 165 base_flags |= CO_REVDATE; 166 break; 167 case 'm': 168 pb.rcs_msg = rcs_optarg; 169 if (pb.rcs_msg == NULL) 170 errx(1, "missing message for -m option"); 171 base_flags &= ~INTERACTIVE; 172 break; 173 case 'N': 174 base_flags |= CI_SYMFORCE; 175 /* FALLTHROUGH */ 176 case 'n': 177 pb.symbol = rcs_optarg; 178 if (rcs_sym_check(pb.symbol) != 1) 179 errx(1, "invalid symbol `%s'", pb.symbol); 180 break; 181 case 'q': 182 base_flags |= QUIET; 183 break; 184 case 'r': 185 rcs_setrevstr(&rev_str, rcs_optarg); 186 base_flags |= CI_DEFAULT; 187 break; 188 case 's': 189 pb.state = rcs_optarg; 190 if (rcs_state_check(pb.state) < 0) 191 errx(1, "invalid state `%s'", pb.state); 192 break; 193 case 'T': 194 base_flags |= PRESERVETIME; 195 break; 196 case 't': 197 /* Ignore bare -t; kept for backwards compatibility. */ 198 if (rcs_optarg == NULL) 199 break; 200 pb.description = rcs_optarg; 201 base_flags |= DESCRIPTION; 202 break; 203 case 'u': 204 rcs_setrevstr(&rev_str, rcs_optarg); 205 base_flags |= CO_UNLOCK; 206 break; 207 case 'V': 208 printf("%s\n", rcs_version); 209 exit(0); 210 case 'w': 211 if (pb.author != NULL) 212 xfree(pb.author); 213 pb.author = xstrdup(rcs_optarg); 214 break; 215 case 'x': 216 /* Use blank extension if none given. */ 217 rcs_suffixes = rcs_optarg ? rcs_optarg : ""; 218 break; 219 case 'z': 220 timezone_flag = rcs_optarg; 221 break; 222 default: 223 (usage)(); 224 exit(1); 225 } 226 } 227 228 argc -= rcs_optind; 229 argv += rcs_optind; 230 231 if (argc == 0) { 232 warnx("no input file"); 233 (usage)(); 234 exit(1); 235 } 236 237 if ((pb.username = getlogin()) == NULL) 238 err(1, "getlogin"); 239 240 for (i = 0; i < argc; i++) { 241 /* 242 * The pb.flags and pb.openflags may change during 243 * loop iteration so restore them for each file. 244 */ 245 pb.flags = base_flags; 246 pb.openflags = base_openflags; 247 248 pb.filename = argv[i]; 249 rcs_strip_suffix(pb.filename); 250 251 if ((workfile_fd = open(pb.filename, O_RDONLY)) == -1) 252 err(1, "%s", pb.filename); 253 254 /* Find RCS file path. */ 255 fd = rcs_choosefile(pb.filename, pb.fpath, sizeof(pb.fpath)); 256 257 if (fd < 0) { 258 if (pb.openflags & RCS_CREATE) 259 pb.flags |= NEWFILE; 260 else { 261 /* XXX - Check if errno == ENOENT. */ 262 warnx("No existing RCS file"); 263 status = 1; 264 (void)close(workfile_fd); 265 continue; 266 } 267 } else { 268 if (pb.flags & CI_INIT) { 269 warnx("%s already exists", pb.fpath); 270 status = 1; 271 (void)close(fd); 272 (void)close(workfile_fd); 273 continue; 274 } 275 pb.openflags &= ~RCS_CREATE; 276 } 277 278 pb.file = rcs_open(pb.fpath, fd, pb.openflags, pb.fmode); 279 if (pb.file == NULL) 280 errx(1, "failed to open rcsfile `%s'", pb.fpath); 281 282 if ((pb.flags & DESCRIPTION) && 283 rcs_set_description(pb.file, pb.description) == -1) 284 err(1, "%s", pb.filename); 285 286 if (!(pb.flags & QUIET)) 287 (void)fprintf(stderr, 288 "%s <-- %s\n", pb.fpath, pb.filename); 289 290 if (rev_str != NULL) 291 if ((pb.newrev = rcs_getrevnum(rev_str, pb.file)) == 292 NULL) 293 errx(1, "invalid revision: %s", rev_str); 294 295 if (!(pb.flags & NEWFILE)) 296 pb.flags |= CI_SKIPDESC; 297 298 /* XXX - support for committing to a file without revisions */ 299 if (pb.file->rf_ndelta == 0) { 300 pb.flags |= NEWFILE; 301 pb.file->rf_flags |= RCS_CREATE; 302 } 303 304 /* 305 * workfile_fd will be closed in checkin_init or 306 * checkin_update 307 */ 308 if (pb.flags & NEWFILE) { 309 if (checkin_init(&pb) == -1) 310 status = 1; 311 } else { 312 if (checkin_update(&pb) == -1) 313 status = 1; 314 } 315 316 rcs_close(pb.file); 317 if (rev_str != NULL) 318 rcsnum_free(pb.newrev); 319 pb.newrev = NULL; 320 } 321 322 if (!(base_flags & QUIET) && status == 0) 323 (void)fprintf(stderr, "done\n"); 324 325 return (status); 326 } 327 328 /* 329 * checkin_diff_file() 330 * 331 * Generate the diff between the working file and a revision. 332 * Returns pointer to a BUF on success, NULL on failure. 333 */ 334 static BUF * 335 checkin_diff_file(struct checkin_params *pb) 336 { 337 char *path1, *path2; 338 BUF *b1, *b2, *b3; 339 340 b1 = b2 = b3 = NULL; 341 path1 = path2 = NULL; 342 343 if ((b1 = buf_load(pb->filename)) == NULL) { 344 warnx("failed to load file: `%s'", pb->filename); 345 goto out; 346 } 347 348 if ((b2 = rcs_getrev(pb->file, pb->frev)) == NULL) { 349 warnx("failed to load revision"); 350 goto out; 351 } 352 b2 = rcs_kwexp_buf(b2, pb->file, pb->frev); 353 b3 = buf_alloc(128); 354 355 (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); 356 buf_write_stmp(b1, path1); 357 358 buf_free(b1); 359 b1 = NULL; 360 361 (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); 362 buf_write_stmp(b2, path2); 363 364 buf_free(b2); 365 b2 = NULL; 366 367 diff_format = D_RCSDIFF; 368 if (diffreg(path1, path2, b3, D_FORCEASCII) == D_ERROR) 369 goto out; 370 371 return (b3); 372 out: 373 if (b1 != NULL) 374 buf_free(b1); 375 if (b2 != NULL) 376 buf_free(b2); 377 if (b3 != NULL) 378 buf_free(b3); 379 if (path1 != NULL) 380 xfree(path1); 381 if (path2 != NULL) 382 xfree(path2); 383 384 return (NULL); 385 } 386 387 /* 388 * checkin_getlogmsg() 389 * 390 * Get log message from user interactively. 391 * Returns pointer to a char array on success, NULL on failure. 392 */ 393 static char * 394 checkin_getlogmsg(RCSNUM *rev, RCSNUM *rev2, int flags) 395 { 396 char *rcs_msg, nrev[RCS_REV_BUFSZ], prev[RCS_REV_BUFSZ]; 397 const char *prompt = 398 "enter log message, terminated with a single '.' or end of file:\n"; 399 RCSNUM *tmprev; 400 401 rcs_msg = NULL; 402 tmprev = rcsnum_alloc(); 403 rcsnum_cpy(rev, tmprev, 16); 404 rcsnum_tostr(tmprev, prev, sizeof(prev)); 405 if (rev2 == NULL) 406 rcsnum_tostr(rcsnum_inc(tmprev), nrev, sizeof(nrev)); 407 else 408 rcsnum_tostr(rev2, nrev, sizeof(nrev)); 409 rcsnum_free(tmprev); 410 411 if (!(flags & QUIET)) 412 (void)fprintf(stderr, "new revision: %s; " 413 "previous revision: %s\n", nrev, prev); 414 415 rcs_msg = rcs_prompt(prompt); 416 417 return (rcs_msg); 418 } 419 420 /* 421 * checkin_update() 422 * 423 * Do a checkin to an existing RCS file. 424 * 425 * On success, return 0. On error return -1. 426 */ 427 static int 428 checkin_update(struct checkin_params *pb) 429 { 430 char numb1[RCS_REV_BUFSZ], numb2[RCS_REV_BUFSZ]; 431 struct stat st; 432 BUF *bp; 433 434 /* 435 * XXX this is wrong, we need to get the revision the user 436 * has the lock for. So we can decide if we want to create a 437 * branch or not. (if it's not current HEAD we need to branch). 438 */ 439 pb->frev = pb->file->rf_head; 440 441 /* Load file contents */ 442 if ((bp = buf_load(pb->filename)) == NULL) 443 return (-1); 444 445 /* If this is a zero-ending RCSNUM eg 4.0, increment it (eg to 4.1) */ 446 if (pb->newrev != NULL && RCSNUM_ZERO_ENDING(pb->newrev)) 447 pb->newrev = rcsnum_inc(pb->newrev); 448 449 if (checkin_checklock(pb) < 0) 450 return (-1); 451 452 /* If revision passed on command line is less than HEAD, bail. 453 * XXX only applies to ci -r1.2 foo for example if HEAD is > 1.2 and 454 * there is no lock set for the user. 455 */ 456 if (pb->newrev != NULL && 457 rcsnum_cmp(pb->newrev, pb->frev, 0) != -1) { 458 warnx("%s: revision %s too low; must be higher than %s", 459 pb->file->rf_path, 460 rcsnum_tostr(pb->newrev, numb1, sizeof(numb1)), 461 rcsnum_tostr(pb->frev, numb2, sizeof(numb2))); 462 return (-1); 463 } 464 465 /* 466 * Set the date of the revision to be the last modification 467 * time of the working file if -d has no argument. 468 */ 469 if (pb->date == DATE_MTIME) 470 checkin_mtimedate(pb); 471 472 /* Date from argv/mtime must be more recent than HEAD */ 473 if (pb->date != DATE_NOW) { 474 time_t head_date = rcs_rev_getdate(pb->file, pb->frev); 475 if (pb->date <= head_date) { 476 static const char fmt[] = "%Y/%m/%d %H:%M:%S"; 477 char dbuf1[256], dbuf2[256]; 478 struct tm *t, *t_head; 479 480 t = gmtime(&pb->date); 481 strftime(dbuf1, sizeof(dbuf1), fmt, t); 482 t_head = gmtime(&head_date); 483 strftime(dbuf2, sizeof(dbuf2), fmt, t_head); 484 485 errx(1, "%s: Date %s precedes %s in revision %s.", 486 pb->file->rf_path, dbuf1, dbuf2, 487 rcsnum_tostr(pb->frev, numb2, sizeof(numb2))); 488 } 489 } 490 491 /* Get RCS patch */ 492 if ((pb->deltatext = checkin_diff_file(pb)) == NULL) { 493 warnx("failed to get diff"); 494 return (-1); 495 } 496 497 /* 498 * If -f is not specified and there are no differences, tell 499 * the user and revert to latest version. 500 */ 501 if (!(pb->flags & FORCE) && (buf_len(pb->deltatext) < 1)) { 502 if (checkin_revert(pb) == -1) 503 return (-1); 504 else 505 return (0); 506 } 507 508 /* If no log message specified, get it interactively. */ 509 if (pb->flags & INTERACTIVE) { 510 if (pb->rcs_msg != NULL) { 511 fprintf(stderr, 512 "reuse log message of previous file? [yn](y): "); 513 if (rcs_yesno('y') != 'y') { 514 xfree(pb->rcs_msg); 515 pb->rcs_msg = NULL; 516 } 517 } 518 if (pb->rcs_msg == NULL) 519 pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev, 520 pb->flags); 521 } 522 523 if ((rcs_lock_remove(pb->file, pb->username, pb->frev) < 0) && 524 (rcs_lock_getmode(pb->file) != RCS_LOCK_LOOSE)) { 525 if (rcs_errno != RCS_ERR_NOENT) 526 warnx("failed to remove lock"); 527 else if (!(pb->flags & CO_LOCK)) 528 warnx("previous revision was not locked; " 529 "ignoring -l option"); 530 } 531 532 /* Current head revision gets the RCS patch as rd_text */ 533 if (rcs_deltatext_set(pb->file, pb->frev, pb->deltatext) == -1) 534 errx(1, "failed to set new rd_text for head rev"); 535 536 /* Now add our new revision */ 537 if (rcs_rev_add(pb->file, 538 (pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev), 539 pb->rcs_msg, pb->date, pb->author) != 0) { 540 warnx("failed to add new revision"); 541 return (-1); 542 } 543 544 /* 545 * If we are checking in to a non-default (ie user-specified) 546 * revision, set head to this revision. 547 */ 548 if (pb->newrev != NULL) { 549 if (rcs_head_set(pb->file, pb->newrev) < 0) 550 errx(1, "rcs_head_set failed"); 551 } else 552 pb->newrev = pb->file->rf_head; 553 554 /* New head revision has to contain entire file; */ 555 if (rcs_deltatext_set(pb->file, pb->frev, bp) == -1) 556 errx(1, "failed to set new head revision"); 557 558 /* Attach a symbolic name to this revision if specified. */ 559 if (pb->symbol != NULL && 560 (checkin_attach_symbol(pb) < 0)) 561 return (-1); 562 563 /* Set the state of this revision if specified. */ 564 if (pb->state != NULL) 565 (void)rcs_state_set(pb->file, pb->newrev, pb->state); 566 567 /* Maintain RCSFILE permissions */ 568 if (fstat(workfile_fd, &st) == -1) 569 err(1, "%s", pb->filename); 570 571 /* Strip all the write bits */ 572 pb->file->rf_mode = st.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH); 573 574 (void)close(workfile_fd); 575 (void)unlink(pb->filename); 576 577 /* Write out RCSFILE before calling checkout_rev() */ 578 rcs_write(pb->file); 579 580 /* Do checkout if -u or -l are specified. */ 581 if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) && 582 !(pb->flags & CI_DEFAULT)) 583 checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags, 584 pb->username, pb->author, NULL, NULL); 585 586 if ((pb->flags & INTERACTIVE) && (pb->rcs_msg[0] == '\0')) { 587 xfree(pb->rcs_msg); /* free empty log message */ 588 pb->rcs_msg = NULL; 589 } 590 591 return (0); 592 } 593 594 /* 595 * checkin_init() 596 * 597 * Does an initial check in, just enough to create the new ,v file 598 * On success, return 0. On error return -1. 599 */ 600 static int 601 checkin_init(struct checkin_params *pb) 602 { 603 BUF *bp; 604 char numb[RCS_REV_BUFSZ]; 605 int fetchlog = 0; 606 struct stat st; 607 608 /* If this is a zero-ending RCSNUM eg 4.0, increment it (eg to 4.1) */ 609 if (pb->newrev != NULL && RCSNUM_ZERO_ENDING(pb->newrev)) { 610 pb->frev = rcsnum_alloc(); 611 rcsnum_cpy(pb->newrev, pb->frev, 0); 612 pb->newrev = rcsnum_inc(pb->newrev); 613 fetchlog = 1; 614 } 615 616 /* Load file contents */ 617 if ((bp = buf_load(pb->filename)) == NULL) 618 return (-1); 619 620 /* Get default values from working copy if -k specified */ 621 if (pb->flags & CI_KEYWORDSCAN) 622 checkin_keywordscan(bp, &pb->newrev, 623 &pb->date, &pb->state, &pb->author); 624 625 if (pb->flags & CI_SKIPDESC) 626 goto skipdesc; 627 628 /* Get description from user */ 629 if (pb->description == NULL && 630 rcs_set_description(pb->file, NULL) == -1) { 631 warn("%s", pb->filename); 632 return (-1); 633 } 634 635 skipdesc: 636 637 /* 638 * If the user had specified a zero-ending revision number e.g. 4.0 639 * emulate odd GNU behaviour and fetch log message. 640 */ 641 if (fetchlog == 1) { 642 pb->rcs_msg = checkin_getlogmsg(pb->frev, pb->newrev, 643 pb->flags); 644 rcsnum_free(pb->frev); 645 } 646 647 /* 648 * Set the date of the revision to be the last modification 649 * time of the working file if -d has no argument. 650 */ 651 if (pb->date == DATE_MTIME) 652 checkin_mtimedate(pb); 653 654 /* Now add our new revision */ 655 if (rcs_rev_add(pb->file, 656 (pb->newrev == NULL ? RCS_HEAD_REV : pb->newrev), 657 (pb->rcs_msg == NULL ? "Initial revision" : pb->rcs_msg), 658 pb->date, pb->author) != 0) { 659 warnx("failed to add new revision"); 660 return (-1); 661 } 662 663 /* 664 * If we are checking in to a non-default (ie user-specified) 665 * revision, set head to this revision. 666 */ 667 if (pb->newrev != NULL) { 668 if (rcs_head_set(pb->file, pb->newrev) < 0) 669 errx(1, "rcs_head_set failed"); 670 } else 671 pb->newrev = pb->file->rf_head; 672 673 /* New head revision has to contain entire file; */ 674 if (rcs_deltatext_set(pb->file, pb->file->rf_head, bp) == -1) { 675 warnx("failed to set new head revision"); 676 return (-1); 677 } 678 679 /* Attach a symbolic name to this revision if specified. */ 680 if (pb->symbol != NULL && checkin_attach_symbol(pb) < 0) 681 return (-1); 682 683 /* Set the state of this revision if specified. */ 684 if (pb->state != NULL) 685 (void)rcs_state_set(pb->file, pb->newrev, pb->state); 686 687 /* Inherit RCSFILE permissions from file being checked in */ 688 if (fstat(workfile_fd, &st) == -1) 689 err(1, "%s", pb->filename); 690 691 /* Strip all the write bits */ 692 pb->file->rf_mode = st.st_mode & ~(S_IWUSR|S_IWGRP|S_IWOTH); 693 694 (void)close(workfile_fd); 695 (void)unlink(pb->filename); 696 697 /* Write out RCSFILE before calling checkout_rev() */ 698 rcs_write(pb->file); 699 700 /* Do checkout if -u or -l are specified. */ 701 if (((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) && 702 !(pb->flags & CI_DEFAULT)) { 703 checkout_rev(pb->file, pb->newrev, pb->filename, pb->flags, 704 pb->username, pb->author, NULL, NULL); 705 } 706 707 if (!(pb->flags & QUIET)) { 708 fprintf(stderr, "initial revision: %s\n", 709 rcsnum_tostr(pb->newrev, numb, sizeof(numb))); 710 } 711 712 return (0); 713 } 714 715 /* 716 * checkin_attach_symbol() 717 * 718 * Attempt to attach the specified symbol to the revision. 719 * On success, return 0. On error return -1. 720 */ 721 static int 722 checkin_attach_symbol(struct checkin_params *pb) 723 { 724 char rbuf[RCS_REV_BUFSZ]; 725 int ret; 726 if (!(pb->flags & QUIET)) 727 printf("symbol: %s\n", pb->symbol); 728 if (pb->flags & CI_SYMFORCE) { 729 if (rcs_sym_remove(pb->file, pb->symbol) < 0) { 730 if (rcs_errno != RCS_ERR_NOENT) { 731 warnx("problem removing symbol: %s", 732 pb->symbol); 733 return (-1); 734 } 735 } 736 } 737 if ((ret = rcs_sym_add(pb->file, pb->symbol, pb->newrev) == -1) && 738 (rcs_errno == RCS_ERR_DUPENT)) { 739 rcsnum_tostr(rcs_sym_getrev(pb->file, pb->symbol), 740 rbuf, sizeof(rbuf)); 741 warnx("symbolic name %s already bound to %s", pb->symbol, rbuf); 742 return (-1); 743 } else if (ret == -1) { 744 warnx("problem adding symbol: %s", pb->symbol); 745 return (-1); 746 } 747 return (0); 748 } 749 750 /* 751 * checkin_revert() 752 * 753 * If there are no differences between the working file and the latest revision 754 * and the -f flag is not specified, simply revert to the latest version and 755 * warn the user. 756 * 757 */ 758 static int 759 checkin_revert(struct checkin_params *pb) 760 { 761 char rbuf[RCS_REV_BUFSZ]; 762 763 rcsnum_tostr(pb->frev, rbuf, sizeof(rbuf)); 764 765 if (!(pb->flags & QUIET)) 766 (void)fprintf(stderr, "file is unchanged; reverting " 767 "to previous revision %s\n", rbuf); 768 769 /* Attach a symbolic name to this revision if specified. */ 770 if (pb->symbol != NULL) { 771 if (checkin_checklock(pb) == -1) 772 return (-1); 773 774 pb->newrev = pb->frev; 775 if (checkin_attach_symbol(pb) == -1) 776 return (-1); 777 } 778 779 pb->flags |= CO_REVERT; 780 (void)close(workfile_fd); 781 (void)unlink(pb->filename); 782 783 /* If needed, write out RCSFILE before calling checkout_rev() */ 784 if (pb->symbol != NULL) 785 rcs_write(pb->file); 786 787 if ((pb->flags & CO_LOCK) || (pb->flags & CO_UNLOCK)) 788 checkout_rev(pb->file, pb->frev, pb->filename, 789 pb->flags, pb->username, pb->author, NULL, NULL); 790 791 return (0); 792 } 793 794 /* 795 * checkin_checklock() 796 * 797 * Check for the existence of a lock on the file. If there are no locks, or it 798 * is not locked by the correct user, return -1. Otherwise, return 0. 799 */ 800 static int 801 checkin_checklock(struct checkin_params *pb) 802 { 803 struct rcs_lock *lkp; 804 805 if (rcs_lock_getmode(pb->file) == RCS_LOCK_LOOSE) 806 return (0); 807 808 TAILQ_FOREACH(lkp, &(pb->file->rf_locks), rl_list) { 809 if (!strcmp(lkp->rl_name, pb->username) && 810 !rcsnum_cmp(lkp->rl_num, pb->frev, 0)) 811 return (0); 812 } 813 814 warnx("%s: no lock set by %s", pb->file->rf_path, pb->username); 815 return (-1); 816 } 817 818 /* 819 * checkin_mtimedate() 820 * 821 * Set the date of the revision to be the last modification 822 * time of the working file. 823 */ 824 static void 825 checkin_mtimedate(struct checkin_params *pb) 826 { 827 struct stat sb; 828 829 if (fstat(workfile_fd, &sb) == -1) 830 err(1, "%s", pb->filename); 831 832 pb->date = sb.st_mtimespec.tv_sec; 833 } 834 835 /* 836 * checkin_keywordscan() 837 * 838 * Searches working file for keyword values to determine its revision 839 * number, creation date and author, and uses these values instead of 840 * calculating them locally. 841 * 842 * Params: The data buffer to scan and pointers to pointers of variables in 843 * which to store the outputs. 844 * 845 * On success, return 0. On error return -1. 846 */ 847 static int 848 checkin_keywordscan(BUF *data, RCSNUM **rev, time_t *date, char **author, 849 char **state) 850 { 851 BUF *buf; 852 size_t left; 853 u_int j; 854 char *kwstr; 855 unsigned char *c, *end, *start; 856 857 end = buf_get(data) + buf_len(data) - 1; 858 kwstr = NULL; 859 860 left = buf_len(data); 861 for (c = buf_get(data); 862 c <= end && (c = memchr(c, '$', left)) != NULL; 863 left = end - c + 1) { 864 size_t len; 865 866 start = c; 867 c++; 868 if (!isalpha(*c)) 869 continue; 870 871 /* look for any matching keywords */ 872 for (j = 0; j < 10; j++) { 873 len = strlen(rcs_expkw[j].kw_str); 874 if (left < len) 875 continue; 876 if (memcmp(c, rcs_expkw[j].kw_str, len) != 0) { 877 kwstr = rcs_expkw[j].kw_str; 878 break; 879 } 880 } 881 882 /* unknown keyword, continue looking */ 883 if (kwstr == NULL) 884 continue; 885 886 c += len; 887 if (c > end) { 888 kwstr = NULL; 889 break; 890 } 891 if (*c != ':') { 892 kwstr = NULL; 893 continue; 894 } 895 896 /* Find end of line or end of keyword. */ 897 while (++c <= end) { 898 if (*c == '\n') { 899 /* Skip newline since it is definitely not `$'. */ 900 ++c; 901 goto loopend; 902 } 903 if (*c == '$') 904 break; 905 } 906 907 len = c - start + 1; 908 buf = buf_alloc(len + 1); 909 buf_append(buf, start, len); 910 911 /* XXX - Not binary safe. */ 912 buf_putc(buf, '\0'); 913 checkin_parsekeyword(buf_get(buf), rev, date, author, state); 914 buf_free(buf); 915 loopend:; 916 } 917 if (kwstr == NULL) 918 return (-1); 919 else 920 return (0); 921 } 922 923 /* 924 * checkin_keywordtype() 925 * 926 * Given an RCS keyword string, determine what type of string it is. 927 * This enables us to know what data should be in it. 928 * 929 * Returns type on success, or -1 on failure. 930 */ 931 static int 932 checkin_keywordtype(char *keystring) 933 { 934 char *p; 935 936 p = keystring; 937 p++; 938 if (strncmp(p, KW_ID, strlen(KW_ID)) == 0 || 939 strncmp(p, KW_OPENBSD, strlen(KW_OPENBSD)) == 0) 940 return (KW_TYPE_ID); 941 else if (strncmp(p, KW_AUTHOR, strlen(KW_AUTHOR)) == 0) 942 return (KW_TYPE_AUTHOR); 943 else if (strncmp(p, KW_DATE, strlen(KW_DATE)) == 0) 944 return (KW_TYPE_DATE); 945 else if (strncmp(p, KW_STATE, strlen(KW_STATE)) == 0) 946 return (KW_TYPE_STATE); 947 else if (strncmp(p, KW_REVISION, strlen(KW_REVISION)) == 0) 948 return (KW_TYPE_REVISION); 949 else 950 return (-1); 951 } 952 953 /* 954 * checkin_parsekeyword() 955 * 956 * Do the actual parsing of an RCS keyword string, setting the values passed 957 * to the function to whatever is found. 958 * 959 * XXX - Don't error out on malformed keywords. 960 */ 961 static void 962 checkin_parsekeyword(char *keystring, RCSNUM **rev, time_t *date, 963 char **author, char **state) 964 { 965 char *tokens[KW_NUMTOKS_MAX], *p, *datestring; 966 int i = 0; 967 968 for ((p = strtok(keystring, " ")); p; (p = strtok(NULL, " "))) { 969 if (i < KW_NUMTOKS_MAX - 1) 970 tokens[i++] = p; 971 else 972 break; 973 } 974 975 /* Parse data out of the expanded keyword */ 976 switch (checkin_keywordtype(keystring)) { 977 case KW_TYPE_ID: 978 if (i < 3) 979 break; 980 /* only parse revision if one is not already set */ 981 if (*rev == NULL) { 982 if ((*rev = rcsnum_parse(tokens[2])) == NULL) 983 errx(1, "could not parse rcsnum"); 984 } 985 986 if (i < 5) 987 break; 988 (void)xasprintf(&datestring, "%s %s", tokens[3], tokens[4]); 989 if ((*date = date_parse(datestring)) == -1) 990 errx(1, "could not parse date"); 991 xfree(datestring); 992 993 if (i < 6) 994 break; 995 if (*author != NULL) 996 xfree(*author); 997 *author = xstrdup(tokens[5]); 998 999 if (i < 7) 1000 break; 1001 if (*state != NULL) 1002 xfree(*state); 1003 *state = xstrdup(tokens[6]); 1004 break; 1005 case KW_TYPE_AUTHOR: 1006 if (i < 2) 1007 break; 1008 if (*author != NULL) 1009 xfree(*author); 1010 *author = xstrdup(tokens[1]); 1011 break; 1012 case KW_TYPE_DATE: 1013 if (i < 3) 1014 break; 1015 (void)xasprintf(&datestring, "%s %s", tokens[1], tokens[2]); 1016 if ((*date = date_parse(datestring)) == -1) 1017 errx(1, "could not parse date"); 1018 xfree(datestring); 1019 break; 1020 case KW_TYPE_STATE: 1021 if (i < 2) 1022 break; 1023 if (*state != NULL) 1024 xfree(*state); 1025 *state = xstrdup(tokens[1]); 1026 break; 1027 case KW_TYPE_REVISION: 1028 if (i < 2) 1029 break; 1030 /* only parse revision if one is not already set */ 1031 if (*rev != NULL) 1032 break; 1033 if ((*rev = rcsnum_parse(tokens[1])) == NULL) 1034 errx(1, "could not parse rcsnum"); 1035 break; 1036 } 1037 } 1038