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 *
Entnode_Create(type,user,vn,ts,options,tag,date,ts_conflict)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
Entnode_Destroy(ent)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
write_ent_proc(node,closure)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
write_entries(list)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
Scratch_Entry(list,fname)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
Register(list,fname,vn,ts,options,tag,date,ts_conflict)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
freesdt(p)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 *
fgetentent(fpin,cmd,sawdir)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
fputentent(fp,p)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 *
Entries_Open(aflag,update_dir)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
Entries_Close(list)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
Entries_delproc(node)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 *
AddEntryNode(list,entdata)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
WriteTag(dir,tag,date,nonbranch,update_dir,repository)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
ParseTag(tagp,datep,nonbranchp)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
Subdirs_Known(entries)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 *
subdir_record(cmd,parent,dir)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
Subdir_Register(entries,parent,dir)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
Subdir_Deregister(entries,parent,dir)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
base_walk(code,finfo,rev)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 *
base_get(finfo)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
base_register(finfo,rev)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
base_deregister(finfo)1183 base_deregister (finfo)
1184 struct file_info *finfo;
1185 {
1186 base_walk (BASE_DEREGISTER, finfo, NULL);
1187 }
1188