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