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