2 * Copyright (c) 1992, Brian Berliner and Jeff Polk
3 * Copyright (c) 1989-1992, Brian Berliner
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 source distribution.
8 * Entries file to Files file
10 * Creates the file Files containing the names that comprise the project, from
20 static Node *AddEntryNode PROTO((List * list, Entnode *entnode));
22 static Entnode *fgetentent PROTO((FILE *, char *, int *));
23 static int fputentent PROTO((FILE *, Entnode *));
25 static Entnode *subdir_record PROTO((int, const char *, const char *));
28 static char *entfilename; /* for error messages */
33 * Construct an Entnode
35 static Entnode *Entnode_Create PROTO ((enum ent_type, const char *,
36 const char *, const char *,
37 const char *, const char *,
38 const char *, const char *));
41 Entnode_Create(type, user, vn, ts, options, tag, date, ts_conflict)
49 const char *ts_conflict;
53 /* Note that timestamp and options must be non-NULL */
54 ent = (Entnode *) xmalloc (sizeof (Entnode));
56 ent->user = xstrdup (user);
57 ent->version = xstrdup (vn);
58 ent->timestamp = xstrdup (ts ? ts : "");
59 ent->options = xstrdup (options ? options : "");
60 ent->tag = xstrdup (tag);
61 ent->date = xstrdup (date);
62 ent->conflict = xstrdup (ts_conflict);
70 static void Entnode_Destroy PROTO ((Entnode *));
78 free (ent->timestamp);
90 * Write out the line associated with a node of an entries file
92 static int write_ent_proc PROTO ((Node *, void *));
94 write_ent_proc (node, closure)
98 Entnode *entnode = node->data;
100 if (closure != NULL && entnode->type != ENT_FILE)
101 *(int *) closure = 1;
103 if (fputentent(entfile, entnode))
104 error (1, errno, "cannot write %s", entfilename);
110 * write out the current entries file given a list, making a backup copy
121 /* open the new one and walk the list writing entries */
122 entfilename = CVSADM_ENTBAK;
123 entfile = CVS_FOPEN (entfilename, "w+");
126 /* Make this a warning, not an error. For example, one user might
127 have checked out a working directory which, for whatever reason,
128 contains an Entries.Log file. A second user, without write access
129 to that working directory, might want to do a "cvs log". The
130 problem rewriting Entries shouldn't affect the ability of "cvs log"
131 to work, although the warning is probably a good idea so that
132 whether Entries gets rewritten is not an inexplicable process. */
133 /* FIXME: should be including update_dir in message. */
134 error (0, errno, "cannot rewrite %s", entfilename);
136 /* Now just return. We leave the Entries.Log file around. As far
137 as I know, there is never any data lying around in 'list' that
138 is not in Entries.Log at this time (if there is an error writing
139 Entries.Log that is a separate problem). */
143 (void) walklist (list, write_ent_proc, (void *) &sawdir);
146 struct stickydirtag *sdtp;
148 /* We didn't write out any directories. Check the list
149 private data to see whether subdirectory information is
150 known. If it is, we need to write out an empty D line. */
151 sdtp = list->list->data;
152 if (sdtp == NULL || sdtp->subdirs)
153 if (fprintf (entfile, "D\n") < 0)
154 error (1, errno, "cannot write %s", entfilename);
156 if (fclose (entfile) == EOF)
157 error (1, errno, "error closing %s", entfilename);
159 /* now, atomically (on systems that support it) rename it */
160 rename_file (entfilename, CVSADM_ENT);
162 /* now, remove the log file */
163 if (unlink_file (CVSADM_ENTLOG) < 0
164 && !existence_error (errno))
165 error (0, errno, "cannot remove %s", CVSADM_ENTLOG);
171 * Removes the argument file from the Entries file if necessary.
174 Scratch_Entry (list, fname)
181 (void) fprintf (stderr, "%s-> Scratch_Entry(%s)\n",
182 CLIENT_SERVER_STR, fname);
184 /* hashlookup to see if it is there */
185 if ((node = findnode_fn (list, fname)) != NULL)
189 entfilename = CVSADM_ENTLOG;
190 entfile = open_file (entfilename, "a");
192 if (fprintf (entfile, "R ") < 0)
193 error (1, errno, "cannot write %s", entfilename);
195 write_ent_proc (node, NULL);
197 if (fclose (entfile) == EOF)
198 error (1, errno, "error closing %s", entfilename);
201 delnode (node); /* delete the node */
203 #ifdef SERVER_SUPPORT
205 server_scratch (fname);
213 * Enters the given file name/version/time-stamp into the Entries file,
214 * removing the old entry first, if necessary.
217 Register (list, fname, vn, ts, options, tag, date, ts_conflict)
225 const char *ts_conflict;
230 #ifdef SERVER_SUPPORT
233 server_register (fname, vn, ts, options, tag, date, ts_conflict);
239 (void) fprintf (stderr, "%s-> Register(%s, %s, %s%s%s, %s, %s %s)\n",
241 fname, vn, ts ? ts : "",
242 ts_conflict ? "+" : "", ts_conflict ? ts_conflict : "",
243 options, tag ? tag : "", date ? date : "");
246 entnode = Entnode_Create (ENT_FILE, fname, vn, ts, options, tag, date,
248 node = AddEntryNode (list, entnode);
252 entfilename = CVSADM_ENTLOG;
253 entfile = CVS_FOPEN (entfilename, "a");
257 /* Warning, not error, as in write_entries. */
258 /* FIXME-update-dir: should be including update_dir in message. */
259 error (0, errno, "cannot open %s", entfilename);
263 if (fprintf (entfile, "A ") < 0)
264 error (1, errno, "cannot write %s", entfilename);
266 write_ent_proc (node, NULL);
268 if (fclose (entfile) == EOF)
269 error (1, errno, "error closing %s", entfilename);
274 * Node delete procedure for list-private sticky dir tag/date info
280 struct stickydirtag *sdtp = p->data;
286 free ((char *) sdtp);
289 /* Return the next real Entries line. On end of file, returns NULL.
290 On error, prints an error message and returns NULL. */
293 fgetentent(fpin, cmd, sawdir)
300 size_t line_chars_allocated;
303 char *l, *user, *vn, *ts, *options;
304 char *tag_or_date, *tag, *date, *ts_conflict;
308 line_chars_allocated = 0;
311 while ((line_length = getline (&line, &line_chars_allocated, fpin)) > 0)
315 /* If CMD is not NULL, we are reading an Entries.Log file.
316 Each line in the Entries.Log file starts with a single
317 character command followed by a space. For backward
318 compatibility, the absence of a space indicates an add
338 /* An empty D line is permitted; it is a signal that this
339 Entries file lists all known subdirectories. */
346 if ((cp = strchr (user, '/')) == NULL)
350 if ((cp = strchr (vn, '/')) == NULL)
354 if ((cp = strchr (ts, '/')) == NULL)
358 if ((cp = strchr (options, '/')) == NULL)
362 if ((cp = strchr (tag_or_date, '\n')) == NULL)
366 date = (char *) NULL;
367 if (*tag_or_date == 'T')
368 tag = tag_or_date + 1;
369 else if (*tag_or_date == 'D')
370 date = tag_or_date + 1;
372 if ((ts_conflict = strchr (ts, '+')))
373 *ts_conflict++ = '\0';
376 * XXX - Convert timestamp from old format to new format.
378 * If the timestamp doesn't match the file's current
379 * mtime, we'd have to generate a string that doesn't
380 * match anyways, so cheat and base it on the existing
381 * string; it doesn't have to match the same mod time.
383 * For an unmodified file, write the correct timestamp.
387 if (strlen (ts) > 30 && CVS_STAT (user, &sb) == 0)
389 char *c = ctime (&sb.st_mtime);
390 /* Fix non-standard format. */
391 if (c[8] == '0') c[8] = ' ';
393 if (!strncmp (ts + 25, c, 24))
394 ts = time_stamp (user);
403 ent = Entnode_Create (type, user, vn, ts, options, tag, date,
408 if (line_length < 0 && !feof (fpin))
409 error (0, errno, "cannot read entries file");
425 if (fprintf (fp, "D") < 0)
430 if (fprintf (fp, "/%s/%s/%s", p->user, p->version, p->timestamp) < 0)
434 if (fprintf (fp, "+%s", p->conflict) < 0)
437 if (fprintf (fp, "/%s/", p->options) < 0)
442 if (fprintf (fp, "T%s\n", p->tag) < 0)
447 if (fprintf (fp, "D%s\n", p->date) < 0)
452 if (fprintf (fp, "\n") < 0)
460 /* Read the entries file into a list, hashing on the file name.
462 UPDATE_DIR is the name of the current directory, for use in error
463 messages, or NULL if not known (that is, noone has gotten around
464 to updating the caller to pass in the information). */
466 Entries_Open (aflag, update_dir)
471 struct stickydirtag *sdtp = NULL;
473 char *dirtag, *dirdate;
479 /* get a fresh list... */
480 entries = getlist ();
483 * Parse the CVS/Tag file, to get any default tag/date settings. Use
484 * list-private storage to tuck them away for Version_TS().
486 ParseTag (&dirtag, &dirdate, &dirnonbranch);
487 if (aflag || dirtag || dirdate)
489 sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
490 memset ((char *) sdtp, 0, sizeof (*sdtp));
492 sdtp->tag = xstrdup (dirtag);
493 sdtp->date = xstrdup (dirdate);
494 sdtp->nonbranch = dirnonbranch;
496 /* feed it into the list-private area */
497 entries->list->data = sdtp;
498 entries->list->delproc = freesdt;
503 fpin = CVS_FOPEN (CVSADM_ENT, "r");
506 if (update_dir != NULL)
507 error (0, 0, "in directory %s:", update_dir);
508 error (0, errno, "cannot open %s for reading", CVSADM_ENT);
512 while ((ent = fgetentent (fpin, (char *) NULL, &sawdir)) != NULL)
514 (void) AddEntryNode (entries, ent);
517 if (fclose (fpin) < 0)
518 /* FIXME-update-dir: should include update_dir in message. */
519 error (0, errno, "cannot close %s", CVSADM_ENT);
522 fpin = CVS_FOPEN (CVSADM_ENTLOG, "r");
528 while ((ent = fgetentent (fpin, &cmd, &sawdir)) != NULL)
533 (void) AddEntryNode (entries, ent);
536 node = findnode_fn (entries, ent->user);
539 Entnode_Destroy (ent);
542 /* Ignore unrecognized commands. */
547 if (fclose (fpin) < 0)
548 /* FIXME-update-dir: should include update_dir in message. */
549 error (0, errno, "cannot close %s", CVSADM_ENTLOG);
552 /* Update the list private data to indicate whether subdirectory
553 information is known. Nonexistent list private data is taken
554 to mean that it is known. */
556 sdtp->subdirs = sawdir;
559 sdtp = (struct stickydirtag *) xmalloc (sizeof (*sdtp));
560 memset ((char *) sdtp, 0, sizeof (*sdtp));
562 entries->list->data = sdtp;
563 entries->list->delproc = freesdt;
566 if (do_rewrite && !noexec)
567 write_entries (entries);
569 /* clean up and return */
585 if (isfile (CVSADM_ENTLOG))
586 write_entries (list);
594 * Free up the memory associated with the data section of an ENTRIES type
598 Entries_delproc (node)
601 Entnode *p = node->data;
607 * Get an Entries file list node, initialize it, and add it to the specified
611 AddEntryNode (list, entdata)
617 /* was it already there? */
618 if ((p = findnode_fn (list, entdata->user)) != NULL)
624 /* get a node and fill in the regular stuff */
627 p->delproc = Entries_delproc;
629 /* this one gets a key of the name for hashing */
630 /* FIXME This results in duplicated data --- the hash package shouldn't
631 assume that the key is dynamically allocated. The user's free proc
632 should be responsible for freeing the key. */
633 p->key = xstrdup (entdata->user);
636 /* put the node into the list */
641 static char *root_template;
644 get_root_template(const char *repository, const char *path)
647 if (strcmp(path, root_template) == 0)
651 if ((root_template = strdup(path)) == NULL)
657 * Write out/Clear the CVS/Template file.
660 WriteTemplate (dir, update_dir)
662 const char *update_dir;
668 if (Parse_Info(CVSROOTADM_RCSINFO, "cvs", get_root_template, 1) < 0)
671 if (asprintf(&tmp, "%s/%s", dir, CVSADM_TEMPLATE) < 0)
672 error (1, errno, "out of memory");
674 if (stat(root_template, &st1) == 0) {
675 if (stat(tmp, &st2) < 0 || st1.st_mtime != st2.st_mtime) {
679 if ((fi = open_file(root_template, "r")) != NULL) {
680 if ((fo = open_file(tmp, "w")) != NULL) {
684 while ((n = fread(buf, 1, sizeof(buf), fi)) > 0)
685 fwrite(buf, 1, n, fo);
687 if (ferror(fi) || ferror(fo)) {
690 error (1, errno, "error copying Template");
692 struct timeval times[2];
694 times[0].tv_sec = st1.st_mtime;
695 times[0].tv_usec = 0;
708 * Write out/Clear the CVS/Tag file.
711 WriteTag (dir, tag, date, nonbranch, update_dir, repository)
716 const char *update_dir;
717 const char *repository;
725 tmp = xmalloc ((dir ? strlen (dir) : 0)
726 + sizeof (CVSADM_TAG)
729 (void) strcpy (tmp, CVSADM_TAG);
731 (void) sprintf (tmp, "%s/%s", dir, CVSADM_TAG);
735 fout = open_file (tmp, "w+");
740 if (fprintf (fout, "N%s\n", tag) < 0)
741 error (1, errno, "write to %s failed", tmp);
745 if (fprintf (fout, "T%s\n", tag) < 0)
746 error (1, errno, "write to %s failed", tmp);
751 if (fprintf (fout, "D%s\n", date) < 0)
752 error (1, errno, "write to %s failed", tmp);
754 if (fclose (fout) == EOF)
755 error (1, errno, "cannot close %s", tmp);
758 if (unlink_file (tmp) < 0 && ! existence_error (errno))
759 error (1, errno, "cannot remove %s", tmp);
761 #ifdef SERVER_SUPPORT
763 server_set_sticky (update_dir, repository, tag, date, nonbranch);
767 /* Parse the CVS/Tag file for the current directory.
769 If it contains a date, sets *DATEP to the date in a newly malloc'd
770 string, *TAGP to NULL, and *NONBRANCHP to an unspecified value.
772 If it contains a branch tag, sets *TAGP to the tag in a newly
773 malloc'd string, *NONBRANCHP to 0, and *DATEP to NULL.
775 If it contains a nonbranch tag, sets *TAGP to the tag in a newly
776 malloc'd string, *NONBRANCHP to 1, and *DATEP to NULL.
778 If it does not exist, or contains something unrecognized by this
779 version of CVS, set *DATEP and *TAGP to NULL and *NONBRANCHP to
780 an unspecified value.
782 If there is an error, print an error message, set *DATEP and *TAGP
783 to NULL, and return. */
785 ParseTag (tagp, datep, nonbranchp)
793 *tagp = (char *) NULL;
795 *datep = (char *) NULL;
796 /* Always store a value here, even in the 'D' case where the value
797 is unspecified. Shuts up tools which check for references to
798 uninitialized memory. */
799 if (nonbranchp != NULL)
801 fp = CVS_FOPEN (CVSADM_TAG, "r");
806 size_t line_chars_allocated;
809 line_chars_allocated = 0;
811 if ((line_length = getline (&line, &line_chars_allocated, fp)) > 0)
813 /* Remove any trailing newline. */
814 if (line[line_length - 1] == '\n')
815 line[--line_length] = '\0';
820 *tagp = xstrdup (line + 1);
824 *datep = xstrdup (line + 1);
828 *tagp = xstrdup (line + 1);
829 if (nonbranchp != NULL)
833 /* Silently ignore it; it may have been
834 written by a future version of CVS which extends the
842 /* FIXME-update-dir: should include update_dir in messages. */
844 error (0, 0, "cannot read %s: end of file", CVSADM_TAG);
846 error (0, errno, "cannot read %s", CVSADM_TAG);
850 /* FIXME-update-dir: should include update_dir in message. */
851 error (0, errno, "cannot close %s", CVSADM_TAG);
855 else if (!existence_error (errno))
856 /* FIXME-update-dir: should include update_dir in message. */
857 error (0, errno, "cannot open %s", CVSADM_TAG);
861 * This is called if all subdirectory information is known, but there
862 * aren't any subdirectories. It records that fact in the list
867 Subdirs_Known (entries)
870 struct stickydirtag *sdtp = entries->list->data;
872 /* If there is no list private data, that means that the
873 subdirectory information is known. */
874 if (sdtp != NULL && ! sdtp->subdirs)
881 /* Create Entries.Log so that Entries_Close will do something. */
882 entfilename = CVSADM_ENTLOG;
883 fp = CVS_FOPEN (entfilename, "a");
886 int save_errno = errno;
888 /* As in subdir_record, just silently skip the whole thing
889 if there is no CVSADM directory. */
890 if (! isdir (CVSADM))
892 error (1, save_errno, "cannot open %s", entfilename);
896 if (fclose (fp) == EOF)
897 error (1, errno, "cannot close %s", entfilename);
903 /* Record subdirectory information. */
906 subdir_record (cmd, parent, dir)
913 /* None of the information associated with a directory is
914 currently meaningful. */
915 entnode = Entnode_Create (ENT_SUBDIR, dir, "", "", "",
916 (char *) NULL, (char *) NULL,
922 entfilename = CVSADM_ENTLOG;
925 entfilename = xmalloc (strlen (parent)
926 + sizeof CVSADM_ENTLOG
928 sprintf (entfilename, "%s/%s", parent, CVSADM_ENTLOG);
931 entfile = CVS_FOPEN (entfilename, "a");
934 int save_errno = errno;
936 /* It is not an error if there is no CVS administration
937 directory. Permitting this case simplifies some
942 if (! isdir (CVSADM))
947 sprintf (entfilename, "%s/%s", parent, CVSADM);
948 if (! isdir (entfilename))
956 error (1, save_errno, "cannot open %s", entfilename);
959 if (fprintf (entfile, "%c ", cmd) < 0)
960 error (1, errno, "cannot write %s", entfilename);
962 if (fputentent (entfile, entnode) != 0)
963 error (1, errno, "cannot write %s", entfilename);
965 if (fclose (entfile) == EOF)
966 error (1, errno, "error closing %s", entfilename);
979 * Record the addition of a new subdirectory DIR in PARENT. PARENT
980 * may be NULL, which means the current directory. ENTRIES is the
981 * current entries list; it may be NULL, which means that it need not
986 Subdir_Register (entries, parent, dir)
993 /* Ignore attempts to register ".". These can happen in the
995 if (dir[0] == '.' && dir[1] == '\0')
998 entnode = subdir_record ('A', parent, dir);
1000 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
1001 (void) AddEntryNode (entries, entnode);
1003 Entnode_Destroy (entnode);
1007 * Record the removal of a subdirectory. The arguments are the same
1008 * as for Subdir_Register.
1012 Subdir_Deregister (entries, parent, dir)
1019 entnode = subdir_record ('R', parent, dir);
1020 Entnode_Destroy (entnode);
1022 if (entries != NULL && (parent == NULL || strcmp (parent, ".") == 0))
1026 p = findnode_fn (entries, dir);
1034 /* OK, the following base_* code tracks the revisions of the files in
1035 CVS/Base. We do this in a file CVS/Baserev. Separate from
1036 CVS/Entries because it needs to go in separate data structures
1037 anyway (the name in Entries must be unique), so this seemed
1038 cleaner. The business of rewriting the whole file in
1039 base_deregister and base_register is the kind of thing we used to
1040 do for Entries and which turned out to be slow, which is why there
1041 is now the Entries.Log machinery. So maybe from that point of
1042 view it is a mistake to do this separately from Entries, I dunno.
1044 We also need something analogous for:
1046 1. CVS/Template (so we can update the Template file automagically
1047 without the user needing to check out a new working directory).
1048 Updating would probably print a message (that part might be
1049 optional, although probably it should be visible because not all
1050 cvs commands would make the update happen and so it is a
1051 user-visible behavior). Constructing version number for template
1052 is a bit hairy (base it on the timestamp on the server? Or see if
1053 the template is in checkoutlist and if yes use its versioning and
1054 if no don't version it?)....
1056 2. cvsignore (need to keep a copy in the working directory to do
1057 "cvs release" on the client side; see comment at src/release.c
1058 (release). Would also allow us to stop needing Questionable. */
1061 /* Set the revision for FILE to *REV. */
1063 /* Get the revision for FILE and put it in a newly malloc'd string
1064 in *REV, or put NULL if not mentioned. */
1070 static void base_walk PROTO ((enum base_walk, struct file_info *, char **));
1072 /* Read through the lines in CVS/Baserev, taking the actions as documented
1076 base_walk (code, finfo, rev)
1077 enum base_walk code;
1078 struct file_info *finfo;
1083 size_t line_allocated;
1085 char *baserev_fullname;
1086 char *baserevtmp_fullname;
1092 /* First compute the fullnames for the error messages. This
1093 computation probably should be broken out into a separate function,
1094 as recurse.c does it too and places like Entries_Open should be
1096 baserev_fullname = xmalloc (sizeof (CVSADM_BASEREV)
1097 + strlen (finfo->update_dir)
1099 baserev_fullname[0] = '\0';
1100 baserevtmp_fullname = xmalloc (sizeof (CVSADM_BASEREVTMP)
1101 + strlen (finfo->update_dir)
1103 baserevtmp_fullname[0] = '\0';
1104 if (finfo->update_dir[0] != '\0')
1106 strcat (baserev_fullname, finfo->update_dir);
1107 strcat (baserev_fullname, "/");
1108 strcat (baserevtmp_fullname, finfo->update_dir);
1109 strcat (baserevtmp_fullname, "/");
1111 strcat (baserev_fullname, CVSADM_BASEREV);
1112 strcat (baserevtmp_fullname, CVSADM_BASEREVTMP);
1114 fp = CVS_FOPEN (CVSADM_BASEREV, "r");
1117 if (!existence_error (errno))
1119 error (0, errno, "cannot open %s for reading", baserev_fullname);
1127 case BASE_DEREGISTER:
1128 newf = CVS_FOPEN (CVSADM_BASEREVTMP, "w");
1131 error (0, errno, "cannot open %s for writing",
1132 baserevtmp_fullname);
1143 while (getline (&line, &line_allocated, fp) >= 0)
1150 /* Ignore, for future expansion. */
1153 linefile = line + 1;
1154 p = strchr (linefile, '/');
1156 /* Syntax error, ignore. */
1159 p = strchr (linerev, '/');
1164 if (fncmp (linefile, finfo->file) == 0)
1169 case BASE_DEREGISTER:
1170 /* Don't copy over the old entry, we don't want it. */
1174 *rev = xstrdup (linerev);
1185 case BASE_DEREGISTER:
1186 if (fprintf (newf, "%s\n", line) < 0)
1187 error (0, errno, "error writing %s",
1188 baserevtmp_fullname);
1196 error (0, errno, "cannot read %s", baserev_fullname);
1200 if (code == BASE_REGISTER)
1202 if (fprintf (newf, "B%s/%s/\n", finfo->file, *rev) < 0)
1203 error (0, errno, "error writing %s",
1204 baserevtmp_fullname);
1214 if (fclose (fp) < 0)
1215 error (0, errno, "cannot close %s", baserev_fullname);
1219 if (fclose (newf) < 0)
1220 error (0, errno, "cannot close %s", baserevtmp_fullname);
1221 rename_file (CVSADM_BASEREVTMP, CVSADM_BASEREV);
1224 free (baserev_fullname);
1225 free (baserevtmp_fullname);
1228 /* Return, in a newly malloc'd string, the revision for FILE in CVS/Baserev,
1229 or NULL if not listed. */
1233 struct file_info *finfo;
1236 base_walk (BASE_GET, finfo, &rev);
1240 /* Set the revision for FILE to REV. */
1243 base_register (finfo, rev)
1244 struct file_info *finfo;
1247 base_walk (BASE_REGISTER, finfo, &rev);
1253 base_deregister (finfo)
1254 struct file_info *finfo;
1256 base_walk (BASE_DEREGISTER, finfo, NULL);