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