entries.c revision 17721
1/*
2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
4 *
5 * You may distribute under the terms of the GNU General Public License as
6 * specified in the README file that comes with the CVS 1.4 kit.
7 *
8 * Entries file to Files file
9 *
10 * Creates the file Files containing the names that comprise the project, from
11 * the Entries file.
12 */
13
14#include "cvs.h"
15#include "getline.h"
16
17static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
18
19static Entnode *fgetentent PROTO((FILE *));
20static int   fputentent PROTO((FILE *, Entnode *));
21
22static FILE *entfile;
23static char *entfilename;		/* for error messages */
24
25/*
26 * Construct an Entnode
27 */
28Entnode *
29Entnode_Create(user, vn, ts, options, tag, date, ts_conflict)
30    const char *user;
31    const char *vn;
32    const char *ts;
33    const char *options;
34    const char *tag;
35    const char *date;
36    const char *ts_conflict;
37{
38    Entnode *ent;
39
40    /* Note that timestamp and options must be non-NULL */
41    ent = (Entnode *) xmalloc (sizeof (Entnode));
42    ent->user      = xstrdup (user);
43    ent->version   = xstrdup (vn);
44    ent->timestamp = xstrdup (ts ? ts : "");
45    ent->options   = xstrdup (options ? options : "");
46    ent->tag       = xstrdup (tag);
47    ent->date      = xstrdup (date);
48    ent->conflict  = xstrdup (ts_conflict);
49
50    return ent;
51}
52
53/*
54 * Destruct an Entnode
55 */
56void
57Entnode_Destroy (ent)
58    Entnode *ent;
59{
60    free (ent->user);
61    free (ent->version);
62    free (ent->timestamp);
63    free (ent->options);
64    if (ent->tag)
65	free (ent->tag);
66    if (ent->date)
67	free (ent->date);
68    if (ent->conflict)
69	free (ent->conflict);
70    free (ent);
71}
72
73/*
74 * Write out the line associated with a node of an entries file
75 */
76static int write_ent_proc PROTO ((Node *, void *));
77static int
78write_ent_proc (node, closure)
79     Node *node;
80     void *closure;
81{
82    if (fputentent(entfile, (Entnode *) node->data))
83	error (1, errno, "cannot write %s", entfilename);
84
85    return (0);
86}
87
88/*
89 * write out the current entries file given a list,  making a backup copy
90 * first of course
91 */
92static void
93write_entries (list)
94    List *list;
95{
96    /* open the new one and walk the list writing entries */
97    entfilename = CVSADM_ENTBAK;
98    entfile = open_file (entfilename, "w+");
99    (void) walklist (list, write_ent_proc, NULL);
100    if (fclose (entfile) == EOF)
101	error (1, errno, "error closing %s", entfilename);
102
103    /* now, atomically (on systems that support it) rename it */
104    rename_file (entfilename, CVSADM_ENT);
105
106    /* now, remove the log file */
107    unlink_file (CVSADM_ENTLOG);
108}
109
110/*
111 * Removes the argument file from the Entries file if necessary.
112 */
113void
114Scratch_Entry (list, fname)
115    List *list;
116    char *fname;
117{
118    Node *node;
119
120    if (trace)
121#ifdef SERVER_SUPPORT
122	(void) fprintf (stderr, "%c-> Scratch_Entry(%s)\n",
123			(server_active) ? 'S' : ' ', fname);
124#else
125	(void) fprintf (stderr, "-> Scratch_Entry(%s)\n", fname);
126#endif
127
128    /* hashlookup to see if it is there */
129    if ((node = findnode_fn (list, fname)) != NULL)
130    {
131	delnode (node);			/* delete the node */
132#ifdef SERVER_SUPPORT
133	if (server_active)
134	    server_scratch (fname);
135#endif
136	if (!noexec)
137	    write_entries (list);	/* re-write the file */
138    }
139}
140
141/*
142 * Enters the given file name/version/time-stamp into the Entries file,
143 * removing the old entry first, if necessary.
144 */
145void
146Register (list, fname, vn, ts, options, tag, date, ts_conflict)
147    List *list;
148    char *fname;
149    char *vn;
150    char *ts;
151    char *options;
152    char *tag;
153    char *date;
154    char *ts_conflict;
155{
156    Entnode *entnode;
157    Node *node;
158
159#ifdef SERVER_SUPPORT
160    if (server_active)
161    {
162	server_register (fname, vn, ts, options, tag, date, ts_conflict);
163    }
164#endif
165
166    if (trace)
167    {
168#ifdef SERVER_SUPPORT
169	(void) fprintf (stderr, "%c-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
170			(server_active) ? 'S' : ' ',
171			fname, vn, ts ? ts : "",
172			ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
173			options, tag ? tag : "", date ? date : "");
174#else
175	(void) fprintf (stderr, "-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
176			fname, vn, ts ? ts : "",
177			ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
178			options, tag ? tag : "", date ? date : "");
179#endif
180    }
181
182    entnode = Entnode_Create(fname, vn, ts, options, tag, date, ts_conflict);
183    node = AddEntryNode (list, entnode);
184
185    if (!noexec)
186    {
187	entfile = open_file (CVSADM_ENTLOG, "a");
188
189	write_ent_proc (node, NULL);
190
191        if (fclose (entfile) == EOF)
192            error (1, errno, "error closing %s", CVSADM_ENTLOG);
193    }
194}
195
196/*
197 * Node delete procedure for list-private sticky dir tag/date info
198 */
199static void
200freesdt (p)
201    Node *p;
202{
203    struct stickydirtag *sdtp;
204
205    sdtp = (struct stickydirtag *) p->data;
206    if (sdtp->tag)
207	free (sdtp->tag);
208    if (sdtp->date)
209	free (sdtp->date);
210    if (sdtp->options)
211	free (sdtp->options);
212    free ((char *) sdtp);
213}
214
215static Entnode *
216fgetentent(fpin)
217    FILE *fpin;
218{
219    Entnode *ent;
220    char *line;
221    size_t line_chars_allocated;
222    register char *cp;
223    char *user, *vn, *ts, *options;
224    char *tag_or_date, *tag, *date, *ts_conflict;
225
226    line = NULL;
227    line_chars_allocated = 0;
228
229    ent = NULL;
230    while (getline (&line, &line_chars_allocated, fpin) > 0)
231    {
232	if (line[0] != '/')
233	    continue;
234
235	user = line + 1;
236	if ((cp = strchr (user, '/')) == NULL)
237	    continue;
238	*cp++ = '\0';
239	vn = cp;
240	if ((cp = strchr (vn, '/')) == NULL)
241	    continue;
242	*cp++ = '\0';
243	ts = cp;
244	if ((cp = strchr (ts, '/')) == NULL)
245	    continue;
246	*cp++ = '\0';
247	options = cp;
248	if ((cp = strchr (options, '/')) == NULL)
249	    continue;
250	*cp++ = '\0';
251	tag_or_date = cp;
252	if ((cp = strchr (tag_or_date, '\n')) == NULL)
253	    continue;
254	*cp = '\0';
255	tag = (char *) NULL;
256	date = (char *) NULL;
257	if (*tag_or_date == 'T')
258	    tag = tag_or_date + 1;
259	else if (*tag_or_date == 'D')
260	    date = tag_or_date + 1;
261
262	if ((ts_conflict = strchr (ts, '+')))
263	    *ts_conflict++ = '\0';
264
265	/*
266	 * XXX - Convert timestamp from old format to new format.
267	 *
268	 * If the timestamp doesn't match the file's current
269	 * mtime, we'd have to generate a string that doesn't
270	 * match anyways, so cheat and base it on the existing
271	 * string; it doesn't have to match the same mod time.
272	 *
273	 * For an unmodified file, write the correct timestamp.
274	 */
275	{
276	    struct stat sb;
277	    if (strlen (ts) > 30 && stat (user, &sb) == 0)
278	    {
279		char *c = ctime (&sb.st_mtime);
280
281		if (!strncmp (ts + 25, c, 24))
282		    ts = time_stamp (user);
283		else
284		{
285		    ts += 24;
286		    ts[0] = '*';
287		}
288	    }
289	}
290
291	ent = Entnode_Create(user, vn, ts, options, tag, date, ts_conflict);
292	break;
293    }
294
295    free (line);
296    return ent;
297}
298
299static int
300fputentent(fp, p)
301    FILE *fp;
302    Entnode *p;
303{
304    if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
305	return 1;
306    if (p->conflict)
307    {
308	if (fprintf (fp, "+%s", p->conflict) < 0)
309	    return 1;
310    }
311    if (fprintf (fp, "/%s/", p->options) < 0)
312	return 1;
313
314    if (p->tag)
315    {
316	if (fprintf (fp, "T%s\n", p->tag) < 0)
317	    return 1;
318    }
319    else if (p->date)
320    {
321	if (fprintf (fp, "D%s\n", p->date) < 0)
322	    return 1;
323    }
324    else
325    {
326	if (fprintf (fp, "\n") < 0)
327	    return 1;
328    }
329
330    return 0;
331}
332
333
334/*
335 * Read the entries file into a list, hashing on the file name.
336 */
337List *
338Entries_Open (aflag)
339    int aflag;
340{
341    List *entries;
342    Entnode *ent;
343    char *dirtag, *dirdate;
344    int do_rewrite = 0;
345    FILE *fpin;
346
347    /* get a fresh list... */
348    entries = getlist ();
349
350    /*
351     * Parse the CVS/Tag file, to get any default tag/date settings. Use
352     * list-private storage to tuck them away for Version_TS().
353     */
354    ParseTag (&dirtag, &dirdate);
355    if (aflag || dirtag || dirdate)
356    {
357	struct stickydirtag *sdtp;
358
359	sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
360	memset ((char *) sdtp, 0, sizeof (*sdtp));
361	sdtp->aflag = aflag;
362	sdtp->tag = xstrdup (dirtag);
363	sdtp->date = xstrdup (dirdate);
364
365	/* feed it into the list-private area */
366	entries->list->data = (char *) sdtp;
367	entries->list->delproc = freesdt;
368    }
369
370    fpin = fopen (CVSADM_ENT, "r");
371    if (fpin == NULL)
372	error (0, errno, "cannot open %s for reading", CVSADM_ENT);
373    else
374    {
375	while ((ent = fgetentent (fpin)) != NULL)
376	{
377	    (void) AddEntryNode (entries, ent);
378	}
379
380	fclose (fpin);
381    }
382
383    fpin = fopen (CVSADM_ENTLOG, "r");
384    if (fpin != NULL)
385    {
386	while ((ent = fgetentent (fpin)) != NULL)
387	{
388	    (void) AddEntryNode (entries, ent);
389	}
390	do_rewrite = 1;
391	fclose (fpin);
392    }
393
394    if (do_rewrite && !noexec)
395	write_entries (entries);
396
397    /* clean up and return */
398    if (dirtag)
399	free (dirtag);
400    if (dirdate)
401	free (dirdate);
402    return (entries);
403}
404
405void
406Entries_Close(list)
407    List *list;
408{
409    if (list)
410    {
411	if (!noexec)
412        {
413            if (isfile (CVSADM_ENTLOG))
414		write_entries (list);
415	}
416	dellist(&list);
417    }
418}
419
420
421/*
422 * Free up the memory associated with the data section of an ENTRIES type
423 * node
424 */
425static void
426Entries_delproc (node)
427    Node *node;
428{
429    Entnode *p;
430
431    p = (Entnode *) node->data;
432    Entnode_Destroy(p);
433}
434
435/*
436 * Get an Entries file list node, initialize it, and add it to the specified
437 * list
438 */
439static Node *
440AddEntryNode (list, entdata)
441    List *list;
442    Entnode *entdata;
443{
444    Node *p;
445
446    /* was it already there? */
447    if ((p  = findnode_fn (list, entdata->user)) != NULL)
448    {
449	/* take it out */
450	delnode (p);
451    }
452
453    /* get a node and fill in the regular stuff */
454    p = getnode ();
455    p->type = ENTRIES;
456    p->delproc = Entries_delproc;
457
458    /* this one gets a key of the name for hashing */
459    /* FIXME This results in duplicated data --- the hash package shouldn't
460       assume that the key is dynamically allocated.  The user's free proc
461       should be responsible for freeing the key. */
462    p->key = xstrdup (entdata->user);
463    p->data = (char *) entdata;
464
465    /* put the node into the list */
466    addnode (list, p);
467    return (p);
468}
469
470/*
471 * Write out/Clear the CVS/Tag file.
472 */
473void
474WriteTag (dir, tag, date)
475    char *dir;
476    char *tag;
477    char *date;
478{
479    FILE *fout;
480    char tmp[PATH_MAX];
481
482    if (noexec)
483	return;
484
485    if (dir == NULL)
486	(void) strcpy (tmp, CVSADM_TAG);
487    else
488	(void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
489
490    if (tag || date)
491    {
492	fout = open_file (tmp, "w+");
493	if (tag)
494	{
495	    if (fprintf (fout, "T%s\n", tag) < 0)
496		error (1, errno, "write to %s failed", tmp);
497	}
498	else
499	{
500	    if (fprintf (fout, "D%s\n", date) < 0)
501		error (1, errno, "write to %s failed", tmp);
502	}
503	if (fclose (fout) == EOF)
504	    error (1, errno, "cannot close %s", tmp);
505    }
506    else
507	if (unlink_file (tmp) < 0 && ! existence_error (errno))
508	    error (1, errno, "cannot remove %s", tmp);
509}
510
511/*
512 * Parse the CVS/Tag file for the current directory.
513 */
514void
515ParseTag (tagp, datep)
516    char **tagp;
517    char **datep;
518{
519    FILE *fp;
520
521    if (tagp)
522	*tagp = (char *) NULL;
523    if (datep)
524	*datep = (char *) NULL;
525    fp = fopen (CVSADM_TAG, "r");
526    if (fp)
527    {
528	char *line;
529	int line_length;
530	size_t line_chars_allocated;
531
532	line = NULL;
533	line_chars_allocated = 0;
534
535	if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
536	{
537	    /* Remove any trailing newline.  */
538	    if (line[line_length - 1] == '\n')
539	        line[--line_length] = '\0';
540	    if (*line == 'T' && tagp)
541		*tagp = xstrdup (line + 1);
542	    else if (*line == 'D' && datep)
543		*datep = xstrdup (line + 1);
544	}
545	(void) fclose (fp);
546	free (line);
547    }
548}
549