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