entries.c revision 225736
1/* 2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc. 3 * 4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>, 5 * and others. 6 * 7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk 8 * Portions Copyright (C) 1989-1992, Brian Berliner 9 * 10 * You may distribute under the terms of the GNU General Public License as 11 * specified in the README file that comes with the CVS source distribution. 12 * 13 * Entries file to Files file 14 * 15 * Creates the file Files containing the names that comprise the project, from 16 * the Entries file. 17 */ 18 19/* 20 * $FreeBSD: stable/9/contrib/cvs/src/entries.c 175271 2008-01-13 06:07:20Z obrien $ 21 */ 22#include "cvs.h" 23#include "getline.h" 24 25static Node *AddEntryNode PROTO((List * list, Entnode *entnode)); 26 27static Entnode *fgetentent PROTO((FILE *, char *, int *)); 28static int fputentent PROTO((FILE *, Entnode *)); 29 30static Entnode *subdir_record PROTO((int, const char *, const char *)); 31 32static FILE *entfile; 33static char *entfilename; /* for error messages */ 34 35 36 37/* 38 * Construct an Entnode 39 */ 40static Entnode *Entnode_Create PROTO ((enum ent_type, const char *, 41 const char *, const char *, 42 const char *, const char *, 43 const char *, const char *)); 44 45static Entnode * 46Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict) 47 enum ent_type type; 48 const char *user; 49 const char *vn; 50 const char *ts; 51 const char *options; 52 const char *tag; 53 const char *date; 54 const char *ts_conflict; 55{ 56 Entnode *ent; 57 58 /* Note that timestamp and options must be non-NULL */ 59 ent = (Entnode *) xmalloc (sizeof (Entnode)); 60 ent->type = type; 61 ent->user = xstrdup (user); 62 ent->version = xstrdup (vn); 63 ent->timestamp = xstrdup (ts ? ts : ""); 64 ent->options = xstrdup (options ? options : ""); 65 ent->tag = xstrdup (tag); 66 ent->date = xstrdup (date); 67 ent->conflict = xstrdup (ts_conflict); 68 69 return ent; 70} 71 72/* 73 * Destruct an Entnode 74 */ 75static void Entnode_Destroy PROTO ((Entnode *)); 76 77static void 78Entnode_Destroy (ent) 79 Entnode *ent; 80{ 81 free (ent->user); 82 free (ent->version); 83 free (ent->timestamp); 84 free (ent->options); 85 if (ent->tag) 86 free (ent->tag); 87 if (ent->date) 88 free (ent->date); 89 if (ent->conflict) 90 free (ent->conflict); 91 free (ent); 92} 93 94/* 95 * Write out the line associated with a node of an entries file 96 */ 97static int write_ent_proc PROTO ((Node *, void *)); 98static int 99write_ent_proc (node, closure) 100 Node *node; 101 void *closure; 102{ 103 Entnode *entnode = node->data; 104 105 if (closure != NULL && entnode->type != ENT_FILE) 106 *(int *) closure = 1; 107 108 if (fputentent(entfile, entnode)) 109 error (1, errno, "cannot write %s", entfilename); 110 111 return (0); 112} 113 114/* 115 * write out the current entries file given a list, making a backup copy 116 * first of course 117 */ 118static void 119write_entries (list) 120 List *list; 121{ 122 int sawdir; 123 124 sawdir = 0; 125 126 /* open the new one and walk the list writing entries */ 127 entfilename = CVSADM_ENTBAK; 128 entfile = CVS_FOPEN (entfilename, "w+"); 129 if (entfile == NULL) 130 { 131 /* Make this a warning, not an error. For example, one user might 132 have checked out a working directory which, for whatever reason, 133 contains an Entries.Log file. A second user, without write access 134 to that working directory, might want to do a "cvs log". The 135 problem rewriting Entries shouldn't affect the ability of "cvs log" 136 to work, although the warning is probably a good idea so that 137 whether Entries gets rewritten is not an inexplicable process. */ 138 /* FIXME: should be including update_dir in message. */ 139 error (0, errno, "cannot rewrite %s", entfilename); 140 141 /* Now just return. We leave the Entries.Log file around. As far 142 as I know, there is never any data lying around in 'list' that 143 is not in Entries.Log at this time (if there is an error writing 144 Entries.Log that is a separate problem). */ 145 return; 146 } 147 148 (void) walklist (list, write_ent_proc, (void *) &sawdir); 149 if (! sawdir) 150 { 151 struct stickydirtag *sdtp; 152 153 /* We didn't write out any directories. Check the list 154 private data to see whether subdirectory information is 155 known. If it is, we need to write out an empty D line. */ 156 sdtp = list->list->data; 157 if (sdtp == NULL || sdtp->subdirs) 158 if (fprintf (entfile, "D\n") < 0) 159 error (1, errno, "cannot write %s", entfilename); 160 } 161 if (fclose (entfile) == EOF) 162 error (1, errno, "error closing %s", entfilename); 163 164 /* now, atomically (on systems that support it) rename it */ 165 rename_file (entfilename, CVSADM_ENT); 166 167 /* now, remove the log file */ 168 if (unlink_file (CVSADM_ENTLOG) < 0 169 && !existence_error (errno)) 170 error (0, errno, "cannot remove %s", CVSADM_ENTLOG); 171} 172 173 174 175/* 176 * Removes the argument file from the Entries file if necessary. 177 */ 178void 179Scratch_Entry (list, fname) 180 List *list; 181 const char *fname; 182{ 183 Node *node; 184 185 if (trace) 186 (void) fprintf (stderr, "%s-> Scratch_Entry(%s)\n", 187 CLIENT_SERVER_STR, fname); 188 189 /* hashlookup to see if it is there */ 190 if ((node = findnode_fn (list, fname)) != NULL) 191 { 192 if (!noexec) 193 { 194 entfilename = CVSADM_ENTLOG; 195 entfile = open_file (entfilename, "a"); 196 197 if (fprintf (entfile, "R ") < 0) 198 error (1, errno, "cannot write %s", entfilename); 199 200 write_ent_proc (node, NULL); 201 202 if (fclose (entfile) == EOF) 203 error (1, errno, "error closing %s", entfilename); 204 } 205 206 delnode (node); /* delete the node */ 207 208#ifdef SERVER_SUPPORT 209 if (server_active) 210 server_scratch (fname); 211#endif 212 } 213} 214 215 216 217/* 218 * Enters the given file name/version/time-stamp into the Entries file, 219 * removing the old entry first, if necessary. 220 */ 221void 222Register (list, fname, vn, ts, options, tag, date, ts_conflict) 223 List *list; 224 const char *fname; 225 const char *vn; 226 const char *ts; 227 const char *options; 228 const char *tag; 229 const char *date; 230 const char *ts_conflict; 231{ 232 Entnode *entnode; 233 Node *node; 234 235#ifdef SERVER_SUPPORT 236 if (server_active) 237 { 238 server_register (fname, vn, ts, options, tag, date, ts_conflict); 239 } 240#endif 241 242 if (trace) 243 { 244 (void) fprintf (stderr, "%s-> Register(%s, %s, %s%s%s, %s, %s %s)\n", 245 CLIENT_SERVER_STR, 246 fname, vn, ts ? ts : "", 247 ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "", 248 options, tag ? tag : "", date ? date : ""); 249 } 250 251 entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date, 252 ts_conflict); 253 node = AddEntryNode (list, entnode); 254 255 if (!noexec) 256 { 257 entfilename = CVSADM_ENTLOG; 258 entfile = CVS_FOPEN (entfilename, "a"); 259 260 if (entfile == NULL) 261 { 262 /* Warning, not error, as in write_entries. */ 263 /* FIXME-update-dir: should be including update_dir in message. */ 264 error (0, errno, "cannot open %s", entfilename); 265 return; 266 } 267 268 if (fprintf (entfile, "A ") < 0) 269 error (1, errno, "cannot write %s", entfilename); 270 271 write_ent_proc (node, NULL); 272 273 if (fclose (entfile) == EOF) 274 error (1, errno, "error closing %s", entfilename); 275 } 276} 277 278/* 279 * Node delete procedure for list-private sticky dir tag/date info 280 */ 281static void 282freesdt (p) 283 Node *p; 284{ 285 struct stickydirtag *sdtp = p->data; 286 287 if (sdtp->tag) 288 free (sdtp->tag); 289 if (sdtp->date) 290 free (sdtp->date); 291 free ((char *) sdtp); 292} 293 294/* Return the next real Entries line. On end of file, returns NULL. 295 On error, prints an error message and returns NULL. */ 296 297static Entnode * 298fgetentent(fpin, cmd, sawdir) 299 FILE *fpin; 300 char *cmd; 301 int *sawdir; 302{ 303 Entnode *ent; 304 char *line; 305 size_t line_chars_allocated; 306 register char *cp; 307 enum ent_type type; 308 char *l, *user, *vn, *ts, *options; 309 char *tag_or_date, *tag, *date, *ts_conflict; 310 int line_length; 311 312 line = NULL; 313 line_chars_allocated = 0; 314 315 ent = NULL; 316 while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 0) 317 { 318 l = line; 319 320 /* If CMD is not NULL, we are reading an Entries.Log file. 321 Each line in the Entries.Log file starts with a single 322 character command followed by a space. For backward 323 compatibility, the absence of a space indicates an add 324 command. */ 325 if (cmd != NULL) 326 { 327 if (l[1] != ' ') 328 *cmd = 'A'; 329 else 330 { 331 *cmd = l[0]; 332 l += 2; 333 } 334 } 335 336 type = ENT_FILE; 337 338 if (l[0] == 'D') 339 { 340 type = ENT_SUBDIR; 341 *sawdir = 1; 342 ++l; 343 /* An empty D line is permitted; it is a signal that this 344 Entries file lists all known subdirectories. */ 345 } 346 347 if (l[0] != '/') 348 continue; 349 350 user = l + 1; 351 if ((cp = strchr (user, '/')) == NULL) 352 continue; 353 *cp++ = '\0'; 354 vn = cp; 355 if ((cp = strchr (vn, '/')) == NULL) 356 continue; 357 *cp++ = '\0'; 358 ts = cp; 359 if ((cp = strchr (ts, '/')) == NULL) 360 continue; 361 *cp++ = '\0'; 362 options = cp; 363 if ((cp = strchr (options, '/')) == NULL) 364 continue; 365 *cp++ = '\0'; 366 tag_or_date = cp; 367 if ((cp = strchr (tag_or_date, '\n')) == NULL) 368 continue; 369 *cp = '\0'; 370 tag = (char *) NULL; 371 date = (char *) NULL; 372 if (*tag_or_date == 'T') 373 tag = tag_or_date + 1; 374 else if (*tag_or_date == 'D') 375 date = tag_or_date + 1; 376 377 if ((ts_conflict = strchr (ts, '+'))) 378 *ts_conflict++ = '\0'; 379 380 /* 381 * XXX - Convert timestamp from old format to new format. 382 * 383 * If the timestamp doesn't match the file's current 384 * mtime, we'd have to generate a string that doesn't 385 * match anyways, so cheat and base it on the existing 386 * string; it doesn't have to match the same mod time. 387 * 388 * For an unmodified file, write the correct timestamp. 389 */ 390 { 391 struct stat sb; 392 if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0) 393 { 394 char *c = ctime (&sb.st_mtime); 395 /* Fix non-standard format. */ 396 if (c[8] == '0') c[8] = ' '; 397 398 if (!strncmp (ts + 25, c, 24)) 399 ts = time_stamp (user); 400 else 401 { 402 ts += 24; 403 ts[0] = '*'; 404 } 405 } 406 } 407 408 ent = Entnode_Create (type, user, vn, ts, options, tag, date, 409 ts_conflict); 410 break; 411 } 412 413 if (line_length < 0 && !feof (fpin)) 414 error (0, errno, "cannot read entries file"); 415 416 free (line); 417 return ent; 418} 419 420static int 421fputentent(fp, p) 422 FILE *fp; 423 Entnode *p; 424{ 425 switch (p->type) 426 { 427 case ENT_FILE: 428 break; 429 case ENT_SUBDIR: 430 if (fprintf (fp, "D") < 0) 431 return 1; 432 break; 433 } 434 435 if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0) 436 return 1; 437 if (p->conflict) 438 { 439 if (fprintf (fp, "+%s", p->conflict) < 0) 440 return 1; 441 } 442 if (fprintf (fp, "/%s/", p->options) < 0) 443 return 1; 444 445 if (p->tag) 446 { 447 if (fprintf (fp, "T%s\n", p->tag) < 0) 448 return 1; 449 } 450 else if (p->date) 451 { 452 if (fprintf (fp, "D%s\n", p->date) < 0) 453 return 1; 454 } 455 else 456 { 457 if (fprintf (fp, "\n") < 0) 458 return 1; 459 } 460 461 return 0; 462} 463 464 465/* Read the entries file into a list, hashing on the file name. 466 467 UPDATE_DIR is the name of the current directory, for use in error 468 messages, or NULL if not known (that is, noone has gotten around 469 to updating the caller to pass in the information). */ 470List * 471Entries_Open (aflag, update_dir) 472 int aflag; 473 char *update_dir; 474{ 475 List *entries; 476 struct stickydirtag *sdtp = NULL; 477 Entnode *ent; 478 char *dirtag, *dirdate; 479 int dirnonbranch; 480 int do_rewrite = 0; 481 FILE *fpin; 482 int sawdir; 483 484 /* get a fresh list... */ 485 entries = getlist (); 486 487 /* 488 * Parse the CVS/Tag file, to get any default tag/date settings. Use 489 * list-private storage to tuck them away for Version_TS(). 490 */ 491 ParseTag (&dirtag, &dirdate, &dirnonbranch); 492 if (aflag || dirtag || dirdate) 493 { 494 sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); 495 memset ((char *) sdtp, 0, sizeof (*sdtp)); 496 sdtp->aflag = aflag; 497 sdtp->tag = xstrdup (dirtag); 498 sdtp->date = xstrdup (dirdate); 499 sdtp->nonbranch = dirnonbranch; 500 501 /* feed it into the list-private area */ 502 entries->list->data = sdtp; 503 entries->list->delproc = freesdt; 504 } 505 506 sawdir = 0; 507 508 fpin = CVS_FOPEN (CVSADM_ENT, "r"); 509 if (fpin == NULL) 510 { 511 if (update_dir != NULL) 512 error (0, 0, "in directory %s:", update_dir); 513 error (0, errno, "cannot open %s for reading", CVSADM_ENT); 514 } 515 else 516 { 517 while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL) 518 { 519 (void) AddEntryNode (entries, ent); 520 } 521 522 if (fclose (fpin) < 0) 523 /* FIXME-update-dir: should include update_dir in message. */ 524 error (0, errno, "cannot close %s", CVSADM_ENT); 525 } 526 527 fpin = CVS_FOPEN (CVSADM_ENTLOG, "r"); 528 if (fpin != NULL) 529 { 530 char cmd; 531 Node *node; 532 533 while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL) 534 { 535 switch (cmd) 536 { 537 case 'A': 538 (void) AddEntryNode (entries, ent); 539 break; 540 case 'R': 541 node = findnode_fn (entries, ent->user); 542 if (node != NULL) 543 delnode (node); 544 Entnode_Destroy (ent); 545 break; 546 default: 547 /* Ignore unrecognized commands. */ 548 Entnode_Destroy (ent); 549 break; 550 } 551 } 552 do_rewrite = 1; 553 if (fclose (fpin) < 0) 554 /* FIXME-update-dir: should include update_dir in message. */ 555 error (0, errno, "cannot close %s", CVSADM_ENTLOG); 556 } 557 558 /* Update the list private data to indicate whether subdirectory 559 information is known. Nonexistent list private data is taken 560 to mean that it is known. */ 561 if (sdtp != NULL) 562 sdtp->subdirs = sawdir; 563 else if (! sawdir) 564 { 565 sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp)); 566 memset ((char *) sdtp, 0, sizeof (*sdtp)); 567 sdtp->subdirs = 0; 568 entries->list->data = sdtp; 569 entries->list->delproc = freesdt; 570 } 571 572 if (do_rewrite && !noexec) 573 write_entries (entries); 574 575 /* clean up and return */ 576 if (dirtag) 577 free (dirtag); 578 if (dirdate) 579 free (dirdate); 580 return (entries); 581} 582 583void 584Entries_Close(list) 585 List *list; 586{ 587 if (list) 588 { 589 if (!noexec) 590 { 591 if (isfile (CVSADM_ENTLOG)) 592 write_entries (list); 593 } 594 dellist(&list); 595 } 596} 597 598 599/* 600 * Free up the memory associated with the data section of an ENTRIES type 601 * node 602 */ 603static void 604Entries_delproc (node) 605 Node *node; 606{ 607 Entnode *p = node->data; 608 609 Entnode_Destroy(p); 610} 611 612/* 613 * Get an Entries file list node, initialize it, and add it to the specified 614 * list 615 */ 616static Node * 617AddEntryNode (list, entdata) 618 List *list; 619 Entnode *entdata; 620{ 621 Node *p; 622 623 /* was it already there? */ 624 if ((p = findnode_fn (list, entdata->user)) != NULL) 625 { 626 /* take it out */ 627 delnode (p); 628 } 629 630 /* get a node and fill in the regular stuff */ 631 p = getnode (); 632 p->type = ENTRIES; 633 p->delproc = Entries_delproc; 634 635 /* this one gets a key of the name for hashing */ 636 /* FIXME This results in duplicated data --- the hash package shouldn't 637 assume that the key is dynamically allocated. The user's free proc 638 should be responsible for freeing the key. */ 639 p->key = xstrdup (entdata->user); 640 p->data = entdata; 641 642 /* put the node into the list */ 643 addnode (list, p); 644 return (p); 645} 646 647static char *root_template; 648 649static int 650get_root_template(const char *repository, const char *path) 651{ 652 if (root_template) { 653 if (strcmp(path, root_template) == 0) 654 return(0); 655 free(root_template); 656 } 657 if ((root_template = strdup(path)) == NULL) 658 return(-1); 659 return(0); 660} 661 662/* 663 * Write out/Clear the CVS/Template file. 664 */ 665void 666WriteTemplate (dir, update_dir) 667 const char *dir; 668 const char *update_dir; 669{ 670 char *tmp = NULL; 671 struct stat st1; 672 struct stat st2; 673 674 if (Parse_Info(CVSROOTADM_RCSINFO, "cvs", get_root_template, 1) < 0) 675 return; 676 677 if (asprintf(&tmp, "%s/%s", dir, CVSADM_TEMPLATE) < 0) 678 error (1, errno, "out of memory"); 679 680 if (stat(root_template, &st1) == 0) { 681 if (stat(tmp, &st2) < 0 || st1.st_mtime != st2.st_mtime) { 682 FILE *fi; 683 FILE *fo; 684 685 if ((fi = open_file(root_template, "r")) != NULL) { 686 if ((fo = open_file(tmp, "w")) != NULL) { 687 int n; 688 char buf[256]; 689 690 while ((n = fread(buf, 1, sizeof(buf), fi)) > 0) 691 fwrite(buf, 1, n, fo); 692 fflush(fo); 693 if (ferror(fi) || ferror(fo)) { 694 fclose(fo); 695 remove(tmp); 696 error (1, errno, "error copying Template"); 697 } else { 698 struct timeval times[2]; 699 fclose(fo); 700 times[0].tv_sec = st1.st_mtime; 701 times[0].tv_usec = 0; 702 times[1] = times[0]; 703 utimes(tmp, times); 704 } 705 } 706 fclose(fi); 707 } 708 } 709 } 710 free(tmp); 711} 712 713/* 714 * Write out/Clear the CVS/Tag file. 715 */ 716void 717WriteTag (dir, tag, date, nonbranch, update_dir, repository) 718 const char *dir; 719 const char *tag; 720 const char *date; 721 int nonbranch; 722 const char *update_dir; 723 const char *repository; 724{ 725 FILE *fout; 726 char *tmp; 727 728 if (noexec) 729 return; 730 731 tmp = xmalloc ((dir ? strlen (dir) : 0) 732 + sizeof (CVSADM_TAG) 733 + 10); 734 if (dir == NULL) 735 (void) strcpy (tmp, CVSADM_TAG); 736 else 737 (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG); 738 739 if (tag || date) 740 { 741 fout = open_file (tmp, "w+"); 742 if (tag) 743 { 744 if (nonbranch) 745 { 746 if (fprintf (fout, "N%s\n", tag) < 0) 747 error (1, errno, "write to %s failed", tmp); 748 } 749 else 750 { 751 if (fprintf (fout, "T%s\n", tag) < 0) 752 error (1, errno, "write to %s failed", tmp); 753 } 754 } 755 else 756 { 757 if (fprintf (fout, "D%s\n", date) < 0) 758 error (1, errno, "write to %s failed", tmp); 759 } 760 if (fclose (fout) == EOF) 761 error (1, errno, "cannot close %s", tmp); 762 } 763 else 764 if (unlink_file (tmp) < 0 && ! existence_error (errno)) 765 error (1, errno, "cannot remove %s", tmp); 766 free (tmp); 767#ifdef SERVER_SUPPORT 768 if (server_active) 769 server_set_sticky (update_dir, repository, tag, date, nonbranch); 770#endif 771} 772 773/* Parse the CVS/Tag file for the current directory. 774 775 If it contains a date, sets *DATEP to the date in a newly malloc'd 776 string, *TAGP to NULL, and *NONBRANCHP to an unspecified value. 777 778 If it contains a branch tag, sets *TAGP to the tag in a newly 779 malloc'd string, *NONBRANCHP to 0, and *DATEP to NULL. 780 781 If it contains a nonbranch tag, sets *TAGP to the tag in a newly 782 malloc'd string, *NONBRANCHP to 1, and *DATEP to NULL. 783 784 If it does not exist, or contains something unrecognized by this 785 version of CVS, set *DATEP and *TAGP to NULL and *NONBRANCHP to 786 an unspecified value. 787 788 If there is an error, print an error message, set *DATEP and *TAGP 789 to NULL, and return. */ 790void 791ParseTag (tagp, datep, nonbranchp) 792 char **tagp; 793 char **datep; 794 int *nonbranchp; 795{ 796 FILE *fp; 797 798 if (tagp) 799 *tagp = (char *) NULL; 800 if (datep) 801 *datep = (char *) NULL; 802 /* Always store a value here, even in the 'D' case where the value 803 is unspecified. Shuts up tools which check for references to 804 uninitialized memory. */ 805 if (nonbranchp != NULL) 806 *nonbranchp = 0; 807 fp = CVS_FOPEN (CVSADM_TAG, "r"); 808 if (fp) 809 { 810 char *line; 811 int line_length; 812 size_t line_chars_allocated; 813 814 line = NULL; 815 line_chars_allocated = 0; 816 817 if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0) 818 { 819 /* Remove any trailing newline. */ 820 if (line[line_length - 1] == '\n') 821 line[--line_length] = '\0'; 822 switch (*line) 823 { 824 case 'T': 825 if (tagp != NULL) 826 *tagp = xstrdup (line + 1); 827 break; 828 case 'D': 829 if (datep != NULL) 830 *datep = xstrdup (line + 1); 831 break; 832 case 'N': 833 if (tagp != NULL) 834 *tagp = xstrdup (line + 1); 835 if (nonbranchp != NULL) 836 *nonbranchp = 1; 837 break; 838 default: 839 /* Silently ignore it; it may have been 840 written by a future version of CVS which extends the 841 syntax. */ 842 break; 843 } 844 } 845 846 if (line_length < 0) 847 { 848 /* FIXME-update-dir: should include update_dir in messages. */ 849 if (feof (fp)) 850 error (0, 0, "cannot read %s: end of file", CVSADM_TAG); 851 else 852 error (0, errno, "cannot read %s", CVSADM_TAG); 853 } 854 855 if (fclose (fp) < 0) 856 /* FIXME-update-dir: should include update_dir in message. */ 857 error (0, errno, "cannot close %s", CVSADM_TAG); 858 859 free (line); 860 } 861 else if (!existence_error (errno)) 862 /* FIXME-update-dir: should include update_dir in message. */ 863 error (0, errno, "cannot open %s", CVSADM_TAG); 864} 865 866/* 867 * This is called if all subdirectory information is known, but there 868 * aren't any subdirectories. It records that fact in the list 869 * private data. 870 */ 871 872void 873Subdirs_Known (entries) 874 List *entries; 875{ 876 struct stickydirtag *sdtp = entries->list->data; 877 878 /* If there is no list private data, that means that the 879 subdirectory information is known. */ 880 if (sdtp != NULL && ! sdtp->subdirs) 881 { 882 FILE *fp; 883 884 sdtp->subdirs = 1; 885 if (!noexec) 886 { 887 /* Create Entries.Log so that Entries_Close will do something. */ 888 entfilename = CVSADM_ENTLOG; 889 fp = CVS_FOPEN (entfilename, "a"); 890 if (fp == NULL) 891 { 892 int save_errno = errno; 893 894 /* As in subdir_record, just silently skip the whole thing 895 if there is no CVSADM directory. */ 896 if (! isdir (CVSADM)) 897 return; 898 error (1, save_errno, "cannot open %s", entfilename); 899 } 900 else 901 { 902 if (fclose (fp) == EOF) 903 error (1, errno, "cannot close %s", entfilename); 904 } 905 } 906 } 907} 908 909/* Record subdirectory information. */ 910 911static Entnode * 912subdir_record (cmd, parent, dir) 913 int cmd; 914 const char *parent; 915 const char *dir; 916{ 917 Entnode *entnode; 918 919 /* None of the information associated with a directory is 920 currently meaningful. */ 921 entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "", 922 (char *) NULL, (char *) NULL, 923 (char *) NULL); 924 925 if (!noexec) 926 { 927 if (parent == NULL) 928 entfilename = CVSADM_ENTLOG; 929 else 930 { 931 entfilename = xmalloc (strlen (parent) 932 + sizeof CVSADM_ENTLOG 933 + 10); 934 sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG); 935 } 936 937 entfile = CVS_FOPEN (entfilename, "a"); 938 if (entfile == NULL) 939 { 940 int save_errno = errno; 941 942 /* It is not an error if there is no CVS administration 943 directory. Permitting this case simplifies some 944 calling code. */ 945 946 if (parent == NULL) 947 { 948 if (! isdir (CVSADM)) 949 return entnode; 950 } 951 else 952 { 953 sprintf (entfilename, "%s/%s", parent, CVSADM); 954 if (! isdir (entfilename)) 955 { 956 free (entfilename); 957 entfilename = NULL; 958 return entnode; 959 } 960 } 961 962 error (1, save_errno, "cannot open %s", entfilename); 963 } 964 965 if (fprintf (entfile, "%c ", cmd) < 0) 966 error (1, errno, "cannot write %s", entfilename); 967 968 if (fputentent (entfile, entnode) != 0) 969 error (1, errno, "cannot write %s", entfilename); 970 971 if (fclose (entfile) == EOF) 972 error (1, errno, "error closing %s", entfilename); 973 974 if (parent != NULL) 975 { 976 free (entfilename); 977 entfilename = NULL; 978 } 979 } 980 981 return entnode; 982} 983 984/* 985 * Record the addition of a new subdirectory DIR in PARENT. PARENT 986 * may be NULL, which means the current directory. ENTRIES is the 987 * current entries list; it may be NULL, which means that it need not 988 * be updated. 989 */ 990 991void 992Subdir_Register (entries, parent, dir) 993 List *entries; 994 const char *parent; 995 const char *dir; 996{ 997 Entnode *entnode; 998 999 /* Ignore attempts to register ".". These can happen in the 1000 server code. */ 1001 if (dir[0] == '.' && dir[1] == '\0') 1002 return; 1003 1004 entnode = subdir_record ('A', parent, dir); 1005 1006 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) 1007 (void) AddEntryNode (entries, entnode); 1008 else 1009 Entnode_Destroy (entnode); 1010} 1011 1012/* 1013 * Record the removal of a subdirectory. The arguments are the same 1014 * as for Subdir_Register. 1015 */ 1016 1017void 1018Subdir_Deregister (entries, parent, dir) 1019 List *entries; 1020 const char *parent; 1021 const char *dir; 1022{ 1023 Entnode *entnode; 1024 1025 entnode = subdir_record ('R', parent, dir); 1026 Entnode_Destroy (entnode); 1027 1028 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) 1029 { 1030 Node *p; 1031 1032 p = findnode_fn (entries, dir); 1033 if (p != NULL) 1034 delnode (p); 1035 } 1036} 1037 1038 1039 1040/* OK, the following base_* code tracks the revisions of the files in 1041 CVS/Base. We do this in a file CVS/Baserev. Separate from 1042 CVS/Entries because it needs to go in separate data structures 1043 anyway (the name in Entries must be unique), so this seemed 1044 cleaner. The business of rewriting the whole file in 1045 base_deregister and base_register is the kind of thing we used to 1046 do for Entries and which turned out to be slow, which is why there 1047 is now the Entries.Log machinery. So maybe from that point of 1048 view it is a mistake to do this separately from Entries, I dunno. 1049 1050 We also need something analogous for: 1051 1052 1. CVS/Template (so we can update the Template file automagically 1053 without the user needing to check out a new working directory). 1054 Updating would probably print a message (that part might be 1055 optional, although probably it should be visible because not all 1056 cvs commands would make the update happen and so it is a 1057 user-visible behavior). Constructing version number for template 1058 is a bit hairy (base it on the timestamp on the server? Or see if 1059 the template is in checkoutlist and if yes use its versioning and 1060 if no don't version it?).... 1061 1062 2. cvsignore (need to keep a copy in the working directory to do 1063 "cvs release" on the client side; see comment at src/release.c 1064 (release). Would also allow us to stop needing Questionable. */ 1065 1066enum base_walk { 1067 /* Set the revision for FILE to *REV. */ 1068 BASE_REGISTER, 1069 /* Get the revision for FILE and put it in a newly malloc'd string 1070 in *REV, or put NULL if not mentioned. */ 1071 BASE_GET, 1072 /* Remove FILE. */ 1073 BASE_DEREGISTER 1074}; 1075 1076static void base_walk PROTO ((enum base_walk, struct file_info *, char **)); 1077 1078/* Read through the lines in CVS/Baserev, taking the actions as documented 1079 for CODE. */ 1080 1081static void 1082base_walk (code, finfo, rev) 1083 enum base_walk code; 1084 struct file_info *finfo; 1085 char **rev; 1086{ 1087 FILE *fp; 1088 char *line; 1089 size_t line_allocated; 1090 FILE *newf; 1091 char *baserev_fullname; 1092 char *baserevtmp_fullname; 1093 1094 line = NULL; 1095 line_allocated = 0; 1096 newf = NULL; 1097 1098 /* First compute the fullnames for the error messages. This 1099 computation probably should be broken out into a separate function, 1100 as recurse.c does it too and places like Entries_Open should be 1101 doing it. */ 1102 baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV) 1103 + strlen (finfo->update_dir) 1104 + 2); 1105 baserev_fullname[0] = '\0'; 1106 baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP) 1107 + strlen (finfo->update_dir) 1108 + 2); 1109 baserevtmp_fullname[0] = '\0'; 1110 if (finfo->update_dir[0] != '\0') 1111 { 1112 strcat (baserev_fullname, finfo->update_dir); 1113 strcat (baserev_fullname, "/"); 1114 strcat (baserevtmp_fullname, finfo->update_dir); 1115 strcat (baserevtmp_fullname, "/"); 1116 } 1117 strcat (baserev_fullname, CVSADM_BASEREV); 1118 strcat (baserevtmp_fullname, CVSADM_BASEREVTMP); 1119 1120 fp = CVS_FOPEN (CVSADM_BASEREV, "r"); 1121 if (fp == NULL) 1122 { 1123 if (!existence_error (errno)) 1124 { 1125 error (0, errno, "cannot open %s for reading", baserev_fullname); 1126 goto out; 1127 } 1128 } 1129 1130 switch (code) 1131 { 1132 case BASE_REGISTER: 1133 case BASE_DEREGISTER: 1134 newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w"); 1135 if (newf == NULL) 1136 { 1137 error (0, errno, "cannot open %s for writing", 1138 baserevtmp_fullname); 1139 goto out; 1140 } 1141 break; 1142 case BASE_GET: 1143 *rev = NULL; 1144 break; 1145 } 1146 1147 if (fp != NULL) 1148 { 1149 while (getline (&line, &line_allocated, fp) >= 0) 1150 { 1151 char *linefile; 1152 char *p; 1153 char *linerev; 1154 1155 if (line[0] != 'B') 1156 /* Ignore, for future expansion. */ 1157 continue; 1158 1159 linefile = line + 1; 1160 p = strchr (linefile, '/'); 1161 if (p == NULL) 1162 /* Syntax error, ignore. */ 1163 continue; 1164 linerev = p + 1; 1165 p = strchr (linerev, '/'); 1166 if (p == NULL) 1167 continue; 1168 1169 linerev[-1] = '\0'; 1170 if (fncmp (linefile, finfo->file) == 0) 1171 { 1172 switch (code) 1173 { 1174 case BASE_REGISTER: 1175 case BASE_DEREGISTER: 1176 /* Don't copy over the old entry, we don't want it. */ 1177 break; 1178 case BASE_GET: 1179 *p = '\0'; 1180 *rev = xstrdup (linerev); 1181 *p = '/'; 1182 goto got_it; 1183 } 1184 } 1185 else 1186 { 1187 linerev[-1] = '/'; 1188 switch (code) 1189 { 1190 case BASE_REGISTER: 1191 case BASE_DEREGISTER: 1192 if (fprintf (newf, "%s\n", line) < 0) 1193 error (0, errno, "error writing %s", 1194 baserevtmp_fullname); 1195 break; 1196 case BASE_GET: 1197 break; 1198 } 1199 } 1200 } 1201 if (ferror (fp)) 1202 error (0, errno, "cannot read %s", baserev_fullname); 1203 } 1204 got_it: 1205 1206 if (code == BASE_REGISTER) 1207 { 1208 if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0) 1209 error (0, errno, "error writing %s", 1210 baserevtmp_fullname); 1211 } 1212 1213 out: 1214 1215 if (line != NULL) 1216 free (line); 1217 1218 if (fp != NULL) 1219 { 1220 if (fclose (fp) < 0) 1221 error (0, errno, "cannot close %s", baserev_fullname); 1222 } 1223 if (newf != NULL) 1224 { 1225 if (fclose (newf) < 0) 1226 error (0, errno, "cannot close %s", baserevtmp_fullname); 1227 rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV); 1228 } 1229 1230 free (baserev_fullname); 1231 free (baserevtmp_fullname); 1232} 1233 1234/* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev, 1235 or NULL if not listed. */ 1236 1237char * 1238base_get (finfo) 1239 struct file_info *finfo; 1240{ 1241 char *rev; 1242 base_walk (BASE_GET, finfo, &rev); 1243 return rev; 1244} 1245 1246/* Set the revision for FILE to REV. */ 1247 1248void 1249base_register (finfo, rev) 1250 struct file_info *finfo; 1251 char *rev; 1252{ 1253 base_walk (BASE_REGISTER, finfo, &rev); 1254} 1255 1256/* Remove FILE. */ 1257 1258void 1259base_deregister (finfo) 1260 struct file_info *finfo; 1261{ 1262 base_walk (BASE_DEREGISTER, finfo, NULL); 1263} 1264