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