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