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