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 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 825 /* None of the information associated with a directory is 826 currently meaningful. */ 827 entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "", 828 NULL, NULL, NULL); 829 830 if (!noexec) 831 { 832 if (parent == NULL) 833 entfilename = CVSADM_ENTLOG; 834 else 835 entfilename = Xasprintf ("%s/%s", parent, CVSADM_ENTLOG); 836 837 entfile = CVS_FOPEN (entfilename, "a"); 838 if (entfile == NULL) 839 { 840 int save_errno = errno; 841 842 /* It is not an error if there is no CVS administration 843 directory. Permitting this case simplifies some 844 calling code. */ 845 846 if (parent == NULL) 847 { 848 if (! isdir (CVSADM)) 849 return entnode; 850 } 851 else 852 { 853 free (entfilename); 854 entfilename = Xasprintf ("%s/%s", parent, CVSADM); 855 if (! isdir (entfilename)) 856 { 857 free (entfilename); 858 entfilename = NULL; 859 return entnode; 860 } 861 } 862 863 error (1, save_errno, "cannot open %s", entfilename); 864 } 865 866 if (fprintf (entfile, "%c ", cmd) < 0) 867 error (1, errno, "cannot write %s", entfilename); 868 869 if (fputentent (entfile, entnode) != 0) 870 error (1, errno, "cannot write %s", entfilename); 871 872 if (fclose (entfile) == EOF) 873 error (1, errno, "error closing %s", entfilename); 874 875 if (parent != NULL) 876 { 877 free (entfilename); 878 entfilename = NULL; 879 } 880 } 881 882 return entnode; 883 } 884 885 /* 886 * Record the addition of a new subdirectory DIR in PARENT. PARENT 887 * may be NULL, which means the current directory. ENTRIES is the 888 * current entries list; it may be NULL, which means that it need not 889 * be updated. 890 */ 891 892 void 893 Subdir_Register (List *entries, const char *parent, const char *dir) 894 { 895 Entnode *entnode; 896 897 /* Ignore attempts to register ".". These can happen in the 898 server code. */ 899 if (dir[0] == '.' && dir[1] == '\0') 900 return; 901 902 entnode = subdir_record ('A', parent, dir); 903 904 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) 905 (void) AddEntryNode (entries, entnode); 906 else 907 Entnode_Destroy (entnode); 908 } 909 910 911 912 /* 913 * Record the removal of a subdirectory. The arguments are the same 914 * as for Subdir_Register. 915 */ 916 917 void 918 Subdir_Deregister (List *entries, const char *parent, const char *dir) 919 { 920 Entnode *entnode; 921 922 entnode = subdir_record ('R', parent, dir); 923 Entnode_Destroy (entnode); 924 925 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0)) 926 { 927 Node *p; 928 929 p = findnode_fn (entries, dir); 930 if (p != NULL) 931 delnode (p); 932 } 933 } 934 935 936 937 /* OK, the following base_* code tracks the revisions of the files in 938 CVS/Base. We do this in a file CVS/Baserev. Separate from 939 CVS/Entries because it needs to go in separate data structures 940 anyway (the name in Entries must be unique), so this seemed 941 cleaner. The business of rewriting the whole file in 942 base_deregister and base_register is the kind of thing we used to 943 do for Entries and which turned out to be slow, which is why there 944 is now the Entries.Log machinery. So maybe from that point of 945 view it is a mistake to do this separately from Entries, I dunno. */ 946 947 enum base_walk 948 { 949 /* Set the revision for FILE to *REV. */ 950 BASE_REGISTER, 951 /* Get the revision for FILE and put it in a newly malloc'd string 952 in *REV, or put NULL if not mentioned. */ 953 BASE_GET, 954 /* Remove FILE. */ 955 BASE_DEREGISTER 956 }; 957 958 static void base_walk (enum base_walk, struct file_info *, char **); 959 960 /* Read through the lines in CVS/Baserev, taking the actions as documented 961 for CODE. */ 962 963 static void 964 base_walk (enum base_walk code, struct file_info *finfo, char **rev) 965 { 966 FILE *fp; 967 char *line; 968 size_t line_allocated; 969 FILE *newf; 970 char *baserev_fullname; 971 char *baserevtmp_fullname; 972 973 line = NULL; 974 line_allocated = 0; 975 newf = NULL; 976 977 /* First compute the fullnames for the error messages. This 978 computation probably should be broken out into a separate function, 979 as recurse.c does it too and places like Entries_Open should be 980 doing it. */ 981 if (finfo->update_dir[0] != '\0') 982 { 983 baserev_fullname = Xasprintf ("%s/%s", finfo->update_dir, 984 CVSADM_BASEREV); 985 baserevtmp_fullname = Xasprintf ("%s/%s", finfo->update_dir, 986 CVSADM_BASEREVTMP); 987 } 988 else 989 { 990 baserev_fullname = xstrdup (CVSADM_BASEREV); 991 baserevtmp_fullname = xstrdup (CVSADM_BASEREVTMP); 992 } 993 994 fp = CVS_FOPEN (CVSADM_BASEREV, "r"); 995 if (fp == NULL) 996 { 997 if (!existence_error (errno)) 998 { 999 error (0, errno, "cannot open %s for reading", baserev_fullname); 1000 goto out; 1001 } 1002 } 1003 1004 switch (code) 1005 { 1006 case BASE_REGISTER: 1007 case BASE_DEREGISTER: 1008 newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w"); 1009 if (newf == NULL) 1010 { 1011 error (0, errno, "cannot open %s for writing", 1012 baserevtmp_fullname); 1013 goto out; 1014 } 1015 break; 1016 case BASE_GET: 1017 *rev = NULL; 1018 break; 1019 } 1020 1021 if (fp != NULL) 1022 { 1023 while (getline (&line, &line_allocated, fp) >= 0) 1024 { 1025 char *linefile; 1026 char *p; 1027 char *linerev; 1028 1029 if (line[0] != 'B') 1030 /* Ignore, for future expansion. */ 1031 continue; 1032 1033 linefile = line + 1; 1034 p = strchr (linefile, '/'); 1035 if (p == NULL) 1036 /* Syntax error, ignore. */ 1037 continue; 1038 linerev = p + 1; 1039 p = strchr (linerev, '/'); 1040 if (p == NULL) 1041 continue; 1042 1043 linerev[-1] = '\0'; 1044 if (fncmp (linefile, finfo->file) == 0) 1045 { 1046 switch (code) 1047 { 1048 case BASE_REGISTER: 1049 case BASE_DEREGISTER: 1050 /* Don't copy over the old entry, we don't want it. */ 1051 break; 1052 case BASE_GET: 1053 *p = '\0'; 1054 *rev = xstrdup (linerev); 1055 *p = '/'; 1056 goto got_it; 1057 } 1058 } 1059 else 1060 { 1061 linerev[-1] = '/'; 1062 switch (code) 1063 { 1064 case BASE_REGISTER: 1065 case BASE_DEREGISTER: 1066 if (fprintf (newf, "%s\n", line) < 0) 1067 error (0, errno, "error writing %s", 1068 baserevtmp_fullname); 1069 break; 1070 case BASE_GET: 1071 break; 1072 } 1073 } 1074 } 1075 if (ferror (fp)) 1076 error (0, errno, "cannot read %s", baserev_fullname); 1077 } 1078 got_it: 1079 1080 if (code == BASE_REGISTER) 1081 { 1082 if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0) 1083 error (0, errno, "error writing %s", 1084 baserevtmp_fullname); 1085 } 1086 1087 out: 1088 1089 if (line != NULL) 1090 free (line); 1091 1092 if (fp != NULL) 1093 { 1094 if (fclose (fp) < 0) 1095 error (0, errno, "cannot close %s", baserev_fullname); 1096 } 1097 if (newf != NULL) 1098 { 1099 if (fclose (newf) < 0) 1100 error (0, errno, "cannot close %s", baserevtmp_fullname); 1101 rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV); 1102 } 1103 1104 free (baserev_fullname); 1105 free (baserevtmp_fullname); 1106 } 1107 1108 /* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev, 1109 or NULL if not listed. */ 1110 1111 char * 1112 base_get (struct file_info *finfo) 1113 { 1114 char *rev; 1115 base_walk (BASE_GET, finfo, &rev); 1116 return rev; 1117 } 1118 1119 /* Set the revision for FILE to REV. */ 1120 1121 void 1122 base_register (struct file_info *finfo, char *rev) 1123 { 1124 base_walk (BASE_REGISTER, finfo, &rev); 1125 } 1126 1127 /* Remove FILE. */ 1128 1129 void 1130 base_deregister (struct file_info *finfo) 1131 { 1132 base_walk (BASE_DEREGISTER, finfo, NULL); 1133 } 1134