rtld - do not allow both dynamic DTV index and static TLS offset
[dragonfly.git] / contrib / cvs-1.12 / src / entries.c
blob0512380980a6a118c68e4dcc6765d03a0bf72372
1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
7 * Portions Copyright (C) 1992, Brian Berliner and Jeff Polk
8 * Portions Copyright (C) 1989-1992, Brian Berliner
9 *
10 * You may distribute under the terms of the GNU General Public License as
11 * specified in the README file that comes with the CVS source distribution.
13 * Entries file to Files file
15 * Creates the file Files containing the names that comprise the project, from
16 * the Entries file.
19 #include "cvs.h"
20 #include "getline.h"
22 static Node *AddEntryNode (List * list, Entnode *entnode);
24 static Entnode *fgetentent (FILE *, char *, int *);
25 static int fputentent (FILE *, Entnode *);
27 static Entnode *subdir_record (int, const char *, const char *);
29 static FILE *entfile;
30 static char *entfilename; /* for error messages */
35 * Construct an Entnode
37 static Entnode *
38 Entnode_Create (enum ent_type type, const char *user, const char *vn,
39 const char *ts, const char *options, const char *tag,
40 const char *date, const char *ts_conflict)
42 Entnode *ent;
44 /* Note that timestamp and options must be non-NULL */
45 ent = xmalloc (sizeof (Entnode));
46 ent->type = type;
47 ent->user = xstrdup (user);
48 ent->version = xstrdup (vn);
49 ent->timestamp = xstrdup (ts ? ts : "");
50 ent->options = xstrdup (options ? options : "");
51 ent->tag = xstrdup (tag);
52 ent->date = xstrdup (date);
53 ent->conflict = xstrdup (ts_conflict);
55 return ent;
59 * Destruct an Entnode
61 static void Entnode_Destroy (Entnode *);
63 static void
64 Entnode_Destroy (Entnode *ent)
66 free (ent->user);
67 free (ent->version);
68 free (ent->timestamp);
69 free (ent->options);
70 if (ent->tag)
71 free (ent->tag);
72 if (ent->date)
73 free (ent->date);
74 if (ent->conflict)
75 free (ent->conflict);
76 free (ent);
80 * Write out the line associated with a node of an entries file
82 static int write_ent_proc (Node *, void *);
83 static int
84 write_ent_proc (Node *node, void *closure)
86 Entnode *entnode = node->data;
88 if (closure != NULL && entnode->type != ENT_FILE)
89 *(int *) closure = 1;
91 if (fputentent (entfile, entnode))
92 error (1, errno, "cannot write %s", entfilename);
94 return 0;
98 * write out the current entries file given a list, making a backup copy
99 * first of course
101 static void
102 write_entries (List *list)
104 int sawdir;
106 sawdir = 0;
108 /* open the new one and walk the list writing entries */
109 entfilename = CVSADM_ENTBAK;
110 entfile = CVS_FOPEN (entfilename, "w+");
111 if (entfile == NULL)
113 /* Make this a warning, not an error. For example, one user might
114 have checked out a working directory which, for whatever reason,
115 contains an Entries.Log file. A second user, without write access
116 to that working directory, might want to do a "cvs log". The
117 problem rewriting Entries shouldn't affect the ability of "cvs log"
118 to work, although the warning is probably a good idea so that
119 whether Entries gets rewritten is not an inexplicable process. */
120 /* FIXME: should be including update_dir in message. */
121 error (0, errno, "cannot rewrite %s", entfilename);
123 /* Now just return. We leave the Entries.Log file around. As far
124 as I know, there is never any data lying around in 'list' that
125 is not in Entries.Log at this time (if there is an error writing
126 Entries.Log that is a separate problem). */
127 return;
130 (void) walklist (list, write_ent_proc, (void *) &sawdir);
131 if (! sawdir)
133 struct stickydirtag *sdtp;
135 /* We didn't write out any directories. Check the list
136 private data to see whether subdirectory information is
137 known. If it is, we need to write out an empty D line. */
138 sdtp = list->list->data;
139 if (sdtp == NULL || sdtp->subdirs)
140 if (fprintf (entfile, "D\n") < 0)
141 error (1, errno, "cannot write %s", entfilename);
143 if (fclose (entfile) == EOF)
144 error (1, errno, "error closing %s", entfilename);
146 /* now, atomically (on systems that support it) rename it */
147 rename_file (entfilename, CVSADM_ENT);
149 /* now, remove the log file */
150 if (unlink_file (CVSADM_ENTLOG) < 0
151 && !existence_error (errno))
152 error (0, errno, "cannot remove %s", CVSADM_ENTLOG);
158 * Removes the argument file from the Entries file if necessary.
160 void
161 Scratch_Entry (List *list, const char *fname)
163 Node *node;
165 TRACE (TRACE_FUNCTION, "Scratch_Entry(%s)", fname);
167 /* hashlookup to see if it is there */
168 if ((node = findnode_fn (list, fname)) != NULL)
170 if (!noexec)
172 entfilename = CVSADM_ENTLOG;
173 entfile = xfopen (entfilename, "a");
175 if (fprintf (entfile, "R ") < 0)
176 error (1, errno, "cannot write %s", entfilename);
178 write_ent_proc (node, NULL);
180 if (fclose (entfile) == EOF)
181 error (1, errno, "error closing %s", entfilename);
184 delnode (node); /* delete the node */
186 #ifdef SERVER_SUPPORT
187 if (server_active)
188 server_scratch (fname);
189 #endif
196 * Enters the given file name/version/time-stamp into the Entries file,
197 * removing the old entry first, if necessary.
199 void
200 Register (List *list, const char *fname, const char *vn, const char *ts,
201 const char *options, const char *tag, const char *date,
202 const char *ts_conflict)
204 Entnode *entnode;
205 Node *node;
207 #ifdef SERVER_SUPPORT
208 if (server_active)
210 server_register (fname, vn, ts, options, tag, date, ts_conflict);
212 #endif
214 TRACE (TRACE_FUNCTION, "Register(%s, %s, %s%s%s, %s, %s %s)",
215 fname, vn, ts ? ts : "",
216 ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
217 options, tag ? tag : "", date ? date : "");
219 entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date,
220 ts_conflict);
221 node = AddEntryNode (list, entnode);
223 if (!noexec)
225 entfilename = CVSADM_ENTLOG;
226 entfile = CVS_FOPEN (entfilename, "a");
228 if (entfile == NULL)
230 /* Warning, not error, as in write_entries. */
231 /* FIXME-update-dir: should be including update_dir in message. */
232 error (0, errno, "cannot open %s", entfilename);
233 return;
236 if (fprintf (entfile, "A ") < 0)
237 error (1, errno, "cannot write %s", entfilename);
239 write_ent_proc (node, NULL);
241 if (fclose (entfile) == EOF)
242 error (1, errno, "error closing %s", entfilename);
247 * Node delete procedure for list-private sticky dir tag/date info
249 static void
250 freesdt (Node *p)
252 struct stickydirtag *sdtp = p->data;
254 if (sdtp->tag)
255 free (sdtp->tag);
256 if (sdtp->date)
257 free (sdtp->date);
258 free ((char *) sdtp);
261 /* Return the next real Entries line. On end of file, returns NULL.
262 On error, prints an error message and returns NULL. */
264 static Entnode *
265 fgetentent (FILE *fpin, char *cmd, int *sawdir)
267 Entnode *ent;
268 char *line;
269 size_t line_chars_allocated;
270 register char *cp;
271 enum ent_type type;
272 char *l, *user, *vn, *ts, *options;
273 char *tag_or_date, *tag, *date, *ts_conflict;
274 int line_length;
276 line = NULL;
277 line_chars_allocated = 0;
279 ent = NULL;
280 while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 0)
282 l = line;
284 /* If CMD is not NULL, we are reading an Entries.Log file.
285 Each line in the Entries.Log file starts with a single
286 character command followed by a space. For backward
287 compatibility, the absence of a space indicates an add
288 command. */
289 if (cmd != NULL)
291 if (l[1] != ' ')
292 *cmd = 'A';
293 else
295 *cmd = l[0];
296 l += 2;
300 type = ENT_FILE;
302 if (l[0] == 'D')
304 type = ENT_SUBDIR;
305 *sawdir = 1;
306 ++l;
307 /* An empty D line is permitted; it is a signal that this
308 Entries file lists all known subdirectories. */
311 if (l[0] != '/')
312 continue;
314 user = l + 1;
315 if ((cp = strchr (user, '/')) == NULL)
316 continue;
317 *cp++ = '\0';
318 vn = cp;
319 if ((cp = strchr (vn, '/')) == NULL)
320 continue;
321 *cp++ = '\0';
322 ts = cp;
323 if ((cp = strchr (ts, '/')) == NULL)
324 continue;
325 *cp++ = '\0';
326 options = cp;
327 if ((cp = strchr (options, '/')) == NULL)
328 continue;
329 *cp++ = '\0';
330 tag_or_date = cp;
331 if ((cp = strchr (tag_or_date, '\n')) == NULL)
332 continue;
333 *cp = '\0';
334 tag = NULL;
335 date = NULL;
336 if (*tag_or_date == 'T')
337 tag = tag_or_date + 1;
338 else if (*tag_or_date == 'D')
339 date = tag_or_date + 1;
341 if ((ts_conflict = strchr (ts, '+')))
342 *ts_conflict++ = '\0';
345 * XXX - Convert timestamp from old format to new format.
347 * If the timestamp doesn't match the file's current
348 * mtime, we'd have to generate a string that doesn't
349 * match anyways, so cheat and base it on the existing
350 * string; it doesn't have to match the same mod time.
352 * For an unmodified file, write the correct timestamp.
355 struct stat sb;
356 if (strlen (ts) > 30 && stat (user, &sb) == 0)
358 char *c = ctime (&sb.st_mtime);
359 /* Fix non-standard format. */
360 if (c[8] == '0') c[8] = ' ';
362 if (!strncmp (ts + 25, c, 24))
363 ts = time_stamp (user);
364 else
366 ts += 24;
367 ts[0] = '*';
372 ent = Entnode_Create (type, user, vn, ts, options, tag, date,
373 ts_conflict);
374 break;
377 if (line_length < 0 && !feof (fpin))
378 error (0, errno, "cannot read entries file");
380 free (line);
381 return ent;
384 static int
385 fputentent (FILE *fp, Entnode *p)
387 switch (p->type)
389 case ENT_FILE:
390 break;
391 case ENT_SUBDIR:
392 if (fprintf (fp, "D") < 0)
393 return 1;
394 break;
397 if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
398 return 1;
399 if (p->conflict)
401 if (fprintf (fp, "+%s", p->conflict) < 0)
402 return 1;
404 if (fprintf (fp, "/%s/", p->options) < 0)
405 return 1;
407 if (p->tag)
409 if (fprintf (fp, "T%s\n", p->tag) < 0)
410 return 1;
412 else if (p->date)
414 if (fprintf (fp, "D%s\n", p->date) < 0)
415 return 1;
417 else
419 if (fprintf (fp, "\n") < 0)
420 return 1;
423 return 0;
427 /* Read the entries file into a list, hashing on the file name.
429 UPDATE_DIR is the name of the current directory, for use in error
430 messages, or NULL if not known (that is, noone has gotten around
431 to updating the caller to pass in the information). */
432 List *
433 Entries_Open (int aflag, char *update_dir)
435 List *entries;
436 struct stickydirtag *sdtp = NULL;
437 Entnode *ent;
438 char *dirtag, *dirdate;
439 int dirnonbranch;
440 int do_rewrite = 0;
441 FILE *fpin;
442 int sawdir;
444 /* get a fresh list... */
445 entries = getlist ();
448 * Parse the CVS/Tag file, to get any default tag/date settings. Use
449 * list-private storage to tuck them away for Version_TS().
451 ParseTag (&dirtag, &dirdate, &dirnonbranch);
452 if (aflag || dirtag || dirdate)
454 sdtp = xmalloc (sizeof (*sdtp));
455 memset (sdtp, 0, sizeof (*sdtp));
456 sdtp->aflag = aflag;
457 sdtp->tag = xstrdup (dirtag);
458 sdtp->date = xstrdup (dirdate);
459 sdtp->nonbranch = dirnonbranch;
461 /* feed it into the list-private area */
462 entries->list->data = sdtp;
463 entries->list->delproc = freesdt;
466 sawdir = 0;
468 fpin = CVS_FOPEN (CVSADM_ENT, "r");
469 if (fpin == NULL)
471 if (update_dir != NULL)
472 error (0, 0, "in directory %s:", update_dir);
473 error (0, errno, "cannot open %s for reading", CVSADM_ENT);
475 else
477 while ((ent = fgetentent (fpin, NULL, &sawdir)) != NULL)
479 (void) AddEntryNode (entries, ent);
482 if (fclose (fpin) < 0)
483 /* FIXME-update-dir: should include update_dir in message. */
484 error (0, errno, "cannot close %s", CVSADM_ENT);
487 fpin = CVS_FOPEN (CVSADM_ENTLOG, "r");
488 if (fpin != NULL)
490 char cmd;
491 Node *node;
493 while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL)
495 switch (cmd)
497 case 'A':
498 (void) AddEntryNode (entries, ent);
499 break;
500 case 'R':
501 node = findnode_fn (entries, ent->user);
502 if (node != NULL)
503 delnode (node);
504 Entnode_Destroy (ent);
505 break;
506 default:
507 /* Ignore unrecognized commands. */
508 Entnode_Destroy (ent);
509 break;
512 do_rewrite = 1;
513 if (fclose (fpin) < 0)
514 /* FIXME-update-dir: should include update_dir in message. */
515 error (0, errno, "cannot close %s", CVSADM_ENTLOG);
518 /* Update the list private data to indicate whether subdirectory
519 information is known. Nonexistent list private data is taken
520 to mean that it is known. */
521 if (sdtp != NULL)
522 sdtp->subdirs = sawdir;
523 else if (! sawdir)
525 sdtp = xmalloc (sizeof (*sdtp));
526 memset (sdtp, 0, sizeof (*sdtp));
527 sdtp->subdirs = 0;
528 entries->list->data = sdtp;
529 entries->list->delproc = freesdt;
532 if (do_rewrite && !noexec)
533 write_entries (entries);
535 /* clean up and return */
536 if (dirtag)
537 free (dirtag);
538 if (dirdate)
539 free (dirdate);
540 return entries;
543 void
544 Entries_Close (List *list)
546 if (list)
548 if (!noexec)
550 if (isfile (CVSADM_ENTLOG))
551 write_entries (list);
553 dellist (&list);
559 * Free up the memory associated with the data section of an ENTRIES type
560 * node
562 static void
563 Entries_delproc (Node *node)
565 Entnode *p = node->data;
567 Entnode_Destroy (p);
571 * Get an Entries file list node, initialize it, and add it to the specified
572 * list
574 static Node *
575 AddEntryNode (List *list, Entnode *entdata)
577 Node *p;
579 /* was it already there? */
580 if ((p = findnode_fn (list, entdata->user)) != NULL)
582 /* take it out */
583 delnode (p);
586 /* get a node and fill in the regular stuff */
587 p = getnode ();
588 p->type = ENTRIES;
589 p->delproc = Entries_delproc;
591 /* this one gets a key of the name for hashing */
592 /* FIXME This results in duplicated data --- the hash package shouldn't
593 assume that the key is dynamically allocated. The user's free proc
594 should be responsible for freeing the key. */
595 p->key = xstrdup (entdata->user);
596 p->data = entdata;
598 /* put the node into the list */
599 addnode (list, p);
600 return p;
606 * Write out the CVS/Template file.
608 void
609 WriteTemplate (const char *update_dir, int xdotemplate, const char *repository)
611 #ifdef SERVER_SUPPORT
612 TRACE (TRACE_FUNCTION, "Write_Template (%s, %s)", update_dir, repository);
614 if (noexec)
615 return;
617 if (server_active && xdotemplate)
619 /* Clear the CVS/Template if supported to allow for the case
620 * where the rcsinfo file no longer has an entry for this
621 * directory.
623 server_clear_template (update_dir, repository);
624 server_template (update_dir, repository);
626 #endif
628 return;
634 * Write out/Clear the CVS/Tag file.
636 void
637 WriteTag (const char *dir, const char *tag, const char *date, int nonbranch,
638 const char *update_dir, const char *repository)
640 FILE *fout;
641 char *tmp;
643 if (noexec)
644 return;
646 if (dir != NULL)
647 tmp = Xasprintf ("%s/%s", dir, CVSADM_TAG);
648 else
649 tmp = xstrdup (CVSADM_TAG);
652 if (tag || date)
654 fout = xfopen (tmp, "w+");
655 if (tag)
657 if (nonbranch)
659 if (fprintf (fout, "N%s\n", tag) < 0)
660 error (1, errno, "write to %s failed", tmp);
662 else
664 if (fprintf (fout, "T%s\n", tag) < 0)
665 error (1, errno, "write to %s failed", tmp);
668 else
670 if (fprintf (fout, "D%s\n", date) < 0)
671 error (1, errno, "write to %s failed", tmp);
673 if (fclose (fout) == EOF)
674 error (1, errno, "cannot close %s", tmp);
676 else
677 if (unlink_file (tmp) < 0 && ! existence_error (errno))
678 error (1, errno, "cannot remove %s", tmp);
679 free (tmp);
680 #ifdef SERVER_SUPPORT
681 if (server_active)
682 server_set_sticky (update_dir, repository, tag, date, nonbranch);
683 #endif
686 /* Parse the CVS/Tag file for the current directory.
688 If it contains a date, sets *DATEP to the date in a newly malloc'd
689 string, *TAGP to NULL, and *NONBRANCHP to an unspecified value.
691 If it contains a branch tag, sets *TAGP to the tag in a newly
692 malloc'd string, *NONBRANCHP to 0, and *DATEP to NULL.
694 If it contains a nonbranch tag, sets *TAGP to the tag in a newly
695 malloc'd string, *NONBRANCHP to 1, and *DATEP to NULL.
697 If it does not exist, or contains something unrecognized by this
698 version of CVS, set *DATEP and *TAGP to NULL and *NONBRANCHP to
699 an unspecified value.
701 If there is an error, print an error message, set *DATEP and *TAGP
702 to NULL, and return. */
703 void
704 ParseTag (char **tagp, char **datep, int *nonbranchp)
706 FILE *fp;
708 if (tagp)
709 *tagp = NULL;
710 if (datep)
711 *datep = NULL;
712 /* Always store a value here, even in the 'D' case where the value
713 is unspecified. Shuts up tools which check for references to
714 uninitialized memory. */
715 if (nonbranchp != NULL)
716 *nonbranchp = 0;
717 fp = CVS_FOPEN (CVSADM_TAG, "r");
718 if (fp)
720 char *line;
721 int line_length;
722 size_t line_chars_allocated;
724 line = NULL;
725 line_chars_allocated = 0;
727 if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
729 /* Remove any trailing newline. */
730 if (line[line_length - 1] == '\n')
731 line[--line_length] = '\0';
732 switch (*line)
734 case 'T':
735 if (tagp != NULL)
736 *tagp = xstrdup (line + 1);
737 break;
738 case 'D':
739 if (datep != NULL)
740 *datep = xstrdup (line + 1);
741 break;
742 case 'N':
743 if (tagp != NULL)
744 *tagp = xstrdup (line + 1);
745 if (nonbranchp != NULL)
746 *nonbranchp = 1;
747 break;
748 default:
749 /* Silently ignore it; it may have been
750 written by a future version of CVS which extends the
751 syntax. */
752 break;
756 if (line_length < 0)
758 /* FIXME-update-dir: should include update_dir in messages. */
759 if (feof (fp))
760 error (0, 0, "cannot read %s: end of file", CVSADM_TAG);
761 else
762 error (0, errno, "cannot read %s", CVSADM_TAG);
765 if (fclose (fp) < 0)
766 /* FIXME-update-dir: should include update_dir in message. */
767 error (0, errno, "cannot close %s", CVSADM_TAG);
769 free (line);
771 else if (!existence_error (errno))
772 /* FIXME-update-dir: should include update_dir in message. */
773 error (0, errno, "cannot open %s", CVSADM_TAG);
777 * This is called if all subdirectory information is known, but there
778 * aren't any subdirectories. It records that fact in the list
779 * private data.
782 void
783 Subdirs_Known (List *entries)
785 struct stickydirtag *sdtp = entries->list->data;
787 /* If there is no list private data, that means that the
788 subdirectory information is known. */
789 if (sdtp != NULL && ! sdtp->subdirs)
791 FILE *fp;
793 sdtp->subdirs = 1;
794 if (!noexec)
796 /* Create Entries.Log so that Entries_Close will do something. */
797 entfilename = CVSADM_ENTLOG;
798 fp = CVS_FOPEN (entfilename, "a");
799 if (fp == NULL)
801 int save_errno = errno;
803 /* As in subdir_record, just silently skip the whole thing
804 if there is no CVSADM directory. */
805 if (! isdir (CVSADM))
806 return;
807 error (1, save_errno, "cannot open %s", entfilename);
809 else
811 if (fclose (fp) == EOF)
812 error (1, errno, "cannot close %s", entfilename);
818 /* Record subdirectory information. */
820 static Entnode *
821 subdir_record (int cmd, const char *parent, const char *dir)
823 Entnode *entnode;
825 /* None of the information associated with a directory is
826 currently meaningful. */
827 entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "",
828 NULL, NULL, NULL);
830 if (!noexec)
832 if (parent == NULL)
833 entfilename = CVSADM_ENTLOG;
834 else
835 entfilename = Xasprintf ("%s/%s", parent, CVSADM_ENTLOG);
837 entfile = CVS_FOPEN (entfilename, "a");
838 if (entfile == NULL)
840 int save_errno = errno;
842 /* It is not an error if there is no CVS administration
843 directory. Permitting this case simplifies some
844 calling code. */
846 if (parent == NULL)
848 if (! isdir (CVSADM))
849 return entnode;
851 else
853 free (entfilename);
854 entfilename = Xasprintf ("%s/%s", parent, CVSADM);
855 if (! isdir (entfilename))
857 free (entfilename);
858 entfilename = NULL;
859 return entnode;
863 error (1, save_errno, "cannot open %s", entfilename);
866 if (fprintf (entfile, "%c ", cmd) < 0)
867 error (1, errno, "cannot write %s", entfilename);
869 if (fputentent (entfile, entnode) != 0)
870 error (1, errno, "cannot write %s", entfilename);
872 if (fclose (entfile) == EOF)
873 error (1, errno, "error closing %s", entfilename);
875 if (parent != NULL)
877 free (entfilename);
878 entfilename = NULL;
882 return entnode;
886 * Record the addition of a new subdirectory DIR in PARENT. PARENT
887 * may be NULL, which means the current directory. ENTRIES is the
888 * current entries list; it may be NULL, which means that it need not
889 * be updated.
892 void
893 Subdir_Register (List *entries, const char *parent, const char *dir)
895 Entnode *entnode;
897 /* Ignore attempts to register ".". These can happen in the
898 server code. */
899 if (dir[0] == '.' && dir[1] == '\0')
900 return;
902 entnode = subdir_record ('A', parent, dir);
904 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
905 (void) AddEntryNode (entries, entnode);
906 else
907 Entnode_Destroy (entnode);
913 * Record the removal of a subdirectory. The arguments are the same
914 * as for Subdir_Register.
917 void
918 Subdir_Deregister (List *entries, const char *parent, const char *dir)
920 Entnode *entnode;
922 entnode = subdir_record ('R', parent, dir);
923 Entnode_Destroy (entnode);
925 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
927 Node *p;
929 p = findnode_fn (entries, dir);
930 if (p != NULL)
931 delnode (p);
937 /* OK, the following base_* code tracks the revisions of the files in
938 CVS/Base. We do this in a file CVS/Baserev. Separate from
939 CVS/Entries because it needs to go in separate data structures
940 anyway (the name in Entries must be unique), so this seemed
941 cleaner. The business of rewriting the whole file in
942 base_deregister and base_register is the kind of thing we used to
943 do for Entries and which turned out to be slow, which is why there
944 is now the Entries.Log machinery. So maybe from that point of
945 view it is a mistake to do this separately from Entries, I dunno. */
947 enum base_walk
949 /* Set the revision for FILE to *REV. */
950 BASE_REGISTER,
951 /* Get the revision for FILE and put it in a newly malloc'd string
952 in *REV, or put NULL if not mentioned. */
953 BASE_GET,
954 /* Remove FILE. */
955 BASE_DEREGISTER
958 static void base_walk (enum base_walk, struct file_info *, char **);
960 /* Read through the lines in CVS/Baserev, taking the actions as documented
961 for CODE. */
963 static void
964 base_walk (enum base_walk code, struct file_info *finfo, char **rev)
966 FILE *fp;
967 char *line;
968 size_t line_allocated;
969 FILE *newf;
970 char *baserev_fullname;
971 char *baserevtmp_fullname;
973 line = NULL;
974 line_allocated = 0;
975 newf = NULL;
977 /* First compute the fullnames for the error messages. This
978 computation probably should be broken out into a separate function,
979 as recurse.c does it too and places like Entries_Open should be
980 doing it. */
981 if (finfo->update_dir[0] != '\0')
983 baserev_fullname = Xasprintf ("%s/%s", finfo->update_dir,
984 CVSADM_BASEREV);
985 baserevtmp_fullname = Xasprintf ("%s/%s", finfo->update_dir,
986 CVSADM_BASEREVTMP);
988 else
990 baserev_fullname = xstrdup (CVSADM_BASEREV);
991 baserevtmp_fullname = xstrdup (CVSADM_BASEREVTMP);
994 fp = CVS_FOPEN (CVSADM_BASEREV, "r");
995 if (fp == NULL)
997 if (!existence_error (errno))
999 error (0, errno, "cannot open %s for reading", baserev_fullname);
1000 goto out;
1004 switch (code)
1006 case BASE_REGISTER:
1007 case BASE_DEREGISTER:
1008 newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w");
1009 if (newf == NULL)
1011 error (0, errno, "cannot open %s for writing",
1012 baserevtmp_fullname);
1013 goto out;
1015 break;
1016 case BASE_GET:
1017 *rev = NULL;
1018 break;
1021 if (fp != NULL)
1023 while (getline (&line, &line_allocated, fp) >= 0)
1025 char *linefile;
1026 char *p;
1027 char *linerev;
1029 if (line[0] != 'B')
1030 /* Ignore, for future expansion. */
1031 continue;
1033 linefile = line + 1;
1034 p = strchr (linefile, '/');
1035 if (p == NULL)
1036 /* Syntax error, ignore. */
1037 continue;
1038 linerev = p + 1;
1039 p = strchr (linerev, '/');
1040 if (p == NULL)
1041 continue;
1043 linerev[-1] = '\0';
1044 if (fncmp (linefile, finfo->file) == 0)
1046 switch (code)
1048 case BASE_REGISTER:
1049 case BASE_DEREGISTER:
1050 /* Don't copy over the old entry, we don't want it. */
1051 break;
1052 case BASE_GET:
1053 *p = '\0';
1054 *rev = xstrdup (linerev);
1055 *p = '/';
1056 goto got_it;
1059 else
1061 linerev[-1] = '/';
1062 switch (code)
1064 case BASE_REGISTER:
1065 case BASE_DEREGISTER:
1066 if (fprintf (newf, "%s\n", line) < 0)
1067 error (0, errno, "error writing %s",
1068 baserevtmp_fullname);
1069 break;
1070 case BASE_GET:
1071 break;
1075 if (ferror (fp))
1076 error (0, errno, "cannot read %s", baserev_fullname);
1078 got_it:
1080 if (code == BASE_REGISTER)
1082 if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0)
1083 error (0, errno, "error writing %s",
1084 baserevtmp_fullname);
1087 out:
1089 if (line != NULL)
1090 free (line);
1092 if (fp != NULL)
1094 if (fclose (fp) < 0)
1095 error (0, errno, "cannot close %s", baserev_fullname);
1097 if (newf != NULL)
1099 if (fclose (newf) < 0)
1100 error (0, errno, "cannot close %s", baserevtmp_fullname);
1101 rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV);
1104 free (baserev_fullname);
1105 free (baserevtmp_fullname);
1108 /* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev,
1109 or NULL if not listed. */
1111 char *
1112 base_get (struct file_info *finfo)
1114 char *rev;
1115 base_walk (BASE_GET, finfo, &rev);
1116 return rev;
1119 /* Set the revision for FILE to REV. */
1121 void
1122 base_register (struct file_info *finfo, char *rev)
1124 base_walk (BASE_REGISTER, finfo, &rev);
1127 /* Remove FILE. */
1129 void
1130 base_deregister (struct file_info *finfo)
1132 base_walk (BASE_DEREGISTER, finfo, NULL);