1 /* $OpenBSD: util.c,v 1.138 2008/02/10 14:00:42 joris Exp $ */ 2 /* 3 * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org> 4 * Copyright (c) 2005, 2006 Joris Vink <joris@openbsd.org> 5 * Copyright (c) 2005, 2006 Xavier Santolaria <xsa@openbsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, 18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 19 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 20 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 23 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 25 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 26 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/stat.h> 30 #include <sys/wait.h> 31 32 #include <errno.h> 33 #include <md5.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <paths.h> 37 #include <unistd.h> 38 39 #include "cvs.h" 40 #include "remote.h" 41 42 extern int print_stdout; 43 44 /* letter -> mode type map */ 45 static const int cvs_modetypes[26] = { 46 -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 47 -1, 2, -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, 48 }; 49 50 /* letter -> mode map */ 51 static const mode_t cvs_modes[3][26] = { 52 { 53 0, 0, 0, 0, 0, 0, 0, /* a - g */ 54 0, 0, 0, 0, 0, 0, 0, /* h - m */ 55 0, 0, 0, S_IRUSR, 0, 0, 0, /* n - u */ 56 0, S_IWUSR, S_IXUSR, 0, 0 /* v - z */ 57 }, 58 { 59 0, 0, 0, 0, 0, 0, 0, /* a - g */ 60 0, 0, 0, 0, 0, 0, 0, /* h - m */ 61 0, 0, 0, S_IRGRP, 0, 0, 0, /* n - u */ 62 0, S_IWGRP, S_IXGRP, 0, 0 /* v - z */ 63 }, 64 { 65 0, 0, 0, 0, 0, 0, 0, /* a - g */ 66 0, 0, 0, 0, 0, 0, 0, /* h - m */ 67 0, 0, 0, S_IROTH, 0, 0, 0, /* n - u */ 68 0, S_IWOTH, S_IXOTH, 0, 0 /* v - z */ 69 } 70 }; 71 72 73 /* octal -> string */ 74 static const char *cvs_modestr[8] = { 75 "", "x", "w", "wx", "r", "rx", "rw", "rwx" 76 }; 77 78 /* 79 * cvs_strtomode() 80 * 81 * Read the contents of the string <str> and generate a permission mode from 82 * the contents of <str>, which is assumed to have the mode format of CVS. 83 * The CVS protocol specification states that any modes or mode types that are 84 * not recognized should be silently ignored. This function does not return 85 * an error in such cases, but will issue warnings. 86 */ 87 void 88 cvs_strtomode(const char *str, mode_t *mode) 89 { 90 char type; 91 size_t l; 92 mode_t m; 93 char buf[32], ms[4], *sp, *ep; 94 95 m = 0; 96 l = strlcpy(buf, str, sizeof(buf)); 97 if (l >= sizeof(buf)) 98 fatal("cvs_strtomode: string truncation"); 99 100 sp = buf; 101 ep = sp; 102 103 for (sp = buf; ep != NULL; sp = ep + 1) { 104 ep = strchr(sp, ','); 105 if (ep != NULL) 106 *ep = '\0'; 107 108 memset(ms, 0, sizeof ms); 109 if (sscanf(sp, "%c=%3s", &type, ms) != 2 && 110 sscanf(sp, "%c=", &type) != 1) { 111 fatal("failed to scan mode string `%s'", sp); 112 } 113 114 if (type <= 'a' || type >= 'z' || 115 cvs_modetypes[type - 'a'] == -1) { 116 cvs_log(LP_ERR, 117 "invalid mode type `%c'" 118 " (`u', `g' or `o' expected), ignoring", type); 119 continue; 120 } 121 122 /* make type contain the actual mode index */ 123 type = cvs_modetypes[type - 'a']; 124 125 for (sp = ms; *sp != '\0'; sp++) { 126 if (*sp <= 'a' || *sp >= 'z' || 127 cvs_modes[(int)type][*sp - 'a'] == 0) { 128 fatal("invalid permission bit `%c'", *sp); 129 } else 130 m |= cvs_modes[(int)type][*sp - 'a']; 131 } 132 } 133 134 *mode = m; 135 } 136 137 /* 138 * cvs_modetostr() 139 * 140 * Generate a CVS-format string to represent the permissions mask on a file 141 * from the mode <mode> and store the result in <buf>, which can accept up to 142 * <len> bytes (including the terminating NUL byte). The result is guaranteed 143 * to be NUL-terminated. 144 */ 145 void 146 cvs_modetostr(mode_t mode, char *buf, size_t len) 147 { 148 char tmp[16], *bp; 149 mode_t um, gm, om; 150 151 um = (mode & S_IRWXU) >> 6; 152 gm = (mode & S_IRWXG) >> 3; 153 om = mode & S_IRWXO; 154 155 bp = buf; 156 *bp = '\0'; 157 158 if (um) { 159 if (strlcpy(tmp, "u=", sizeof(tmp)) >= sizeof(tmp) || 160 strlcat(tmp, cvs_modestr[um], sizeof(tmp)) >= sizeof(tmp)) 161 fatal("cvs_modetostr: overflow for user mode"); 162 163 if (strlcat(buf, tmp, len) >= len) 164 fatal("cvs_modetostr: string truncation"); 165 } 166 167 if (gm) { 168 if (um) { 169 if (strlcat(buf, ",", len) >= len) 170 fatal("cvs_modetostr: string truncation"); 171 } 172 173 if (strlcpy(tmp, "g=", sizeof(tmp)) >= sizeof(tmp) || 174 strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp)) 175 fatal("cvs_modetostr: overflow for group mode"); 176 177 if (strlcat(buf, tmp, len) >= len) 178 fatal("cvs_modetostr: string truncation"); 179 } 180 181 if (om) { 182 if (um || gm) { 183 if (strlcat(buf, ",", len) >= len) 184 fatal("cvs_modetostr: string truncation"); 185 } 186 187 if (strlcpy(tmp, "o=", sizeof(tmp)) >= sizeof(tmp) || 188 strlcat(tmp, cvs_modestr[gm], sizeof(tmp)) >= sizeof(tmp)) 189 fatal("cvs_modetostr: overflow for others mode"); 190 191 if (strlcat(buf, tmp, len) >= len) 192 fatal("cvs_modetostr: string truncation"); 193 } 194 } 195 196 /* 197 * cvs_cksum() 198 * 199 * Calculate the MD5 checksum of the file whose path is <file> and generate 200 * a CVS-format 32 hex-digit string, which is stored in <dst>, whose size is 201 * given in <len> and must be at least 33. 202 * Returns 0 on success, or -1 on failure. 203 */ 204 int 205 cvs_cksum(const char *file, char *dst, size_t len) 206 { 207 if (len < CVS_CKSUM_LEN) { 208 cvs_log(LP_ERR, "buffer too small for checksum"); 209 return (-1); 210 } 211 if (MD5File(file, dst) == NULL) { 212 cvs_log(LP_ERR, "failed to generate checksum for %s", file); 213 return (-1); 214 } 215 216 return (0); 217 } 218 219 /* 220 * cvs_getargv() 221 * 222 * Parse a line contained in <line> and generate an argument vector by 223 * splitting the line on spaces and tabs. The resulting vector is stored in 224 * <argv>, which can accept up to <argvlen> entries. 225 * Returns the number of arguments in the vector, or -1 if an error occurred. 226 */ 227 int 228 cvs_getargv(const char *line, char **argv, int argvlen) 229 { 230 u_int i; 231 int argc, error; 232 char *linebuf, *lp, *cp; 233 234 linebuf = xstrdup(line); 235 236 memset(argv, 0, argvlen * sizeof(char *)); 237 argc = 0; 238 239 /* build the argument vector */ 240 error = 0; 241 for (lp = linebuf; lp != NULL;) { 242 cp = strsep(&lp, " \t"); 243 if (cp == NULL) 244 break; 245 else if (*cp == '\0') 246 continue; 247 248 if (argc == argvlen) { 249 error++; 250 break; 251 } 252 253 argv[argc] = xstrdup(cp); 254 argc++; 255 } 256 257 if (error != 0) { 258 /* ditch the argument vector */ 259 for (i = 0; i < (u_int)argc; i++) 260 xfree(argv[i]); 261 argc = -1; 262 } 263 264 xfree(linebuf); 265 return (argc); 266 } 267 268 /* 269 * cvs_makeargv() 270 * 271 * Allocate an argument vector large enough to accommodate for all the 272 * arguments found in <line> and return it. 273 */ 274 char ** 275 cvs_makeargv(const char *line, int *argc) 276 { 277 int i, ret; 278 char *argv[1024], **copy; 279 280 ret = cvs_getargv(line, argv, 1024); 281 if (ret == -1) 282 return (NULL); 283 284 copy = xcalloc(ret + 1, sizeof(char *)); 285 286 for (i = 0; i < ret; i++) 287 copy[i] = argv[i]; 288 copy[ret] = NULL; 289 290 *argc = ret; 291 return (copy); 292 } 293 294 /* 295 * cvs_freeargv() 296 * 297 * Free an argument vector previously generated by cvs_getargv(). 298 */ 299 void 300 cvs_freeargv(char **argv, int argc) 301 { 302 int i; 303 304 for (i = 0; i < argc; i++) 305 if (argv[i] != NULL) 306 xfree(argv[i]); 307 } 308 309 /* 310 * cvs_chdir() 311 * 312 * Change to directory <path>. 313 * If <rm> is equal to `1', <path> is removed if chdir() fails so we 314 * do not have temporary directories leftovers. 315 * Returns 0 on success. 316 */ 317 int 318 cvs_chdir(const char *path, int rm) 319 { 320 if (chdir(path) == -1) { 321 if (rm == 1) 322 cvs_unlink(path); 323 fatal("cvs_chdir: `%s': %s", path, strerror(errno)); 324 } 325 326 return (0); 327 } 328 329 /* 330 * cvs_rename() 331 * Change the name of a file. 332 * rename() wrapper with an error message. 333 * Returns 0 on success. 334 */ 335 int 336 cvs_rename(const char *from, const char *to) 337 { 338 if (cvs_server_active == 0) 339 cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to); 340 341 if (cvs_noexec == 1) 342 return (0); 343 344 if (rename(from, to) == -1) 345 fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno)); 346 347 return (0); 348 } 349 350 /* 351 * cvs_unlink() 352 * 353 * Removes the link named by <path>. 354 * unlink() wrapper with an error message. 355 * Returns 0 on success, or -1 on failure. 356 */ 357 int 358 cvs_unlink(const char *path) 359 { 360 if (cvs_server_active == 0) 361 cvs_log(LP_TRACE, "cvs_unlink(%s)", path); 362 363 if (cvs_noexec == 1) 364 return (0); 365 366 if (unlink(path) == -1 && errno != ENOENT) { 367 cvs_log(LP_ERRNO, "%s", path); 368 return (-1); 369 } 370 371 return (0); 372 } 373 374 /* 375 * cvs_rmdir() 376 * 377 * Remove a directory tree from disk. 378 * Returns 0 on success, or -1 on failure. 379 */ 380 int 381 cvs_rmdir(const char *path) 382 { 383 int type, ret = -1; 384 DIR *dirp; 385 struct dirent *ent; 386 struct stat st; 387 char fpath[MAXPATHLEN]; 388 389 if (cvs_server_active == 0) 390 cvs_log(LP_TRACE, "cvs_rmdir(%s)", path); 391 392 if (cvs_noexec == 1) 393 return (0); 394 395 if ((dirp = opendir(path)) == NULL) { 396 cvs_log(LP_ERR, "failed to open '%s'", path); 397 return (-1); 398 } 399 400 while ((ent = readdir(dirp)) != NULL) { 401 if (!strcmp(ent->d_name, ".") || 402 !strcmp(ent->d_name, "..")) 403 continue; 404 405 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s", 406 path, ent->d_name); 407 408 if (ent->d_type == DT_UNKNOWN) { 409 if (lstat(fpath, &st) == -1) 410 fatal("'%s': %s", fpath, strerror(errno)); 411 412 switch (st.st_mode & S_IFMT) { 413 case S_IFDIR: 414 type = CVS_DIR; 415 break; 416 case S_IFREG: 417 type = CVS_FILE; 418 break; 419 default: 420 fatal("'%s': Unknown file type in copy", 421 fpath); 422 } 423 } else { 424 switch (ent->d_type) { 425 case DT_DIR: 426 type = CVS_DIR; 427 break; 428 case DT_REG: 429 type = CVS_FILE; 430 break; 431 default: 432 fatal("'%s': Unknown file type in copy", 433 fpath); 434 } 435 } 436 switch (type) { 437 case CVS_DIR: 438 if (cvs_rmdir(fpath) == -1) 439 goto done; 440 break; 441 case CVS_FILE: 442 if (cvs_unlink(fpath) == -1 && errno != ENOENT) 443 goto done; 444 break; 445 default: 446 fatal("type %d unknown, shouldn't happen", type); 447 } 448 } 449 450 451 if (rmdir(path) == -1 && errno != ENOENT) { 452 cvs_log(LP_ERRNO, "%s", path); 453 goto done; 454 } 455 456 ret = 0; 457 done: 458 closedir(dirp); 459 return (ret); 460 } 461 462 void 463 cvs_get_repository_path(const char *dir, char *dst, size_t len) 464 { 465 char buf[MAXPATHLEN]; 466 467 cvs_get_repository_name(dir, buf, sizeof(buf)); 468 (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf); 469 } 470 471 void 472 cvs_get_repository_name(const char *dir, char *dst, size_t len) 473 { 474 FILE *fp; 475 char fpath[MAXPATHLEN]; 476 477 dst[0] = '\0'; 478 479 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) { 480 if (strlcpy(dst, dir, len) >= len) 481 fatal("cvs_get_repository_name: truncation"); 482 return; 483 } 484 485 switch (cvs_cmdop) { 486 case CVS_OP_EXPORT: 487 if (strcmp(dir, ".")) 488 if (strlcpy(dst, dir, len) >= len) 489 fatal("cvs_get_repository_name: truncation"); 490 break; 491 case CVS_OP_IMPORT: 492 if (strlcpy(dst, import_repository, len) >= len) 493 fatal("cvs_get_repository_name: truncation"); 494 if (strlcat(dst, "/", len) >= len) 495 fatal("cvs_get_repository_name: truncation"); 496 497 if (strcmp(dir, ".")) 498 if (strlcat(dst, dir, len) >= len) 499 fatal("cvs_get_repository_name: truncation"); 500 break; 501 default: 502 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s", 503 dir, CVS_PATH_REPOSITORY); 504 if ((fp = fopen(fpath, "r")) != NULL) { 505 if ((fgets(dst, len, fp)) == NULL) 506 fatal("%s: bad repository file", fpath); 507 dst[strcspn(dst, "\n")] = '\0'; 508 (void)fclose(fp); 509 } else if (cvs_cmdop != CVS_OP_CHECKOUT) 510 fatal("%s is missing", fpath); 511 break; 512 } 513 } 514 515 void 516 cvs_mkadmin(const char *path, const char *root, const char *repo, 517 char *tag, char *date) 518 { 519 FILE *fp; 520 char buf[MAXPATHLEN]; 521 522 if (cvs_server_active == 0) 523 cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)", 524 path, root, repo, (tag != NULL) ? tag : "", 525 (date != NULL) ? date : ""); 526 527 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR); 528 529 if (mkdir(buf, 0755) == -1 && errno != EEXIST) 530 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 531 532 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ROOTSPEC); 533 534 if ((fp = fopen(buf, "w")) == NULL) 535 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 536 537 fprintf(fp, "%s\n", root); 538 (void)fclose(fp); 539 540 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY); 541 542 if ((fp = fopen(buf, "w")) == NULL) 543 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 544 545 fprintf(fp, "%s\n", repo); 546 (void)fclose(fp); 547 548 cvs_write_tagfile(path, tag, date); 549 } 550 551 void 552 cvs_mkpath(const char *path, char *tag) 553 { 554 CVSENTRIES *ent; 555 FILE *fp; 556 size_t len; 557 char *entry, sticky[CVS_REV_BUFSZ]; 558 char *sp, *dp, *dir, *p, rpath[MAXPATHLEN], repo[MAXPATHLEN]; 559 560 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 561 cvs_server_active == 1) 562 cvs_validate_directory(path); 563 564 dir = xstrdup(path); 565 566 STRIP_SLASH(dir); 567 568 if (cvs_server_active == 0) 569 cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir); 570 571 repo[0] = '\0'; 572 rpath[0] = '\0'; 573 574 if (cvs_cmdop == CVS_OP_UPDATE || cvs_cmdop == CVS_OP_COMMIT) { 575 if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) { 576 if ((fgets(repo, sizeof(repo), fp)) == NULL) 577 fatal("cvs_mkpath: bad repository file"); 578 repo[strcspn(repo, "\n")] = '\0'; 579 (void)fclose(fp); 580 } 581 } 582 583 for (sp = dir; sp != NULL; sp = dp) { 584 dp = strchr(sp, '/'); 585 if (dp != NULL) 586 *(dp++) = '\0'; 587 588 if (sp == dir && module_repo_root != NULL) { 589 len = strlcpy(repo, module_repo_root, sizeof(repo)); 590 if (len >= (int)sizeof(repo)) 591 fatal("cvs_mkpath: overflow"); 592 } else if (strcmp(sp, ".")) { 593 if (repo[0] != '\0') { 594 len = strlcat(repo, "/", sizeof(repo)); 595 if (len >= (int)sizeof(repo)) 596 fatal("cvs_mkpath: overflow"); 597 } 598 599 len = strlcat(repo, sp, sizeof(repo)); 600 if (len >= (int)sizeof(repo)) 601 fatal("cvs_mkpath: overflow"); 602 } 603 604 if (rpath[0] != '\0') { 605 len = strlcat(rpath, "/", sizeof(rpath)); 606 if (len >= (int)sizeof(rpath)) 607 fatal("cvs_mkpath: overflow"); 608 } 609 610 len = strlcat(rpath, sp, sizeof(rpath)); 611 if (len >= (int)sizeof(rpath)) 612 fatal("cvs_mkpath: overflow"); 613 614 if (mkdir(rpath, 0755) == -1 && errno != EEXIST) 615 fatal("cvs_mkpath: %s: %s", rpath, strerror(errno)); 616 617 if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active) 618 continue; 619 620 cvs_mkadmin(rpath, current_cvsroot->cr_str, repo, 621 tag, NULL); 622 623 if (dp != NULL) { 624 if ((p = strchr(dp, '/')) != NULL) 625 *p = '\0'; 626 627 entry = xmalloc(CVS_ENT_MAXLINELEN); 628 cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0, 629 entry, CVS_ENT_MAXLINELEN); 630 631 ent = cvs_ent_open(rpath); 632 cvs_ent_add(ent, entry); 633 cvs_ent_close(ent, ENT_SYNC); 634 xfree(entry); 635 636 if (p != NULL) 637 *p = '/'; 638 } 639 640 if (cvs_server_active == 1 && strcmp(rpath, ".")) { 641 if (tag != NULL) { 642 (void)xsnprintf(sticky, sizeof(sticky), 643 "T%s", tag); 644 cvs_server_set_sticky(rpath, sticky); 645 } 646 } 647 } 648 649 xfree(dir); 650 } 651 652 /* 653 * Split the contents of a file into a list of lines. 654 */ 655 struct cvs_lines * 656 cvs_splitlines(u_char *data, size_t len) 657 { 658 u_char *p, *c; 659 size_t i, tlen; 660 struct cvs_lines *lines; 661 struct cvs_line *lp; 662 663 lines = xcalloc(1, sizeof(*lines)); 664 TAILQ_INIT(&(lines->l_lines)); 665 666 lp = xcalloc(1, sizeof(*lp)); 667 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 668 669 p = c = data; 670 for (i = 0; i < len; i++) { 671 if (*p == '\n' || (i == len - 1)) { 672 tlen = p - c + 1; 673 lp = xcalloc(1, sizeof(*lp)); 674 lp->l_line = c; 675 lp->l_len = tlen; 676 lp->l_lineno = ++(lines->l_nblines); 677 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 678 c = p + 1; 679 } 680 p++; 681 } 682 683 return (lines); 684 } 685 686 void 687 cvs_freelines(struct cvs_lines *lines) 688 { 689 struct cvs_line *lp; 690 691 while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) { 692 TAILQ_REMOVE(&(lines->l_lines), lp, l_list); 693 if (lp->l_needsfree == 1) 694 xfree(lp->l_line); 695 xfree(lp); 696 } 697 698 xfree(lines); 699 } 700 701 /* 702 * cvs_strsplit() 703 * 704 * Split a string <str> of <sep>-separated values and allocate 705 * an argument vector for the values found. 706 */ 707 struct cvs_argvector * 708 cvs_strsplit(char *str, const char *sep) 709 { 710 struct cvs_argvector *av; 711 size_t i = 0; 712 char *cp, *p; 713 714 cp = xstrdup(str); 715 av = xmalloc(sizeof(*av)); 716 av->str = cp; 717 av->argv = xmalloc(sizeof(*(av->argv))); 718 719 while ((p = strsep(&cp, sep)) != NULL) { 720 av->argv[i++] = p; 721 av->argv = xrealloc(av->argv, 722 i + 1, sizeof(*(av->argv))); 723 } 724 av->argv[i] = NULL; 725 726 return (av); 727 } 728 729 /* 730 * cvs_argv_destroy() 731 * 732 * Free an argument vector previously allocated by cvs_strsplit(). 733 */ 734 void 735 cvs_argv_destroy(struct cvs_argvector *av) 736 { 737 xfree(av->str); 738 xfree(av->argv); 739 xfree(av); 740 } 741 742 u_int 743 cvs_revision_select(RCSFILE *file, char *range) 744 { 745 int i; 746 u_int nrev; 747 char *lstr, *rstr; 748 struct rcs_delta *rdp; 749 struct cvs_argvector *revargv, *revrange; 750 RCSNUM *lnum, *rnum; 751 752 nrev = 0; 753 lnum = rnum = NULL; 754 755 revargv = cvs_strsplit(range, ","); 756 for (i = 0; revargv->argv[i] != NULL; i++) { 757 revrange = cvs_strsplit(revargv->argv[i], ":"); 758 if (revrange->argv[0] == NULL) 759 fatal("invalid revision range: %s", revargv->argv[i]); 760 else if (revrange->argv[1] == NULL) 761 lstr = rstr = revrange->argv[0]; 762 else { 763 if (revrange->argv[2] != NULL) 764 fatal("invalid revision range: %s", 765 revargv->argv[i]); 766 767 lstr = revrange->argv[0]; 768 rstr = revrange->argv[1]; 769 770 if (strcmp(lstr, "") == 0) 771 lstr = NULL; 772 if (strcmp(rstr, "") == 0) 773 rstr = NULL; 774 } 775 776 if (lstr == NULL) 777 lstr = RCS_HEAD_INIT; 778 779 if ((lnum = rcs_translate_tag(lstr, file)) == NULL) 780 fatal("cvs_revision_select: could not translate tag `%s'", lstr); 781 782 if (rstr != NULL) { 783 if ((rnum = rcs_translate_tag(rstr, file)) == NULL) 784 fatal("cvs_revision_select: could not translate tag `%s'", rstr); 785 } else { 786 rnum = rcsnum_alloc(); 787 rcsnum_cpy(file->rf_head, rnum, 0); 788 } 789 790 cvs_argv_destroy(revrange); 791 792 TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) { 793 if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 && 794 rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 && 795 !(rdp->rd_flags & RCS_RD_SELECT)) { 796 rdp->rd_flags |= RCS_RD_SELECT; 797 nrev++; 798 } 799 } 800 801 rcsnum_free(lnum); 802 rcsnum_free(rnum); 803 } 804 805 cvs_argv_destroy(revargv); 806 807 return (nrev); 808 } 809 810 int 811 cvs_yesno(void) 812 { 813 int c, ret; 814 815 ret = 0; 816 817 fflush(stderr); 818 fflush(stdout); 819 820 if ((c = getchar()) != 'y' && c != 'Y') 821 ret = -1; 822 else 823 while (c != EOF && c != '\n') 824 c = getchar(); 825 826 return (ret); 827 } 828 829 void 830 cvs_exec(const char *prog) 831 { 832 pid_t pid; 833 char *argp[] = { "sh", "-c", NULL, NULL }; 834 835 argp[2] = prog; 836 837 if ((pid = fork()) == -1) { 838 cvs_log(LP_ERR, "cvs_exec: fork failed"); 839 return; 840 } else if (pid == 0) { 841 execv(_PATH_BSHELL, argp); 842 cvs_log(LP_ERR, "failed to run '%s'", prog); 843 _exit(127); 844 } 845 } 846