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