1 /* $OpenBSD: util.c,v 1.158 2015/11/05 09:48:21 nicm 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 (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 578 cvs_server_active == 1) 579 cvs_validate_directory(path); 580 581 dir = xstrdup(path); 582 583 STRIP_SLASH(dir); 584 585 if (cvs_server_active == 0) 586 cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir); 587 588 repo[0] = '\0'; 589 rpath[0] = '\0'; 590 591 if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) { 592 if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) { 593 if ((fgets(repo, sizeof(repo), fp)) == NULL) 594 fatal("cvs_mkpath: bad repository file"); 595 repo[strcspn(repo, "\n")] = '\0'; 596 (void)fclose(fp); 597 } 598 } 599 600 for (sp = dir; sp != NULL; sp = dp) { 601 dp = strchr(sp, '/'); 602 if (dp != NULL) 603 *(dp++) = '\0'; 604 605 if (sp == dir && module_repo_root != NULL) { 606 len = strlcpy(repo, module_repo_root, sizeof(repo)); 607 if (len >= (int)sizeof(repo)) 608 fatal("cvs_mkpath: overflow"); 609 } else if (strcmp(sp, ".")) { 610 if (repo[0] != '\0') { 611 len = strlcat(repo, "/", sizeof(repo)); 612 if (len >= (int)sizeof(repo)) 613 fatal("cvs_mkpath: overflow"); 614 } 615 616 len = strlcat(repo, sp, sizeof(repo)); 617 if (len >= (int)sizeof(repo)) 618 fatal("cvs_mkpath: overflow"); 619 } 620 621 if (rpath[0] != '\0') { 622 len = strlcat(rpath, "/", sizeof(rpath)); 623 if (len >= (int)sizeof(rpath)) 624 fatal("cvs_mkpath: overflow"); 625 } 626 627 len = strlcat(rpath, sp, sizeof(rpath)); 628 if (len >= (int)sizeof(rpath)) 629 fatal("cvs_mkpath: overflow"); 630 631 if (mkdir(rpath, 0755) == -1 && errno != EEXIST) 632 fatal("cvs_mkpath: %s: %s", rpath, strerror(errno)); 633 634 if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active) 635 continue; 636 637 cvs_mkadmin(rpath, current_cvsroot->cr_str, repo, 638 tag, NULL); 639 640 if (dp != NULL) { 641 if ((p = strchr(dp, '/')) != NULL) 642 *p = '\0'; 643 644 entry = xmalloc(CVS_ENT_MAXLINELEN); 645 cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0, 646 entry, CVS_ENT_MAXLINELEN); 647 648 ent = cvs_ent_open(rpath); 649 cvs_ent_add(ent, entry); 650 free(entry); 651 652 if (p != NULL) 653 *p = '/'; 654 } 655 } 656 657 free(dir); 658 } 659 660 void 661 cvs_mkdir(const char *path, mode_t mode) 662 { 663 size_t len; 664 char *sp, *dp, *dir, rpath[PATH_MAX]; 665 666 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 667 cvs_server_active == 1) 668 cvs_validate_directory(path); 669 670 dir = xstrdup(path); 671 672 STRIP_SLASH(dir); 673 674 if (cvs_server_active == 0) 675 cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir); 676 677 rpath[0] = '\0'; 678 679 for (sp = dir; sp != NULL; sp = dp) { 680 dp = strchr(sp, '/'); 681 if (dp != NULL) 682 *(dp++) = '\0'; 683 684 len = strlcat(rpath, "/", sizeof(rpath)); 685 if (len >= (int)sizeof(rpath)) 686 fatal("cvs_mkdir: overflow"); 687 688 len = strlcat(rpath, sp, sizeof(rpath)); 689 if (len >= (int)sizeof(rpath)) 690 fatal("cvs_mkdir: overflow"); 691 if (1 == len) 692 continue; 693 694 if (mkdir(rpath, mode) == -1 && errno != EEXIST) 695 fatal("cvs_mkdir: %s: %s", rpath, strerror(errno)); 696 } 697 698 free(dir); 699 } 700 701 /* 702 * Split the contents of a file into a list of lines. 703 */ 704 struct rcs_lines * 705 cvs_splitlines(u_char *data, size_t len) 706 { 707 u_char *p, *c; 708 size_t i, tlen; 709 struct rcs_lines *lines; 710 struct rcs_line *lp; 711 712 lines = xcalloc(1, sizeof(*lines)); 713 TAILQ_INIT(&(lines->l_lines)); 714 715 lp = xcalloc(1, sizeof(*lp)); 716 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 717 718 p = c = data; 719 for (i = 0; i < len; i++) { 720 if (*p == '\n' || (i == len - 1)) { 721 tlen = p - c + 1; 722 lp = xcalloc(1, sizeof(*lp)); 723 lp->l_line = c; 724 lp->l_len = tlen; 725 lp->l_lineno = ++(lines->l_nblines); 726 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 727 c = p + 1; 728 } 729 p++; 730 } 731 732 return (lines); 733 } 734 735 void 736 cvs_freelines(struct rcs_lines *lines) 737 { 738 struct rcs_line *lp; 739 740 while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) { 741 TAILQ_REMOVE(&(lines->l_lines), lp, l_list); 742 if (lp->l_needsfree == 1) 743 free(lp->l_line); 744 free(lp); 745 } 746 747 free(lines); 748 } 749 750 /* 751 * cvs_strsplit() 752 * 753 * Split a string <str> of <sep>-separated values and allocate 754 * an argument vector for the values found. 755 */ 756 struct cvs_argvector * 757 cvs_strsplit(char *str, const char *sep) 758 { 759 struct cvs_argvector *av; 760 size_t i = 0; 761 char *cp, *p; 762 763 cp = xstrdup(str); 764 av = xmalloc(sizeof(*av)); 765 av->str = cp; 766 av->argv = xmalloc(sizeof(*(av->argv))); 767 768 while ((p = strsep(&cp, sep)) != NULL) { 769 av->argv[i++] = p; 770 av->argv = xreallocarray(av->argv, 771 i + 1, sizeof(*(av->argv))); 772 } 773 av->argv[i] = NULL; 774 775 return (av); 776 } 777 778 /* 779 * cvs_argv_destroy() 780 * 781 * Free an argument vector previously allocated by cvs_strsplit(). 782 */ 783 void 784 cvs_argv_destroy(struct cvs_argvector *av) 785 { 786 free(av->str); 787 free(av->argv); 788 free(av); 789 } 790 791 u_int 792 cvs_revision_select(RCSFILE *file, char *range) 793 { 794 int i; 795 u_int nrev; 796 char *lstr, *rstr; 797 struct rcs_delta *rdp; 798 struct cvs_argvector *revargv, *revrange; 799 RCSNUM *lnum, *rnum; 800 801 nrev = 0; 802 lnum = rnum = NULL; 803 804 revargv = cvs_strsplit(range, ","); 805 for (i = 0; revargv->argv[i] != NULL; i++) { 806 revrange = cvs_strsplit(revargv->argv[i], ":"); 807 if (revrange->argv[0] == NULL) 808 fatal("invalid revision range: %s", revargv->argv[i]); 809 else if (revrange->argv[1] == NULL) 810 lstr = rstr = revrange->argv[0]; 811 else { 812 if (revrange->argv[2] != NULL) 813 fatal("invalid revision range: %s", 814 revargv->argv[i]); 815 816 lstr = revrange->argv[0]; 817 rstr = revrange->argv[1]; 818 819 if (strcmp(lstr, "") == 0) 820 lstr = NULL; 821 if (strcmp(rstr, "") == 0) 822 rstr = NULL; 823 } 824 825 if (lstr == NULL) 826 lstr = RCS_HEAD_INIT; 827 828 if ((lnum = rcs_translate_tag(lstr, file)) == NULL) 829 fatal("cvs_revision_select: could not translate tag `%s'", lstr); 830 831 if (rstr != NULL) { 832 if ((rnum = rcs_translate_tag(rstr, file)) == NULL) 833 fatal("cvs_revision_select: could not translate tag `%s'", rstr); 834 } else { 835 rnum = rcsnum_alloc(); 836 rcsnum_cpy(file->rf_head, rnum, 0); 837 } 838 839 cvs_argv_destroy(revrange); 840 841 TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) { 842 if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 && 843 rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 && 844 !(rdp->rd_flags & RCS_RD_SELECT)) { 845 rdp->rd_flags |= RCS_RD_SELECT; 846 nrev++; 847 } 848 } 849 850 rcsnum_free(lnum); 851 rcsnum_free(rnum); 852 } 853 854 cvs_argv_destroy(revargv); 855 856 return (nrev); 857 } 858 859 int 860 cvs_yesno(void) 861 { 862 int c, ret; 863 864 ret = 0; 865 866 fflush(stderr); 867 fflush(stdout); 868 869 if ((c = getchar()) != 'y' && c != 'Y') 870 ret = -1; 871 else 872 while (c != EOF && c != '\n') 873 c = getchar(); 874 875 return (ret); 876 } 877 878 /* 879 * cvs_exec() 880 * 881 * Execute <prog> and send <in> to the STDIN if not NULL. 882 * If <needwait> == 1, return the result of <prog>, 883 * else, 0 or -1 if an error occur. 884 */ 885 int 886 cvs_exec(char *prog, const char *in, int needwait) 887 { 888 pid_t pid; 889 size_t size; 890 int fds[2], st; 891 char *argp[4] = { "sh", "-c", prog, NULL }; 892 893 if (in != NULL && pipe(fds) < 0) { 894 cvs_log(LP_ERR, "cvs_exec: pipe failed"); 895 return (-1); 896 } 897 898 if ((pid = fork()) == -1) { 899 cvs_log(LP_ERR, "cvs_exec: fork failed"); 900 return (-1); 901 } else if (pid == 0) { 902 if (in != NULL) { 903 close(fds[1]); 904 dup2(fds[0], STDIN_FILENO); 905 } 906 907 setenv("CVSROOT", current_cvsroot->cr_dir, 1); 908 execv(_PATH_BSHELL, argp); 909 cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog); 910 _exit(127); 911 } 912 913 if (in != NULL) { 914 close(fds[0]); 915 size = strlen(in); 916 if (atomicio(vwrite, fds[1], in, size) != size) 917 cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN"); 918 close(fds[1]); 919 } 920 921 if (needwait == 1) { 922 while (waitpid(pid, &st, 0) == -1) 923 ; 924 if (!WIFEXITED(st)) { 925 errno = EINTR; 926 return (-1); 927 } 928 return (WEXITSTATUS(st)); 929 } 930 931 return (0); 932 } 933