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