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