1 /* $OpenBSD: util.c,v 1.155 2011/04/20 18:43:57 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 xfree(argv[i]); 243 argc = -1; 244 } 245 246 xfree(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 if (argv[i] != NULL) 288 xfree(argv[i]); 289 } 290 291 /* 292 * cvs_chdir() 293 * 294 * Change to directory <path>. 295 * If <rm> is equal to `1', <path> is removed if chdir() fails so we 296 * do not have temporary directories leftovers. 297 * Returns 0 on success. 298 */ 299 int 300 cvs_chdir(const char *path, int rm) 301 { 302 if (chdir(path) == -1) { 303 if (rm == 1) 304 cvs_unlink(path); 305 fatal("cvs_chdir: `%s': %s", path, strerror(errno)); 306 } 307 308 return (0); 309 } 310 311 /* 312 * cvs_rename() 313 * Change the name of a file. 314 * rename() wrapper with an error message. 315 * Returns 0 on success. 316 */ 317 int 318 cvs_rename(const char *from, const char *to) 319 { 320 if (cvs_server_active == 0) 321 cvs_log(LP_TRACE, "cvs_rename(%s,%s)", from, to); 322 323 if (cvs_noexec == 1) 324 return (0); 325 326 if (rename(from, to) == -1) 327 fatal("cvs_rename: `%s'->`%s': %s", from, to, strerror(errno)); 328 329 return (0); 330 } 331 332 /* 333 * cvs_unlink() 334 * 335 * Removes the link named by <path>. 336 * unlink() wrapper with an error message. 337 * Returns 0 on success, or -1 on failure. 338 */ 339 int 340 cvs_unlink(const char *path) 341 { 342 if (cvs_server_active == 0) 343 cvs_log(LP_TRACE, "cvs_unlink(%s)", path); 344 345 if (cvs_noexec == 1 && disable_fast_checkout != 0) 346 return (0); 347 348 if (unlink(path) == -1 && errno != ENOENT) { 349 cvs_log(LP_ERRNO, "%s", path); 350 return (-1); 351 } 352 353 return (0); 354 } 355 356 /* 357 * cvs_rmdir() 358 * 359 * Remove a directory tree from disk. 360 * Returns 0 on success, or -1 on failure. 361 */ 362 int 363 cvs_rmdir(const char *path) 364 { 365 int type, ret = -1; 366 DIR *dirp; 367 struct dirent *ent; 368 struct stat st; 369 char fpath[MAXPATHLEN]; 370 371 if (cvs_server_active == 0) 372 cvs_log(LP_TRACE, "cvs_rmdir(%s)", path); 373 374 if (cvs_noexec == 1 && disable_fast_checkout != 0) 375 return (0); 376 377 if ((dirp = opendir(path)) == NULL) { 378 cvs_log(LP_ERR, "failed to open '%s'", path); 379 return (-1); 380 } 381 382 while ((ent = readdir(dirp)) != NULL) { 383 if (!strcmp(ent->d_name, ".") || 384 !strcmp(ent->d_name, "..")) 385 continue; 386 387 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s", 388 path, ent->d_name); 389 390 if (ent->d_type == DT_UNKNOWN) { 391 if (lstat(fpath, &st) == -1) 392 fatal("'%s': %s", fpath, strerror(errno)); 393 394 switch (st.st_mode & S_IFMT) { 395 case S_IFDIR: 396 type = CVS_DIR; 397 break; 398 case S_IFREG: 399 type = CVS_FILE; 400 break; 401 default: 402 fatal("'%s': Unknown file type in copy", 403 fpath); 404 } 405 } else { 406 switch (ent->d_type) { 407 case DT_DIR: 408 type = CVS_DIR; 409 break; 410 case DT_REG: 411 type = CVS_FILE; 412 break; 413 default: 414 fatal("'%s': Unknown file type in copy", 415 fpath); 416 } 417 } 418 switch (type) { 419 case CVS_DIR: 420 if (cvs_rmdir(fpath) == -1) 421 goto done; 422 break; 423 case CVS_FILE: 424 if (cvs_unlink(fpath) == -1 && errno != ENOENT) 425 goto done; 426 break; 427 default: 428 fatal("type %d unknown, shouldn't happen", type); 429 } 430 } 431 432 433 if (rmdir(path) == -1 && errno != ENOENT) { 434 cvs_log(LP_ERRNO, "%s", path); 435 goto done; 436 } 437 438 ret = 0; 439 done: 440 closedir(dirp); 441 return (ret); 442 } 443 444 void 445 cvs_get_repository_path(const char *dir, char *dst, size_t len) 446 { 447 char buf[MAXPATHLEN]; 448 449 cvs_get_repository_name(dir, buf, sizeof(buf)); 450 (void)xsnprintf(dst, len, "%s/%s", current_cvsroot->cr_dir, buf); 451 cvs_validate_directory(dst); 452 } 453 454 void 455 cvs_get_repository_name(const char *dir, char *dst, size_t len) 456 { 457 FILE *fp; 458 char fpath[MAXPATHLEN]; 459 460 dst[0] = '\0'; 461 462 if (!(cmdp->cmd_flags & CVS_USE_WDIR)) { 463 if (strlcpy(dst, dir, len) >= len) 464 fatal("cvs_get_repository_name: truncation"); 465 return; 466 } 467 468 switch (cvs_cmdop) { 469 case CVS_OP_EXPORT: 470 if (strcmp(dir, ".")) 471 if (strlcpy(dst, dir, len) >= len) 472 fatal("cvs_get_repository_name: truncation"); 473 break; 474 case CVS_OP_IMPORT: 475 if (strlcpy(dst, import_repository, len) >= len) 476 fatal("cvs_get_repository_name: truncation"); 477 if (strlcat(dst, "/", len) >= len) 478 fatal("cvs_get_repository_name: truncation"); 479 480 if (strcmp(dir, ".")) 481 if (strlcat(dst, dir, len) >= len) 482 fatal("cvs_get_repository_name: truncation"); 483 break; 484 default: 485 (void)xsnprintf(fpath, sizeof(fpath), "%s/%s", 486 dir, CVS_PATH_REPOSITORY); 487 if ((fp = fopen(fpath, "r")) != NULL) { 488 if ((fgets(dst, len, fp)) == NULL) 489 fatal("%s: bad repository file", fpath); 490 dst[strcspn(dst, "\n")] = '\0'; 491 (void)fclose(fp); 492 } else if (cvs_cmdop != CVS_OP_CHECKOUT) 493 fatal("%s is missing", fpath); 494 break; 495 } 496 } 497 498 void 499 cvs_mkadmin(const char *path, const char *root, const char *repo, 500 char *tag, char *date) 501 { 502 FILE *fp; 503 int fd; 504 char buf[MAXPATHLEN]; 505 struct hash_data *hdata, hd; 506 507 hdata = hash_table_find(&created_cvs_directories, path, strlen(path)); 508 if (hdata != NULL) 509 return; 510 511 hd.h_key = xstrdup(path); 512 hd.h_data = NULL; 513 hash_table_enter(&created_cvs_directories, &hd); 514 515 if (cvs_server_active == 0) 516 cvs_log(LP_TRACE, "cvs_mkadmin(%s, %s, %s, %s, %s)", 517 path, root, repo, (tag != NULL) ? tag : "", 518 (date != NULL) ? date : ""); 519 520 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_CVSDIR); 521 522 if (mkdir(buf, 0755) == -1 && errno != EEXIST) 523 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 524 525 if (cvs_cmdop == CVS_OP_CHECKOUT || cvs_cmdop == CVS_OP_ADD || 526 (cvs_cmdop == CVS_OP_UPDATE && build_dirs == 1)) { 527 (void)xsnprintf(buf, sizeof(buf), "%s/%s", 528 path, CVS_PATH_ROOTSPEC); 529 530 if ((fp = fopen(buf, "w")) == NULL) 531 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 532 533 fprintf(fp, "%s\n", root); 534 (void)fclose(fp); 535 } 536 537 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_REPOSITORY); 538 539 if ((fp = fopen(buf, "w")) == NULL) 540 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 541 542 fprintf(fp, "%s\n", repo); 543 (void)fclose(fp); 544 545 cvs_write_tagfile(path, tag, date); 546 547 (void)xsnprintf(buf, sizeof(buf), "%s/%s", path, CVS_PATH_ENTRIES); 548 549 if ((fd = open(buf, O_WRONLY|O_CREAT|O_EXCL, 0666 & ~cvs_umask)) 550 == -1) { 551 if (errno == EEXIST) 552 return; 553 fatal("cvs_mkadmin: %s: %s", buf, strerror(errno)); 554 } 555 556 if (atomicio(vwrite, fd, "D\n", 2) != 2) 557 fatal("cvs_mkadmin: %s", strerror(errno)); 558 close(fd); 559 } 560 561 void 562 cvs_mkpath(const char *path, char *tag) 563 { 564 CVSENTRIES *ent; 565 FILE *fp; 566 size_t len; 567 struct hash_data *hdata, hd; 568 char *entry, *sp, *dp, *dir, *p, rpath[MAXPATHLEN], repo[MAXPATHLEN]; 569 570 hdata = hash_table_find(&created_directories, path, strlen(path)); 571 if (hdata != NULL) 572 return; 573 574 hd.h_key = xstrdup(path); 575 hd.h_data = NULL; 576 hash_table_enter(&created_directories, &hd); 577 578 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 579 cvs_server_active == 1) 580 cvs_validate_directory(path); 581 582 dir = xstrdup(path); 583 584 STRIP_SLASH(dir); 585 586 if (cvs_server_active == 0) 587 cvs_log(LP_TRACE, "cvs_mkpath(%s)", dir); 588 589 repo[0] = '\0'; 590 rpath[0] = '\0'; 591 592 if ((cvs_cmdop != CVS_OP_CHECKOUT) && (cvs_cmdop != CVS_OP_EXPORT)) { 593 if ((fp = fopen(CVS_PATH_REPOSITORY, "r")) != NULL) { 594 if ((fgets(repo, sizeof(repo), fp)) == NULL) 595 fatal("cvs_mkpath: bad repository file"); 596 repo[strcspn(repo, "\n")] = '\0'; 597 (void)fclose(fp); 598 } 599 } 600 601 for (sp = dir; sp != NULL; sp = dp) { 602 dp = strchr(sp, '/'); 603 if (dp != NULL) 604 *(dp++) = '\0'; 605 606 if (sp == dir && module_repo_root != NULL) { 607 len = strlcpy(repo, module_repo_root, sizeof(repo)); 608 if (len >= (int)sizeof(repo)) 609 fatal("cvs_mkpath: overflow"); 610 } else if (strcmp(sp, ".")) { 611 if (repo[0] != '\0') { 612 len = strlcat(repo, "/", sizeof(repo)); 613 if (len >= (int)sizeof(repo)) 614 fatal("cvs_mkpath: overflow"); 615 } 616 617 len = strlcat(repo, sp, sizeof(repo)); 618 if (len >= (int)sizeof(repo)) 619 fatal("cvs_mkpath: overflow"); 620 } 621 622 if (rpath[0] != '\0') { 623 len = strlcat(rpath, "/", sizeof(rpath)); 624 if (len >= (int)sizeof(rpath)) 625 fatal("cvs_mkpath: overflow"); 626 } 627 628 len = strlcat(rpath, sp, sizeof(rpath)); 629 if (len >= (int)sizeof(rpath)) 630 fatal("cvs_mkpath: overflow"); 631 632 if (mkdir(rpath, 0755) == -1 && errno != EEXIST) 633 fatal("cvs_mkpath: %s: %s", rpath, strerror(errno)); 634 635 if (cvs_cmdop == CVS_OP_EXPORT && !cvs_server_active) 636 continue; 637 638 cvs_mkadmin(rpath, current_cvsroot->cr_str, repo, 639 tag, NULL); 640 641 if (dp != NULL) { 642 if ((p = strchr(dp, '/')) != NULL) 643 *p = '\0'; 644 645 entry = xmalloc(CVS_ENT_MAXLINELEN); 646 cvs_ent_line_str(dp, NULL, NULL, NULL, NULL, 1, 0, 647 entry, CVS_ENT_MAXLINELEN); 648 649 ent = cvs_ent_open(rpath); 650 cvs_ent_add(ent, entry); 651 xfree(entry); 652 653 if (p != NULL) 654 *p = '/'; 655 } 656 } 657 658 xfree(dir); 659 } 660 661 void 662 cvs_mkdir(const char *path, mode_t mode) 663 { 664 size_t len; 665 char *sp, *dp, *dir, rpath[MAXPATHLEN]; 666 667 if (current_cvsroot->cr_method != CVS_METHOD_LOCAL || 668 cvs_server_active == 1) 669 cvs_validate_directory(path); 670 671 dir = xstrdup(path); 672 673 STRIP_SLASH(dir); 674 675 if (cvs_server_active == 0) 676 cvs_log(LP_TRACE, "cvs_mkdir(%s)", dir); 677 678 rpath[0] = '\0'; 679 680 for (sp = dir; sp != NULL; sp = dp) { 681 dp = strchr(sp, '/'); 682 if (dp != NULL) 683 *(dp++) = '\0'; 684 685 len = strlcat(rpath, "/", sizeof(rpath)); 686 if (len >= (int)sizeof(rpath)) 687 fatal("cvs_mkdir: overflow"); 688 689 len = strlcat(rpath, sp, sizeof(rpath)); 690 if (len >= (int)sizeof(rpath)) 691 fatal("cvs_mkdir: overflow"); 692 if (1 == len) 693 continue; 694 695 if (mkdir(rpath, mode) == -1 && errno != EEXIST) 696 fatal("cvs_mkdir: %s: %s", rpath, strerror(errno)); 697 } 698 699 xfree(dir); 700 } 701 702 /* 703 * Split the contents of a file into a list of lines. 704 */ 705 struct rcs_lines * 706 cvs_splitlines(u_char *data, size_t len) 707 { 708 u_char *p, *c; 709 size_t i, tlen; 710 struct rcs_lines *lines; 711 struct rcs_line *lp; 712 713 lines = xcalloc(1, sizeof(*lines)); 714 TAILQ_INIT(&(lines->l_lines)); 715 716 lp = xcalloc(1, sizeof(*lp)); 717 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 718 719 p = c = data; 720 for (i = 0; i < len; i++) { 721 if (*p == '\n' || (i == len - 1)) { 722 tlen = p - c + 1; 723 lp = xcalloc(1, sizeof(*lp)); 724 lp->l_line = c; 725 lp->l_len = tlen; 726 lp->l_lineno = ++(lines->l_nblines); 727 TAILQ_INSERT_TAIL(&(lines->l_lines), lp, l_list); 728 c = p + 1; 729 } 730 p++; 731 } 732 733 return (lines); 734 } 735 736 void 737 cvs_freelines(struct rcs_lines *lines) 738 { 739 struct rcs_line *lp; 740 741 while ((lp = TAILQ_FIRST(&(lines->l_lines))) != NULL) { 742 TAILQ_REMOVE(&(lines->l_lines), lp, l_list); 743 if (lp->l_needsfree == 1) 744 xfree(lp->l_line); 745 xfree(lp); 746 } 747 748 xfree(lines); 749 } 750 751 /* 752 * cvs_strsplit() 753 * 754 * Split a string <str> of <sep>-separated values and allocate 755 * an argument vector for the values found. 756 */ 757 struct cvs_argvector * 758 cvs_strsplit(char *str, const char *sep) 759 { 760 struct cvs_argvector *av; 761 size_t i = 0; 762 char *cp, *p; 763 764 cp = xstrdup(str); 765 av = xmalloc(sizeof(*av)); 766 av->str = cp; 767 av->argv = xmalloc(sizeof(*(av->argv))); 768 769 while ((p = strsep(&cp, sep)) != NULL) { 770 av->argv[i++] = p; 771 av->argv = xrealloc(av->argv, 772 i + 1, sizeof(*(av->argv))); 773 } 774 av->argv[i] = NULL; 775 776 return (av); 777 } 778 779 /* 780 * cvs_argv_destroy() 781 * 782 * Free an argument vector previously allocated by cvs_strsplit(). 783 */ 784 void 785 cvs_argv_destroy(struct cvs_argvector *av) 786 { 787 xfree(av->str); 788 xfree(av->argv); 789 xfree(av); 790 } 791 792 u_int 793 cvs_revision_select(RCSFILE *file, char *range) 794 { 795 int i; 796 u_int nrev; 797 char *lstr, *rstr; 798 struct rcs_delta *rdp; 799 struct cvs_argvector *revargv, *revrange; 800 RCSNUM *lnum, *rnum; 801 802 nrev = 0; 803 lnum = rnum = NULL; 804 805 revargv = cvs_strsplit(range, ","); 806 for (i = 0; revargv->argv[i] != NULL; i++) { 807 revrange = cvs_strsplit(revargv->argv[i], ":"); 808 if (revrange->argv[0] == NULL) 809 fatal("invalid revision range: %s", revargv->argv[i]); 810 else if (revrange->argv[1] == NULL) 811 lstr = rstr = revrange->argv[0]; 812 else { 813 if (revrange->argv[2] != NULL) 814 fatal("invalid revision range: %s", 815 revargv->argv[i]); 816 817 lstr = revrange->argv[0]; 818 rstr = revrange->argv[1]; 819 820 if (strcmp(lstr, "") == 0) 821 lstr = NULL; 822 if (strcmp(rstr, "") == 0) 823 rstr = NULL; 824 } 825 826 if (lstr == NULL) 827 lstr = RCS_HEAD_INIT; 828 829 if ((lnum = rcs_translate_tag(lstr, file)) == NULL) 830 fatal("cvs_revision_select: could not translate tag `%s'", lstr); 831 832 if (rstr != NULL) { 833 if ((rnum = rcs_translate_tag(rstr, file)) == NULL) 834 fatal("cvs_revision_select: could not translate tag `%s'", rstr); 835 } else { 836 rnum = rcsnum_alloc(); 837 rcsnum_cpy(file->rf_head, rnum, 0); 838 } 839 840 cvs_argv_destroy(revrange); 841 842 TAILQ_FOREACH(rdp, &(file->rf_delta), rd_list) { 843 if (rcsnum_cmp(rdp->rd_num, lnum, 0) <= 0 && 844 rcsnum_cmp(rdp->rd_num, rnum, 0) >= 0 && 845 !(rdp->rd_flags & RCS_RD_SELECT)) { 846 rdp->rd_flags |= RCS_RD_SELECT; 847 nrev++; 848 } 849 } 850 851 rcsnum_free(lnum); 852 rcsnum_free(rnum); 853 } 854 855 cvs_argv_destroy(revargv); 856 857 return (nrev); 858 } 859 860 int 861 cvs_yesno(void) 862 { 863 int c, ret; 864 865 ret = 0; 866 867 fflush(stderr); 868 fflush(stdout); 869 870 if ((c = getchar()) != 'y' && c != 'Y') 871 ret = -1; 872 else 873 while (c != EOF && c != '\n') 874 c = getchar(); 875 876 return (ret); 877 } 878 879 /* 880 * cvs_exec() 881 * 882 * Execute <prog> and send <in> to the STDIN if not NULL. 883 * If <needwait> == 1, return the result of <prog>, 884 * else, 0 or -1 if an error occur. 885 */ 886 int 887 cvs_exec(char *prog, const char *in, int needwait) 888 { 889 pid_t pid; 890 size_t size; 891 int fds[2], st; 892 char *argp[4] = { "sh", "-c", prog, NULL }; 893 894 if (in != NULL && pipe(fds) < 0) { 895 cvs_log(LP_ERR, "cvs_exec: pipe failed"); 896 return (-1); 897 } 898 899 if ((pid = fork()) == -1) { 900 cvs_log(LP_ERR, "cvs_exec: fork failed"); 901 return (-1); 902 } else if (pid == 0) { 903 if (in != NULL) { 904 close(fds[1]); 905 dup2(fds[0], STDIN_FILENO); 906 } 907 908 setenv("CVSROOT", current_cvsroot->cr_dir, 1); 909 execv(_PATH_BSHELL, argp); 910 cvs_log(LP_ERR, "cvs_exec: failed to run '%s'", prog); 911 _exit(127); 912 } 913 914 if (in != NULL) { 915 close(fds[0]); 916 size = strlen(in); 917 if (atomicio(vwrite, fds[1], in, size) != size) 918 cvs_log(LP_ERR, "cvs_exec: failed to write on STDIN"); 919 close(fds[1]); 920 } 921 922 if (needwait == 1) { 923 while (waitpid(pid, &st, 0) == -1) 924 ; 925 if (!WIFEXITED(st)) { 926 errno = EINTR; 927 return (-1); 928 } 929 return (WEXITSTATUS(st)); 930 } 931 932 return (0); 933 } 934