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