xref: /openbsd/gnu/usr.bin/cvs/src/rcs.c (revision 58a8d7d9)
1 /*
2  * Copyright (c) 1992, Brian Berliner and Jeff Polk
3  *
4  * You may distribute under the terms of the GNU General Public License as
5  * specified in the README file that comes with the CVS source distribution.
6  *
7  * The routines contained in this file do all the rcs file parsing and
8  * manipulation
9  */
10 
11 #include <assert.h>
12 #include <err.h>
13 #include "cvs.h"
14 #include "edit.h"
15 #include "hardlink.h"
16 
17 int preserve_perms = 0;
18 
19 /* The RCS -k options, and a set of enums that must match the array.
20    These come first so that we can use enum kflag in function
21    prototypes.  */
22 static const char *const kflags[] =
23   {"kv", "kvl", "k", "v", "o", "b", (char *) NULL};
24 enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B };
25 
26 /* A structure we use to buffer the contents of an RCS file.  The
27    various fields are only referenced directly by the rcsbuf_*
28    functions.  We declare the struct here so that we can allocate it
29    on the stack, rather than in memory.  */
30 
31 struct rcsbuffer
32 {
33     /* Points to the current position in the buffer.  */
34     char *ptr;
35     /* Points just after the last valid character in the buffer.  */
36     char *ptrend;
37     /* The file.  */
38     FILE *fp;
39     /* The name of the file, used for error messages.  */
40     const char *filename;
41     /* The starting file position of the data in the buffer.  */
42     unsigned long pos;
43     /* The length of the value.  */
44     size_t vlen;
45     /* Whether the value contains an '@' string.  If so, we can not
46        compress whitespace characters.  */
47     int at_string;
48     /* The number of embedded '@' characters in an '@' string.  If
49        this is non-zero, we must search the string for pairs of '@'
50        and convert them to a single '@'.  */
51     int embedded_at;
52 };
53 
54 static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile));
55 static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch));
56 static void rcsbuf_open PROTO ((struct rcsbuffer *, FILE *fp,
57 				const char *filename, unsigned long pos));
58 static void rcsbuf_close PROTO ((struct rcsbuffer *));
59 static int rcsbuf_getkey PROTO ((struct rcsbuffer *, char **keyp,
60 				 char **valp));
61 static int rcsbuf_getrevnum PROTO ((struct rcsbuffer *, char **revp));
62 static char *rcsbuf_fill PROTO ((struct rcsbuffer *, char *ptr, char **keyp,
63 				 char **valp));
64 static int rcsbuf_valcmp PROTO ((struct rcsbuffer *));
65 static char *rcsbuf_valcopy PROTO ((struct rcsbuffer *, char *val, int polish,
66 				    size_t *lenp));
67 static void rcsbuf_valpolish PROTO ((struct rcsbuffer *, char *val, int polish,
68 				     size_t *lenp));
69 static void rcsbuf_valpolish_internal PROTO ((struct rcsbuffer *, char *to,
70 					      const char *from, size_t *lenp));
71 static unsigned long rcsbuf_ftell PROTO ((struct rcsbuffer *));
72 static void rcsbuf_get_buffered PROTO ((struct rcsbuffer *, char **datap,
73 					size_t *lenp));
74 static void rcsbuf_cache PROTO ((RCSNode *, struct rcsbuffer *));
75 static void rcsbuf_cache_close PROTO ((void));
76 static void rcsbuf_cache_open PROTO ((RCSNode *, long, FILE **,
77 				      struct rcsbuffer *));
78 static int checkmagic_proc PROTO((Node *p, void *closure));
79 static void do_branches PROTO((List * list, char *val));
80 static void do_symbols PROTO((List * list, char *val));
81 static void do_locks PROTO((List * list, char *val));
82 static void free_rcsnode_contents PROTO((RCSNode *));
83 static void free_rcsvers_contents PROTO((RCSVers *));
84 static void rcsvers_delproc PROTO((Node * p));
85 static char *translate_symtag PROTO((RCSNode *, const char *));
86 static char *RCS_addbranch PROTO ((RCSNode *, const char *));
87 static char *truncate_revnum_in_place PROTO ((char *));
88 static char *truncate_revnum PROTO ((const char *));
89 static char *printable_date PROTO((const char *));
90 static char *mdoc_date PROTO((const char *));
91 static char *escape_keyword_value PROTO ((const char *, int *));
92 static void expand_keywords PROTO((RCSNode *, RCSVers *, const char *,
93 				   const char *, size_t, enum kflag, char *,
94 				   size_t, char **, size_t *));
95 static void cmp_file_buffer PROTO((void *, const char *, size_t));
96 
97 /* Routines for reading, parsing and writing RCS files. */
98 static RCSVers *getdelta PROTO ((struct rcsbuffer *, char *, char **,
99 				 char **));
100 static Deltatext *RCS_getdeltatext PROTO ((RCSNode *, FILE *,
101 					   struct rcsbuffer *));
102 static void freedeltatext PROTO ((Deltatext *));
103 
104 static void RCS_putadmin PROTO ((RCSNode *, FILE *));
105 static void RCS_putdtree PROTO ((RCSNode *, char *, FILE *));
106 static void RCS_putdesc PROTO ((RCSNode *, FILE *));
107 static void putdelta PROTO ((RCSVers *, FILE *));
108 static int putrcsfield_proc PROTO ((Node *, void *));
109 static int putsymbol_proc PROTO ((Node *, void *));
110 static void RCS_copydeltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *,
111 				   FILE *, Deltatext *, char *));
112 static int count_delta_actions PROTO ((Node *, void *));
113 static void putdeltatext PROTO ((FILE *, Deltatext *));
114 
115 static FILE *rcs_internal_lockfile PROTO ((char *));
116 static void rcs_internal_unlockfile PROTO ((FILE *, char *));
117 static char *rcs_lockfilename PROTO ((char *));
118 
119 /* The RCS file reading functions are called a lot, and they do some
120    string comparisons.  This macro speeds things up a bit by skipping
121    the function call when the first characters are different.  It
122    evaluates its arguments multiple times.  */
123 #define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
124 
125 /*
126  * We don't want to use isspace() from the C library because:
127  *
128  * 1. The definition of "whitespace" in RCS files includes ASCII
129  *    backspace, but the C locale doesn't.
130  * 2. isspace is an very expensive function call in some implementations
131  *    due to the addition of wide character support.
132  */
133 static const char spacetab[] = {
134         0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,	/* 0x00 - 0x0f */
135         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
136         1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
137         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
138         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
139         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
140         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */
141         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
142         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
143         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
144         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
145         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
146         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
147         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
148         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
149         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  /* 0xf0 - 0xff */
150 };
151 
152 #define whitespace(c)	(spacetab[(unsigned char)c] != 0)
153 
154 static char *rcs_lockfile;
155 static int rcs_lockfd = -1;
156 
157 /* A few generic thoughts on error handling, in particular the
158    printing of unexpected characters that we find in the RCS file
159    (that is, why we use '\x%x' rather than %c or some such).
160 
161    * Avoiding %c means we don't have to worry about what is printable
162    and other such stuff.  In error handling, often better to keep it
163    simple.
164 
165    * Hex rather than decimal or octal because character set standards
166    tend to use hex.
167 
168    * Saying "character 0x%x" might make it sound like we are printing
169    a file offset.  So we use '\x%x'.
170 
171    * Would be nice to print the offset within the file, but I can
172    imagine various portability hassles (in particular, whether
173    unsigned long is always big enough to hold file offsets).  */
174 
175 /* Parse an rcsfile given a user file name and a repository.  If there is
176    an error, we print an error message and return NULL.  If the file
177    does not exist, we return NULL without printing anything (I'm not
178    sure this allows the caller to do anything reasonable, but it is
179    the current behavior).  */
180 RCSNode *
RCS_parse(file,repos)181 RCS_parse (file, repos)
182     const char *file;
183     const char *repos;
184 {
185     RCSNode *rcs;
186     FILE *fp;
187     RCSNode *retval;
188     char *rcsfile;
189 
190     /* We're creating a new RCSNode, so there is no hope of finding it
191        in the cache.  */
192     rcsbuf_cache_close ();
193 
194     rcsfile = xmalloc (strlen (repos) + strlen (file)
195 		       + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10);
196     (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
197     if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL)
198     {
199         rcs = RCS_parsercsfile_i(fp, rcsfile);
200 	if (rcs != NULL)
201 	    rcs->flags |= VALID;
202 
203 	retval = rcs;
204 	goto out;
205     }
206     else if (! existence_error (errno))
207     {
208 	error (0, errno, "cannot open %s", rcsfile);
209 	retval = NULL;
210 	goto out;
211     }
212 
213     (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT);
214     if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL)
215     {
216         rcs = RCS_parsercsfile_i(fp, rcsfile);
217 	if (rcs != NULL)
218 	{
219 	    rcs->flags |= INATTIC;
220 	    rcs->flags |= VALID;
221 	}
222 
223 	retval = rcs;
224 	goto out;
225     }
226     else if (! existence_error (errno))
227     {
228 	error (0, errno, "cannot open %s", rcsfile);
229 	retval = NULL;
230 	goto out;
231     }
232 #if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
233     else if (ign_case)
234     {
235 	int status;
236 	char *found_path;
237 
238 	/* The client might be asking for a file which we do have
239 	   (which the client doesn't know about), but for which the
240 	   filename case differs.  We only consider this case if the
241 	   regular CVS_FOPENs fail, because fopen_case is such an
242 	   expensive call.  */
243 	(void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
244 	status = fopen_case (rcsfile, "rb", &fp, &found_path);
245 	if (status == 0)
246 	{
247 	    rcs = RCS_parsercsfile_i (fp, rcsfile);
248 	    if (rcs != NULL)
249 		rcs->flags |= VALID;
250 
251 	    free (rcs->path);
252 	    rcs->path = found_path;
253 	    retval = rcs;
254 	    goto out;
255 	}
256 	else if (! existence_error (status))
257 	{
258 	    error (0, status, "cannot open %s", rcsfile);
259 	    retval = NULL;
260 	    goto out;
261 	}
262 
263 	(void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT);
264 	status = fopen_case (rcsfile, "rb", &fp, &found_path);
265 	if (status == 0)
266 	{
267 	    rcs = RCS_parsercsfile_i (fp, rcsfile);
268 	    if (rcs != NULL)
269 	    {
270 		rcs->flags |= INATTIC;
271 		rcs->flags |= VALID;
272 	    }
273 
274 	    free (rcs->path);
275 	    rcs->path = found_path;
276 	    retval = rcs;
277 	    goto out;
278 	}
279 	else if (! existence_error (status))
280 	{
281 	    error (0, status, "cannot open %s", rcsfile);
282 	    retval = NULL;
283 	    goto out;
284 	}
285     }
286 #endif
287     retval = NULL;
288 
289  out:
290     free (rcsfile);
291 
292     return retval;
293 }
294 
295 /*
296  * Parse a specific rcsfile.
297  */
298 RCSNode *
RCS_parsercsfile(rcsfile)299 RCS_parsercsfile (rcsfile)
300     char *rcsfile;
301 {
302     FILE *fp;
303     RCSNode *rcs;
304 
305     /* We're creating a new RCSNode, so there is no hope of finding it
306        in the cache.  */
307     rcsbuf_cache_close ();
308 
309     /* open the rcsfile */
310     if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL)
311     {
312 	error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
313 	return (NULL);
314     }
315 
316     rcs = RCS_parsercsfile_i (fp, rcsfile);
317 
318     return (rcs);
319 }
320 
321 
322 /*
323  */
324 static RCSNode *
RCS_parsercsfile_i(fp,rcsfile)325 RCS_parsercsfile_i (fp, rcsfile)
326     FILE *fp;
327     const char *rcsfile;
328 {
329     RCSNode *rdata;
330     struct rcsbuffer rcsbuf;
331     char *key, *value;
332 
333     /* make a node */
334     rdata = (RCSNode *) xmalloc (sizeof (RCSNode));
335     memset ((char *) rdata, 0, sizeof (RCSNode));
336     rdata->refcount = 1;
337     rdata->path = xstrdup (rcsfile);
338 
339     /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header.
340 
341        Most cvs operations on the main branch don't need any more
342        information.  Those that do call RCS_reparsercsfile to parse
343        the rest of the header and the deltas.  */
344 
345     rcsbuf_open (&rcsbuf, fp, rcsfile, 0);
346 
347     if (! rcsbuf_getkey (&rcsbuf, &key, &value))
348 	goto l_error;
349     if (STREQ (key, RCSDESC))
350 	goto l_error;
351 
352     if (STREQ (RCSHEAD, key) && value != NULL)
353 	rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL);
354 
355     if (! rcsbuf_getkey (&rcsbuf, &key, &value))
356 	goto l_error;
357     if (STREQ (key, RCSDESC))
358 	goto l_error;
359 
360     if (STREQ (RCSBRANCH, key) && value != NULL)
361     {
362 	char *cp;
363 
364 	rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL);
365 	if ((numdots (rdata->branch) & 1) != 0)
366 	{
367 	    /* turn it into a branch if it's a revision */
368 	    cp = strrchr (rdata->branch, '.');
369 	    *cp = '\0';
370 	}
371     }
372 
373     /* Look ahead for expand, stopping when we see desc or a revision
374        number.  */
375     while (1)
376     {
377 	char *cp;
378 
379 	if (STREQ (RCSEXPAND, key))
380 	{
381 	    rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0,
382 					    (size_t *) NULL);
383 	    break;
384 	}
385 
386 	for (cp = key;
387 	     (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
388 	     cp++)
389 	    /* do nothing */ ;
390 	if (*cp == '\0')
391 	    break;
392 
393 	if (STREQ (RCSDESC, key))
394 	    break;
395 
396 	if (! rcsbuf_getkey (&rcsbuf, &key, &value))
397 	    break;
398     }
399 
400     rdata->flags |= PARTIAL;
401 
402     rcsbuf_cache (rdata, &rcsbuf);
403 
404     return rdata;
405 
406 l_error:
407     error (0, 0, "`%s' does not appear to be a valid rcs file",
408 	   rcsfile);
409     rcsbuf_close (&rcsbuf);
410     freercsnode (&rdata);
411     fclose (fp);
412     return (NULL);
413 }
414 
415 
416 /* Do the real work of parsing an RCS file.
417 
418    On error, die with a fatal error; if it returns at all it was successful.
419 
420    If PFP is NULL, close the file when done.  Otherwise, leave it open
421    and store the FILE * in *PFP.  */
422 void
RCS_reparsercsfile(rdata,pfp,rcsbufp)423 RCS_reparsercsfile (rdata, pfp, rcsbufp)
424     RCSNode *rdata;
425     FILE **pfp;
426     struct rcsbuffer *rcsbufp;
427 {
428     FILE *fp;
429     char *rcsfile;
430     struct rcsbuffer rcsbuf;
431     Node *q, *kv;
432     RCSVers *vnode;
433     int gotkey;
434     char *cp;
435     char *key, *value;
436 
437     assert (rdata != NULL);
438     rcsfile = rdata->path;
439 
440     rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf);
441 
442     /* make a node */
443     /* This probably shouldn't be done until later: if a file has an
444        empty revision tree (which is permissible), rdata->versions
445        should be NULL. -twp */
446     rdata->versions = getlist ();
447 
448     /*
449      * process all the special header information, break out when we get to
450      * the first revision delta
451      */
452     gotkey = 0;
453     for (;;)
454     {
455 	/* get the next key/value pair */
456 	if (!gotkey)
457 	{
458 	    if (! rcsbuf_getkey (&rcsbuf, &key, &value))
459 	    {
460 		error (1, 0, "`%s' does not appear to be a valid rcs file",
461 		       rcsfile);
462 	    }
463 	}
464 
465 	gotkey = 0;
466 
467 	/* Skip head, branch and expand tags; we already have them. */
468 	if (STREQ (key, RCSHEAD)
469 	    || STREQ (key, RCSBRANCH)
470 	    || STREQ (key, RCSEXPAND))
471 	{
472 	    continue;
473 	}
474 
475 	if (STREQ (key, "access"))
476 	{
477 	    if (value != NULL)
478 	    {
479 		/* We pass the POLISH parameter as 1 because
480                    RCS_addaccess expects nothing but spaces.  FIXME:
481                    It would be easy and more efficient to change
482                    RCS_addaccess.  */
483 		rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1,
484 						(size_t *) NULL);
485 	    }
486 	    continue;
487 	}
488 
489 	/* We always save lock information, so that we can handle
490            -kkvl correctly when checking out a file. */
491 	if (STREQ (key, "locks"))
492 	{
493 	    if (value != NULL)
494 		rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0,
495 						    (size_t *) NULL);
496 	    if (! rcsbuf_getkey (&rcsbuf, &key, &value))
497 	    {
498 		error (1, 0, "premature end of file reading %s", rcsfile);
499 	    }
500 	    if (STREQ (key, "strict") && value == NULL)
501 	    {
502 		rdata->strict_locks = 1;
503 	    }
504 	    else
505 		gotkey = 1;
506 	    continue;
507 	}
508 
509 	if (STREQ (RCSSYMBOLS, key))
510 	{
511 	    if (value != NULL)
512 		rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0,
513 						      (size_t *) NULL);
514 	    continue;
515 	}
516 
517 	/*
518 	 * check key for '.''s and digits (probably a rev) if it is a
519 	 * revision or `desc', we are done with the headers and are down to the
520 	 * revision deltas, so we break out of the loop
521 	 */
522 	for (cp = key;
523 	     (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
524 	     cp++)
525 	     /* do nothing */ ;
526 	/* Note that when comparing with RCSDATE, we are not massaging
527            VALUE from the string found in the RCS file.  This is OK
528            since we know exactly what to expect.  */
529 	if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0)
530 	    break;
531 
532 	if (STREQ (key, RCSDESC))
533 	    break;
534 
535 	if (STREQ (key, "comment"))
536 	{
537 	    rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0,
538 					     (size_t *) NULL);
539 	    continue;
540 	}
541 	if (rdata->other == NULL)
542 	    rdata->other = getlist ();
543 	kv = getnode ();
544 	kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
545 	kv->key = xstrdup (key);
546 	kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
547 				   (size_t *) NULL);
548 	if (addnode (rdata->other, kv) != 0)
549 	{
550 	    error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
551 		   key, rcsfile);
552 	    freenode (kv);
553 	}
554 
555 	/* if we haven't grabbed it yet, we didn't want it */
556     }
557 
558     /* We got out of the loop, so we have the first part of the first
559        revision delta in KEY (the revision) and VALUE (the date key
560        and its value).  This is what getdelta expects to receive.  */
561 
562     while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL)
563     {
564 	/* get the node */
565 	q = getnode ();
566 	q->type = RCSVERS;
567 	q->delproc = rcsvers_delproc;
568 	q->data = (char *) vnode;
569 	q->key = vnode->version;
570 
571 	/* add the nodes to the list */
572 	if (addnode (rdata->versions, q) != 0)
573 	{
574 #if 0
575 		purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
576 			 q->key, rcsfile);
577 		freenode (q);
578 #endif
579 	}
580     }
581 
582     /* Here KEY and VALUE are whatever caused getdelta to return NULL.  */
583 
584     if (STREQ (key, RCSDESC))
585     {
586 	if (rdata->desc != NULL)
587 	{
588 	    error (0, 0,
589 		   "warning: duplicate key `%s' in RCS file `%s'",
590 		   key, rcsfile);
591 	    free (rdata->desc);
592 	}
593 	rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 1, (size_t *) NULL);
594     }
595 
596     rdata->delta_pos = rcsbuf_ftell (&rcsbuf);
597 
598     if (pfp == NULL)
599 	rcsbuf_cache (rdata, &rcsbuf);
600     else
601     {
602 	*pfp = fp;
603 	*rcsbufp = rcsbuf;
604     }
605     rdata->flags &= ~PARTIAL;
606 }
607 
608 /* Move RCS into or out of the Attic, depending on TOATTIC.  If the
609    file is already in the desired place, return without doing
610    anything.  At some point may want to think about how this relates
611    to RCS_rewrite but that is a bit hairy (if one wants renames to be
612    atomic, or that kind of thing).  If there is an error, print a message
613    and return 1.  On success, return 0.  */
614 int
RCS_setattic(rcs,toattic)615 RCS_setattic (rcs, toattic)
616     RCSNode *rcs;
617     int toattic;
618 {
619     char *newpath;
620     char *p;
621     char *q;
622 
623     /* Some systems aren't going to let us rename an open file.  */
624     rcsbuf_cache_close ();
625 
626     /* Could make the pathname computations in this file, and probably
627        in other parts of rcs.c too, easier if the REPOS and FILE
628        arguments to RCS_parse got stashed in the RCSNode.  */
629 
630     if (toattic)
631     {
632 	mode_t omask;
633 
634 	if (rcs->flags & INATTIC)
635 	    return 0;
636 
637 	/* Example: rcs->path is "/foo/bar/baz,v".  */
638 	newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
639 	p = last_component (rcs->path);
640 	strncpy (newpath, rcs->path, p - rcs->path);
641 	strcpy (newpath + (p - rcs->path), CVSATTIC);
642 
643 	/* Create the Attic directory if it doesn't exist.  */
644 	omask = umask (cvsumask);
645 	if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
646 	    error (0, errno, "cannot make directory %s", newpath);
647 	(void) umask (omask);
648 
649 	strcat (newpath, "/");
650 	strcat (newpath, p);
651 
652 	if (CVS_RENAME (rcs->path, newpath) < 0)
653 	{
654 	    int save_errno = errno;
655 
656 	    /* The checks for isreadable look awfully fishy, but
657 	       I'm going to leave them here for now until I
658 	       can think harder about whether they take care of
659 	       some cases which should be handled somehow.  */
660 
661 	    if (isreadable (rcs->path) || !isreadable (newpath))
662 	    {
663 		error (0, save_errno, "cannot rename %s to %s",
664 		       rcs->path, newpath);
665 		free (newpath);
666 		return 1;
667 	    }
668 	}
669     }
670     else
671     {
672 	if (!(rcs->flags & INATTIC))
673 	    return 0;
674 
675 	newpath = xmalloc (strlen (rcs->path));
676 
677 	/* Example: rcs->path is "/foo/bar/Attic/baz,v".  */
678 	p = last_component (rcs->path);
679 	strncpy (newpath, rcs->path, p - rcs->path - 1);
680 	newpath[p - rcs->path - 1] = '\0';
681 	q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
682 	assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
683 	strcpy (q, p);
684 
685 	if (CVS_RENAME (rcs->path, newpath) < 0)
686 	{
687 	    error (0, errno, "failed to move `%s' out of the attic",
688 		   rcs->path);
689 	    free (newpath);
690 	    return 1;
691 	}
692     }
693 
694     free (rcs->path);
695     rcs->path = newpath;
696 
697     return 0;
698 }
699 
700 /*
701  * Fully parse the RCS file.  Store all keyword/value pairs, fetch the
702  * log messages for each revision, and fetch add and delete counts for
703  * each revision (we could fetch the entire text for each revision,
704  * but the only caller, log_fileproc, doesn't need that information,
705  * so we don't waste the memory required to store it).  The add and
706  * delete counts are stored on the OTHER field of the RCSVERSNODE
707  * structure, under the names ";add" and ";delete", so that we don't
708  * waste the memory space of extra fields in RCSVERSNODE for code
709  * which doesn't need this information.
710  */
711 
712 void
RCS_fully_parse(rcs)713 RCS_fully_parse (rcs)
714     RCSNode *rcs;
715 {
716     FILE *fp;
717     struct rcsbuffer rcsbuf;
718 
719     RCS_reparsercsfile (rcs, &fp, &rcsbuf);
720 
721     while (1)
722     {
723 	char *key, *value;
724 	Node *vers;
725 	RCSVers *vnode;
726 
727 	/* Rather than try to keep track of how much information we
728            have read, just read to the end of the file.  */
729 	if (! rcsbuf_getrevnum (&rcsbuf, &key))
730 	    break;
731 
732 	vers = findnode (rcs->versions, key);
733 	if (vers == NULL)
734 	    error (1, 0,
735 		   "mismatch in rcs file %s between deltas and deltatexts",
736 		   rcs->path);
737 
738 	vnode = (RCSVers *) vers->data;
739 
740 	while (rcsbuf_getkey (&rcsbuf, &key, &value))
741 	{
742 	    if (! STREQ (key, "text"))
743 	    {
744 		Node *kv;
745 
746 		if (vnode->other == NULL)
747 		    vnode->other = getlist ();
748 		kv = getnode ();
749 		kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
750 		kv->key = xstrdup (key);
751 		kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
752 					   (size_t *) NULL);
753 		if (addnode (vnode->other, kv) != 0)
754 		{
755 		    error (0, 0,
756 			   "\
757 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
758 			   key, vnode->version, rcs->path);
759 		    freenode (kv);
760 		}
761 
762 		continue;
763 	    }
764 
765 	    if (! STREQ (vnode->version, rcs->head))
766 	    {
767 		unsigned long add, del;
768 		char buf[50];
769 		Node *kv;
770 
771 		/* This is a change text.  Store the add and delete
772                    counts.  */
773 		add = 0;
774 		del = 0;
775 		if (value != NULL)
776 		{
777 		    size_t vallen;
778 		    const char *cp;
779 
780 		    rcsbuf_valpolish (&rcsbuf, value, 0, &vallen);
781 		    cp = value;
782 		    while (cp < value + vallen)
783 		    {
784 			char op;
785 			unsigned long count;
786 
787 			op = *cp++;
788 			if (op != 'a' && op  != 'd')
789 			    error (1, 0, "\
790 unrecognized operation '\\x%x' in %s",
791 				   op, rcs->path);
792 			(void) strtoul (cp, (char **) &cp, 10);
793 			if (*cp++ != ' ')
794 			    error (1, 0, "space expected in %s",
795 				   rcs->path);
796 			count = strtoul (cp, (char **) &cp, 10);
797 			if (*cp++ != '\012')
798 			    error (1, 0, "linefeed expected in %s",
799 				   rcs->path);
800 
801 			if (op == 'd')
802 			    del += count;
803 			else
804 			{
805 			    add += count;
806 			    while (count != 0)
807 			    {
808 				if (*cp == '\012')
809 				    --count;
810 				else if (cp == value + vallen)
811 				{
812 				    if (count != 1)
813 					error (1, 0, "\
814 invalid rcs file %s: premature end of value",
815 					       rcs->path);
816 				    else
817 					break;
818 				}
819 				++cp;
820 			    }
821 			}
822 		    }
823 		}
824 
825 		sprintf (buf, "%lu", add);
826 		kv = getnode ();
827 		kv->type = RCSFIELD;
828 		kv->key = xstrdup (";add");
829 		kv->data = xstrdup (buf);
830 		if (addnode (vnode->other, kv) != 0)
831 		{
832 		    error (0, 0,
833 			   "\
834 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
835 			   key, vnode->version, rcs->path);
836 		    freenode (kv);
837 		}
838 
839 		sprintf (buf, "%lu", del);
840 		kv = getnode ();
841 		kv->type = RCSFIELD;
842 		kv->key = xstrdup (";delete");
843 		kv->data = xstrdup (buf);
844 		if (addnode (vnode->other, kv) != 0)
845 		{
846 		    error (0, 0,
847 			   "\
848 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
849 			   key, vnode->version, rcs->path);
850 		    freenode (kv);
851 		}
852 	    }
853 
854 	    /* We have found the "text" key which ends the data for
855                this revision.  Break out of the loop and go on to the
856                next revision.  */
857 	    break;
858 	}
859     }
860 
861     rcsbuf_cache (rcs, &rcsbuf);
862 }
863 
864 /*
865  * freercsnode - free up the info for an RCSNode
866  */
867 void
freercsnode(rnodep)868 freercsnode (rnodep)
869     RCSNode **rnodep;
870 {
871     if (rnodep == NULL || *rnodep == NULL)
872 	return;
873 
874     ((*rnodep)->refcount)--;
875     if ((*rnodep)->refcount != 0)
876     {
877 	*rnodep = (RCSNode *) NULL;
878 	return;
879     }
880     free ((*rnodep)->path);
881     if ((*rnodep)->head != (char *) NULL)
882 	free ((*rnodep)->head);
883     if ((*rnodep)->branch != (char *) NULL)
884 	free ((*rnodep)->branch);
885     free_rcsnode_contents (*rnodep);
886     free ((char *) *rnodep);
887     *rnodep = (RCSNode *) NULL;
888 }
889 
890 /*
891  * free_rcsnode_contents - free up the contents of an RCSNode without
892  * freeing the node itself, or the file name, or the head, or the
893  * path.  This returns the RCSNode to the state it is in immediately
894  * after a call to RCS_parse.
895  */
896 static void
free_rcsnode_contents(rnode)897 free_rcsnode_contents (rnode)
898     RCSNode *rnode;
899 {
900     dellist (&rnode->versions);
901     if (rnode->symbols != (List *) NULL)
902 	dellist (&rnode->symbols);
903     if (rnode->symbols_data != (char *) NULL)
904 	free (rnode->symbols_data);
905     if (rnode->expand != NULL)
906 	free (rnode->expand);
907     if (rnode->other != (List *) NULL)
908 	dellist (&rnode->other);
909     if (rnode->access != NULL)
910 	free (rnode->access);
911     if (rnode->locks_data != NULL)
912 	free (rnode->locks_data);
913     if (rnode->locks != (List *) NULL)
914 	dellist (&rnode->locks);
915     if (rnode->comment != NULL)
916 	free (rnode->comment);
917     if (rnode->desc != NULL)
918 	free (rnode->desc);
919 }
920 
921 /* free_rcsvers_contents -- free up the contents of an RCSVers node,
922    but also free the pointer to the node itself. */
923 /* Note: The `hardlinks' list is *not* freed, since it is merely a
924    pointer into the `hardlist' structure (defined in hardlink.c), and
925    that structure is freed elsewhere in the program. */
926 
927 static void
free_rcsvers_contents(rnode)928 free_rcsvers_contents (rnode)
929     RCSVers *rnode;
930 {
931     if (rnode->branches != (List *) NULL)
932 	dellist (&rnode->branches);
933     if (rnode->date != (char *) NULL)
934 	free (rnode->date);
935     if (rnode->next != (char *) NULL)
936 	free (rnode->next);
937     if (rnode->author != (char *) NULL)
938 	free (rnode->author);
939     if (rnode->state != (char *) NULL)
940 	free (rnode->state);
941     if (rnode->other != (List *) NULL)
942 	dellist (&rnode->other);
943     if (rnode->other_delta != NULL)
944 	dellist (&rnode->other_delta);
945     if (rnode->text != NULL)
946 	freedeltatext (rnode->text);
947     free ((char *) rnode);
948 }
949 
950 /*
951  * rcsvers_delproc - free up an RCSVers type node
952  */
953 static void
rcsvers_delproc(p)954 rcsvers_delproc (p)
955     Node *p;
956 {
957     free_rcsvers_contents ((RCSVers *) p->data);
958 }
959 
960 /* These functions retrieve keys and values from an RCS file using a
961    buffer.  We use this somewhat complex approach because it turns out
962    that for many common operations, CVS spends most of its time
963    reading keys, so it's worth doing some fairly hairy optimization.  */
964 
965 /* The number of bytes we try to read each time we need more data.  */
966 
967 #define RCSBUF_BUFSIZE (8192)
968 
969 /* The buffer we use to store data.  This grows as needed.  */
970 
971 static char *rcsbuf_buffer = NULL;
972 static size_t rcsbuf_buffer_size = 0;
973 
974 /* Whether rcsbuf_buffer is in use.  This is used as a sanity check.  */
975 
976 static int rcsbuf_inuse;
977 
978 /* Set up to start gathering keys and values from an RCS file.  This
979    initializes RCSBUF.  */
980 
981 static void
rcsbuf_open(rcsbuf,fp,filename,pos)982 rcsbuf_open (rcsbuf, fp, filename, pos)
983     struct rcsbuffer *rcsbuf;
984     FILE *fp;
985     const char *filename;
986     unsigned long pos;
987 {
988     if (rcsbuf_inuse)
989 	error (1, 0, "rcsbuf_open: internal error");
990     rcsbuf_inuse = 1;
991 
992     if (rcsbuf_buffer_size < RCSBUF_BUFSIZE)
993 	expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE);
994 
995     rcsbuf->ptr = rcsbuf_buffer;
996     rcsbuf->ptrend = rcsbuf_buffer;
997     rcsbuf->fp = fp;
998     rcsbuf->filename = filename;
999     rcsbuf->pos = pos;
1000     rcsbuf->vlen = 0;
1001     rcsbuf->at_string = 0;
1002     rcsbuf->embedded_at = 0;
1003 }
1004 
1005 /* Stop gathering keys from an RCS file.  */
1006 
1007 static void
rcsbuf_close(rcsbuf)1008 rcsbuf_close (rcsbuf)
1009     struct rcsbuffer *rcsbuf;
1010 {
1011     if (! rcsbuf_inuse)
1012 	error (1, 0, "rcsbuf_close: internal error");
1013     rcsbuf_inuse = 0;
1014 }
1015 
1016 /* Read a key/value pair from an RCS file.  This sets *KEYP to point
1017    to the key, and *VALUEP to point to the value.  A missing or empty
1018    value is indicated by setting *VALUEP to NULL.
1019 
1020    This function returns 1 on success, or 0 on EOF.  If there is an
1021    error reading the file, or an EOF in an unexpected location, it
1022    gives a fatal error.
1023 
1024    This sets *KEYP and *VALUEP to point to storage managed by
1025    rcsbuf_getkey.  Moreover, *VALUEP has not been massaged from the
1026    RCS format: it may contain embedded whitespace and embedded '@'
1027    characters.  Call rcsbuf_valcopy or rcsbuf_valpolish to do
1028    appropriate massaging.  */
1029 
1030 /* Note that the extreme hair in rcsbuf_getkey is because profiling
1031    statistics show that it was worth it. */
1032 
1033 static int
rcsbuf_getkey(rcsbuf,keyp,valp)1034 rcsbuf_getkey (rcsbuf, keyp, valp)
1035     struct rcsbuffer *rcsbuf;
1036     char **keyp;
1037     char **valp;
1038 {
1039     register const char * const my_spacetab = spacetab;
1040     register char *ptr, *ptrend;
1041     char c;
1042 
1043 #define my_whitespace(c)	(my_spacetab[(unsigned char)c] != 0)
1044 
1045     rcsbuf->vlen = 0;
1046     rcsbuf->at_string = 0;
1047     rcsbuf->embedded_at = 0;
1048 
1049     ptr = rcsbuf->ptr;
1050     ptrend = rcsbuf->ptrend;
1051 
1052     /* Sanity check.  */
1053     if (ptr < rcsbuf_buffer || ptr > rcsbuf_buffer + rcsbuf_buffer_size)
1054 	abort ();
1055 
1056     /* If the pointer is more than RCSBUF_BUFSIZE bytes into the
1057        buffer, move back to the start of the buffer.  This keeps the
1058        buffer from growing indefinitely.  */
1059     if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE)
1060     {
1061 	int len;
1062 
1063 	len = ptrend - ptr;
1064 
1065 	/* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes
1066            at a time, so we can't have more bytes than that past PTR.  */
1067 	if (len > RCSBUF_BUFSIZE)
1068 	    abort ();
1069 
1070 	/* Update the POS field, which holds the file offset of the
1071            first byte in the RCSBUF_BUFFER buffer.  */
1072 	rcsbuf->pos += ptr - rcsbuf_buffer;
1073 
1074 	memcpy (rcsbuf_buffer, ptr, len);
1075 	ptr = rcsbuf_buffer;
1076 	ptrend = ptr + len;
1077 	rcsbuf->ptrend = ptrend;
1078     }
1079 
1080     /* Skip leading whitespace.  */
1081 
1082     while (1)
1083     {
1084 	if (ptr >= ptrend)
1085 	{
1086 	    ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL);
1087 	    if (ptr == NULL)
1088 		return 0;
1089 	    ptrend = rcsbuf->ptrend;
1090 	}
1091 
1092 	c = *ptr;
1093 	if (! my_whitespace (c))
1094 	    break;
1095 
1096 	++ptr;
1097     }
1098 
1099     /* We've found the start of the key.  */
1100 
1101     *keyp = ptr;
1102 
1103     if (c != ';')
1104     {
1105 	while (1)
1106 	{
1107 	    ++ptr;
1108 	    if (ptr >= ptrend)
1109 	    {
1110 		ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL);
1111 		if (ptr == NULL)
1112 		    error (1, 0, "EOF in key in RCS file %s",
1113 			   rcsbuf->filename);
1114 		ptrend = rcsbuf->ptrend;
1115 	    }
1116 	    c = *ptr;
1117 	    if (c == ';' || my_whitespace (c))
1118 		break;
1119 	}
1120     }
1121 
1122     /* Here *KEYP points to the key in the buffer, C is the character
1123        we found at the of the key, and PTR points to the location in
1124        the buffer where we found C.  We must set *PTR to \0 in order
1125        to terminate the key.  If the key ended with ';', then there is
1126        no value.  */
1127 
1128     *ptr = '\0';
1129     ++ptr;
1130 
1131     if (c == ';')
1132     {
1133 	*valp = NULL;
1134 	rcsbuf->ptr = ptr;
1135 	return 1;
1136     }
1137 
1138     /* C must be whitespace.  Skip whitespace between the key and the
1139        value.  If we find ';' now, there is no value.  */
1140 
1141     while (1)
1142     {
1143 	if (ptr >= ptrend)
1144 	{
1145 	    ptr = rcsbuf_fill (rcsbuf, ptr, keyp, (char **) NULL);
1146 	    if (ptr == NULL)
1147 		error (1, 0, "EOF while looking for value in RCS file %s",
1148 		       rcsbuf->filename);
1149 	    ptrend = rcsbuf->ptrend;
1150 	}
1151 	c = *ptr;
1152 	if (c == ';')
1153 	{
1154 	    *valp = NULL;
1155 	    rcsbuf->ptr = ptr + 1;
1156 	    return 1;
1157 	}
1158 	if (! my_whitespace (c))
1159 	    break;
1160 	++ptr;
1161     }
1162 
1163     /* Now PTR points to the start of the value, and C is the first
1164        character of the value.  */
1165 
1166     if (c != '@')
1167 	*valp = ptr;
1168     else
1169     {
1170 	char *pat;
1171 	size_t vlen;
1172 
1173 	/* Optimize the common case of a value composed of a single
1174 	   '@' string.  */
1175 
1176 	rcsbuf->at_string = 1;
1177 
1178 	++ptr;
1179 
1180 	*valp = ptr;
1181 
1182 	while (1)
1183 	{
1184 	    while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1185 	    {
1186 		/* Note that we pass PTREND as the PTR value to
1187                    rcsbuf_fill, so that we will wind up setting PTR to
1188                    the location corresponding to the old PTREND, so
1189                    that we don't search the same bytes again.  */
1190 		ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1191 		if (ptr == NULL)
1192 		    error (1, 0,
1193 			   "EOF while looking for end of string in RCS file %s",
1194 			   rcsbuf->filename);
1195 		ptrend = rcsbuf->ptrend;
1196 	    }
1197 
1198 	    /* Handle the special case of an '@' right at the end of
1199                the known bytes.  */
1200 	    if (pat + 1 >= ptrend)
1201 	    {
1202 		/* Note that we pass PAT, not PTR, here.  */
1203 		pat = rcsbuf_fill (rcsbuf, pat, keyp, valp);
1204 		if (pat == NULL)
1205 		{
1206 		    /* EOF here is OK; it just means that the last
1207 		       character of the file was an '@' terminating a
1208 		       value for a key type which does not require a
1209 		       trailing ';'.  */
1210 		    pat = rcsbuf->ptrend - 1;
1211 
1212 		}
1213 		ptrend = rcsbuf->ptrend;
1214 
1215 		/* Note that the value of PTR is bogus here.  This is
1216 		   OK, because we don't use it.  */
1217 	    }
1218 
1219 	    if (pat + 1 >= ptrend || pat[1] != '@')
1220 		break;
1221 
1222 	    /* We found an '@' pair in the string.  Keep looking.  */
1223 	    ++rcsbuf->embedded_at;
1224 	    ptr = pat + 2;
1225 	}
1226 
1227 	/* Here PAT points to the final '@' in the string.  */
1228 
1229 	*pat = '\0';
1230 
1231 	vlen = pat - *valp;
1232 	if (vlen == 0)
1233 	    *valp = NULL;
1234 	rcsbuf->vlen = vlen;
1235 
1236 	ptr = pat + 1;
1237     }
1238 
1239     /* Certain keywords only have a '@' string.  If there is no '@'
1240        string, then the old getrcskey function assumed that they had
1241        no value, and we do the same.  */
1242 
1243     {
1244 	char *k;
1245 
1246 	k = *keyp;
1247 	if (STREQ (k, RCSDESC)
1248 	    || STREQ (k, "text")
1249 	    || STREQ (k, "log"))
1250 	{
1251 	    if (c != '@')
1252 		*valp = NULL;
1253 	    rcsbuf->ptr = ptr;
1254 	    return 1;
1255 	}
1256     }
1257 
1258     /* If we've already gathered a '@' string, try to skip whitespace
1259        and find a ';'.  */
1260     if (c == '@')
1261     {
1262 	while (1)
1263 	{
1264 	    char n;
1265 
1266 	    if (ptr >= ptrend)
1267 	    {
1268 		ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1269 		if (ptr == NULL)
1270 		    error (1, 0, "EOF in value in RCS file %s",
1271 			   rcsbuf->filename);
1272 		ptrend = rcsbuf->ptrend;
1273 	    }
1274 	    n = *ptr;
1275 	    if (n == ';')
1276 	    {
1277 		/* We're done.  We already set everything up for this
1278                    case above.  */
1279 		rcsbuf->ptr = ptr + 1;
1280 		return 1;
1281 	    }
1282 	    if (! my_whitespace (n))
1283 		break;
1284 	    ++ptr;
1285 	}
1286 
1287 	/* The value extends past the '@' string.  We need to undo the
1288            '@' stripping done in the default case above.  This
1289            case never happens in a plain RCS file, but it can happen
1290            if user defined phrases are used.  */
1291 	((*valp)--)[rcsbuf->vlen++] = '@';
1292     }
1293 
1294     /* Here we have a value which is not a simple '@' string.  We need
1295        to gather up everything until the next ';', including any '@'
1296        strings.  *VALP points to the start of the value.  If
1297        RCSBUF->VLEN is not zero, then we have already read an '@'
1298        string, and PTR points to the data following the '@' string.
1299        Otherwise, PTR points to the start of the value.  */
1300 
1301     while (1)
1302     {
1303 	char *start, *psemi, *pat;
1304 
1305 	/* Find the ';' which must end the value.  */
1306 	start = ptr;
1307 	while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL)
1308 	{
1309 	    int slen;
1310 
1311 	    /* Note that we pass PTREND as the PTR value to
1312 	       rcsbuf_fill, so that we will wind up setting PTR to the
1313 	       location corresponding to the old PTREND, so that we
1314 	       don't search the same bytes again.  */
1315 	    slen = start - *valp;
1316 	    ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1317 	    if (ptr == NULL)
1318 		error (1, 0, "EOF in value in RCS file %s", rcsbuf->filename);
1319 	    start = *valp + slen;
1320 	    ptrend = rcsbuf->ptrend;
1321 	}
1322 
1323 	/* See if there are any '@' strings in the value.  */
1324 	pat = memchr (start, '@', psemi - start);
1325 
1326 	if (pat == NULL)
1327 	{
1328 	    size_t vlen;
1329 
1330 	    /* We're done with the value.  Trim any trailing
1331                whitespace.  */
1332 
1333 	    rcsbuf->ptr = psemi + 1;
1334 
1335 	    start = *valp;
1336 	    while (psemi > start && my_whitespace (psemi[-1]))
1337 		--psemi;
1338 	    *psemi = '\0';
1339 
1340 	    vlen = psemi - start;
1341 	    if (vlen == 0)
1342 		*valp = NULL;
1343 	    rcsbuf->vlen = vlen;
1344 
1345 	    return 1;
1346 	}
1347 
1348 	/* We found an '@' string in the value.  We set RCSBUF->AT_STRING
1349 	   and RCSBUF->EMBEDDED_AT to indicate that we won't be able to
1350 	   compress whitespace correctly for this type of value.
1351 	   Since this type of value never arises in a normal RCS file,
1352 	   this should not be a big deal.  It means that if anybody
1353 	   adds a phrase which can have both an '@' string and regular
1354 	   text, they will have to handle whitespace compression
1355 	   themselves.  */
1356 
1357 	rcsbuf->at_string = 1;
1358 	rcsbuf->embedded_at = -1;
1359 
1360 	ptr = pat + 1;
1361 
1362 	while (1)
1363 	{
1364 	    while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1365 	    {
1366 		/* Note that we pass PTREND as the PTR value to
1367                    rcsbuff_fill, so that we will wind up setting PTR
1368                    to the location corresponding to the old PTREND, so
1369                    that we don't search the same bytes again.  */
1370 		ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1371 		if (ptr == NULL)
1372 		    error (1, 0,
1373 			   "EOF while looking for end of string in RCS file %s",
1374 			   rcsbuf->filename);
1375 		ptrend = rcsbuf->ptrend;
1376 	    }
1377 
1378 	    /* Handle the special case of an '@' right at the end of
1379                the known bytes.  */
1380 	    if (pat + 1 >= ptrend)
1381 	    {
1382 		ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1383 		if (ptr == NULL)
1384 		    error (1, 0, "EOF in value in RCS file %s",
1385 			   rcsbuf->filename);
1386 		ptrend = rcsbuf->ptrend;
1387 	    }
1388 
1389 	    if (pat[1] != '@')
1390 		break;
1391 
1392 	    /* We found an '@' pair in the string.  Keep looking.  */
1393 	    ptr = pat + 2;
1394 	}
1395 
1396 	/* Here PAT points to the final '@' in the string.  */
1397 	ptr = pat + 1;
1398     }
1399 
1400 #undef my_whitespace
1401 }
1402 
1403 /* Read an RCS revision number from an RCS file.  This sets *REVP to
1404    point to the revision number; it will point to space that is
1405    managed by the rcsbuf functions, and is only good until the next
1406    call to rcsbuf_getkey or rcsbuf_getrevnum.
1407 
1408    This function returns 1 on success, or 0 on EOF.  If there is an
1409    error reading the file, or an EOF in an unexpected location, it
1410    gives a fatal error.  */
1411 
1412 static int
rcsbuf_getrevnum(rcsbuf,revp)1413 rcsbuf_getrevnum (rcsbuf, revp)
1414     struct rcsbuffer *rcsbuf;
1415     char **revp;
1416 {
1417     char *ptr, *ptrend;
1418     char c;
1419 
1420     ptr = rcsbuf->ptr;
1421     ptrend = rcsbuf->ptrend;
1422 
1423     *revp = NULL;
1424 
1425     /* Skip leading whitespace.  */
1426 
1427     while (1)
1428     {
1429 	if (ptr >= ptrend)
1430 	{
1431 	    ptr = rcsbuf_fill (rcsbuf, ptr, (char **) NULL, (char **) NULL);
1432 	    if (ptr == NULL)
1433 		return 0;
1434 	    ptrend = rcsbuf->ptrend;
1435 	}
1436 
1437 	c = *ptr;
1438 	if (! whitespace (c))
1439 	    break;
1440 
1441 	++ptr;
1442     }
1443 
1444     if (! isdigit ((unsigned char) c) && c != '.')
1445 	error (1, 0,
1446 	       "\
1447 unexpected '\\x%x' reading revision number in RCS file %s",
1448 	       c, rcsbuf->filename);
1449 
1450     *revp = ptr;
1451 
1452     do
1453     {
1454 	++ptr;
1455 	if (ptr >= ptrend)
1456 	{
1457 	    ptr = rcsbuf_fill (rcsbuf, ptr, revp, (char **) NULL);
1458 	    if (ptr == NULL)
1459 		error (1, 0,
1460 		       "unexpected EOF reading revision number in RCS file %s",
1461 		       rcsbuf->filename);
1462 	    ptrend = rcsbuf->ptrend;
1463 	}
1464 
1465 	c = *ptr;
1466     }
1467     while (isdigit ((unsigned char) c) || c == '.');
1468 
1469     if (! whitespace (c))
1470 	error (1, 0, "\
1471 unexpected '\\x%x' reading revision number in RCS file %s",
1472 	       c, rcsbuf->filename);
1473 
1474     *ptr = '\0';
1475 
1476     rcsbuf->ptr = ptr + 1;
1477 
1478     return 1;
1479 }
1480 
1481 /* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF,
1482    updating PTR and the PTREND field.  If KEYP and *KEYP are not NULL,
1483    then *KEYP points into the buffer, and must be adjusted if the
1484    buffer is changed.  Likewise for VALP.  Returns the new value of
1485    PTR, or NULL on error.  */
1486 
1487 static char *
rcsbuf_fill(rcsbuf,ptr,keyp,valp)1488 rcsbuf_fill (rcsbuf, ptr, keyp, valp)
1489     struct rcsbuffer *rcsbuf;
1490     char *ptr;
1491     char **keyp;
1492     char **valp;
1493 {
1494     int got;
1495 
1496     if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size)
1497     {
1498 	int poff, peoff, koff, voff;
1499 
1500 	poff = ptr - rcsbuf_buffer;
1501 	peoff = rcsbuf->ptrend - rcsbuf_buffer;
1502 	if (keyp != NULL && *keyp != NULL)
1503 	    koff = *keyp - rcsbuf_buffer;
1504 	if (valp != NULL && *valp != NULL)
1505 	    voff = *valp - rcsbuf_buffer;
1506 	koff = keyp == NULL ? 0 : *keyp - rcsbuf_buffer;
1507 	voff = valp == NULL ? 0 : *valp - rcsbuf_buffer;
1508 
1509 	expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size,
1510 		       rcsbuf_buffer_size + RCSBUF_BUFSIZE);
1511 
1512 	ptr = rcsbuf_buffer + poff;
1513 	rcsbuf->ptrend = rcsbuf_buffer + peoff;
1514 	if (keyp != NULL && *keyp != NULL)
1515 	    *keyp = rcsbuf_buffer + koff;
1516 	if (valp != NULL && *valp != NULL)
1517 	    *valp = rcsbuf_buffer + voff;
1518     }
1519 
1520     got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp);
1521     if (got == 0)
1522     {
1523 	if (ferror (rcsbuf->fp))
1524 	    error (1, errno, "cannot read %s", rcsbuf->filename);
1525 	return NULL;
1526     }
1527 
1528     rcsbuf->ptrend += got;
1529 
1530     return ptr;
1531 }
1532 
1533 /* Test whether the last value returned by rcsbuf_getkey is a composite
1534    value or not. */
1535 
1536 static int
rcsbuf_valcmp(rcsbuf)1537 rcsbuf_valcmp (rcsbuf)
1538     struct rcsbuffer *rcsbuf;
1539 {
1540     return rcsbuf->at_string && rcsbuf->embedded_at < 0;
1541 }
1542 
1543 /* Copy the value VAL returned by rcsbuf_getkey into a memory buffer,
1544    returning the memory buffer.  Polish the value like
1545    rcsbuf_valpolish, q.v.  */
1546 
1547 static char *
rcsbuf_valcopy(rcsbuf,val,polish,lenp)1548 rcsbuf_valcopy (rcsbuf, val, polish, lenp)
1549     struct rcsbuffer *rcsbuf;
1550     char *val;
1551     int polish;
1552     size_t *lenp;
1553 {
1554     size_t vlen;
1555     int embedded_at;
1556     char *ret;
1557 
1558     if (val == NULL)
1559     {
1560 	if (lenp != NULL)
1561 	    *lenp = 0;
1562 	return NULL;
1563     }
1564 
1565     vlen = rcsbuf->vlen;
1566     embedded_at = rcsbuf->embedded_at < 0 ? 0 : rcsbuf->embedded_at;
1567 
1568     ret = xmalloc (vlen - embedded_at + 1);
1569 
1570     if (rcsbuf->at_string ? embedded_at == 0 : ! polish)
1571     {
1572 	/* No special action to take.  */
1573 	memcpy (ret, val, vlen + 1);
1574 	if (lenp != NULL)
1575 	    *lenp = vlen;
1576 	return ret;
1577     }
1578 
1579     rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp);
1580     return ret;
1581 }
1582 
1583 /* Polish the value VAL returned by rcsbuf_getkey.  The POLISH
1584    parameter is non-zero if multiple embedded whitespace characters
1585    should be compressed into a single whitespace character.  Note that
1586    leading and trailing whitespace was already removed by
1587    rcsbuf_getkey.  Within an '@' string, pairs of '@' characters are
1588    compressed into a single '@' character regardless of the value of
1589    POLISH.  If LENP is not NULL, set *LENP to the length of the value.  */
1590 
1591 static void
rcsbuf_valpolish(rcsbuf,val,polish,lenp)1592 rcsbuf_valpolish (rcsbuf, val, polish, lenp)
1593     struct rcsbuffer *rcsbuf;
1594     char *val;
1595     int polish;
1596     size_t *lenp;
1597 {
1598     if (val == NULL)
1599     {
1600 	if (lenp != NULL)
1601 	    *lenp= 0;
1602 	return;
1603     }
1604 
1605     if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish)
1606     {
1607 	/* No special action to take.  */
1608 	if (lenp != NULL)
1609 	    *lenp = rcsbuf->vlen;
1610 	return;
1611     }
1612 
1613     rcsbuf_valpolish_internal (rcsbuf, val, val, lenp);
1614 }
1615 
1616 /* Internal polishing routine, called from rcsbuf_valcopy and
1617    rcsbuf_valpolish.  */
1618 
1619 static void
rcsbuf_valpolish_internal(rcsbuf,to,from,lenp)1620 rcsbuf_valpolish_internal (rcsbuf, to, from, lenp)
1621     struct rcsbuffer *rcsbuf;
1622     char *to;
1623     const char *from;
1624     size_t *lenp;
1625 {
1626     size_t len;
1627 
1628     len = rcsbuf->vlen;
1629 
1630     if (! rcsbuf->at_string)
1631     {
1632 	char *orig_to;
1633 	size_t clen;
1634 
1635 	orig_to = to;
1636 
1637 	for (clen = len; clen > 0; ++from, --clen)
1638 	{
1639 	    char c;
1640 
1641 	    c = *from;
1642 	    if (whitespace (c))
1643 	    {
1644 		/* Note that we know that clen can not drop to zero
1645                    while we have whitespace, because we know there is
1646                    no trailing whitespace.  */
1647 		while (whitespace (from[1]))
1648 		{
1649 		    ++from;
1650 		    --clen;
1651 		}
1652 		c = ' ';
1653 	    }
1654 	    *to++ = c;
1655 	}
1656 
1657 	*to = '\0';
1658 
1659 	if (lenp != NULL)
1660 	    *lenp = to - orig_to;
1661     }
1662     else
1663     {
1664 	const char *orig_from;
1665 	char *orig_to;
1666 	int embedded_at;
1667 	size_t clen;
1668 
1669 	orig_from = from;
1670 	orig_to = to;
1671 
1672 	embedded_at = rcsbuf->embedded_at;
1673 	assert (embedded_at > 0);
1674 
1675 	if (lenp != NULL)
1676 	    *lenp = len - embedded_at;
1677 
1678 	for (clen = len; clen > 0; ++from, --clen)
1679 	{
1680 	    char c;
1681 
1682 	    c = *from;
1683 	    *to++ = c;
1684 	    if (c == '@')
1685 	    {
1686 		++from;
1687 
1688 		/* Sanity check.  */
1689 		if (*from != '@' || clen == 0)
1690 		    abort ();
1691 
1692 		--clen;
1693 
1694 		--embedded_at;
1695 		if (embedded_at == 0)
1696 		{
1697 		    /* We've found all the embedded '@' characters.
1698                        We can just memcpy the rest of the buffer after
1699                        this '@' character.  */
1700 		    if (orig_to != orig_from)
1701 			memcpy (to, from + 1, clen - 1);
1702 		    else
1703 			memmove (to, from + 1, clen - 1);
1704 		    from += clen;
1705 		    to += clen - 1;
1706 		    break;
1707 		}
1708 	    }
1709 	}
1710 
1711 	/* Sanity check.  */
1712 	if (from != orig_from + len
1713 	    || to != orig_to + (len - rcsbuf->embedded_at))
1714 	{
1715 	    abort ();
1716 	}
1717 
1718 	*to = '\0';
1719     }
1720 }
1721 
1722 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1723 
1724 /* Copy the next word from the value VALP returned by rcsbuf_getkey into a
1725    memory buffer, updating VALP and returning the memory buffer.  Return
1726    NULL when there are no more words. */
1727 
1728 static char *
rcsbuf_valword(rcsbuf,valp)1729 rcsbuf_valword (rcsbuf, valp)
1730     struct rcsbuffer *rcsbuf;
1731     char **valp;
1732 {
1733     register const char * const my_spacetab = spacetab;
1734     register char *ptr, *pat;
1735     char c;
1736 
1737 #define my_whitespace(c)	(my_spacetab[(unsigned char)c] != 0)
1738 
1739     if (*valp == NULL)
1740 	return NULL;
1741 
1742     for (ptr = *valp; my_whitespace (*ptr); ++ptr) ;
1743     if (*ptr == '\0')
1744     {
1745 	assert (ptr - *valp == rcsbuf->vlen);
1746 	*valp = NULL;
1747 	rcsbuf->vlen = 0;
1748 	return NULL;
1749     }
1750 
1751     /* PTR now points to the start of a value.  Find out whether it is
1752        a num, an id, a string or a colon. */
1753     c = *ptr;
1754     if (c == ':')
1755     {
1756 	rcsbuf->vlen -= ++ptr - *valp;
1757 	*valp = ptr;
1758 	return xstrdup (":");
1759     }
1760 
1761     if (c == '@')
1762     {
1763 	int embedded_at = 0;
1764 	size_t vlen;
1765 
1766 	pat = ++ptr;
1767 	while ((pat = strchr (pat, '@')) != NULL)
1768 	{
1769 	    if (pat[1] != '@')
1770 		break;
1771 	    ++embedded_at;
1772 	    pat += 2;
1773 	}
1774 
1775 	/* Here PAT points to the final '@' in the string.  */
1776 	*pat++ = '\0';
1777 	assert (rcsbuf->at_string);
1778 	vlen = rcsbuf->vlen - (pat - *valp);
1779 	rcsbuf->vlen = pat - ptr - 1;
1780 	rcsbuf->embedded_at = embedded_at;
1781 	ptr = rcsbuf_valcopy (rcsbuf, ptr, 0, (size_t *) NULL);
1782 	*valp = pat;
1783 	rcsbuf->vlen = vlen;
1784 	if (strchr (pat, '@') == NULL)
1785 	    rcsbuf->at_string = 0;
1786 	else
1787 	    rcsbuf->embedded_at = -1;
1788 	return ptr;
1789     }
1790 
1791     /* *PTR is neither `:', `;' nor `@', so it should be the start of a num
1792        or an id.  Make sure it is not another special character. */
1793     if (c == '$' || c == '.' || c == ',')
1794     {
1795 	error (1, 0, "illegal special character in RCS field in %s",
1796 	       rcsbuf->filename);
1797     }
1798 
1799     pat = ptr;
1800     while (1)
1801     {
1802 	/* Legitimate ID characters are digits, dots and any `graphic
1803            printing character that is not a special.' This test ought
1804 	   to do the trick. */
1805 	c = *++pat;
1806 	if (!isprint ((unsigned char) c) ||
1807 	    c == ';' || c == '$' || c == ',' || c == '@' || c == ':')
1808 	    break;
1809     }
1810 
1811     /* PAT points to the last non-id character in this word, and C is
1812        the character in its memory cell.  Check to make sure that it
1813        is a legitimate word delimiter -- whitespace or end. */
1814     if (c != '\0' && !my_whitespace (c))
1815 	error (1, 0, "illegal special character in RCS field in %s",
1816 	       rcsbuf->filename);
1817 
1818     *pat = '\0';
1819     rcsbuf->vlen -= pat - *valp;
1820     *valp = pat;
1821     return xstrdup (ptr);
1822 
1823 #undef my_whitespace
1824 }
1825 
1826 #endif
1827 
1828 /* Return the current position of an rcsbuf.  */
1829 
1830 static unsigned long
rcsbuf_ftell(rcsbuf)1831 rcsbuf_ftell (rcsbuf)
1832     struct rcsbuffer *rcsbuf;
1833 {
1834     return rcsbuf->pos + (rcsbuf->ptr - rcsbuf_buffer);
1835 }
1836 
1837 /* Return a pointer to any data buffered for RCSBUF, along with the
1838    length.  */
1839 
1840 static void
rcsbuf_get_buffered(rcsbuf,datap,lenp)1841 rcsbuf_get_buffered (rcsbuf, datap, lenp)
1842     struct rcsbuffer *rcsbuf;
1843     char **datap;
1844     size_t *lenp;
1845 {
1846     *datap = rcsbuf->ptr;
1847     *lenp = rcsbuf->ptrend - rcsbuf->ptr;
1848 }
1849 
1850 /* CVS optimizes by quickly reading some header information from a
1851    file.  If it decides it needs to do more with the file, it reopens
1852    it.  We speed that up here by maintaining a cache of a single open
1853    file, to save the time it takes to reopen the file in the common
1854    case.  */
1855 
1856 static RCSNode *cached_rcs;
1857 static struct rcsbuffer cached_rcsbuf;
1858 
1859 /* Cache RCS and RCSBUF.  This takes responsibility for closing
1860    RCSBUF->FP.  */
1861 
1862 static void
rcsbuf_cache(rcs,rcsbuf)1863 rcsbuf_cache (rcs, rcsbuf)
1864     RCSNode *rcs;
1865     struct rcsbuffer *rcsbuf;
1866 {
1867     if (cached_rcs != NULL)
1868 	rcsbuf_cache_close ();
1869     cached_rcs = rcs;
1870     ++rcs->refcount;
1871     cached_rcsbuf = *rcsbuf;
1872 }
1873 
1874 /* If there is anything in the cache, close it.  */
1875 
1876 static void
rcsbuf_cache_close()1877 rcsbuf_cache_close ()
1878 {
1879     if (cached_rcs != NULL)
1880     {
1881 	if (fclose (cached_rcsbuf.fp) != 0)
1882 	    error (0, errno, "cannot close %s", cached_rcsbuf.filename);
1883 	rcsbuf_close (&cached_rcsbuf);
1884 	freercsnode (&cached_rcs);
1885 	cached_rcs = NULL;
1886     }
1887 }
1888 
1889 /* Open an rcsbuffer for RCS, getting it from the cache if possible.
1890    Set *FPP to the file, and *RCSBUFP to the rcsbuf.  The file should
1891    be put at position POS.  */
1892 
1893 static void
rcsbuf_cache_open(rcs,pos,pfp,prcsbuf)1894 rcsbuf_cache_open (rcs, pos, pfp, prcsbuf)
1895     RCSNode *rcs;
1896     long pos;
1897     FILE **pfp;
1898     struct rcsbuffer *prcsbuf;
1899 {
1900     if (cached_rcs == rcs)
1901     {
1902 	if (rcsbuf_ftell (&cached_rcsbuf) != pos)
1903 	{
1904 	    if (fseek (cached_rcsbuf.fp, pos, SEEK_SET) != 0)
1905 		error (1, 0, "cannot fseek RCS file %s",
1906 		       cached_rcsbuf.filename);
1907 	    cached_rcsbuf.ptr = rcsbuf_buffer;
1908 	    cached_rcsbuf.ptrend = rcsbuf_buffer;
1909 	    cached_rcsbuf.pos = pos;
1910 	}
1911 	*pfp = cached_rcsbuf.fp;
1912 
1913 	/* When RCS_parse opens a file using fopen_case, it frees the
1914            filename which we cached in CACHED_RCSBUF and stores a new
1915            file name in RCS->PATH.  We avoid problems here by always
1916            copying the filename over.  FIXME: This is hackish.  */
1917 	cached_rcsbuf.filename = rcs->path;
1918 
1919 	*prcsbuf = cached_rcsbuf;
1920 
1921 	cached_rcs = NULL;
1922 
1923 	/* Removing RCS from the cache removes a reference to it.  */
1924 	--rcs->refcount;
1925 	if (rcs->refcount <= 0)
1926 	    error (1, 0, "rcsbuf_cache_open: internal error");
1927     }
1928     else
1929     {
1930 	if (cached_rcs != NULL)
1931 	    rcsbuf_cache_close ();
1932 
1933 	*pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ);
1934 	if (*pfp == NULL)
1935 	    error (1, 0, "unable to reopen `%s'", rcs->path);
1936 	if (pos != 0)
1937 	{
1938 	    if (fseek (*pfp, pos, SEEK_SET) != 0)
1939 		error (1, 0, "cannot fseek RCS file %s", rcs->path);
1940 	}
1941 	rcsbuf_open (prcsbuf, *pfp, rcs->path, pos);
1942     }
1943 }
1944 
1945 
1946 /*
1947  * process the symbols list of the rcs file
1948  */
1949 static void
do_symbols(list,val)1950 do_symbols (list, val)
1951     List *list;
1952     char *val;
1953 {
1954     Node *p;
1955     char *cp = val;
1956     char *tag, *rev;
1957 
1958     for (;;)
1959     {
1960 	/* skip leading whitespace */
1961 	while (whitespace (*cp))
1962 	    cp++;
1963 
1964 	/* if we got to the end, we are done */
1965 	if (*cp == '\0')
1966 	    break;
1967 
1968 	/* split it up into tag and rev */
1969 	tag = cp;
1970 	cp = strchr (cp, ':');
1971 	*cp++ = '\0';
1972 	rev = cp;
1973 	while (!whitespace (*cp) && *cp != '\0')
1974 	    cp++;
1975 	if (*cp != '\0')
1976 	    *cp++ = '\0';
1977 
1978 	/* make a new node and add it to the list */
1979 	p = getnode ();
1980 	p->key = xstrdup (tag);
1981 	p->data = xstrdup (rev);
1982 	(void) addnode (list, p);
1983     }
1984 }
1985 
1986 /*
1987  * process the locks list of the rcs file
1988  * Like do_symbols, but hash entries are keyed backwards: i.e.
1989  * an entry like `user:rev' is keyed on REV rather than on USER.
1990  */
1991 static void
do_locks(list,val)1992 do_locks (list, val)
1993     List *list;
1994     char *val;
1995 {
1996     Node *p;
1997     char *cp = val;
1998     char *user, *rev;
1999 
2000     for (;;)
2001     {
2002 	/* skip leading whitespace */
2003 	while (whitespace (*cp))
2004 	    cp++;
2005 
2006 	/* if we got to the end, we are done */
2007 	if (*cp == '\0')
2008 	    break;
2009 
2010 	/* split it up into user and rev */
2011 	user = cp;
2012 	cp = strchr (cp, ':');
2013 	*cp++ = '\0';
2014 	rev = cp;
2015 	while (!whitespace (*cp) && *cp != '\0')
2016 	    cp++;
2017 	if (*cp != '\0')
2018 	    *cp++ = '\0';
2019 
2020 	/* make a new node and add it to the list */
2021 	p = getnode ();
2022 	p->key = xstrdup (rev);
2023 	p->data = xstrdup (user);
2024 	(void) addnode (list, p);
2025     }
2026 }
2027 
2028 /*
2029  * process the branches list of a revision delta
2030  */
2031 static void
do_branches(list,val)2032 do_branches (list, val)
2033     List *list;
2034     char *val;
2035 {
2036     Node *p;
2037     char *cp = val;
2038     char *branch;
2039 
2040     for (;;)
2041     {
2042 	/* skip leading whitespace */
2043 	while (whitespace (*cp))
2044 	    cp++;
2045 
2046 	/* if we got to the end, we are done */
2047 	if (*cp == '\0')
2048 	    break;
2049 
2050 	/* find the end of this branch */
2051 	branch = cp;
2052 	while (!whitespace (*cp) && *cp != '\0')
2053 	    cp++;
2054 	if (*cp != '\0')
2055 	    *cp++ = '\0';
2056 
2057 	/* make a new node and add it to the list */
2058 	p = getnode ();
2059 	p->key = xstrdup (branch);
2060 	(void) addnode (list, p);
2061     }
2062 }
2063 
2064 /*
2065  * Version Number
2066  *
2067  * Returns the requested version number of the RCS file, satisfying tags and/or
2068  * dates, and walking branches, if necessary.
2069  *
2070  * The result is returned; null-string if error.
2071  */
2072 char *
RCS_getversion(rcs,tag,date,force_tag_match,simple_tag)2073 RCS_getversion (rcs, tag, date, force_tag_match, simple_tag)
2074     RCSNode *rcs;
2075     char *tag;
2076     char *date;
2077     int force_tag_match;
2078     int *simple_tag;
2079 {
2080     if (simple_tag != NULL)
2081 	*simple_tag = 0;
2082 
2083     /* make sure we have something to look at... */
2084     assert (rcs != NULL);
2085 
2086     if (tag && date)
2087     {
2088 	char *branch, *rev;
2089 
2090 	if (! RCS_nodeisbranch (rcs, tag))
2091 	{
2092 	    /* We can't get a particular date if the tag is not a
2093                branch.  */
2094 	    return NULL;
2095 	}
2096 
2097 	/* Work out the branch.  */
2098 	if (! isdigit ((unsigned char) tag[0]))
2099 	    branch = RCS_whatbranch (rcs, tag);
2100 	else
2101 	    branch = xstrdup (tag);
2102 
2103 	/* Fetch the revision of branch as of date.  */
2104 	rev = RCS_getdatebranch (rcs, date, branch);
2105 	free (branch);
2106 	return (rev);
2107     }
2108     else if (tag)
2109 	return (RCS_gettag (rcs, tag, force_tag_match, simple_tag));
2110     else if (date)
2111 	return (RCS_getdate (rcs, date, force_tag_match));
2112     else
2113 	return (RCS_head (rcs));
2114 
2115 }
2116 
2117 /*
2118  * Get existing revision number corresponding to tag or revision.
2119  * Similar to RCS_gettag but less interpretation imposed.
2120  * For example:
2121  * -- If tag designates a magic branch, RCS_tag2rev
2122  *    returns the magic branch number.
2123  * -- If tag is a branch tag, returns the branch number, not
2124  *    the revision of the head of the branch.
2125  * If tag or revision is not valid or does not exist in file,
2126  * return NULL.
2127  */
2128 char *
RCS_tag2rev(rcs,tag)2129 RCS_tag2rev (rcs, tag)
2130     RCSNode *rcs;
2131     char *tag;
2132 {
2133     char *rev, *pa, *pb;
2134     int i;
2135 
2136     assert (rcs != NULL);
2137 
2138     if (rcs->flags & PARTIAL)
2139 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2140 
2141     /* If a valid revision, try to look it up */
2142     if ( RCS_valid_rev (tag) )
2143     {
2144 	/* Make a copy so we can scribble on it */
2145 	rev =  xstrdup (tag);
2146 
2147 	/* If revision exists, return the copy */
2148 	if (RCS_exist_rev (rcs, tag))
2149 	    return rev;
2150 
2151 	/* Nope, none such. If tag is not a branch we're done. */
2152 	i = numdots (rev);
2153 	if ((i & 1) == 1 )
2154 	{
2155 	    pa = strrchr (rev, '.');
2156 	    if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.')
2157 	    {
2158 		free (rev);
2159 		error (1, 0, "revision `%s' does not exist", tag);
2160 	    }
2161 	}
2162 
2163 	/* Try for a real (that is, exists in the RCS deltas) branch
2164 	   (RCS_exist_rev just checks for real revisions and revisions
2165 	   which have tags pointing to them).  */
2166 	pa = RCS_getbranch (rcs, rev, 1);
2167 	if (pa != NULL)
2168 	{
2169 	    free (pa);
2170 	    return rev;
2171 	}
2172 
2173        /* Tag is branch, but does not exist, try corresponding
2174 	* magic branch tag.
2175 	*
2176 	* FIXME: assumes all magic branches are of
2177 	* form "n.n.n ... .0.n".  I'll fix if somebody can
2178 	* send me a method to get a magic branch tag with
2179 	* the 0 in some other position -- <dan@gasboy.com>
2180 	*/
2181 	pa = strrchr (rev, '.');
2182 	pb = xmalloc (strlen (rev) + 3);
2183 	*pa++ = 0;
2184 	(void) sprintf (pb, "%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa);
2185 	free (rev);
2186 	rev = pb;
2187 	if (RCS_exist_rev (rcs, rev))
2188 	    return rev;
2189 	error (1, 0, "revision `%s' does not exist", tag);
2190     }
2191 
2192 
2193     RCS_check_tag (tag); /* exit if not a valid tag */
2194 
2195     /* If tag is "HEAD", special case to get head RCS revision */
2196     if (tag && STREQ (tag, TAG_HEAD))
2197         return (RCS_head (rcs));
2198 
2199     /* If valid tag let translate_symtag say yea or nay. */
2200     rev = translate_symtag (rcs, tag);
2201 
2202     if (rev)
2203         return rev;
2204 
2205     /* Trust the caller to print warnings. */
2206     return NULL;
2207 }
2208 
2209 /*
2210  * Find the revision for a specific tag.
2211  * If force_tag_match is set, return NULL if an exact match is not
2212  * possible otherwise return RCS_head ().  We are careful to look for
2213  * and handle "magic" revisions specially.
2214  *
2215  * If the matched tag is a branch tag, find the head of the branch.
2216  *
2217  * Returns pointer to newly malloc'd string, or NULL.
2218  */
2219 char *
RCS_gettag(rcs,symtag,force_tag_match,simple_tag)2220 RCS_gettag (rcs, symtag, force_tag_match, simple_tag)
2221     RCSNode *rcs;
2222     char *symtag;
2223     int force_tag_match;
2224     int *simple_tag;
2225 {
2226     char *tag = symtag;
2227     int tag_allocated = 0;
2228 
2229     if (simple_tag != NULL)
2230 	*simple_tag = 0;
2231 
2232     /* make sure we have something to look at... */
2233     assert (rcs != NULL);
2234 
2235     /* XXX this is probably not necessary, --jtc */
2236     if (rcs->flags & PARTIAL)
2237 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2238 
2239     /* If tag is "HEAD", special case to get head RCS revision */
2240     if (tag && (STREQ (tag, TAG_HEAD) || *tag == '\0'))
2241 #if 0 /* This #if 0 is only in the Cygnus code.  Why?  Death support?  */
2242 	if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
2243 	    return ((char *) NULL);	/* head request for removed file */
2244 	else
2245 #endif
2246 	    return (RCS_head (rcs));
2247 
2248     if (!isdigit ((unsigned char) tag[0]))
2249     {
2250 	char *version;
2251 
2252 	/* If we got a symbolic tag, resolve it to a numeric */
2253 	version = translate_symtag (rcs, tag);
2254 	if (version != NULL)
2255 	{
2256 	    int dots;
2257 	    char *magic, *branch, *cp;
2258 
2259 	    tag = version;
2260 	    tag_allocated = 1;
2261 
2262 	    /*
2263 	     * If this is a magic revision, we turn it into either its
2264 	     * physical branch equivalent (if one exists) or into
2265 	     * its base revision, which we assume exists.
2266 	     */
2267 	    dots = numdots (tag);
2268 	    if (dots > 2 && (dots & 1) != 0)
2269 	    {
2270 		branch = strrchr (tag, '.');
2271 		cp = branch++ - 1;
2272 		while (*cp != '.')
2273 		    cp--;
2274 
2275 		/* see if we have .magic-branch. (".0.") */
2276 		magic = xmalloc (strlen (tag) + 1);
2277 		(void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2278 		if (strncmp (magic, cp, strlen (magic)) == 0)
2279 		{
2280 		    /* it's magic.  See if the branch exists */
2281 		    *cp = '\0';		/* turn it into a revision */
2282 		    (void) sprintf (magic, "%s.%s", tag, branch);
2283 		    branch = RCS_getbranch (rcs, magic, 1);
2284 		    free (magic);
2285 		    if (branch != NULL)
2286 		    {
2287 			free (tag);
2288 			return (branch);
2289 		    }
2290 		    return (tag);
2291 		}
2292 		free (magic);
2293 	    }
2294 	}
2295 	else
2296 	{
2297 	    /* The tag wasn't there, so return the head or NULL */
2298 	    if (force_tag_match)
2299 		return (NULL);
2300 	    else
2301 		return (RCS_head (rcs));
2302 	}
2303     }
2304 
2305     /*
2306      * numeric tag processing:
2307      *		1) revision number - just return it
2308      *		2) branch number   - find head of branch
2309      */
2310 
2311     /* strip trailing dots */
2312     while (tag[strlen (tag) - 1] == '.')
2313 	tag[strlen (tag) - 1] = '\0';
2314 
2315     if ((numdots (tag) & 1) == 0)
2316     {
2317 	char *branch;
2318 
2319 	/* we have a branch tag, so we need to walk the branch */
2320 	branch = RCS_getbranch (rcs, tag, force_tag_match);
2321 	if (tag_allocated)
2322 	    free (tag);
2323 	return branch;
2324     }
2325     else
2326     {
2327 	Node *p;
2328 
2329 	/* we have a revision tag, so make sure it exists */
2330 	p = findnode (rcs->versions, tag);
2331 	if (p != NULL)
2332 	{
2333 	    /* We have found a numeric revision for the revision tag.
2334 	       To support expanding the RCS keyword Name, if
2335 	       SIMPLE_TAG is not NULL, tell the the caller that this
2336 	       is a simple tag which co will recognize.  FIXME: Are
2337 	       there other cases in which we should set this?  In
2338 	       particular, what if we expand RCS keywords internally
2339 	       without calling co?  */
2340 	    if (simple_tag != NULL)
2341 		*simple_tag = 1;
2342 	    if (! tag_allocated)
2343 		tag = xstrdup (tag);
2344 	    return (tag);
2345 	}
2346 	else
2347 	{
2348 	    /* The revision wasn't there, so return the head or NULL */
2349 	    if (tag_allocated)
2350 		free (tag);
2351 	    if (force_tag_match)
2352 		return (NULL);
2353 	    else
2354 		return (RCS_head (rcs));
2355 	}
2356     }
2357 }
2358 
2359 /*
2360  * Return a "magic" revision as a virtual branch off of REV for the RCS file.
2361  * A "magic" revision is one which is unique in the RCS file.  By unique, I
2362  * mean we return a revision which:
2363  *	- has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
2364  *	- has a revision component which is not an existing branch off REV
2365  *	- has a revision component which is not an existing magic revision
2366  *	- is an even-numbered revision, to avoid conflicts with vendor branches
2367  * The first point is what makes it "magic".
2368  *
2369  * As an example, if we pass in 1.37 as REV, we will look for an existing
2370  * branch called 1.37.2.  If it did not exist, we would look for an
2371  * existing symbolic tag with a numeric part equal to 1.37.0.2.  If that
2372  * didn't exist, then we know that the 1.37.2 branch can be reserved by
2373  * creating a symbolic tag with 1.37.0.2 as the numeric part.
2374  *
2375  * This allows us to fork development with very little overhead -- just a
2376  * symbolic tag is used in the RCS file.  When a commit is done, a physical
2377  * branch is dynamically created to hold the new revision.
2378  *
2379  * Note: We assume that REV is an RCS revision and not a branch number.
2380  */
2381 static char *check_rev;
2382 char *
RCS_magicrev(rcs,rev)2383 RCS_magicrev (rcs, rev)
2384     RCSNode *rcs;
2385     char *rev;
2386 {
2387     int rev_num;
2388     char *xrev, *test_branch;
2389 
2390     xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
2391     check_rev = xrev;
2392 
2393     /* only look at even numbered branches */
2394     for (rev_num = 2; ; rev_num += 2)
2395     {
2396 	/* see if the physical branch exists */
2397 	(void) sprintf (xrev, "%s.%d", rev, rev_num);
2398 	test_branch = RCS_getbranch (rcs, xrev, 1);
2399 	if (test_branch != NULL)	/* it did, so keep looking */
2400 	{
2401 	    free (test_branch);
2402 	    continue;
2403 	}
2404 
2405 	/* now, create a "magic" revision */
2406 	(void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
2407 
2408 	/* walk the symbols list to see if a magic one already exists */
2409 	if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0)
2410 	    continue;
2411 
2412 	/* we found a free magic branch.  Claim it as ours */
2413 	return (xrev);
2414     }
2415 }
2416 
2417 /*
2418  * walklist proc to look for a match in the symbols list.
2419  * Returns 0 if the symbol does not match, 1 if it does.
2420  */
2421 static int
checkmagic_proc(p,closure)2422 checkmagic_proc (p, closure)
2423     Node *p;
2424     void *closure;
2425 {
2426     if (STREQ (check_rev, p->data))
2427 	return (1);
2428     else
2429 	return (0);
2430 }
2431 
2432 /*
2433  * Given an RCSNode, returns non-zero if the specified revision number
2434  * or symbolic tag resolves to a "branch" within the rcs file.
2435  *
2436  * FIXME: this is the same as RCS_nodeisbranch except for the special
2437  *        case for handling a null rcsnode.
2438  */
2439 int
RCS_isbranch(rcs,rev)2440 RCS_isbranch (rcs, rev)
2441     RCSNode *rcs;
2442     const char *rev;
2443 {
2444     /* numeric revisions are easy -- even number of dots is a branch */
2445     if (isdigit ((unsigned char) *rev))
2446 	return ((numdots (rev) & 1) == 0);
2447 
2448     /* assume a revision if you can't find the RCS info */
2449     if (rcs == NULL)
2450 	return (0);
2451 
2452     /* now, look for a match in the symbols list */
2453     return (RCS_nodeisbranch (rcs, rev));
2454 }
2455 
2456 /*
2457  * Given an RCSNode, returns non-zero if the specified revision number
2458  * or symbolic tag resolves to a "branch" within the rcs file.  We do
2459  * take into account any magic branches as well.
2460  */
2461 int
RCS_nodeisbranch(rcs,rev)2462 RCS_nodeisbranch (rcs, rev)
2463     RCSNode *rcs;
2464     const char *rev;
2465 {
2466     int dots;
2467     char *version;
2468 
2469     assert (rcs != NULL);
2470 
2471     /* numeric revisions are easy -- even number of dots is a branch */
2472     if (isdigit ((unsigned char) *rev))
2473 	return ((numdots (rev) & 1) == 0);
2474 
2475     version = translate_symtag (rcs, rev);
2476     if (version == NULL)
2477 	return (0);
2478     dots = numdots (version);
2479     if ((dots & 1) == 0)
2480     {
2481 	free (version);
2482 	return (1);
2483     }
2484 
2485     /* got a symbolic tag match, but it's not a branch; see if it's magic */
2486     if (dots > 2)
2487     {
2488 	char *magic;
2489 	char *branch = strrchr (version, '.');
2490 	char *cp = branch - 1;
2491 	while (*cp != '.')
2492 	    cp--;
2493 
2494 	/* see if we have .magic-branch. (".0.") */
2495 	magic = xmalloc (strlen (version) + 1);
2496 	(void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2497 	if (strncmp (magic, cp, strlen (magic)) == 0)
2498 	{
2499 	    free (magic);
2500 	    free (version);
2501 	    return (1);
2502 	}
2503 	free (magic);
2504     }
2505     free (version);
2506     return (0);
2507 }
2508 
2509 /*
2510  * Returns a pointer to malloc'ed memory which contains the branch
2511  * for the specified *symbolic* tag.  Magic branches are handled correctly.
2512  */
2513 char *
RCS_whatbranch(rcs,rev)2514 RCS_whatbranch (rcs, rev)
2515     RCSNode *rcs;
2516     const char *rev;
2517 {
2518     char *version;
2519     int dots;
2520 
2521     /* assume no branch if you can't find the RCS info */
2522     if (rcs == NULL)
2523 	return ((char *) NULL);
2524 
2525     /* now, look for a match in the symbols list */
2526     version = translate_symtag (rcs, rev);
2527     if (version == NULL)
2528 	return ((char *) NULL);
2529     dots = numdots (version);
2530     if ((dots & 1) == 0)
2531 	return (version);
2532 
2533     /* got a symbolic tag match, but it's not a branch; see if it's magic */
2534     if (dots > 2)
2535     {
2536 	char *magic;
2537 	char *branch = strrchr (version, '.');
2538 	char *cp = branch++ - 1;
2539 	while (*cp != '.')
2540 	    cp--;
2541 
2542 	/* see if we have .magic-branch. (".0.") */
2543 	magic = xmalloc (strlen (version) + 1);
2544 	(void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2545 	if (strncmp (magic, cp, strlen (magic)) == 0)
2546 	{
2547 	    /* yep.  it's magic.  now, construct the real branch */
2548 	    *cp = '\0';			/* turn it into a revision */
2549 	    (void) sprintf (magic, "%s.%s", version, branch);
2550 	    free (version);
2551 	    return (magic);
2552 	}
2553 	free (magic);
2554     }
2555     free (version);
2556     return ((char *) NULL);
2557 }
2558 
2559 /*
2560  * Get the head of the specified branch.  If the branch does not exist,
2561  * return NULL or RCS_head depending on force_tag_match.
2562  * Returns NULL or a newly malloc'd string.
2563  */
2564 char *
RCS_getbranch(rcs,tag,force_tag_match)2565 RCS_getbranch (rcs, tag, force_tag_match)
2566     RCSNode *rcs;
2567     char *tag;
2568     int force_tag_match;
2569 {
2570     Node *p, *head;
2571     RCSVers *vn;
2572     char *xtag;
2573     char *nextvers;
2574     char *cp;
2575 
2576     /* make sure we have something to look at... */
2577     assert (rcs != NULL);
2578 
2579     if (rcs->flags & PARTIAL)
2580 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2581 
2582     /* find out if the tag contains a dot, or is on the trunk */
2583     cp = strrchr (tag, '.');
2584 
2585     /* trunk processing is the special case */
2586     if (cp == NULL)
2587     {
2588 	xtag = xmalloc (strlen (tag) + 1 + 1);	/* +1 for an extra . */
2589 	(void) strcpy (xtag, tag);
2590 	(void) strcat (xtag, ".");
2591 	for (cp = rcs->head; cp != NULL;)
2592 	{
2593 	    if (strncmp (xtag, cp, strlen (xtag)) == 0)
2594 		break;
2595 	    p = findnode (rcs->versions, cp);
2596 	    if (p == NULL)
2597 	    {
2598 		free (xtag);
2599 		if (force_tag_match)
2600 		    return (NULL);
2601 		else
2602 		    return (RCS_head (rcs));
2603 	    }
2604 	    vn = (RCSVers *) p->data;
2605 	    cp = vn->next;
2606 	}
2607 	free (xtag);
2608 	if (cp == NULL)
2609 	{
2610 	    if (force_tag_match)
2611 		return (NULL);
2612 	    else
2613 		return (RCS_head (rcs));
2614 	}
2615 	return (xstrdup (cp));
2616     }
2617 
2618     /* if it had a `.', terminate the string so we have the base revision */
2619     *cp = '\0';
2620 
2621     /* look up the revision this branch is based on */
2622     p = findnode (rcs->versions, tag);
2623 
2624     /* put the . back so we have the branch again */
2625     *cp = '.';
2626 
2627     if (p == NULL)
2628     {
2629 	/* if the base revision didn't exist, return head or NULL */
2630 	if (force_tag_match)
2631 	    return (NULL);
2632 	else
2633 	    return (RCS_head (rcs));
2634     }
2635 
2636     /* find the first element of the branch we are looking for */
2637     vn = (RCSVers *) p->data;
2638     if (vn->branches == NULL)
2639 	return (NULL);
2640     xtag = xmalloc (strlen (tag) + 1 + 1);	/* 1 for the extra '.' */
2641     (void) strcpy (xtag, tag);
2642     (void) strcat (xtag, ".");
2643     head = vn->branches->list;
2644     for (p = head->next; p != head; p = p->next)
2645 	if (strncmp (p->key, xtag, strlen (xtag)) == 0)
2646 	    break;
2647     free (xtag);
2648 
2649     if (p == head)
2650     {
2651 	/* we didn't find a match so return head or NULL */
2652 	if (force_tag_match)
2653 	    return (NULL);
2654 	else
2655 	    return (RCS_head (rcs));
2656     }
2657 
2658     /* now walk the next pointers of the branch */
2659     nextvers = p->key;
2660     do
2661     {
2662 	p = findnode (rcs->versions, nextvers);
2663 	if (p == NULL)
2664 	{
2665 	    /* a link in the chain is missing - return head or NULL */
2666 	    if (force_tag_match)
2667 		return (NULL);
2668 	    else
2669 		return (RCS_head (rcs));
2670 	}
2671 	vn = (RCSVers *) p->data;
2672 	nextvers = vn->next;
2673     } while (nextvers != NULL);
2674 
2675     /* we have the version in our hand, so go for it */
2676     return (xstrdup (vn->version));
2677 }
2678 
2679 /* Returns the head of the branch which REV is on.  REV can be a
2680    branch tag or non-branch tag; symbolic or numeric.
2681 
2682    Returns a newly malloc'd string.  Returns NULL if a symbolic name
2683    isn't found.  */
2684 
2685 char *
RCS_branch_head(rcs,rev)2686 RCS_branch_head (rcs, rev)
2687     RCSNode *rcs;
2688     char *rev;
2689 {
2690     char *num;
2691     char *br;
2692     char *retval;
2693 
2694     assert (rcs != NULL);
2695 
2696     if (RCS_nodeisbranch (rcs, rev))
2697 	return RCS_getbranch (rcs, rev, 1);
2698 
2699     if (isdigit ((unsigned char) *rev))
2700 	num = xstrdup (rev);
2701     else
2702     {
2703 	num = translate_symtag (rcs, rev);
2704 	if (num == NULL)
2705 	    return NULL;
2706     }
2707     br = truncate_revnum (num);
2708     retval = RCS_getbranch (rcs, br, 1);
2709     free (br);
2710     free (num);
2711     return retval;
2712 }
2713 
2714 /* Get the branch point for a particular branch, that is the first
2715    revision on that branch.  For example, RCS_getbranchpoint (rcs,
2716    "1.3.2") will normally return "1.3.2.1".  TARGET may be either a
2717    branch number or a revision number; if a revnum, find the
2718    branchpoint of the branch to which TARGET belongs.
2719 
2720    Return RCS_head if TARGET is on the trunk or if the root node could
2721    not be found (this is sort of backwards from our behavior on a branch;
2722    the rationale is that the return value is a revision from which you
2723    can start walking the next fields and end up at TARGET).
2724    Return NULL on error.  */
2725 
2726 static char *
RCS_getbranchpoint(rcs,target)2727 RCS_getbranchpoint (rcs, target)
2728     RCSNode *rcs;
2729     char *target;
2730 {
2731     char *branch, *bp;
2732     Node *vp;
2733     RCSVers *rev;
2734     int dots, isrevnum, brlen;
2735 
2736     dots = numdots (target);
2737     isrevnum = dots & 1;
2738 
2739     if (dots == 1)
2740 	/* TARGET is a trunk revision; return rcs->head. */
2741 	return (RCS_head (rcs));
2742 
2743     /* Get the revision number of the node at which TARGET's branch is
2744        rooted.  If TARGET is a branch number, lop off the last field;
2745        if it's a revision number, lop off the last *two* fields. */
2746     branch = xstrdup (target);
2747     bp = strrchr (branch, '.');
2748     if (bp == NULL)
2749 	error (1, 0, "%s: confused revision number %s",
2750 	       rcs->path, target);
2751     if (isrevnum)
2752 	while (*--bp != '.')
2753 	    ;
2754     *bp = '\0';
2755 
2756     vp = findnode (rcs->versions, branch);
2757     if (vp == NULL)
2758     {
2759 	error (0, 0, "%s: can't find branch point %s", rcs->path, target);
2760 	return NULL;
2761     }
2762     rev = (RCSVers *) vp->data;
2763 
2764     *bp++ = '.';
2765     while (*bp && *bp != '.')
2766 	++bp;
2767     brlen = bp - branch;
2768 
2769     vp = rev->branches->list->next;
2770     while (vp != rev->branches->list)
2771     {
2772 	/* BRANCH may be a genuine branch number, e.g. `1.1.3', or
2773 	   maybe a full revision number, e.g. `1.1.3.6'.  We have
2774 	   found our branch point if the first BRANCHLEN characters
2775 	   of the revision number match, *and* if the following
2776 	   character is a dot. */
2777 	if (strncmp (vp->key, branch, brlen) == 0 && vp->key[brlen] == '.')
2778 	    break;
2779 	vp = vp->next;
2780     }
2781 
2782     free (branch);
2783     if (vp == rev->branches->list)
2784     {
2785 	error (0, 0, "%s: can't find branch point %s", rcs->path, target);
2786 	return NULL;
2787     }
2788     else
2789 	return (xstrdup (vp->key));
2790 }
2791 
2792 /*
2793  * Get the head of the RCS file.  If branch is set, this is the head of the
2794  * branch, otherwise the real head.
2795  * Returns NULL or a newly malloc'd string.
2796  */
2797 char *
RCS_head(rcs)2798 RCS_head (rcs)
2799     RCSNode *rcs;
2800 {
2801     /* make sure we have something to look at... */
2802     assert (rcs != NULL);
2803 
2804     /*
2805      * NOTE: we call getbranch with force_tag_match set to avoid any
2806      * possibility of recursion
2807      */
2808     if (rcs->branch)
2809 	return (RCS_getbranch (rcs, rcs->branch, 1));
2810     else
2811 	return (xstrdup (rcs->head));
2812 }
2813 
2814 /*
2815  * Get the most recent revision, based on the supplied date, but use some
2816  * funky stuff and follow the vendor branch maybe
2817  */
2818 char *
RCS_getdate(rcs,date,force_tag_match)2819 RCS_getdate (rcs, date, force_tag_match)
2820     RCSNode *rcs;
2821     char *date;
2822     int force_tag_match;
2823 {
2824     char *cur_rev = NULL;
2825     char *retval = NULL;
2826     Node *p;
2827     RCSVers *cur_vers;
2828     RCSVers *vers = NULL;
2829 
2830     /* make sure we have something to look at... */
2831     assert (rcs != NULL);
2832 
2833     if (rcs->flags & PARTIAL)
2834 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2835 
2836     /* if the head is on a branch, try the branch first */
2837     if (rcs->branch != NULL)
2838 	retval = RCS_getdatebranch (rcs, date, rcs->branch);
2839 
2840     /* if we found a match, we are done */
2841     if (retval != NULL)
2842 	return (retval);
2843 
2844     /* otherwise if we have a trunk, try it */
2845     if (rcs->head)
2846     {
2847 	p = findnode (rcs->versions, rcs->head);
2848 	while (p != NULL)
2849 	{
2850 	    /* if the date of this one is before date, take it */
2851 	    vers = (RCSVers *) p->data;
2852 	    if (RCS_datecmp (vers->date, date) <= 0)
2853 	    {
2854 		cur_rev = vers->version;
2855 		cur_vers = vers;
2856 		break;
2857 	    }
2858 
2859 	    /* if there is a next version, find the node */
2860 	    if (vers->next != NULL)
2861 		p = findnode (rcs->versions, vers->next);
2862 	    else
2863 		p = (Node *) NULL;
2864 	}
2865     }
2866 
2867     /*
2868      * at this point, either we have the revision we want, or we have the
2869      * first revision on the trunk (1.1?) in our hands
2870      */
2871 
2872     /* if we found what we're looking for, and it's not 1.1 return it */
2873     if (cur_rev != NULL)
2874     {
2875 	if (! STREQ (cur_rev, "1.1"))
2876 	    return (xstrdup (cur_rev));
2877 
2878 	/* This is 1.1;  if the date of 1.1 is not the same as that for the
2879 	   1.1.1.1 version, then return 1.1.  This happens when the first
2880 	   version of a file is created by a regular cvs add and commit,
2881 	   and there is a subsequent cvs import of the same file.  */
2882 	p = findnode (rcs->versions, "1.1.1.1");
2883 	if (p)
2884 	{
2885 	    vers = (RCSVers *) p->data;
2886 	    if (RCS_datecmp (vers->date, cur_vers->date) != 0)
2887 		return xstrdup ("1.1");
2888 	}
2889     }
2890 
2891     /* look on the vendor branch */
2892     retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
2893 
2894     /*
2895      * if we found a match, return it; otherwise, we return the first
2896      * revision on the trunk or NULL depending on force_tag_match and the
2897      * date of the first rev
2898      */
2899     if (retval != NULL)
2900 	return (retval);
2901 
2902     if (vers && (!force_tag_match || RCS_datecmp (vers->date, date) <= 0))
2903 	return (xstrdup (vers->version));
2904     else
2905 	return (NULL);
2906 }
2907 
2908 /*
2909  * Look up the last element on a branch that was put in before the specified
2910  * date (return the rev or NULL)
2911  */
2912 static char *
RCS_getdatebranch(rcs,date,branch)2913 RCS_getdatebranch (rcs, date, branch)
2914     RCSNode *rcs;
2915     char *date;
2916     char *branch;
2917 {
2918     char *cur_rev = NULL;
2919     char *cp;
2920     char *xbranch, *xrev;
2921     Node *p;
2922     RCSVers *vers;
2923 
2924     /* look up the first revision on the branch */
2925     xrev = xstrdup (branch);
2926     cp = strrchr (xrev, '.');
2927     if (cp == NULL)
2928     {
2929 	free (xrev);
2930 	return (NULL);
2931     }
2932     *cp = '\0';				/* turn it into a revision */
2933 
2934     assert (rcs != NULL);
2935 
2936     if (rcs->flags & PARTIAL)
2937 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2938 
2939     p = findnode (rcs->versions, xrev);
2940     free (xrev);
2941     if (p == NULL)
2942 	return (NULL);
2943     vers = (RCSVers *) p->data;
2944 
2945     /* Tentatively use this revision, if it is early enough.  */
2946     if (RCS_datecmp (vers->date, date) <= 0)
2947 	cur_rev = vers->version;
2948 
2949     /* If no branches list, return now.  This is what happens if the branch
2950        is a (magic) branch with no revisions yet.  */
2951     if (vers->branches == NULL)
2952 	return xstrdup (cur_rev);
2953 
2954     /* walk the branches list looking for the branch number */
2955     xbranch = xmalloc (strlen (branch) + 1 + 1); /* +1 for the extra dot */
2956     (void) strcpy (xbranch, branch);
2957     (void) strcat (xbranch, ".");
2958     for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
2959 	if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
2960 	    break;
2961     free (xbranch);
2962     if (p == vers->branches->list)
2963     {
2964 	/* This is what happens if the branch is a (magic) branch with
2965 	   no revisions yet.  Similar to the case where vers->branches ==
2966 	   NULL, except here there was a another branch off the same
2967 	   branchpoint.  */
2968 	return xstrdup (cur_rev);
2969     }
2970 
2971     p = findnode (rcs->versions, p->key);
2972 
2973     /* walk the next pointers until you find the end, or the date is too late */
2974     while (p != NULL)
2975     {
2976 	vers = (RCSVers *) p->data;
2977 	if (RCS_datecmp (vers->date, date) <= 0)
2978 	    cur_rev = vers->version;
2979 	else
2980 	    break;
2981 
2982 	/* if there is a next version, find the node */
2983 	if (vers->next != NULL)
2984 	    p = findnode (rcs->versions, vers->next);
2985 	else
2986 	    p = (Node *) NULL;
2987     }
2988 
2989     /* Return whatever we found, which may be NULL.  */
2990     return xstrdup (cur_rev);
2991 }
2992 
2993 /*
2994  * Compare two dates in RCS format. Beware the change in format on January 1,
2995  * 2000, when years go from 2-digit to full format.
2996  */
2997 int
RCS_datecmp(date1,date2)2998 RCS_datecmp (date1, date2)
2999     char *date1, *date2;
3000 {
3001     int length_diff = strlen (date1) - strlen (date2);
3002 
3003     return (length_diff ? length_diff : strcmp (date1, date2));
3004 }
3005 
3006 /* Look up revision REV in RCS and return the date specified for the
3007    revision minus FUDGE seconds (FUDGE will generally be one, so that the
3008    logically previous revision will be found later, or zero, if we want
3009    the exact date).
3010 
3011    The return value is the date being returned as a time_t, or (time_t)-1
3012    on error (previously was documented as zero on error; I haven't checked
3013    the callers to make sure that they really check for (time_t)-1, but
3014    the latter is what this function really returns).  If DATE is non-NULL,
3015    then it must point to MAXDATELEN characters, and we store the same
3016    return value there in DATEFORM format.  */
3017 time_t
RCS_getrevtime(rcs,rev,date,fudge)3018 RCS_getrevtime (rcs, rev, date, fudge)
3019     RCSNode *rcs;
3020     char *rev;
3021     char *date;
3022     int fudge;
3023 {
3024     char tdate[MAXDATELEN];
3025     struct tm xtm, *ftm;
3026     time_t revdate = 0;
3027     Node *p;
3028     RCSVers *vers;
3029 
3030     /* make sure we have something to look at... */
3031     assert (rcs != NULL);
3032 
3033     if (rcs->flags & PARTIAL)
3034 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3035 
3036     /* look up the revision */
3037     p = findnode (rcs->versions, rev);
3038     if (p == NULL)
3039 	return (-1);
3040     vers = (RCSVers *) p->data;
3041 
3042     /* split up the date */
3043     ftm = &xtm;
3044     (void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
3045 		   &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
3046 		   &ftm->tm_sec);
3047 
3048     /* If the year is from 1900 to 1999, RCS files contain only two
3049        digits, and sscanf gives us a year from 0-99.  If the year is
3050        2000+, RCS files contain all four digits and we subtract 1900,
3051        because the tm_year field should contain years since 1900.  */
3052 
3053     if (ftm->tm_year > 1900)
3054 	ftm->tm_year -= 1900;
3055 
3056     /* put the date in a form getdate can grok */
3057     (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon,
3058 		    ftm->tm_mday, ftm->tm_year + 1900, ftm->tm_hour,
3059 		    ftm->tm_min, ftm->tm_sec);
3060 
3061     /* turn it into seconds since the epoch */
3062     revdate = get_date (tdate);
3063     if (revdate != (time_t) -1)
3064     {
3065 	revdate -= fudge;		/* remove "fudge" seconds */
3066 	if (date)
3067 	{
3068 	    /* put an appropriate string into ``date'' if we were given one */
3069 	    ftm = gmtime (&revdate);
3070 	    (void) sprintf (date, DATEFORM,
3071 			    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
3072 			    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
3073 			    ftm->tm_min, ftm->tm_sec);
3074 	}
3075     }
3076     return (revdate);
3077 }
3078 
3079 List *
RCS_getlocks(rcs)3080 RCS_getlocks (rcs)
3081     RCSNode *rcs;
3082 {
3083     assert(rcs != NULL);
3084 
3085     if (rcs->flags & PARTIAL)
3086 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3087 
3088     if (rcs->locks_data) {
3089 	rcs->locks = getlist ();
3090 	do_locks (rcs->locks, rcs->locks_data);
3091 	free(rcs->locks_data);
3092 	rcs->locks_data = NULL;
3093     }
3094 
3095     return rcs->locks;
3096 }
3097 
3098 List *
RCS_symbols(rcs)3099 RCS_symbols(rcs)
3100     RCSNode *rcs;
3101 {
3102     assert(rcs != NULL);
3103 
3104     if (rcs->flags & PARTIAL)
3105 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3106 
3107     if (rcs->symbols_data) {
3108 	rcs->symbols = getlist ();
3109 	do_symbols (rcs->symbols, rcs->symbols_data);
3110 	free(rcs->symbols_data);
3111 	rcs->symbols_data = NULL;
3112     }
3113 
3114     return rcs->symbols;
3115 }
3116 
3117 /*
3118  * Return the version associated with a particular symbolic tag.
3119  * Returns NULL or a newly malloc'd string.
3120  */
3121 static char *
translate_symtag(rcs,tag)3122 translate_symtag (rcs, tag)
3123     RCSNode *rcs;
3124     const char *tag;
3125 {
3126     if (rcs->flags & PARTIAL)
3127 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3128 
3129     if (rcs->symbols != NULL)
3130     {
3131 	Node *p;
3132 
3133 	/* The symbols have already been converted into a list.  */
3134 	p = findnode (rcs->symbols, tag);
3135 	if (p == NULL)
3136 	    return NULL;
3137 
3138 	return xstrdup (p->data);
3139     }
3140 
3141     if (rcs->symbols_data != NULL)
3142     {
3143 	size_t len;
3144 	char *cp;
3145 
3146 	/* Look through the RCS symbols information.  This is like
3147            do_symbols, but we don't add the information to a list.  In
3148            most cases, we will only be called once for this file, so
3149            generating the list is unnecessary overhead.  */
3150 
3151 	len = strlen (tag);
3152 	cp = rcs->symbols_data;
3153 	while ((cp = strchr (cp, tag[0])) != NULL)
3154 	{
3155 	    if ((cp == rcs->symbols_data || whitespace (cp[-1]))
3156 		&& strncmp (cp, tag, len) == 0
3157 		&& cp[len] == ':')
3158 	    {
3159 		char *v, *r;
3160 
3161 		/* We found the tag.  Return the version number.  */
3162 
3163 		cp += len + 1;
3164 		v = cp;
3165 		while (! whitespace (*cp) && *cp != '\0')
3166 		    ++cp;
3167 		r = xmalloc (cp - v + 1);
3168 		strncpy (r, v, cp - v);
3169 		r[cp - v] = '\0';
3170 		return r;
3171 	    }
3172 
3173 	    while (! whitespace (*cp) && *cp != '\0')
3174 		++cp;
3175 	}
3176     }
3177 
3178     return NULL;
3179 }
3180 
3181 /*
3182  * The argument ARG is the getopt remainder of the -k option specified on the
3183  * command line.  This function returns malloc'ed space that can be used
3184  * directly in calls to RCS V5, with the -k flag munged correctly.
3185  */
3186 char *
RCS_check_kflag(arg)3187 RCS_check_kflag (arg)
3188     const char *arg;
3189 {
3190     static const char *const  keyword_usage[] =
3191     {
3192       "%s %s: invalid RCS keyword expansion mode\n",
3193       "Valid expansion modes include:\n",
3194       "   -kkv\tGenerate keywords using the default form.\n",
3195       "   -kkvl\tLike -kkv, except locker's name inserted.\n",
3196       "   -kk\tGenerate only keyword names in keyword strings.\n",
3197       "   -kv\tGenerate only keyword values in keyword strings.\n",
3198       "   -ko\tGenerate the old keyword string (no changes from checked in file).\n",
3199       "   -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n",
3200       "(Specify the --help global option for a list of other help options)\n",
3201       NULL,
3202     };
3203     /* Big enough to hold any of the strings from kflags.  */
3204     char karg[10];
3205     char const *const *cpp = NULL;
3206 
3207     if (arg)
3208     {
3209 	for (cpp = kflags; *cpp != NULL; cpp++)
3210 	{
3211 	    if (STREQ (arg, *cpp))
3212 		break;
3213 	}
3214     }
3215 
3216     if (arg == NULL || *cpp == NULL)
3217     {
3218 	usage (keyword_usage);
3219     }
3220 
3221     (void) sprintf (karg, "-k%s", *cpp);
3222     return (xstrdup (karg));
3223 }
3224 
3225 /*
3226  * Do some consistency checks on the symbolic tag... These should equate
3227  * pretty close to what RCS checks, though I don't know for certain.
3228  */
3229 void
RCS_check_tag(tag)3230 RCS_check_tag (tag)
3231     const char *tag;
3232 {
3233     char *invalid = "$,.:;@";		/* invalid RCS tag characters */
3234     const char *cp;
3235 
3236     /*
3237      * The first character must be an alphabetic letter. The remaining
3238      * characters cannot be non-visible graphic characters, and must not be
3239      * in the set of "invalid" RCS identifier characters.
3240      */
3241     if (isalpha ((unsigned char) *tag))
3242     {
3243 	for (cp = tag; *cp; cp++)
3244 	{
3245 	    if (!isgraph ((unsigned char) *cp))
3246 		error (1, 0, "tag `%s' has non-visible graphic characters",
3247 		       tag);
3248 	    if (strchr (invalid, *cp))
3249 		error (1, 0, "tag `%s' must not contain the characters `%s'",
3250 		       tag, invalid);
3251 	}
3252     }
3253     else
3254 	error (1, 0, "tag `%s' must start with a letter", tag);
3255 }
3256 
3257 /*
3258  * TRUE if argument has valid syntax for an RCS revision or
3259  * branch number.  All characters must be digits or dots, first
3260  * and last characters must be digits, and no two consecutive
3261  * characters may be dots.
3262  *
3263  * Intended for classifying things, so this function doesn't
3264  * call error.
3265  */
3266 int
RCS_valid_rev(rev)3267 RCS_valid_rev (rev)
3268     char *rev;
3269 {
3270    char last, c;
3271    last = *rev++;
3272    if (!isdigit ((unsigned char) last))
3273        return 0;
3274    while ((c = *rev++))   /* Extra parens placate -Wall gcc option */
3275    {
3276        if (c == '.')
3277        {
3278            if (last == '.')
3279                return 0;
3280            continue;
3281        }
3282        last = c;
3283        if (!isdigit ((unsigned char) c))
3284            return 0;
3285    }
3286    if (!isdigit ((unsigned char) last))
3287        return 0;
3288    return 1;
3289 }
3290 
3291 /*
3292  * Return true if RCS revision with TAG is a dead revision.
3293  */
3294 int
RCS_isdead(rcs,tag)3295 RCS_isdead (rcs, tag)
3296     RCSNode *rcs;
3297     const char *tag;
3298 {
3299     Node *p;
3300     RCSVers *version;
3301 
3302     if (rcs->flags & PARTIAL)
3303 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3304 
3305     p = findnode (rcs->versions, tag);
3306     if (p == NULL)
3307 	return (0);
3308 
3309     version = (RCSVers *) p->data;
3310     return (version->dead);
3311 }
3312 
3313 /* Return the RCS keyword expansion mode.  For example "b" for binary.
3314    Returns a pointer into storage which is allocated and freed along with
3315    the rest of the RCS information; the caller should not modify this
3316    storage.  Returns NULL if the RCS file does not specify a keyword
3317    expansion mode; for all other errors, die with a fatal error.  */
3318 char *
RCS_getexpand(rcs)3319 RCS_getexpand (rcs)
3320     RCSNode *rcs;
3321 {
3322     /* Since RCS_parsercsfile_i now reads expand, don't need to worry
3323        about RCS_reparsercsfile.  */
3324     assert (rcs != NULL);
3325     return rcs->expand;
3326 }
3327 
3328 /* Set keyword expansion mode to EXPAND.  For example "b" for binary.  */
3329 void
RCS_setexpand(rcs,expand)3330 RCS_setexpand (rcs, expand)
3331     RCSNode *rcs;
3332     char *expand;
3333 {
3334     /* Since RCS_parsercsfile_i now reads expand, don't need to worry
3335        about RCS_reparsercsfile.  */
3336     assert (rcs != NULL);
3337     if (rcs->expand != NULL)
3338 	free (rcs->expand);
3339     rcs->expand = xstrdup (expand);
3340 }
3341 
3342 /* RCS keywords, and a matching enum.  */
3343 struct rcs_keyword
3344 {
3345     const char *string;
3346     size_t len;
3347 };
3348 #define KEYWORD_INIT(s) (s), sizeof (s) - 1
3349 static struct rcs_keyword keywords[] =
3350 {
3351     { KEYWORD_INIT ("Author") },
3352     { KEYWORD_INIT ("Date") },
3353     { KEYWORD_INIT ("Header") },
3354     { KEYWORD_INIT ("Id") },
3355     { KEYWORD_INIT ("Locker") },
3356     { KEYWORD_INIT ("Log") },
3357     { KEYWORD_INIT ("Name") },
3358     { KEYWORD_INIT ("RCSfile") },
3359     { KEYWORD_INIT ("Revision") },
3360     { KEYWORD_INIT ("Source") },
3361     { KEYWORD_INIT ("State") },
3362     { KEYWORD_INIT ("Mdocdate") },
3363     { NULL, 0 },
3364     { NULL, 0 }
3365 };
3366 enum keyword
3367 {
3368     KEYWORD_AUTHOR = 0,
3369     KEYWORD_DATE,
3370     KEYWORD_HEADER,
3371     KEYWORD_ID,
3372     KEYWORD_LOCKER,
3373     KEYWORD_LOG,
3374     KEYWORD_NAME,
3375     KEYWORD_RCSFILE,
3376     KEYWORD_REVISION,
3377     KEYWORD_SOURCE,
3378     KEYWORD_STATE,
3379     KEYWORD_MDOCDATE,
3380     KEYWORD_LOCALID
3381 };
3382 
3383 /* Convert an RCS date string into a readable string.  This is like
3384    the RCS date2str function.  */
3385 
3386 static char *
printable_date(rcs_date)3387 printable_date (rcs_date)
3388      const char *rcs_date;
3389 {
3390     int year, mon, mday, hour, min, sec;
3391     char buf[100];
3392 
3393     (void) sscanf (rcs_date, SDATEFORM, &year, &mon, &mday, &hour, &min,
3394 		   &sec);
3395     if (year < 1900)
3396 	year += 1900;
3397     sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
3398 	     hour, min, sec);
3399     return xstrdup (buf);
3400 }
3401 
3402 static char *
mdoc_date(rcs_date)3403 mdoc_date (rcs_date)
3404      const char *rcs_date;
3405 {
3406     int year, mon, mday, hour, min, sec;
3407     char buf[100];
3408     char *months[] = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
3409     (void) sscanf (rcs_date, SDATEFORM, &year, &mon, &mday, &hour, &min,
3410 		   &sec);
3411     if (mon < 1 || mon > 12)
3412 	errx(1, "mdoc_date: month index out of bounds");
3413 
3414     if (year < 1900)
3415 	year += 1900;
3416     sprintf (buf, "%s %d %04d", months[mon - 1], mday, year);
3417     return xstrdup (buf);
3418 }
3419 
3420 /* Escape the characters in a string so that it can be included in an
3421    RCS value.  */
3422 
3423 static char *
escape_keyword_value(value,free_value)3424 escape_keyword_value (value, free_value)
3425      const char *value;
3426      int *free_value;
3427 {
3428     char *ret, *t;
3429     const char *s;
3430 
3431     for (s = value; *s != '\0'; s++)
3432     {
3433 	char c;
3434 
3435 	c = *s;
3436 	if (c == '\t'
3437 	    || c == '\n'
3438 	    || c == '\\'
3439 	    || c == ' '
3440 	    || c == '$')
3441 	{
3442 	    break;
3443 	}
3444     }
3445 
3446     if (*s == '\0')
3447     {
3448 	*free_value = 0;
3449 	return (char *) value;
3450     }
3451 
3452     ret = xmalloc (strlen (value) * 4 + 1);
3453     *free_value = 1;
3454 
3455     for (s = value, t = ret; *s != '\0'; s++, t++)
3456     {
3457 	switch (*s)
3458 	{
3459 	default:
3460 	    *t = *s;
3461 	    break;
3462 	case '\t':
3463 	    *t++ = '\\';
3464 	    *t = 't';
3465 	    break;
3466 	case '\n':
3467 	    *t++ = '\\';
3468 	    *t = 'n';
3469 	    break;
3470 	case '\\':
3471 	    *t++ = '\\';
3472 	    *t = '\\';
3473 	    break;
3474 	case ' ':
3475 	    *t++ = '\\';
3476 	    *t++ = '0';
3477 	    *t++ = '4';
3478 	    *t = '0';
3479 	    break;
3480 	case '$':
3481 	    *t++ = '\\';
3482 	    *t++ = '0';
3483 	    *t++ = '4';
3484 	    *t = '4';
3485 	    break;
3486 	}
3487     }
3488 
3489     *t = '\0';
3490 
3491     return ret;
3492 }
3493 
3494 /* Expand RCS keywords in the memory buffer BUF of length LEN.  This
3495    applies to file RCS and version VERS.  If NAME is not NULL, and is
3496    not a numeric revision, then it is the symbolic tag used for the
3497    checkout.  EXPAND indicates how to expand the keywords.  This
3498    function sets *RETBUF and *RETLEN to the new buffer and length.
3499    This function may modify the buffer BUF.  If BUF != *RETBUF, then
3500    RETBUF is a newly allocated buffer.  */
3501 
3502 static void
expand_keywords(rcs,ver,name,log,loglen,expand,buf,len,retbuf,retlen)3503 expand_keywords (rcs, ver, name, log, loglen, expand, buf, len, retbuf, retlen)
3504      RCSNode *rcs;
3505      RCSVers *ver;
3506      const char *name;
3507      const char *log;
3508      size_t loglen;
3509      enum kflag expand;
3510      char *buf;
3511      size_t len;
3512      char **retbuf;
3513      size_t *retlen;
3514 {
3515     struct expand_buffer
3516     {
3517 	struct expand_buffer *next;
3518 	char *data;
3519 	size_t len;
3520 	int free_data;
3521     } *ebufs = NULL;
3522     struct expand_buffer *ebuf_last = NULL;
3523     size_t ebuf_len = 0;
3524     char *locker;
3525     char *srch, *srch_next;
3526     size_t srch_len;
3527 
3528     if (expand == KFLAG_O || expand == KFLAG_B)
3529     {
3530 	*retbuf = buf;
3531 	*retlen = len;
3532 	return;
3533     }
3534 
3535     if (RCS_citag != NULL && *RCS_citag && *RCS_citag != '-'
3536 	&& keywords[KEYWORD_LOCALID].string == NULL) {
3537 	keywords[KEYWORD_LOCALID].string = RCS_citag;
3538 	keywords[KEYWORD_LOCALID].len = strlen(RCS_citag);
3539     }
3540 
3541     /* If we are using -kkvl, dig out the locker information if any.  */
3542     locker = NULL;
3543     if (expand == KFLAG_KVL)
3544     {
3545 	Node *lock;
3546 	lock = findnode (RCS_getlocks(rcs), ver->version);
3547 	if (lock != NULL)
3548 	    locker = xstrdup (lock->data);
3549     }
3550 
3551     /* RCS keywords look like $STRING$ or $STRING: VALUE$.  */
3552     srch = buf;
3553     srch_len = len;
3554     while ((srch_next = memchr (srch, '$', srch_len)) != NULL)
3555     {
3556 	char *s, *send;
3557 	size_t slen;
3558 	const struct rcs_keyword *keyword;
3559 	enum keyword kw;
3560 	char *value;
3561 	int free_value;
3562 	char *sub;
3563 	size_t sublen;
3564 
3565 	srch_len -= (srch_next + 1) - srch;
3566 	srch = srch_next + 1;
3567 
3568 	/* Look for the first non alphanumeric character after the '$'.  */
3569 	send = srch + srch_len;
3570 	if (! isalpha((unsigned char) *srch))
3571 	    continue;	/* first character of a tag must be a letter */
3572 	for (s = srch+1; s < send; s++)
3573 	    if (! isalnum ((unsigned char) *s))
3574 		break;
3575 
3576 	/* If the first non alphanumeric character is not '$' or ':',
3577            then this is not an RCS keyword.  */
3578 	if (s == send || (*s != '$' && *s != ':'))
3579 	    continue;
3580 
3581 	/* See if this is one of the keywords.  */
3582 	slen = s - srch;
3583 	for (keyword = keywords; keyword->string != NULL; keyword++)
3584 	{
3585 	    if (keyword->len == slen
3586 		&& strncmp (keyword->string, srch, slen) == 0)
3587 	    {
3588 		break;
3589 	    }
3590 	}
3591 	if (keyword->string == NULL)
3592 	    continue;
3593 
3594 	kw = (enum keyword) (keyword - keywords);
3595 
3596 	/* If the keyword ends with a ':', then the old value consists
3597            of the characters up to the next '$'.  If there is no '$'
3598            before the end of the line, though, then this wasn't an RCS
3599            keyword after all.  */
3600 	if (*s == ':')
3601 	{
3602 	    for (; s < send; s++)
3603 		if (*s == '$' || *s == '\n')
3604 		    break;
3605 	    if (s == send || *s != '$')
3606 		continue;
3607 	}
3608 
3609 	/* At this point we must replace the string from SRCH to S
3610            with the expansion of the keyword KW.  */
3611 
3612 	/* Get the value to use.  */
3613 	free_value = 0;
3614 	if (expand == KFLAG_K)
3615 	    value = NULL;
3616 	else
3617 	{
3618 	    switch (kw)
3619 	    {
3620 	    default:
3621 		abort ();
3622 
3623 	    case KEYWORD_AUTHOR:
3624 		value = ver->author;
3625 		break;
3626 
3627 	    case KEYWORD_DATE:
3628 		value = printable_date (ver->date);
3629 		free_value = 1;
3630 		break;
3631 
3632 	    case KEYWORD_MDOCDATE:
3633 		if (disable_mdocdate)
3634 			continue;
3635 		value = mdoc_date (ver->date);
3636 		free_value = 1;
3637 		break;
3638 
3639 	    case KEYWORD_HEADER:
3640 	    case KEYWORD_ID:
3641 	    case KEYWORD_LOCALID:
3642 		{
3643 		    char *path;
3644 		    int free_path;
3645 		    char *date;
3646 
3647 		    if (kw == KEYWORD_HEADER)
3648 			path = rcs->path;
3649 		    else
3650 			path = last_component (rcs->path);
3651 		    path = escape_keyword_value (path, &free_path);
3652 		    date = printable_date (ver->date);
3653 		    value = xmalloc (strlen (path)
3654 				     + strlen (ver->version)
3655 				     + strlen (date)
3656 				     + strlen (ver->author)
3657 				     + strlen (ver->state)
3658 				     + (locker == NULL ? 0 : strlen (locker))
3659 				     + 20);
3660 
3661 		    sprintf (value, "%s %s %s %s %s%s%s",
3662 			     path, ver->version, date, ver->author,
3663 			     ver->state,
3664 			     locker != NULL ? " " : "",
3665 			     locker != NULL ? locker : "");
3666 		    if (free_path)
3667 			free (path);
3668 		    free (date);
3669 		    free_value = 1;
3670 		}
3671 		break;
3672 
3673 	    case KEYWORD_LOCKER:
3674 		value = locker;
3675 		break;
3676 
3677 	    case KEYWORD_LOG:
3678 	    case KEYWORD_RCSFILE:
3679 		value = escape_keyword_value (last_component (rcs->path),
3680 					      &free_value);
3681 		break;
3682 
3683 	    case KEYWORD_NAME:
3684 		if (name != NULL && ! isdigit ((unsigned char) *name))
3685 		    value = (char *) name;
3686 		else
3687 		    value = NULL;
3688 		break;
3689 
3690 	    case KEYWORD_REVISION:
3691 		value = ver->version;
3692 		break;
3693 
3694 	    case KEYWORD_SOURCE:
3695 		value = escape_keyword_value (rcs->path, &free_value);
3696 		break;
3697 
3698 	    case KEYWORD_STATE:
3699 		value = ver->state;
3700 		break;
3701 	    }
3702 	}
3703 
3704 	sub = xmalloc (keyword->len
3705 		       + (value == NULL ? 0 : strlen (value))
3706 		       + 10);
3707 	if (expand == KFLAG_V)
3708 	{
3709 	    /* Decrement SRCH and increment S to remove the $
3710                characters.  */
3711 	    --srch;
3712 	    ++srch_len;
3713 	    ++s;
3714 	    sublen = 0;
3715 	}
3716 	else
3717 	{
3718 	    strcpy (sub, keyword->string);
3719 	    sublen = strlen (keyword->string);
3720 	    if (expand != KFLAG_K)
3721 	    {
3722 		sub[sublen] = ':';
3723 		sub[sublen + 1] = ' ';
3724 		sublen += 2;
3725 	    }
3726 	}
3727 	if (value != NULL)
3728 	{
3729 	    strcpy (sub + sublen, value);
3730 	    sublen += strlen (value);
3731 	}
3732 	if (expand != KFLAG_V && expand != KFLAG_K)
3733 	{
3734 	    sub[sublen] = ' ';
3735 	    ++sublen;
3736 	    sub[sublen] = '\0';
3737 	}
3738 
3739 	if (free_value)
3740 	    free (value);
3741 
3742 	/* The Log keyword requires special handling.  This behaviour
3743            is taken from RCS 5.7.  The special log message is what RCS
3744            uses for ci -k.  */
3745 	if (kw == KEYWORD_LOG
3746 	    && (sizeof "checked in with -k by " <= loglen
3747 		|| log == NULL
3748 		|| strncmp (log, "checked in with -k by ",
3749 			    sizeof "checked in with -k by " - 1) != 0))
3750 	{
3751 	    char *start;
3752 	    char *leader;
3753 	    size_t leader_len, leader_sp_len;
3754 	    const char *logend;
3755 	    const char *snl;
3756 	    int cnl;
3757 	    char *date;
3758 	    const char *sl;
3759 
3760 	    /* We are going to insert the trailing $ ourselves, before
3761                the log message, so we must remove it from S, if we
3762                haven't done so already.  */
3763 	    if (expand != KFLAG_V)
3764 		++s;
3765 
3766 	    /* CVS never has empty log messages, but old RCS files might.  */
3767 	    if (log == NULL)
3768 		log = "";
3769 
3770 	    /* Find the start of the line.  */
3771 	    start = srch;
3772 	    while (start > buf && start[-1] != '\n')
3773 		--start;
3774 
3775 	    /* Copy the start of the line to use as a comment leader.  */
3776 	    leader_len = srch - start;
3777 	    if (expand != KFLAG_V)
3778 		--leader_len;
3779 	    leader = xmalloc (leader_len);
3780 	    memcpy (leader, start, leader_len);
3781 	    leader_sp_len = leader_len;
3782 	    while (leader_sp_len > 0 && leader[leader_sp_len - 1] == ' ')
3783 		--leader_sp_len;
3784 
3785 	    /* RCS does some checking for an old style of Log here,
3786 	       but we don't bother.  RCS issues a warning if it
3787 	       changes anything.  */
3788 
3789 	    /* Count the number of newlines in the log message so that
3790 	       we know how many copies of the leader we will need.  */
3791 	    cnl = 0;
3792 	    logend = log + loglen;
3793 	    for (snl = log; snl < logend; snl++)
3794 		if (*snl == '\n')
3795 		    ++cnl;
3796 
3797 	    date = printable_date (ver->date);
3798 	    sub = xrealloc (sub,
3799 			    (sublen
3800 			     + sizeof "Revision"
3801 			     + strlen (ver->version)
3802 			     + strlen (date)
3803 			     + strlen (ver->author)
3804 			     + loglen
3805 			     + (cnl + 2) * leader_len
3806 			     + 20));
3807 	    if (expand != KFLAG_V)
3808 	    {
3809 		sub[sublen] = '$';
3810 		++sublen;
3811 	    }
3812 	    sub[sublen] = '\n';
3813 	    ++sublen;
3814 	    memcpy (sub + sublen, leader, leader_len);
3815 	    sublen += leader_len;
3816 	    sprintf (sub + sublen, "Revision %s  %s  %s\n",
3817 		     ver->version, date, ver->author);
3818 	    sublen += strlen (sub + sublen);
3819 	    free (date);
3820 
3821 	    sl = log;
3822 	    while (sl < logend)
3823 	    {
3824 		if (*sl == '\n')
3825 		{
3826 		    memcpy (sub + sublen, leader, leader_sp_len);
3827 		    sublen += leader_sp_len;
3828 		    sub[sublen] = '\n';
3829 		    ++sublen;
3830 		    ++sl;
3831 		}
3832 		else
3833 		{
3834 		    const char *slnl;
3835 
3836 		    memcpy (sub + sublen, leader, leader_len);
3837 		    sublen += leader_len;
3838 		    for (slnl = sl; slnl < logend && *slnl != '\n'; ++slnl)
3839 			;
3840 		    if (slnl < logend)
3841 			++slnl;
3842 		    memcpy (sub + sublen, sl, slnl - sl);
3843 		    sublen += slnl - sl;
3844 		    sl = slnl;
3845 		}
3846 	    }
3847 
3848 	    memcpy (sub + sublen, leader, leader_sp_len);
3849 	    sublen += leader_sp_len;
3850 
3851 	    free (leader);
3852 	}
3853 
3854 	/* Now SUB contains a string which is to replace the string
3855 	   from SRCH to S.  SUBLEN is the length of SUB.  */
3856 
3857 	if (srch + sublen == s)
3858 	{
3859 	    memcpy (srch, sub, sublen);
3860 	    free (sub);
3861 	}
3862 	else
3863 	{
3864 	    struct expand_buffer *ebuf;
3865 
3866 	    /* We need to change the size of the buffer.  We build a
3867                list of expand_buffer structures.  Each expand_buffer
3868                structure represents a portion of the final output.  We
3869                concatenate them back into a single buffer when we are
3870                done.  This minimizes the number of potentially large
3871                buffer copies we must do.  */
3872 
3873 	    if (ebufs == NULL)
3874 	    {
3875 		ebufs = (struct expand_buffer *) xmalloc (sizeof *ebuf);
3876 		ebufs->next = NULL;
3877 		ebufs->data = buf;
3878 		ebufs->free_data = 0;
3879 		ebuf_len = srch - buf;
3880 		ebufs->len = ebuf_len;
3881 		ebuf_last = ebufs;
3882 	    }
3883 	    else
3884 	    {
3885 		assert (srch >= ebuf_last->data);
3886 		assert (srch <= ebuf_last->data + ebuf_last->len);
3887 		ebuf_len -= ebuf_last->len - (srch - ebuf_last->data);
3888 		ebuf_last->len = srch - ebuf_last->data;
3889 	    }
3890 
3891 	    ebuf = (struct expand_buffer *) xmalloc (sizeof *ebuf);
3892 	    ebuf->data = sub;
3893 	    ebuf->len = sublen;
3894 	    ebuf->free_data = 1;
3895 	    ebuf->next = NULL;
3896 	    ebuf_last->next = ebuf;
3897 	    ebuf_last = ebuf;
3898 	    ebuf_len += sublen;
3899 
3900 	    ebuf = (struct expand_buffer *) xmalloc (sizeof *ebuf);
3901 	    ebuf->data = s;
3902 	    ebuf->len = srch_len - (s - srch);
3903 	    ebuf->free_data = 0;
3904 	    ebuf->next = NULL;
3905 	    ebuf_last->next = ebuf;
3906 	    ebuf_last = ebuf;
3907 	    ebuf_len += srch_len - (s - srch);
3908 	}
3909 
3910 	srch_len -= (s - srch);
3911 	srch = s;
3912     }
3913 
3914     if (locker != NULL)
3915 	free (locker);
3916 
3917     if (ebufs == NULL)
3918     {
3919 	*retbuf = buf;
3920 	*retlen = len;
3921     }
3922     else
3923     {
3924 	char *ret;
3925 
3926 	ret = xmalloc (ebuf_len);
3927 	*retbuf = ret;
3928 	*retlen = ebuf_len;
3929 	while (ebufs != NULL)
3930 	{
3931 	    struct expand_buffer *next;
3932 
3933 	    memcpy (ret, ebufs->data, ebufs->len);
3934 	    ret += ebufs->len;
3935 	    if (ebufs->free_data)
3936 		free (ebufs->data);
3937 	    next = ebufs->next;
3938 	    free (ebufs);
3939 	    ebufs = next;
3940 	}
3941     }
3942 }
3943 
3944 /* Check out a revision from an RCS file.
3945 
3946    If PFN is not NULL, then ignore WORKFILE and SOUT.  Call PFN zero
3947    or more times with the contents of the file.  CALLERDAT is passed,
3948    uninterpreted, to PFN.  (The current code will always call PFN
3949    exactly once for a non empty file; however, the current code
3950    assumes that it can hold the entire file contents in memory, which
3951    is not a good assumption, and might change in the future).
3952 
3953    Otherwise, if WORKFILE is not NULL, check out the revision to
3954    WORKFILE.  However, if WORKFILE is not NULL, and noexec is set,
3955    then don't do anything.
3956 
3957    Otherwise, if WORKFILE is NULL, check out the revision to SOUT.  If
3958    SOUT is RUN_TTY, then write the contents of the revision to
3959    standard output.  When using SOUT, the output is generally a
3960    temporary file; don't bother to get the file modes correct.
3961 
3962    REV is the numeric revision to check out.  It may be NULL, which
3963    means to check out the head of the default branch.
3964 
3965    If NAMETAG is not NULL, and is not a numeric revision, then it is
3966    the tag that should be used when expanding the RCS Name keyword.
3967 
3968    OPTIONS is a string such as "-kb" or "-kv" for keyword expansion
3969    options.  It may be NULL to use the default expansion mode of the
3970    file, typically "-kkv".
3971 
3972    On an error which prevented checking out the file, either print a
3973    nonfatal error and return 1, or give a fatal error.  On success,
3974    return 0.  */
3975 
3976 /* This function mimics the behavior of `rcs co' almost exactly.  The
3977    chief difference is in its support for preserving file ownership,
3978    permissions, and special files across checkin and checkout -- see
3979    comments in RCS_checkin for some issues about this. -twp */
3980 
3981 int
RCS_checkout(rcs,workfile,rev,nametag,options,sout,pfn,callerdat)3982 RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
3983      RCSNode *rcs;
3984      char *workfile;
3985      char *rev;
3986      char *nametag;
3987      char *options;
3988      char *sout;
3989      RCSCHECKOUTPROC pfn;
3990      void *callerdat;
3991 {
3992     int free_rev = 0;
3993     enum kflag expand;
3994     FILE *fp, *ofp;
3995     struct stat sb;
3996     struct rcsbuffer rcsbuf;
3997     char *key;
3998     char *value;
3999     size_t len;
4000     int free_value = 0;
4001     char *log = NULL;
4002     size_t loglen = 0;
4003     Node *vp = NULL;
4004 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4005     uid_t rcs_owner = (uid_t) -1;
4006     gid_t rcs_group = (gid_t) -1;
4007     mode_t rcs_mode;
4008     int change_rcs_owner_or_group = 0;
4009     int change_rcs_mode = 0;
4010     int special_file = 0;
4011     unsigned long devnum_long;
4012     dev_t devnum = 0;
4013 #endif
4014 
4015     if (trace)
4016     {
4017 	(void) fprintf (stderr, "%s-> checkout (%s, %s, %s, %s)\n",
4018 #ifdef SERVER_SUPPORT
4019 			server_active ? "S" : " ",
4020 #else
4021 			"",
4022 #endif
4023 			rcs->path,
4024 			rev != NULL ? rev : "",
4025 			options != NULL ? options : "",
4026 			(pfn != NULL ? "(function)"
4027 			 : (workfile != NULL
4028 			    ? workfile
4029 			    : (sout != RUN_TTY ? sout : "(stdout)"))));
4030     }
4031 
4032     assert (rev == NULL || isdigit ((unsigned char) *rev));
4033 
4034     if (noexec && workfile != NULL)
4035 	return 0;
4036 
4037     assert (sout == RUN_TTY || workfile == NULL);
4038     assert (pfn == NULL || (sout == RUN_TTY && workfile == NULL));
4039 
4040     /* Some callers, such as Checkin or remove_file, will pass us a
4041        branch.  */
4042     if (rev != NULL && (numdots (rev) & 1) == 0)
4043     {
4044 	rev = RCS_getbranch (rcs, rev, 1);
4045 	if (rev == NULL)
4046 	    error (1, 0, "internal error: bad branch tag in checkout");
4047 	free_rev = 1;
4048     }
4049 
4050     if (rev == NULL || STREQ (rev, rcs->head))
4051     {
4052 	int gothead;
4053 
4054 	/* We want the head revision.  Try to read it directly.  */
4055 
4056 	if (rcs->flags & PARTIAL)
4057 	    RCS_reparsercsfile (rcs, &fp, &rcsbuf);
4058 	else
4059 	    rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf);
4060 
4061 	gothead = 0;
4062 	if (! rcsbuf_getrevnum (&rcsbuf, &key))
4063 	    error (1, 0, "unexpected EOF reading %s", rcs->path);
4064 	while (rcsbuf_getkey (&rcsbuf, &key, &value))
4065 	{
4066 	    if (STREQ (key, "log"))
4067 		log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen);
4068 	    else if (STREQ (key, "text"))
4069 	    {
4070 		gothead = 1;
4071 		break;
4072 	    }
4073 	}
4074 
4075 	if (! gothead)
4076 	{
4077 	    error (0, 0, "internal error: cannot find head text");
4078 	    if (free_rev)
4079 		free (rev);
4080 	    return 1;
4081 	}
4082 
4083 	rcsbuf_valpolish (&rcsbuf, value, 0, &len);
4084 
4085 	if (fstat (fileno (fp), &sb) < 0)
4086 	    error (1, errno, "cannot fstat %s", rcs->path);
4087 
4088 	rcsbuf_cache (rcs, &rcsbuf);
4089     }
4090     else
4091     {
4092 	struct rcsbuffer *rcsbufp;
4093 
4094 	/* It isn't the head revision of the trunk.  We'll need to
4095 	   walk through the deltas.  */
4096 
4097 	fp = NULL;
4098 	if (rcs->flags & PARTIAL)
4099 	    RCS_reparsercsfile (rcs, &fp, &rcsbuf);
4100 
4101 	if (fp == NULL)
4102 	{
4103 	    /* If RCS_deltas didn't close the file, we could use fstat
4104 	       here too.  Probably should change it thusly....  */
4105 	    if (stat (rcs->path, &sb) < 0)
4106 		error (1, errno, "cannot stat %s", rcs->path);
4107 	    rcsbufp = NULL;
4108 	}
4109 	else
4110 	{
4111 	    if (fstat (fileno (fp), &sb) < 0)
4112 		error (1, errno, "cannot fstat %s", rcs->path);
4113 	    rcsbufp = &rcsbuf;
4114 	}
4115 
4116 	RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len,
4117 		    &log, &loglen);
4118 	free_value = 1;
4119     }
4120 
4121     /* If OPTIONS is NULL or the empty string, then the old code would
4122        invoke the RCS co program with no -k option, which means that
4123        co would use the string we have stored in rcs->expand.  */
4124     if ((options == NULL || options[0] == '\0') && rcs->expand == NULL)
4125 	expand = KFLAG_KV;
4126     else
4127     {
4128 	const char *ouroptions;
4129 	const char * const *cpp;
4130 
4131 	if (options != NULL && options[0] != '\0')
4132 	{
4133 	    assert (options[0] == '-' && options[1] == 'k');
4134 	    ouroptions = options + 2;
4135 	}
4136 	else
4137 	    ouroptions = rcs->expand;
4138 
4139 	for (cpp = kflags; *cpp != NULL; cpp++)
4140 	    if (STREQ (*cpp, ouroptions))
4141 		break;
4142 
4143 	if (*cpp != NULL)
4144 	    expand = (enum kflag) (cpp - kflags);
4145 	else
4146 	{
4147 	    error (0, 0,
4148 		   "internal error: unsupported substitution string -k%s",
4149 		   ouroptions);
4150 	    expand = KFLAG_KV;
4151 	}
4152     }
4153 
4154 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4155     /* Handle special files and permissions, if that is desired. */
4156     if (preserve_perms)
4157     {
4158 	RCSVers *vers;
4159 	Node *info;
4160 
4161 	vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4162 	if (vp == NULL)
4163 	    error (1, 0, "internal error: no revision information for %s",
4164 		   rev == NULL ? rcs->head : rev);
4165 	vers = (RCSVers *) vp->data;
4166 
4167 	/* First we look for symlinks, which are simplest to handle. */
4168 	info = findnode (vers->other_delta, "symlink");
4169 	if (info != NULL)
4170 	{
4171 	    char *dest;
4172 
4173 	    if (pfn != NULL || (workfile == NULL && sout == RUN_TTY))
4174 		error (1, 0, "symbolic link %s:%s cannot be piped",
4175 		       rcs->path, vers->version);
4176 	    if (workfile == NULL)
4177 		dest = sout;
4178 	    else
4179 		dest = workfile;
4180 
4181 	    /* Remove `dest', just in case.  It's okay to get ENOENT here,
4182 	       since we just want the file not to be there.  (TODO: decide
4183 	       whether it should be considered an error for `dest' to exist
4184 	       at this point.  If so, the unlink call should be removed and
4185 	       `symlink' should signal the error. -twp) */
4186 	    if (CVS_UNLINK (dest) < 0 && !existence_error (errno))
4187 		error (1, errno, "cannot remove %s", dest);
4188 	    if (symlink (info->data, dest) < 0)
4189 		error (1, errno, "cannot create symbolic link from %s to %s",
4190 		       dest, info->data);
4191 	    if (free_value)
4192 		free (value);
4193 	    if (free_rev)
4194 		free (rev);
4195 	    return 0;
4196 	}
4197 
4198 	/* Next, we look at this file's hardlinks field, and see whether
4199 	   it is linked to any other file that has been checked out.
4200 	   If so, we don't do anything else -- just link it to that file.
4201 
4202 	   If we are checking out a file to a pipe or temporary storage,
4203 	   none of this should matter.  Hence the `workfile != NULL'
4204 	   wrapper around the whole thing. -twp */
4205 
4206 	if (workfile != NULL)
4207 	{
4208 	    List *links = vers->hardlinks;
4209 	    if (links != NULL)
4210 	    {
4211 		Node *uptodate_link;
4212 
4213 		/* For each file in the hardlinks field, check to see
4214 		   if it exists, and if so, if it has been checked out
4215 		   this iteration.  When walklist returns, uptodate_link
4216 		   should point to a hardlist node representing a file
4217 		   in `links' which has recently been checked out, or
4218 		   NULL if no file in `links' has yet been checked out. */
4219 
4220 		uptodate_link = NULL;
4221 		(void) walklist (links, find_checkedout_proc, &uptodate_link);
4222 		dellist (&links);
4223 
4224 		/* If we've found a file that `workfile' is supposed to be
4225 		   linked to, and it has been checked out since CVS was
4226 		   invoked, then simply link workfile to that file and return.
4227 
4228 		   If one of these conditions is not met, then
4229 		   workfile is the first one in its hardlink group to
4230 		   be checked out, and we must continue with a full
4231 		   checkout. */
4232 
4233 		if (uptodate_link != NULL)
4234 		{
4235 		    struct hardlink_info *hlinfo =
4236 			(struct hardlink_info *) uptodate_link->data;
4237 
4238 		    if (link (uptodate_link->key, workfile) < 0)
4239 			error (1, errno, "cannot link %s to %s",
4240 			       workfile, uptodate_link->key);
4241 		    hlinfo->checked_out = 1;	/* probably unnecessary */
4242 		    if (free_value)
4243 			free (value);
4244 		    if (free_rev)
4245 			free (rev);
4246 		    return 0;
4247 		}
4248 	    }
4249 	}
4250 
4251 	info = findnode (vers->other_delta, "owner");
4252 	if (info != NULL)
4253 	{
4254 	    change_rcs_owner_or_group = 1;
4255 	    rcs_owner = (uid_t) strtoul (info->data, NULL, 10);
4256 	}
4257 	info = findnode (vers->other_delta, "group");
4258 	if (info != NULL)
4259 	{
4260 	    change_rcs_owner_or_group = 1;
4261 	    rcs_group = (gid_t) strtoul (info->data, NULL, 10);
4262 	}
4263 	info = findnode (vers->other_delta, "permissions");
4264 	if (info != NULL)
4265 	{
4266 	    change_rcs_mode = 1;
4267 	    rcs_mode = (mode_t) strtoul (info->data, NULL, 8);
4268 	}
4269 	info = findnode (vers->other_delta, "special");
4270 	if (info != NULL)
4271 	{
4272 	    /* If the size of `devtype' changes, fix the sscanf call also */
4273 	    char devtype[16+1];
4274 
4275 	    if (sscanf (info->data, "%16s %lu",
4276 			devtype, &devnum_long) < 2)
4277 		error (1, 0, "%s:%s has bad `special' newphrase %s",
4278 		       workfile, vers->version, info->data);
4279 	    devnum = devnum_long;
4280 	    if (STREQ (devtype, "character"))
4281 		special_file = S_IFCHR;
4282 	    else if (STREQ (devtype, "block"))
4283 		special_file = S_IFBLK;
4284 	    else
4285 		error (0, 0, "%s is a special file of unsupported type `%s'",
4286 		       workfile, info->data);
4287 	}
4288     }
4289 #endif
4290 
4291     if (expand != KFLAG_O && expand != KFLAG_B)
4292     {
4293 	char *newvalue;
4294 
4295 	/* Don't fetch the delta node again if we already have it. */
4296 	if (vp == NULL)
4297 	{
4298 	    vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4299 	    if (vp == NULL)
4300 		error (1, 0, "internal error: no revision information for %s",
4301 		       rev == NULL ? rcs->head : rev);
4302 	}
4303 
4304 	expand_keywords (rcs, (RCSVers *) vp->data, nametag, log, loglen,
4305 			 expand, value, len, &newvalue, &len);
4306 
4307 	if (newvalue != value)
4308 	{
4309 	    if (free_value)
4310 		free (value);
4311 	    value = newvalue;
4312 	    free_value = 1;
4313 	}
4314     }
4315 
4316     if (free_rev)
4317 	free (rev);
4318 
4319     if (log != NULL)
4320     {
4321 	free (log);
4322 	log = NULL;
4323     }
4324 
4325     if (pfn != NULL)
4326     {
4327 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4328 	if (special_file)
4329 	    error (1, 0, "special file %s cannot be piped to anything",
4330 		   rcs->path);
4331 #endif
4332 	/* The PFN interface is very simple to implement right now, as
4333            we always have the entire file in memory.  */
4334 	if (len != 0)
4335 	    pfn (callerdat, value, len);
4336     }
4337 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4338     else if (special_file)
4339     {
4340 #ifdef HAVE_MKNOD
4341 	char *dest;
4342 
4343 	/* Can send either to WORKFILE or to SOUT, as long as SOUT is
4344 	   not RUN_TTY. */
4345 	dest = workfile;
4346 	if (dest == NULL)
4347 	{
4348 	    if (sout == RUN_TTY)
4349 		error (1, 0, "special file %s cannot be written to stdout",
4350 		       rcs->path);
4351 	    dest = sout;
4352 	}
4353 
4354 	/* Unlink `dest', just in case.  It's okay if this provokes a
4355 	   ENOENT error. */
4356 	if (CVS_UNLINK (dest) < 0 && existence_error (errno))
4357 	    error (1, errno, "cannot remove %s", dest);
4358 	if (mknod (dest, special_file, devnum) < 0)
4359 	    error (1, errno, "could not create special file %s",
4360 		   dest);
4361 #else
4362 	error (1, 0,
4363 "cannot create %s: unable to create special files on this system",
4364 workfile);
4365 #endif
4366     }
4367 #endif
4368     else
4369     {
4370 	/* Not a special file: write to WORKFILE or SOUT. */
4371 	if (workfile == NULL)
4372 	{
4373 	    if (sout == RUN_TTY)
4374 		ofp = stdout;
4375 	    else
4376 	    {
4377 		/* Symbolic links should be removed before replacement, so that
4378 		   `fopen' doesn't follow the link and open the wrong file. */
4379 		if (islink (sout))
4380 		    if (unlink_file (sout) < 0)
4381 			error (1, errno, "cannot remove %s", sout);
4382 		ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w");
4383 		if (ofp == NULL)
4384 		    error (1, errno, "cannot open %s", sout);
4385 	    }
4386 	}
4387 	else
4388 	{
4389 	    /* Output is supposed to go to WORKFILE, so we should open that
4390 	       file.  Symbolic links should be removed first (see above). */
4391 	    if (islink (workfile))
4392 		if (unlink_file (workfile) < 0)
4393 		    error (1, errno, "cannot remove %s", workfile);
4394 
4395 	    ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
4396 
4397 	    /* If the open failed because the existing workfile was not
4398 	       writable, try to chmod the file and retry the open.  */
4399 	    if (ofp == NULL && errno == EACCES
4400 		&& isfile (workfile) && !iswritable (workfile))
4401 	    {
4402 		xchmod (workfile, 1);
4403 		ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
4404 	    }
4405 
4406 	    if (ofp == NULL)
4407 	    {
4408 		error (0, errno, "cannot open %s", workfile);
4409 		if (free_value)
4410 		    free (value);
4411 		return 1;
4412 	    }
4413 	}
4414 
4415 	if (workfile == NULL && sout == RUN_TTY)
4416 	{
4417 	    if (expand == KFLAG_B)
4418 		cvs_output_binary (value, len);
4419 	    else
4420 	    {
4421 		/* cvs_output requires the caller to check for zero
4422 		   length.  */
4423 		if (len > 0)
4424 		    cvs_output (value, len);
4425 	    }
4426 	}
4427 	else
4428 	{
4429 	    /* NT 4.0 is said to have trouble writing 2099999 bytes
4430 	       (for example) in a single fwrite.  So break it down
4431 	       (there is no need to be writing that much at once
4432 	       anyway; it is possible that LARGEST_FWRITE should be
4433 	       somewhat larger for good performance, but for testing I
4434 	       want to start with a small value until/unless a bigger
4435 	       one proves useful).  */
4436 #define LARGEST_FWRITE 8192
4437 	    size_t nleft = len;
4438 	    size_t nstep = (len < LARGEST_FWRITE ? len : LARGEST_FWRITE);
4439 	    char *p = value;
4440 
4441 	    while (nleft > 0)
4442 	    {
4443 		if (fwrite (p, 1, nstep, ofp) != nstep)
4444 		{
4445 		    error (0, errno, "cannot write %s",
4446 			   (workfile != NULL
4447 			    ? workfile
4448 			    : (sout != RUN_TTY ? sout : "stdout")));
4449 		    if (free_value)
4450 			free (value);
4451 		    return 1;
4452 		}
4453 		p += nstep;
4454 		nleft -= nstep;
4455 		if (nleft < nstep)
4456 		    nstep = nleft;
4457 	    }
4458 	}
4459     }
4460 
4461     if (free_value)
4462 	free (value);
4463 
4464     if (workfile != NULL)
4465     {
4466 	int ret;
4467 
4468 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4469 	if (!special_file && fclose (ofp) < 0)
4470 	{
4471 	    error (0, errno, "cannot close %s", workfile);
4472 	    return 1;
4473 	}
4474 
4475 	if (change_rcs_owner_or_group)
4476 	{
4477 	    if (chown (workfile, rcs_owner, rcs_group) < 0)
4478 		error (0, errno, "could not change owner or group of %s",
4479 		       workfile);
4480 	}
4481 
4482 	ret = chmod (workfile,
4483 		     change_rcs_mode
4484 		     ? rcs_mode
4485 		     : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
4486 #else
4487 	if (fclose (ofp) < 0)
4488 	{
4489 	    error (0, errno, "cannot close %s", workfile);
4490 	    return 1;
4491 	}
4492 
4493 	ret = chmod (workfile,
4494 		     sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
4495 #endif
4496 	if (ret < 0)
4497 	{
4498 	    error (0, errno, "cannot change mode of file %s",
4499 		   workfile);
4500 	}
4501     }
4502     else if (sout != RUN_TTY)
4503     {
4504 	if (
4505 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4506 	    !special_file &&
4507 #endif
4508 	    fclose (ofp) < 0)
4509 	{
4510 	    error (0, errno, "cannot close %s", sout);
4511 	    return 1;
4512 	}
4513     }
4514 
4515 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4516     /* If we are in the business of preserving hardlinks, then
4517        mark this file as having been checked out. */
4518     if (preserve_perms && workfile != NULL)
4519 	update_hardlink_info (workfile);
4520 #endif
4521 
4522     return 0;
4523 }
4524 
4525 static RCSVers *RCS_findlock_or_tip PROTO ((RCSNode *rcs));
4526 
4527 /* Find the delta currently locked by the user.  From the `ci' man page:
4528 
4529 	"If rev is omitted, ci tries to  derive  the  new  revision
4530 	 number  from  the  caller's  last lock.  If the caller has
4531 	 locked the tip revision of a branch, the new  revision  is
4532 	 appended  to  that  branch.   The  new  revision number is
4533 	 obtained by incrementing the tip revision number.  If  the
4534 	 caller  locked a non-tip revision, a new branch is started
4535 	 at that revision by incrementing the highest branch number
4536 	 at  that  revision.   The default initial branch and level
4537 	 numbers are 1.
4538 
4539 	 If rev is omitted and the caller has no lock, but owns the
4540 	 file  and  locking is not set to strict, then the revision
4541 	 is appended to the default branch (normally the trunk; see
4542 	 the -b option of rcs(1))."
4543 
4544    RCS_findlock_or_tip finds the unique revision locked by the caller
4545    and returns its delta node.  If the caller has not locked any
4546    revisions (and is permitted to commit to an unlocked delta, as
4547    described above), return the tip of the default branch. */
4548 
4549 static RCSVers *
RCS_findlock_or_tip(rcs)4550 RCS_findlock_or_tip (rcs)
4551     RCSNode *rcs;
4552 {
4553     char *user = getcaller();
4554     Node *lock, *p;
4555     List *locklist;
4556 
4557     /* Find unique delta locked by caller. This code is very similar
4558        to the code in RCS_unlock -- perhaps it could be abstracted
4559        into a RCS_findlock function. */
4560     locklist = RCS_getlocks (rcs);
4561     lock = NULL;
4562     for (p = locklist->list->next; p != locklist->list; p = p->next)
4563     {
4564 	if (STREQ (p->data, user))
4565 	{
4566 	    if (lock != NULL)
4567 	    {
4568 		error (0, 0, "\
4569 %s: multiple revisions locked by %s; please specify one", rcs->path, user);
4570 		return NULL;
4571 	    }
4572 	    lock = p;
4573 	}
4574     }
4575 
4576     if (lock != NULL)
4577     {
4578 	/* Found an old lock, but check that the revision still exists. */
4579 	p = findnode (rcs->versions, lock->key);
4580 	if (p == NULL)
4581 	{
4582 	    error (0, 0, "%s: can't unlock nonexistent revision %s",
4583 		   rcs->path,
4584 		   lock->key);
4585 	    return NULL;
4586 	}
4587 	return (RCSVers *) p->data;
4588     }
4589 
4590     /* No existing lock.  The RCS rule is that this is an error unless
4591        locking is nonstrict AND the file is owned by the current
4592        user.  Trying to determine the latter is a portability nightmare
4593        in the face of NT, VMS, AFS, and other systems with non-unix-like
4594        ideas of users and owners.  In the case of CVS, we should never get
4595        here (as long as the traditional behavior of making sure to call
4596        RCS_lock persists).  Anyway, we skip the RCS error checks
4597        and just return the default branch or head.  The reasoning is that
4598        those error checks are to make users lock before a checkin, and we do
4599        that in other ways if at all anyway (e.g. rcslock.pl).  */
4600 
4601     p = findnode (rcs->versions, RCS_getbranch (rcs, rcs->branch, 0));
4602     return (RCSVers *) p->data;
4603 }
4604 
4605 /* Revision number string, R, must contain a `.'.
4606    Return a newly-malloc'd copy of the prefix of R up
4607    to but not including the final `.'.  */
4608 
4609 static char *
truncate_revnum(r)4610 truncate_revnum (r)
4611     const char *r;
4612 {
4613     size_t len;
4614     char *new_r;
4615     char *dot = strrchr (r, '.');
4616 
4617     assert (dot);
4618     len = dot - r;
4619     new_r = xmalloc (len + 1);
4620     memcpy (new_r, r, len);
4621     *(new_r + len) = '\0';
4622     return new_r;
4623 }
4624 
4625 /* Revision number string, R, must contain a `.'.
4626    R must be writable.  Replace the rightmost `.' in R with
4627    the NUL byte and return a pointer to that NUL byte.  */
4628 
4629 static char *
truncate_revnum_in_place(r)4630 truncate_revnum_in_place (r)
4631     char *r;
4632 {
4633     char *dot = strrchr (r, '.');
4634     assert (dot);
4635     *dot = '\0';
4636     return dot;
4637 }
4638 
4639 /* Revision number strings, R and S, must each contain a `.'.
4640    R and S must be writable and must have the same number of dots.
4641    Truncate R and S for the comparison, then restored them to their
4642    original state.
4643    Return the result (see compare_revnums) of comparing R and S
4644    ignoring differences in any component after the rightmost `.'.  */
4645 
4646 static int
compare_truncated_revnums(r,s)4647 compare_truncated_revnums (r, s)
4648     char *r;
4649     char *s;
4650 {
4651     char *r_dot = truncate_revnum_in_place (r);
4652     char *s_dot = truncate_revnum_in_place (s);
4653     int cmp;
4654 
4655     assert (numdots (r) == numdots (s));
4656 
4657     cmp = compare_revnums (r, s);
4658 
4659     *r_dot = '.';
4660     *s_dot = '.';
4661 
4662     return cmp;
4663 }
4664 
4665 /* Return a malloc'd copy of the string representing the highest branch
4666    number on BRANCHNODE.  If there are no branches on BRANCHNODE, return NULL.
4667    FIXME: isn't the max rev always the last one?
4668    If so, we don't even need a loop.  */
4669 
4670 static char *max_rev PROTO ((const RCSVers *));
4671 
4672 static char *
max_rev(branchnode)4673 max_rev (branchnode)
4674     const RCSVers *branchnode;
4675 {
4676     Node *head;
4677     Node *bp;
4678     char *max;
4679 
4680     if (branchnode->branches == NULL)
4681     {
4682         return NULL;
4683     }
4684 
4685     max = NULL;
4686     head = branchnode->branches->list;
4687     for (bp = head->next; bp != head; bp = bp->next)
4688     {
4689 	if (max == NULL || compare_truncated_revnums (max, bp->key) < 0)
4690 	{
4691 	    max = bp->key;
4692 	}
4693     }
4694     assert (max);
4695 
4696     return truncate_revnum (max);
4697 }
4698 
4699 /* Create BRANCH in RCS's delta tree.  BRANCH may be either a branch
4700    number or a revision number.  In the former case, create the branch
4701    with the specified number; in the latter case, create a new branch
4702    rooted at node BRANCH with a higher branch number than any others.
4703    Return the number of the tip node on the new branch. */
4704 
4705 static char *
RCS_addbranch(rcs,branch)4706 RCS_addbranch (rcs, branch)
4707     RCSNode *rcs;
4708     const char *branch;
4709 {
4710     char *branchpoint, *newrevnum;
4711     Node *nodep, *bp;
4712     Node *marker;
4713     RCSVers *branchnode;
4714 
4715     /* Append to end by default.  */
4716     marker = NULL;
4717 
4718     branchpoint = xstrdup (branch);
4719     if ((numdots (branchpoint) & 1) == 0)
4720     {
4721 	truncate_revnum_in_place (branchpoint);
4722     }
4723 
4724     /* Find the branch rooted at BRANCHPOINT. */
4725     nodep = findnode (rcs->versions, branchpoint);
4726     if (nodep == NULL)
4727     {
4728 	error (0, 0, "%s: can't find branch point %s", rcs->path, branchpoint);
4729 	free (branchpoint);
4730 	return NULL;
4731     }
4732     free (branchpoint);
4733     branchnode = (RCSVers *) nodep->data;
4734 
4735     /* If BRANCH was a full branch number, make sure it is higher than MAX. */
4736     if ((numdots (branch) & 1) == 1)
4737     {
4738 	if (branchnode->branches == NULL)
4739 	{
4740 	    /* We have to create the first branch on this node, which means
4741 	       appending ".2" to the revision number. */
4742 	    newrevnum = (char *) xmalloc (strlen (branch) + 3);
4743 	    strcpy (newrevnum, branch);
4744 	    strcat (newrevnum, ".2");
4745 	}
4746 	else
4747 	{
4748 	    char *max = max_rev (branchnode);
4749 	    assert (max);
4750 	    newrevnum = increment_revnum (max);
4751 	    free (max);
4752 	}
4753     }
4754     else
4755     {
4756 	newrevnum = xstrdup (branch);
4757 
4758 	if (branchnode->branches != NULL)
4759 	{
4760 	    Node *head;
4761 	    Node *bp;
4762 
4763 	    /* Find the position of this new branch in the sorted list
4764 	       of branches.  */
4765 	    head = branchnode->branches->list;
4766 	    for (bp = head->next; bp != head; bp = bp->next)
4767 	    {
4768 		char *dot;
4769 		int found_pos;
4770 
4771 		/* The existing list must be sorted on increasing revnum.  */
4772 		assert (bp->next == head
4773 			|| compare_truncated_revnums (bp->key,
4774 						      bp->next->key) < 0);
4775 		dot = truncate_revnum_in_place (bp->key);
4776 		found_pos = (compare_revnums (branch, bp->key) < 0);
4777 		*dot = '.';
4778 
4779 		if (found_pos)
4780 		{
4781 		    break;
4782 		}
4783 	    }
4784 	    marker = bp;
4785 	}
4786     }
4787 
4788     newrevnum = (char *) xrealloc (newrevnum, strlen (newrevnum) + 3);
4789     strcat (newrevnum, ".1");
4790 
4791     /* Add this new revision number to BRANCHPOINT's branches list. */
4792     if (branchnode->branches == NULL)
4793 	branchnode->branches = getlist();
4794     bp = getnode();
4795     bp->key = xstrdup (newrevnum);
4796 
4797     /* Append to the end of the list by default, that is, just before
4798        the header node, `list'.  */
4799     if (marker == NULL)
4800 	marker = branchnode->branches->list;
4801 
4802     {
4803 	int fail;
4804 	fail = insert_before (branchnode->branches, marker, bp);
4805 	assert (!fail);
4806     }
4807 
4808     return newrevnum;
4809 }
4810 
4811 /* Check in to RCSFILE with revision REV (which must be greater than
4812    the largest revision) and message MESSAGE (which is checked for
4813    legality).  If FLAGS & RCS_FLAGS_DEAD, check in a dead revision.
4814    If FLAGS & RCS_FLAGS_QUIET, tell ci to be quiet.  If FLAGS &
4815    RCS_FLAGS_MODTIME, use the working file's modification time for the
4816    checkin time.  WORKFILE is the working file to check in from, or
4817    NULL to use the usual RCS rules for deriving it from the RCSFILE.
4818    If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file;
4819    unlinking the working file is standard RCS behavior, but is rarely
4820    appropriate for CVS.
4821 
4822    This function should almost exactly mimic the behavior of `rcs ci'.  The
4823    principal point of difference is the support here for preserving file
4824    ownership and permissions in the delta nodes.  This is not a clean
4825    solution -- precisely because it diverges from RCS's behavior -- but
4826    it doesn't seem feasible to do this anywhere else in the code. [-twp]
4827 
4828    Return value is -1 for error (and errno is set to indicate the
4829    error), positive for error (and an error message has been printed),
4830    or zero for success.  */
4831 
4832 int
RCS_checkin(rcs,workfile,message,rev,flags)4833 RCS_checkin (rcs, workfile, message, rev, flags)
4834     RCSNode *rcs;
4835     char *workfile;
4836     char *message;
4837     char *rev;
4838     int flags;
4839 {
4840     RCSVers *delta, *commitpt;
4841     Deltatext *dtext;
4842     Node *nodep;
4843     char *tmpfile, *changefile, *chtext;
4844     char *diffopts;
4845     size_t bufsize;
4846     int buflen, chtextlen;
4847     int status, checkin_quiet, allocated_workfile;
4848     struct tm *ftm;
4849     time_t modtime;
4850     int adding_branch = 0;
4851 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4852     struct stat sb;
4853 #endif
4854     Node *np;
4855 
4856     commitpt = NULL;
4857 
4858     if (rcs->flags & PARTIAL)
4859 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
4860 
4861     /* Get basename of working file.  Is there a library function to
4862        do this?  I couldn't find one. -twp */
4863     allocated_workfile = 0;
4864     if (workfile == NULL)
4865     {
4866 	char *p;
4867 	int extlen = strlen (RCSEXT);
4868 	workfile = xstrdup (last_component (rcs->path));
4869 	p = workfile + (strlen (workfile) - extlen);
4870 	assert (strncmp (p, RCSEXT, extlen) == 0);
4871 	*p = '\0';
4872 	allocated_workfile = 1;
4873     }
4874 
4875     /* If the filename is a symbolic link, follow it and replace it
4876        with the destination of the link.  We need to do this before
4877        calling rcs_internal_lockfile, or else we won't put the lock in
4878        the right place. */
4879     resolve_symlink (&(rcs->path));
4880 
4881     checkin_quiet = flags & RCS_FLAGS_QUIET;
4882     if (!checkin_quiet)
4883     {
4884 	cvs_output (rcs->path, 0);
4885 	cvs_output ("  <--  ", 7);
4886 	cvs_output (workfile, 0);
4887 	cvs_output ("\n", 1);
4888     }
4889 
4890     /* Create new delta node. */
4891     delta = (RCSVers *) xmalloc (sizeof (RCSVers));
4892     memset (delta, 0, sizeof (RCSVers));
4893     delta->author = xstrdup (getcaller ());
4894     if (flags & RCS_FLAGS_MODTIME)
4895     {
4896 	struct stat ws;
4897 	if (stat (workfile, &ws) < 0)
4898 	{
4899 	    error (1, errno, "cannot stat %s", workfile);
4900 	}
4901 	modtime = ws.st_mtime;
4902     }
4903     else
4904 	(void) time (&modtime);
4905     ftm = gmtime (&modtime);
4906     delta->date = (char *) xmalloc (MAXDATELEN);
4907     (void) sprintf (delta->date, DATEFORM,
4908 		    ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
4909 		    ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
4910 		    ftm->tm_min, ftm->tm_sec);
4911     if (flags & RCS_FLAGS_DEAD)
4912     {
4913 	delta->state = xstrdup (RCSDEAD);
4914 	delta->dead = 1;
4915     }
4916     else
4917 	delta->state = xstrdup ("Exp");
4918 
4919     delta->other_delta = getlist();
4920 
4921     /* save the commit ID */
4922     np = getnode();
4923     np->type = RCSFIELD;
4924     np->key = xstrdup ("commitid");
4925     np->data = xstrdup(global_session_id);
4926     addnode (delta->other_delta, np);
4927 
4928 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4929     /* If permissions should be preserved on this project, then
4930        save the permission info. */
4931     if (preserve_perms)
4932     {
4933 	Node *np;
4934 	char buf[64];	/* static buffer should be safe: see usage. -twp */
4935 
4936 	delta->other_delta = getlist();
4937 
4938 	if (CVS_LSTAT (workfile, &sb) < 0)
4939 	    error (1, 1, "cannot lstat %s", workfile);
4940 
4941 	if (S_ISLNK (sb.st_mode))
4942 	{
4943 	    np = getnode();
4944 	    np->type = RCSFIELD;
4945 	    np->key = xstrdup ("symlink");
4946 	    np->data = xreadlink (workfile);
4947 	    addnode (delta->other_delta, np);
4948 	}
4949 	else
4950 	{
4951 	    (void) sprintf (buf, "%u", sb.st_uid);
4952 	    np = getnode();
4953 	    np->type = RCSFIELD;
4954 	    np->key = xstrdup ("owner");
4955 	    np->data = xstrdup (buf);
4956 	    addnode (delta->other_delta, np);
4957 
4958 	    (void) sprintf (buf, "%u", sb.st_gid);
4959 	    np = getnode();
4960 	    np->type = RCSFIELD;
4961 	    np->key = xstrdup ("group");
4962 	    np->data = xstrdup (buf);
4963 	    addnode (delta->other_delta, np);
4964 
4965 	    (void) sprintf (buf, "%o", sb.st_mode & 07777);
4966 	    np = getnode();
4967 	    np->type = RCSFIELD;
4968 	    np->key = xstrdup ("permissions");
4969 	    np->data = xstrdup (buf);
4970 	    addnode (delta->other_delta, np);
4971 
4972 	    /* Save device number. */
4973 	    switch (sb.st_mode & S_IFMT)
4974 	    {
4975 		case S_IFREG: break;
4976 		case S_IFCHR:
4977 		case S_IFBLK:
4978 #ifdef HAVE_ST_RDEV
4979 		    np = getnode();
4980 		    np->type = RCSFIELD;
4981 		    np->key = xstrdup ("special");
4982 		    sprintf (buf, "%s %lu",
4983 			     ((sb.st_mode & S_IFMT) == S_IFCHR
4984 			      ? "character" : "block"),
4985 			     (unsigned long) sb.st_rdev);
4986 		    np->data = xstrdup (buf);
4987 		    addnode (delta->other_delta, np);
4988 #else
4989 		    error (0, 0,
4990 "can't preserve %s: unable to save device files on this system",
4991 workfile);
4992 #endif
4993 		    break;
4994 
4995 		default:
4996 		    error (0, 0, "special file %s has unknown type", workfile);
4997 	    }
4998 
4999 	    /* Save hardlinks. */
5000 	    delta->hardlinks = list_linked_files_on_disk (workfile);
5001 	}
5002     }
5003 #endif
5004 
5005     /* Create a new deltatext node. */
5006     dtext = (Deltatext *) xmalloc (sizeof (Deltatext));
5007     memset (dtext, 0, sizeof (Deltatext));
5008 
5009     dtext->log = make_message_rcslegal (message);
5010 
5011     /* If the delta tree is empty, then there's nothing to link the
5012        new delta into.  So make a new delta tree, snarf the working
5013        file contents, and just write the new RCS file. */
5014     if (rcs->head == NULL)
5015     {
5016 	char *newrev;
5017 	FILE *fout;
5018 
5019 	/* Figure out what the first revision number should be. */
5020 	if (rev == NULL || *rev == '\0')
5021 	    newrev = xstrdup ("1.1");
5022 	else if (numdots (rev) == 0)
5023 	{
5024 	    newrev = (char *) xmalloc (strlen (rev) + 3);
5025 	    strcpy (newrev, rev);
5026 	    strcat (newrev, ".1");
5027 	}
5028 	else
5029 	    newrev = xstrdup (rev);
5030 
5031 	/* Don't need to xstrdup NEWREV because it's already dynamic, and
5032 	   not used for anything else.  (Don't need to free it, either.) */
5033 	rcs->head = newrev;
5034 	delta->version = xstrdup (newrev);
5035 	nodep = getnode();
5036 	nodep->type = RCSVERS;
5037 	nodep->delproc = rcsvers_delproc;
5038 	nodep->data = (char *) delta;
5039 	nodep->key = delta->version;
5040 	(void) addnode (rcs->versions, nodep);
5041 
5042 	dtext->version = xstrdup (newrev);
5043 	bufsize = 0;
5044 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5045 	if (preserve_perms && !S_ISREG (sb.st_mode))
5046 	    /* Pretend file is empty.  */
5047 	    bufsize = 0;
5048 	else
5049 #endif
5050 	get_file (workfile, workfile,
5051 		  rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5052 		  &dtext->text, &bufsize, &dtext->len);
5053 
5054 	if (!checkin_quiet)
5055 	{
5056 	    cvs_output ("initial revision: ", 0);
5057 	    cvs_output (rcs->head, 0);
5058 	    cvs_output ("\n", 1);
5059 	}
5060 
5061 	/* We are probably about to invalidate any cached file.  */
5062 	rcsbuf_cache_close ();
5063 
5064 	fout = rcs_internal_lockfile (rcs->path);
5065 	RCS_putadmin (rcs, fout);
5066 	RCS_putdtree (rcs, rcs->head, fout);
5067 	RCS_putdesc (rcs, fout);
5068 	rcs->delta_pos = ftell (fout);
5069 	if (rcs->delta_pos == -1)
5070 	    error (1, errno, "cannot ftell for %s", rcs->path);
5071 	putdeltatext (fout, dtext);
5072 	rcs_internal_unlockfile (fout, rcs->path);
5073 
5074 	if ((flags & RCS_FLAGS_KEEPFILE) == 0)
5075 	{
5076 	    if (unlink_file (workfile) < 0)
5077 		/* FIXME-update-dir: message does not include update_dir.  */
5078 		error (0, errno, "cannot remove %s", workfile);
5079 	}
5080 
5081 	if (!checkin_quiet)
5082 	    cvs_output ("done\n", 5);
5083 
5084 	status = 0;
5085 	goto checkin_done;
5086     }
5087 
5088     /* Derive a new revision number.  From the `ci' man page:
5089 
5090 	 "If rev  is  a revision number, it must be higher than the
5091 	 latest one on the branch to which  rev  belongs,  or  must
5092 	 start a new branch.
5093 
5094 	 If  rev is a branch rather than a revision number, the new
5095 	 revision is appended to that branch.  The level number  is
5096 	 obtained  by  incrementing the tip revision number of that
5097 	 branch.  If rev  indicates  a  non-existing  branch,  that
5098 	 branch  is  created  with  the  initial  revision numbered
5099 	 rev.1."
5100 
5101        RCS_findlock_or_tip handles the case where REV is omitted.
5102        RCS 5.7 also permits REV to be "$" or to begin with a dot, but
5103        we do not address those cases -- every routine that calls
5104        RCS_checkin passes it a numeric revision. */
5105 
5106     if (rev == NULL || *rev == '\0')
5107     {
5108 	/* Figure out where the commit point is by looking for locks.
5109 	   If the commit point is at the tip of a branch (or is the
5110 	   head of the delta tree), then increment its revision number
5111 	   to obtain the new revnum.  Otherwise, start a new
5112 	   branch. */
5113 	commitpt = RCS_findlock_or_tip (rcs);
5114 	if (commitpt == NULL)
5115 	{
5116 	    status = 1;
5117 	    goto checkin_done;
5118 	}
5119 	else if (commitpt->next == NULL
5120 		 || STREQ (commitpt->version, rcs->head))
5121 	    delta->version = increment_revnum (commitpt->version);
5122 	else
5123 	    delta->version = RCS_addbranch (rcs, commitpt->version);
5124     }
5125     else
5126     {
5127 	/* REV is either a revision number or a branch number.  Find the
5128 	   tip of the target branch. */
5129 	char *branch, *tip, *newrev, *p;
5130 	int dots, isrevnum;
5131 
5132 	assert (isdigit ((unsigned char) *rev));
5133 
5134 	newrev = xstrdup (rev);
5135 	dots = numdots (newrev);
5136 	isrevnum = dots & 1;
5137 
5138 	branch = xstrdup (rev);
5139 	if (isrevnum)
5140 	{
5141 	    p = strrchr (branch, '.');
5142 	    *p = '\0';
5143 	}
5144 
5145 	/* Find the tip of the target branch.  If we got a one- or two-digit
5146 	   revision number, this will be the head of the tree.  Exception:
5147 	   if rev is a single-field revision equal to the branch number of
5148 	   the trunk (usually "1") then we want to treat it like an ordinary
5149 	   branch revision. */
5150 	if (dots == 0)
5151 	{
5152 	    tip = xstrdup (rcs->head);
5153 	    if (atoi (tip) != atoi (branch))
5154 	    {
5155 		newrev = (char *) xrealloc (newrev, strlen (newrev) + 3);
5156 		strcat (newrev, ".1");
5157 		dots = isrevnum = 1;
5158 	    }
5159 	}
5160 	else if (dots == 1)
5161 	    tip = xstrdup (rcs->head);
5162 	else
5163 	    tip = RCS_getbranch (rcs, branch, 1);
5164 
5165 	/* If the branch does not exist, and we were supplied an exact
5166 	   revision number, signal an error.  Otherwise, if we were
5167 	   given only a branch number, create it and set COMMITPT to
5168 	   the branch point. */
5169 	if (tip == NULL)
5170 	{
5171 	    if (isrevnum)
5172 	    {
5173 		error (0, 0, "%s: can't find branch point %s",
5174 		       rcs->path, branch);
5175 		free (branch);
5176 		free (newrev);
5177 		status = 1;
5178 		goto checkin_done;
5179 	    }
5180 	    delta->version = RCS_addbranch (rcs, branch);
5181 	    if (!delta->version)
5182 	    {
5183 		free (branch);
5184 		free (newrev);
5185 		status = 1;
5186 		goto checkin_done;
5187 	    }
5188 	    adding_branch = 1;
5189 	    p = strrchr (branch, '.');
5190 	    *p = '\0';
5191 	    tip = xstrdup (branch);
5192 	}
5193 	else
5194 	{
5195 	    if (isrevnum)
5196 	    {
5197 		/* NEWREV must be higher than TIP. */
5198 		if (compare_revnums (tip, newrev) >= 0)
5199 		{
5200 		    error (0, 0,
5201 			   "%s: revision %s too low; must be higher than %s",
5202 			   rcs->path,
5203 			   newrev, tip);
5204 		    free (branch);
5205 		    free (newrev);
5206 		    free (tip);
5207 		    status = 1;
5208 		    goto checkin_done;
5209 		}
5210 		delta->version = xstrdup (newrev);
5211 	    }
5212 	    else
5213 		/* Just increment the tip number to get the new revision. */
5214 		delta->version = increment_revnum (tip);
5215 	}
5216 
5217 	nodep = findnode (rcs->versions, tip);
5218 	commitpt = (RCSVers *) nodep->data;
5219 
5220 	free (branch);
5221 	free (newrev);
5222 	free (tip);
5223     }
5224 
5225     assert (delta->version != NULL);
5226 
5227     /* If COMMITPT is locked by us, break the lock.  If it's locked
5228        by someone else, signal an error. */
5229     nodep = findnode (RCS_getlocks (rcs), commitpt->version);
5230     if (nodep != NULL)
5231     {
5232 	if (! STREQ (nodep->data, delta->author))
5233 	{
5234 	    /* If we are adding a branch, then leave the old lock around.
5235 	       That is sensible in the sense that when adding a branch,
5236 	       we don't need to use the lock to tell us where to check
5237 	       in.  It is fishy in the sense that if it is our own lock,
5238 	       we break it.  However, this is the RCS 5.7 behavior (at
5239 	       the end of addbranch in ci.c in RCS 5.7, it calls
5240 	       removelock only if it is our own lock, not someone
5241 	       else's).  */
5242 
5243 	    if (!adding_branch)
5244 	    {
5245 		error (0, 0, "%s: revision %s locked by %s",
5246 		       rcs->path,
5247 		       nodep->key, nodep->data);
5248 		status = 1;
5249 		goto checkin_done;
5250 	    }
5251 	}
5252 	else
5253 	    delnode (nodep);
5254     }
5255 
5256     dtext->version = xstrdup (delta->version);
5257 
5258     /* Obtain the change text for the new delta.  If DELTA is to be the
5259        new head of the tree, then its change text should be the contents
5260        of the working file, and LEAFNODE's change text should be a diff.
5261        Else, DELTA's change text should be a diff between LEAFNODE and
5262        the working file. */
5263 
5264     tmpfile = cvs_temp_name();
5265     status = RCS_checkout (rcs, NULL, commitpt->version, NULL,
5266 			   ((rcs->expand != NULL
5267 			     && STREQ (rcs->expand, "b"))
5268 			    ? "-kb"
5269 			    : "-ko"),
5270 			   tmpfile,
5271 			   (RCSCHECKOUTPROC)0, NULL);
5272     if (status != 0)
5273 	error (1, 0,
5274 	       "could not check out revision %s of `%s'",
5275 	       commitpt->version, rcs->path);
5276 
5277     bufsize = buflen = 0;
5278     chtext = NULL;
5279     chtextlen = 0;
5280     changefile = cvs_temp_name();
5281 
5282     /* Diff options should include --binary if the RCS file has -kb set
5283        in its `expand' field. */
5284     diffopts = (rcs->expand != NULL && STREQ (rcs->expand, "b")
5285 		? "-a -n --binary"
5286 		: "-a -n");
5287 
5288     if (STREQ (commitpt->version, rcs->head) &&
5289 	numdots (delta->version) == 1)
5290     {
5291 	/* If this revision is being inserted on the trunk, the change text
5292 	   for the new delta should be the contents of the working file ... */
5293 	bufsize = 0;
5294 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5295 	if (preserve_perms && !S_ISREG (sb.st_mode))
5296 	    /* Pretend file is empty.  */
5297 	    ;
5298 	else
5299 #endif
5300 	get_file (workfile, workfile,
5301 		  rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5302 		  &dtext->text, &bufsize, &dtext->len);
5303 
5304 	/* ... and the change text for the old delta should be a diff. */
5305 	commitpt->text = (Deltatext *) xmalloc (sizeof (Deltatext));
5306 	memset (commitpt->text, 0, sizeof (Deltatext));
5307 
5308 	bufsize = 0;
5309 	switch (diff_exec (workfile, tmpfile, NULL, NULL, diffopts, changefile))
5310 	{
5311 	    case 0:
5312 	    case 1:
5313 		break;
5314 	    case -1:
5315 		/* FIXME-update-dir: message does not include update_dir.  */
5316 		error (1, errno, "error diffing %s", workfile);
5317 		break;
5318 	    default:
5319 		/* FIXME-update-dir: message does not include update_dir.  */
5320 		error (1, 0, "error diffing %s", workfile);
5321 		break;
5322 	}
5323 
5324 	/* OK, the text file case here is really dumb.  Logically
5325 	   speaking we want diff to read the files in text mode,
5326 	   convert them to the canonical form found in RCS files
5327 	   (which, we hope at least, is independent of OS--always
5328 	   bare linefeeds), and then work with change texts in that
5329 	   format.  However, diff_exec both generates change
5330 	   texts and produces output for user purposes (e.g. patch.c),
5331 	   and there is no way to distinguish between the two cases.
5332 	   So we actually implement the text file case by writing the
5333 	   change text as a text file, then reading it as a text file.
5334 	   This should cause no harm, but doesn't strike me as
5335 	   immensely clean.  */
5336 	get_file (changefile, changefile,
5337 		  rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5338 		  &commitpt->text->text, &bufsize, &commitpt->text->len);
5339 
5340 	/* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE
5341 	   was empty and that there are no differences between revisions.
5342 	   In that event, we want to force RCS_rewrite to write an empty
5343 	   string for COMMITPT's change text.  Leaving the change text
5344 	   field set NULL won't work, since that means "preserve the original
5345 	   change text for this delta." */
5346 	if (commitpt->text->text == NULL)
5347 	{
5348 	    commitpt->text->text = xstrdup ("");
5349 	    commitpt->text->len = 0;
5350 	}
5351     }
5352     else
5353     {
5354 	/* This file is not being inserted at the head, but on a side
5355 	   branch somewhere.  Make a diff from the previous revision
5356 	   to the working file. */
5357 	switch (diff_exec (tmpfile, workfile, NULL, NULL, diffopts, changefile))
5358 	{
5359 	    case 0:
5360 	    case 1:
5361 		break;
5362 	    case -1:
5363 		/* FIXME-update-dir: message does not include update_dir.  */
5364 		error (1, errno, "error diffing %s", workfile);
5365 		break;
5366 	    default:
5367 		/* FIXME-update-dir: message does not include update_dir.  */
5368 		error (1, 0, "error diffing %s", workfile);
5369 		break;
5370 	}
5371 	/* See the comment above, at the other get_file invocation,
5372 	   regarding binary vs. text.  */
5373 	get_file (changefile, changefile,
5374 		  rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5375 		  &dtext->text, &bufsize,
5376 		  &dtext->len);
5377 	if (dtext->text == NULL)
5378 	{
5379 	    dtext->text = xstrdup ("");
5380 	    dtext->len = 0;
5381 	}
5382     }
5383 
5384     /* Update DELTA linkage.  It is important not to do this before
5385        the very end of RCS_checkin; if an error arises that forces
5386        us to abort checking in, we must not have malformed deltas
5387        partially linked into the tree.
5388 
5389        If DELTA and COMMITPT are on different branches, do nothing --
5390        DELTA is linked to the tree through COMMITPT->BRANCHES, and we
5391        don't want to change `next' pointers.
5392 
5393        Otherwise, if the nodes are both on the trunk, link DELTA to
5394        COMMITPT; otherwise, link COMMITPT to DELTA. */
5395 
5396     if (numdots (commitpt->version) == numdots (delta->version))
5397     {
5398 	if (STREQ (commitpt->version, rcs->head))
5399 	{
5400 	    delta->next = rcs->head;
5401 	    rcs->head = xstrdup (delta->version);
5402 	}
5403 	else
5404 	    commitpt->next = xstrdup (delta->version);
5405     }
5406 
5407     /* Add DELTA to RCS->VERSIONS. */
5408     if (rcs->versions == NULL)
5409 	rcs->versions = getlist();
5410     nodep = getnode();
5411     nodep->type = RCSVERS;
5412     nodep->delproc = rcsvers_delproc;
5413     nodep->data = (char *) delta;
5414     nodep->key = delta->version;
5415     (void) addnode (rcs->versions, nodep);
5416 
5417     /* Write the new RCS file, inserting the new delta at COMMITPT. */
5418     if (!checkin_quiet)
5419     {
5420 	cvs_output ("new revision: ", 14);
5421 	cvs_output (delta->version, 0);
5422 	cvs_output ("; previous revision: ", 21);
5423 	cvs_output (commitpt->version, 0);
5424 	cvs_output ("\n", 1);
5425     }
5426 
5427     RCS_rewrite (rcs, dtext, commitpt->version);
5428 
5429     if ((flags & RCS_FLAGS_KEEPFILE) == 0)
5430     {
5431 	if (unlink_file (workfile) < 0)
5432 	    /* FIXME-update-dir: message does not include update_dir.  */
5433 	    error (1, errno, "cannot remove %s", workfile);
5434     }
5435     if (unlink_file (tmpfile) < 0)
5436 	error (0, errno, "cannot remove %s", tmpfile);
5437     free (tmpfile);
5438     if (unlink_file (changefile) < 0)
5439 	error (0, errno, "cannot remove %s", changefile);
5440     free (changefile);
5441 
5442     if (!checkin_quiet)
5443 	cvs_output ("done\n", 5);
5444 
5445  checkin_done:
5446     if (allocated_workfile)
5447 	free (workfile);
5448 
5449     if (commitpt != NULL && commitpt->text != NULL)
5450     {
5451 	freedeltatext (commitpt->text);
5452 	commitpt->text = NULL;
5453     }
5454 
5455     freedeltatext (dtext);
5456     if (status != 0)
5457 	free_rcsvers_contents (delta);
5458 
5459     return status;
5460 }
5461 
5462 /* This structure is passed between RCS_cmp_file and cmp_file_buffer.  */
5463 
5464 struct cmp_file_data
5465 {
5466     const char *filename;
5467     FILE *fp;
5468     int different;
5469 };
5470 
5471 /* Compare the contents of revision REV of RCS file RCS with the
5472    contents of the file FILENAME.  OPTIONS is a string for the keyword
5473    expansion options.  Return 0 if the contents of the revision are
5474    the same as the contents of the file, 1 if they are different.  */
5475 
5476 int
RCS_cmp_file(rcs,rev,options,filename)5477 RCS_cmp_file (rcs, rev, options, filename)
5478      RCSNode *rcs;
5479      char *rev;
5480      char *options;
5481      const char *filename;
5482 {
5483     int binary;
5484     FILE *fp;
5485     struct cmp_file_data data;
5486     int retcode;
5487 
5488     if (options != NULL && options[0] != '\0')
5489 	binary = STREQ (options, "-kb");
5490     else
5491     {
5492 	char *expand;
5493 
5494 	expand = RCS_getexpand (rcs);
5495 	if (expand != NULL && STREQ (expand, "b"))
5496 	    binary = 1;
5497 	else
5498 	    binary = 0;
5499     }
5500 
5501 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5502     /* If CVS is to deal properly with special files (when
5503        PreservePermissions is on), the best way is to check out the
5504        revision to a temporary file and call `xcmp' on the two disk
5505        files.  xcmp needs to handle non-regular files properly anyway,
5506        so calling it simplifies RCS_cmp_file.  We *could* just yank
5507        the delta node out of the version tree and look for device
5508        numbers, but writing to disk and calling xcmp is a better
5509        abstraction (therefore probably more robust). -twp */
5510 
5511     if (preserve_perms)
5512     {
5513 	char *tmp;
5514 
5515 	tmp = cvs_temp_name();
5516 	retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL);
5517 	if (retcode != 0)
5518 	    return 1;
5519 
5520 	retcode = xcmp (tmp, filename);
5521 	if (CVS_UNLINK (tmp) < 0)
5522 	    error (0, errno, "cannot remove %s", tmp);
5523 	free (tmp);
5524 	return retcode;
5525     }
5526     else
5527 #endif
5528     {
5529         fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r");
5530 	if (fp == NULL)
5531 	    /* FIXME-update-dir: should include update_dir in message.  */
5532 	    error (1, errno, "cannot open file %s for comparing", filename);
5533 
5534         data.filename = filename;
5535         data.fp = fp;
5536         data.different = 0;
5537 
5538         retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL,
5539 				options, RUN_TTY, cmp_file_buffer,
5540 				(void *) &data);
5541 
5542         /* If we have not yet found a difference, make sure that we are at
5543            the end of the file.  */
5544         if (! data.different)
5545         {
5546 	    if (getc (fp) != EOF)
5547 		data.different = 1;
5548         }
5549 
5550         fclose (fp);
5551 
5552 	if (retcode != 0)
5553 	    return 1;
5554 
5555         return data.different;
5556     }
5557 }
5558 
5559 /* This is a subroutine of RCS_cmp_file.  It is passed to
5560    RCS_checkout.  */
5561 
5562 #define CMP_BUF_SIZE (8 * 1024)
5563 
5564 static void
cmp_file_buffer(callerdat,buffer,len)5565 cmp_file_buffer (callerdat, buffer, len)
5566      void *callerdat;
5567      const char *buffer;
5568      size_t len;
5569 {
5570     struct cmp_file_data *data = (struct cmp_file_data *) callerdat;
5571     char *filebuf;
5572 
5573     /* If we've already found a difference, we don't need to check
5574        further.  */
5575     if (data->different)
5576 	return;
5577 
5578     filebuf = xmalloc (len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len);
5579 
5580     while (len > 0)
5581     {
5582 	size_t checklen;
5583 
5584 	checklen = len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len;
5585 	if (fread (filebuf, 1, checklen, data->fp) != checklen)
5586 	{
5587 	    if (ferror (data->fp))
5588 		error (1, errno, "cannot read file %s for comparing",
5589 		       data->filename);
5590 	    data->different = 1;
5591 	    free (filebuf);
5592 	    return;
5593 	}
5594 
5595 	if (memcmp (filebuf, buffer, checklen) != 0)
5596 	{
5597 	    data->different = 1;
5598 	    free (filebuf);
5599 	    return;
5600 	}
5601 
5602 	buffer += checklen;
5603 	len -= checklen;
5604     }
5605 
5606     free (filebuf);
5607 }
5608 
5609 /* For RCS file RCS, make symbolic tag TAG point to revision REV.
5610    This validates that TAG is OK for a user to use.  Return value is
5611    -1 for error (and errno is set to indicate the error), positive for
5612    error (and an error message has been printed), or zero for success.  */
5613 
5614 int
RCS_settag(rcs,tag,rev)5615 RCS_settag (rcs, tag, rev)
5616     RCSNode *rcs;
5617     const char *tag;
5618     const char *rev;
5619 {
5620     List *symbols;
5621     Node *node;
5622 
5623     if (rcs->flags & PARTIAL)
5624 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5625 
5626     /* FIXME: This check should be moved to RCS_check_tag.  There is no
5627        reason for it to be here.  */
5628     if (STREQ (tag, TAG_BASE)
5629 	|| STREQ (tag, TAG_HEAD))
5630     {
5631 	/* Print the name of the tag might be considered redundant
5632 	   with the caller, which also prints it.  Perhaps this helps
5633 	   clarify why the tag name is considered reserved, I don't
5634 	   know.  */
5635 	error (0, 0, "Attempt to add reserved tag name %s", tag);
5636 	return 1;
5637     }
5638 
5639     /* A revision number of NULL means use the head or default branch.
5640        If rev is not NULL, it may be a symbolic tag or branch number;
5641        expand it to the correct numeric revision or branch head. */
5642     if (rev == NULL)
5643 	rev = rcs->branch ? rcs->branch : rcs->head;
5644 
5645     /* At this point rcs->symbol_data may not have been parsed.
5646        Calling RCS_symbols will force it to be parsed into a list
5647        which we can easily manipulate.  */
5648     symbols = RCS_symbols (rcs);
5649     if (symbols == NULL)
5650     {
5651 	symbols = getlist ();
5652 	rcs->symbols = symbols;
5653     }
5654     node = findnode (symbols, tag);
5655     if (node != NULL)
5656     {
5657 	free (node->data);
5658 	node->data = xstrdup (rev);
5659     }
5660     else
5661     {
5662 	node = getnode ();
5663 	node->key = xstrdup (tag);
5664 	node->data = xstrdup (rev);
5665 	(void) addnode_at_front (symbols, node);
5666     }
5667 
5668     return 0;
5669 }
5670 
5671 /* Delete the symbolic tag TAG from the RCS file RCS.  Return 0 if
5672    the tag was found (and removed), or 1 if it was not present.  (In
5673    either case, the tag will no longer be in RCS->SYMBOLS.) */
5674 
5675 int
RCS_deltag(rcs,tag)5676 RCS_deltag (rcs, tag)
5677     RCSNode *rcs;
5678     const char *tag;
5679 {
5680     List *symbols;
5681     Node *node;
5682     if (rcs->flags & PARTIAL)
5683 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5684 
5685     symbols = RCS_symbols (rcs);
5686     if (symbols == NULL)
5687 	return 1;
5688 
5689     node = findnode (symbols, tag);
5690     if (node == NULL)
5691 	return 1;
5692 
5693     delnode (node);
5694 
5695     return 0;
5696 }
5697 
5698 /* Set the default branch of RCS to REV.  */
5699 
5700 int
RCS_setbranch(rcs,rev)5701 RCS_setbranch (rcs, rev)
5702      RCSNode *rcs;
5703      const char *rev;
5704 {
5705     if (rcs->flags & PARTIAL)
5706 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5707 
5708     if (rev && ! *rev)
5709 	rev = NULL;
5710 
5711     if (rev == NULL && rcs->branch == NULL)
5712 	return 0;
5713     if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch))
5714 	return 0;
5715 
5716     if (rcs->branch != NULL)
5717 	free (rcs->branch);
5718     rcs->branch = xstrdup (rev);
5719 
5720     return 0;
5721 }
5722 
5723 /* Lock revision REV.  LOCK_QUIET is 1 to suppress output.  FIXME:
5724    Most of the callers only call us because RCS_checkin still tends to
5725    like a lock (a relic of old behavior inherited from the RCS ci
5726    program).  If we clean this up, only "cvs admin -l" will still need
5727    to call RCS_lock.  */
5728 
5729 /* FIXME-twp: if a lock owned by someone else is broken, should this
5730    send mail to the lock owner?  Prompt user?  It seems like such an
5731    obscure situation for CVS as almost not worth worrying much
5732    about. */
5733 
5734 int
RCS_lock(rcs,rev,lock_quiet)5735 RCS_lock (rcs, rev, lock_quiet)
5736      RCSNode *rcs;
5737      char *rev;
5738      int lock_quiet;
5739 {
5740     List *locks;
5741     Node *p;
5742     char *user;
5743     char *xrev = NULL;
5744 
5745     if (rcs->flags & PARTIAL)
5746 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5747 
5748     locks = RCS_getlocks (rcs);
5749     if (locks == NULL)
5750 	locks = rcs->locks = getlist();
5751     user = getcaller();
5752 
5753     /* A revision number of NULL means lock the head or default branch. */
5754     if (rev == NULL)
5755 	xrev = RCS_head (rcs);
5756     else
5757 	xrev = RCS_gettag (rcs, rev, 1, (int *) NULL);
5758 
5759     /* Make sure that the desired revision exists.  Technically,
5760        we can update the locks list without even checking this,
5761        but RCS 5.7 did this.  And it can't hurt. */
5762     if (xrev == NULL || findnode (rcs->versions, xrev) == NULL)
5763     {
5764 	if (!lock_quiet)
5765 	    error (0, 0, "%s: revision %s absent", rcs->path, rev);
5766 	free (xrev);
5767 	return 1;
5768     }
5769 
5770     /* Is this rev already locked? */
5771     p = findnode (locks, xrev);
5772     if (p != NULL)
5773     {
5774 	if (STREQ (p->data, user))
5775 	{
5776 	    /* We already own the lock on this revision, so do nothing. */
5777 	    free (xrev);
5778 	    return 0;
5779 	}
5780 
5781 #if 0
5782 	/* Well, first of all, "rev" below should be "xrev" to avoid
5783 	   core dumps.  But more importantly, should we really be
5784 	   breaking the lock unconditionally?  What CVS 1.9 does (via
5785 	   RCS) is to prompt "Revision 1.1 is already locked by fred.
5786 	   Do you want to break the lock? [ny](n): ".  Well, we don't
5787 	   want to interact with the user (certainly not at the
5788 	   server/protocol level, and probably not in the command-line
5789 	   client), but isn't it more sensible to give an error and
5790 	   let the user run "cvs admin -u" if they want to break the
5791 	   lock?  */
5792 
5793 	/* Break the lock. */
5794 	if (!lock_quiet)
5795 	{
5796 	    cvs_output (rev, 0);
5797 	    cvs_output (" unlocked\n", 0);
5798 	}
5799 	delnode (p);
5800 #else
5801 	error (1, 0, "Revision %s is already locked by %s", xrev, p->data);
5802 #endif
5803     }
5804 
5805     /* Create a new lock. */
5806     p = getnode();
5807     p->key = xrev;	/* already xstrdupped */
5808     p->data = xstrdup (getcaller());
5809     (void) addnode_at_front (locks, p);
5810 
5811     if (!lock_quiet)
5812     {
5813 	cvs_output (xrev, 0);
5814 	cvs_output (" locked\n", 0);
5815     }
5816 
5817     return 0;
5818 }
5819 
5820 /* Unlock revision REV.  UNLOCK_QUIET is 1 to suppress output.  FIXME:
5821    Like RCS_lock, this can become a no-op if we do the checkin
5822    ourselves.
5823 
5824    If REV is not null and is locked by someone else, break their
5825    lock and notify them.  It is an open issue whether RCS_unlock
5826    queries the user about whether or not to break the lock. */
5827 
5828 int
RCS_unlock(rcs,rev,unlock_quiet)5829 RCS_unlock (rcs, rev, unlock_quiet)
5830      RCSNode *rcs;
5831      char *rev;
5832      int unlock_quiet;
5833 {
5834     Node *lock;
5835     List *locks;
5836     char *user;
5837     char *xrev = NULL;
5838 
5839     user = getcaller();
5840     if (rcs->flags & PARTIAL)
5841 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5842 
5843     /* If rev is NULL, unlock the latest revision (first in
5844        rcs->locks) held by the caller. */
5845     if (rev == NULL)
5846     {
5847 	Node *p;
5848 
5849 	/* No-ops: attempts to unlock an empty tree or an unlocked file. */
5850 	if (rcs->head == NULL)
5851 	{
5852 	    if (!unlock_quiet)
5853 		cvs_outerr ("can't unlock an empty tree\n", 0);
5854 	    return 0;
5855 	}
5856 
5857 	locks = RCS_getlocks (rcs);
5858 	if (locks == NULL)
5859 	{
5860 	    if (!unlock_quiet)
5861 		cvs_outerr ("No locks are set.\n", 0);
5862 	    return 0;
5863 	}
5864 
5865 	lock = NULL;
5866 	for (p = locks->list->next; p != locks->list; p = p->next)
5867 	{
5868 	    if (lock != NULL)
5869 	    {
5870 		if (!unlock_quiet)
5871 		    error (0, 0, "\
5872 %s: multiple revisions locked by %s; please specify one", rcs->path, user);
5873 		return 1;
5874 	    }
5875 	    lock = p;
5876 	}
5877 	if (lock == NULL)
5878 	    return 0;	/* no lock found, ergo nothing to do */
5879 	xrev = xstrdup (lock->key);
5880     }
5881     else
5882     {
5883 	xrev = RCS_gettag (rcs, rev, 1, (int *) NULL);
5884 	if (xrev == NULL)
5885 	{
5886 	    error (0, 0, "%s: revision %s absent", rcs->path, rev);
5887 	    return 1;
5888 	}
5889     }
5890 
5891     lock = findnode (RCS_getlocks (rcs), xrev);
5892     if (lock == NULL)
5893     {
5894 	/* This revision isn't locked. */
5895 	free (xrev);
5896 	return 0;
5897     }
5898 
5899     if (! STREQ (lock->data, user))
5900     {
5901         /* If the revision is locked by someone else, notify
5902 	   them.  Note that this shouldn't ever happen if RCS_unlock
5903 	   is called with a NULL revision, since that means "whatever
5904 	   revision is currently locked by the caller." */
5905 	char *repos, *workfile;
5906 	repos = xstrdup (rcs->path);
5907 	workfile = strrchr (repos, '/');
5908 	*workfile++ = '\0';
5909 	notify_do ('C', workfile, user, NULL, NULL, repos);
5910 	free (repos);
5911     }
5912 
5913     delnode (lock);
5914     if (!unlock_quiet)
5915     {
5916 	cvs_output (xrev, 0);
5917 	cvs_output (" unlocked\n", 0);
5918     }
5919 
5920     free (xrev);
5921     return 0;
5922 }
5923 
5924 /* Add USER to the access list of RCS.  Do nothing if already present.
5925    FIXME-twp: check syntax of USER to make sure it's a valid id. */
5926 
5927 void
RCS_addaccess(rcs,user)5928 RCS_addaccess (rcs, user)
5929     RCSNode *rcs;
5930     char *user;
5931 {
5932     char *access, *a;
5933 
5934     if (rcs->flags & PARTIAL)
5935 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5936 
5937     if (rcs->access == NULL)
5938 	rcs->access = xstrdup (user);
5939     else
5940     {
5941 	access = xstrdup (rcs->access);
5942 	for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " "))
5943 	{
5944 	    if (STREQ (a, user))
5945 	    {
5946 		free (access);
5947 		return;
5948 	    }
5949 	}
5950 	free (access);
5951 	rcs->access = (char *) xrealloc
5952 	    (rcs->access, strlen (rcs->access) + strlen (user) + 2);
5953 	strcat (rcs->access, " ");
5954 	strcat (rcs->access, user);
5955     }
5956 }
5957 
5958 /* Remove USER from the access list of RCS. */
5959 
5960 void
RCS_delaccess(rcs,user)5961 RCS_delaccess (rcs, user)
5962     RCSNode *rcs;
5963     char *user;
5964 {
5965     char *p, *s;
5966     int ulen;
5967 
5968     if (rcs->flags & PARTIAL)
5969 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
5970 
5971     if (rcs->access == NULL)
5972 	return;
5973 
5974     if (user == NULL)
5975     {
5976         free (rcs->access);
5977         rcs->access = NULL;
5978         return;
5979     }
5980 
5981     p = rcs->access;
5982     ulen = strlen (user);
5983     while (p != NULL)
5984     {
5985 	if (strncmp (p, user, ulen) == 0 && (p[ulen] == '\0' || p[ulen] == ' '))
5986 	    break;
5987 	p = strchr (p, ' ');
5988 	if (p != NULL)
5989 	    ++p;
5990     }
5991 
5992     if (p == NULL)
5993 	return;
5994 
5995     s = p + ulen;
5996     while (*s != '\0')
5997 	*p++ = *s++;
5998     *p = '\0';
5999 }
6000 
6001 char *
RCS_getaccess(rcs)6002 RCS_getaccess (rcs)
6003     RCSNode *rcs;
6004 {
6005     if (rcs->flags & PARTIAL)
6006 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
6007 
6008     return rcs->access;
6009 }
6010 
6011 static int findtag PROTO ((Node *, void *));
6012 
6013 /* Return a nonzero value if the revision specified by ARG is found.  */
6014 
6015 static int
findtag(node,arg)6016 findtag (node, arg)
6017     Node *node;
6018     void *arg;
6019 {
6020     char *rev = (char *)arg;
6021 
6022     if (STREQ (node->data, rev))
6023 	return 1;
6024     else
6025 	return 0;
6026 }
6027 
6028 /* Delete revisions between REV1 and REV2.  The changes between the two
6029    revisions must be collapsed, and the result stored in the revision
6030    immediately preceding the lower one.  Return 0 for successful completion,
6031    1 otherwise.
6032 
6033    Solution: check out the revision preceding REV1 and the revision
6034    following REV2.  Use call_diff to find aggregate diffs between
6035    these two revisions, and replace the delta text for the latter one
6036    with the new aggregate diff.  Alternatively, we could write a
6037    function that takes two change texts and combines them to produce a
6038    new change text, without checking out any revs or calling diff.  It
6039    would be hairy, but so, so cool.
6040 
6041    If INCLUSIVE is set, then TAG1 and TAG2, if non-NULL, tell us to
6042    delete that revision as well (cvs admin -o tag1:tag2).  If clear,
6043    delete up to but not including that revision (cvs admin -o tag1::tag2).
6044    This does not affect TAG1 or TAG2 being NULL; the meaning of the start
6045    point in ::tag2 and :tag2 is the same and likewise for end points.  */
6046 
6047 int
RCS_delete_revs(rcs,tag1,tag2,inclusive)6048 RCS_delete_revs (rcs, tag1, tag2, inclusive)
6049     RCSNode *rcs;
6050     char *tag1;
6051     char *tag2;
6052     int inclusive;
6053 {
6054     char *next;
6055     Node *nodep;
6056     RCSVers *revp = NULL;
6057     RCSVers *beforep;
6058     int status, found;
6059     int save_noexec;
6060 
6061     char *branchpoint = NULL;
6062     char *rev1 = NULL;
6063     char *rev2 = NULL;
6064     int rev1_inclusive = inclusive;
6065     int rev2_inclusive = inclusive;
6066     char *before = NULL;
6067     char *after = NULL;
6068     char *beforefile = NULL;
6069     char *afterfile = NULL;
6070     char *outfile = NULL;
6071 
6072     if (tag1 == NULL && tag2 == NULL)
6073 	return 0;
6074 
6075     /* Assume error status until everything is finished. */
6076     status = 1;
6077 
6078     /* Make sure both revisions exist. */
6079     if (tag1 != NULL)
6080     {
6081 	rev1 = RCS_gettag (rcs, tag1, 1, NULL);
6082 	if (rev1 == NULL || (nodep = findnode (rcs->versions, rev1)) == NULL)
6083 	{
6084 	    error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, tag1);
6085 	    goto delrev_done;
6086 	}
6087     }
6088     if (tag2 != NULL)
6089     {
6090 	rev2 = RCS_gettag (rcs, tag2, 1, NULL);
6091 	if (rev2 == NULL || (nodep = findnode (rcs->versions, rev2)) == NULL)
6092 	{
6093 	    error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, tag2);
6094 	    goto delrev_done;
6095 	}
6096     }
6097 
6098     /* If rev1 is on the trunk and rev2 is NULL, rev2 should be
6099        RCS->HEAD.  (*Not* RCS_head(rcs), which may return rcs->branch
6100        instead.)  We need to check this special case early, in order
6101        to make sure that rev1 and rev2 get ordered correctly. */
6102     if (rev2 == NULL && numdots (rev1) == 1)
6103     {
6104 	rev2 = xstrdup (rcs->head);
6105 	rev2_inclusive = 1;
6106     }
6107 
6108     if (rev2 == NULL)
6109 	rev2_inclusive = 1;
6110 
6111     if (rev1 != NULL && rev2 != NULL)
6112     {
6113 	/* A range consisting of a branch number means the latest revision
6114 	   on that branch. */
6115 	if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2))
6116 	    rev1 = rev2 = RCS_getbranch (rcs, rev1, 0);
6117 	else
6118 	{
6119 	    /* Make sure REV1 and REV2 are ordered correctly (in the
6120 	       same order as the next field).  For revisions on the
6121 	       trunk, REV1 should be higher than REV2; for branches,
6122 	       REV1 should be lower.  */
6123 	    /* Shouldn't we just be giving an error in the case where
6124 	       the user specifies the revisions in the wrong order
6125 	       (that is, always swap on the trunk, never swap on a
6126 	       branch, in the non-error cases)?  It is not at all
6127 	       clear to me that users who specify -o 1.4:1.2 really
6128 	       meant to type -o 1.2:1.4, and the out of order usage
6129 	       has never been documented, either by cvs.texinfo or
6130 	       rcs(1).  */
6131 	    char *temp;
6132 	    int temp_inclusive;
6133 	    if (numdots (rev1) == 1)
6134 	    {
6135 		if (compare_revnums (rev1, rev2) <= 0)
6136 		{
6137 		    temp = rev2;
6138 		    rev2 = rev1;
6139 		    rev1 = temp;
6140 
6141 		    temp_inclusive = rev2_inclusive;
6142 		    rev2_inclusive = rev1_inclusive;
6143 		    rev1_inclusive = temp_inclusive;
6144 		}
6145 	    }
6146 	    else if (compare_revnums (rev1, rev2) > 0)
6147 	    {
6148 		temp = rev2;
6149 		rev2 = rev1;
6150 		rev1 = temp;
6151 
6152 		temp_inclusive = rev2_inclusive;
6153 		rev2_inclusive = rev1_inclusive;
6154 		rev1_inclusive = temp_inclusive;
6155 	    }
6156 	}
6157     }
6158 
6159     /* Basically the same thing; make sure that the ordering is what we
6160        need.  */
6161     if (rev1 == NULL)
6162     {
6163 	assert (rev2 != NULL);
6164 	if (numdots (rev2) == 1)
6165 	{
6166 	    /* Swap rev1 and rev2.  */
6167 	    int temp_inclusive;
6168 
6169 	    rev1 = rev2;
6170 	    rev2 = NULL;
6171 
6172 	    temp_inclusive = rev2_inclusive;
6173 	    rev2_inclusive = rev1_inclusive;
6174 	    rev1_inclusive = temp_inclusive;
6175 	}
6176     }
6177 
6178     /* Put the revision number preceding the first one to delete into
6179        BEFORE (where "preceding" means according to the next field).
6180        If the first revision to delete is the first revision on its
6181        branch (e.g. 1.3.2.1), BEFORE should be the node on the trunk
6182        at which the branch is rooted.  If the first revision to delete
6183        is the head revision of the trunk, set BEFORE to NULL.
6184 
6185        Note that because BEFORE may not be on the same branch as REV1,
6186        it is not very handy for navigating the revision tree.  It's
6187        most useful just for checking out the revision preceding REV1. */
6188     before = NULL;
6189     branchpoint = RCS_getbranchpoint (rcs, rev1 != NULL ? rev1 : rev2);
6190     if (rev1 == NULL)
6191     {
6192 	rev1 = xstrdup (branchpoint);
6193 	if (numdots (branchpoint) > 1)
6194 	{
6195 	    char *bp;
6196 	    bp = strrchr (branchpoint, '.');
6197 	    while (*--bp != '.')
6198 		;
6199 	    *bp = '\0';
6200 	    /* Note that this is exclusive, always, because the inclusive
6201 	       flag doesn't affect the meaning when rev1 == NULL.  */
6202 	    before = xstrdup (branchpoint);
6203 	    *bp = '.';
6204 	}
6205     }
6206     else if (! STREQ (rev1, branchpoint))
6207     {
6208 	/* Walk deltas from BRANCHPOINT on, looking for REV1. */
6209 	nodep = findnode (rcs->versions, branchpoint);
6210 	revp = (RCSVers *) nodep->data;
6211 	while (revp->next != NULL && ! STREQ (revp->next, rev1))
6212 	{
6213 	    revp = (RCSVers *) nodep->data;
6214 	    nodep = findnode (rcs->versions, revp->next);
6215 	}
6216 	if (revp->next == NULL)
6217 	{
6218 	    error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, rev1);
6219 	    goto delrev_done;
6220 	}
6221 	if (rev1_inclusive)
6222 	    before = xstrdup (revp->version);
6223 	else
6224 	{
6225 	    before = rev1;
6226 	    nodep = findnode (rcs->versions, before);
6227 	    rev1 = xstrdup (((RCSVers *)nodep->data)->next);
6228 	}
6229     }
6230     else if (!rev1_inclusive)
6231     {
6232 	before = rev1;
6233 	nodep = findnode (rcs->versions, before);
6234 	rev1 = xstrdup (((RCSVers *)nodep->data)->next);
6235     }
6236     else if (numdots (branchpoint) > 1)
6237     {
6238 	/* Example: rev1 is "1.3.2.1", branchpoint is "1.3.2.1".
6239 	   Set before to "1.3".  */
6240 	char *bp;
6241 	bp = strrchr (branchpoint, '.');
6242 	while (*--bp != '.')
6243 	    ;
6244 	*bp = '\0';
6245 	before = xstrdup (branchpoint);
6246 	*bp = '.';
6247     }
6248 
6249     /* If any revision between REV1 and REV2 is locked or is a branch point,
6250        we can't delete that revision and must abort. */
6251     after = NULL;
6252     next = rev1;
6253     found = 0;
6254     while (!found && next != NULL)
6255     {
6256 	nodep = findnode (rcs->versions, next);
6257 	revp = (RCSVers *) nodep->data;
6258 
6259 	if (rev2 != NULL)
6260 	    found = STREQ (revp->version, rev2);
6261 	next = revp->next;
6262 
6263 	if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL)
6264 	{
6265 	    if (findnode (RCS_getlocks (rcs), revp->version))
6266 	    {
6267 		error (0, 0, "%s: can't remove locked revision %s",
6268 		       rcs->path,
6269 		       revp->version);
6270 		goto delrev_done;
6271 	    }
6272 	    if (revp->branches != NULL)
6273 	    {
6274 		error (0, 0, "%s: can't remove branch point %s",
6275 		       rcs->path,
6276 		       revp->version);
6277 		goto delrev_done;
6278 	    }
6279 
6280 	    /* Doing this only for the :: syntax is for compatibility.
6281 	       See cvs.texinfo for somewhat more discussion.  */
6282 	    if (!inclusive
6283 		&& walklist (RCS_symbols (rcs), findtag, revp->version))
6284 	    {
6285 		/* We don't print which file this happens to on the theory
6286 		   that the caller will print the name of the file in a
6287 		   more useful fashion (fullname not rcs->path).  */
6288 		error (0, 0, "cannot remove revision %s because it has tags",
6289 		       revp->version);
6290 		goto delrev_done;
6291 	    }
6292 
6293 	    /* It's misleading to print the `deleting revision' output
6294 	       here, since we may not actually delete these revisions.
6295 	       But that's how RCS does it.  Bleah.  Someday this should be
6296 	       moved to the point where the revs are actually marked for
6297 	       deletion. -twp */
6298 	    cvs_output ("deleting revision ", 0);
6299 	    cvs_output (revp->version, 0);
6300 	    cvs_output ("\n", 1);
6301 	}
6302     }
6303 
6304     if (rev2 == NULL)
6305 	;
6306     else if (found)
6307     {
6308 	if (rev2_inclusive)
6309 	    after = xstrdup (next);
6310 	else
6311 	    after = xstrdup (revp->version);
6312     }
6313     else if (!inclusive)
6314     {
6315 	/* In the case of an empty range, for example 1.2::1.2 or
6316 	   1.2::1.3, we want to just do nothing.  */
6317 	status = 0;
6318 	goto delrev_done;
6319     }
6320     else
6321     {
6322 	/* This looks fishy in the cases where tag1 == NULL or tag2 == NULL.
6323 	   Are those cases really impossible?  */
6324 	assert (tag1 != NULL);
6325 	assert (tag2 != NULL);
6326 
6327 	error (0, 0, "%s: invalid revision range %s:%s", rcs->path,
6328 	       tag1, tag2);
6329 	goto delrev_done;
6330     }
6331 
6332     if (after == NULL && before == NULL)
6333     {
6334 	/* The user is trying to delete all revisions.  While an
6335 	   RCS file without revisions makes sense to RCS (e.g. the
6336 	   state after "rcs -i"), CVS has never been able to cope with
6337 	   it.  So at least for now we just make this an error.
6338 
6339 	   We don't include rcs->path in the message since "cvs admin"
6340 	   already printed "RCS file:" and the name.  */
6341 	error (1, 0, "attempt to delete all revisions");
6342     }
6343 
6344     /* The conditionals at this point get really hairy.  Here is the
6345        general idea:
6346 
6347        IF before != NULL and after == NULL
6348          THEN don't check out any revisions, just delete them
6349        IF before == NULL and after != NULL
6350          THEN only check out after's revision, and use it for the new deltatext
6351        ELSE
6352          check out both revisions and diff -n them.  This could use
6353 	 RCS_exec_rcsdiff with some changes, like being able
6354 	 to suppress diagnostic messages and to direct output. */
6355 
6356     if (after != NULL)
6357     {
6358 	char *diffbuf;
6359 	size_t bufsize, len;
6360 
6361 #if defined (__CYGWIN32__) || defined (_WIN32)
6362 	/* FIXME: This is an awful kludge, but at least until I have
6363 	   time to work on it a little more and test it, I'd rather
6364 	   give a fatal error than corrupt the file.  I think that we
6365 	   need to use "-kb" and "--binary" and "rb" to get_file
6366 	   (probably can do it always, not just for binary files, if
6367 	   we are consistent between the RCS_checkout and the diff).  */
6368 	{
6369 	    char *expand = RCS_getexpand (rcs);
6370 	    if (expand != NULL && STREQ (expand, "b"))
6371 		error (1, 0,
6372 		   "admin -o not implemented yet for binary on this system");
6373 	}
6374 #endif
6375 
6376 	afterfile = cvs_temp_name();
6377 	status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
6378 			       (RCSCHECKOUTPROC)0, NULL);
6379 	if (status > 0)
6380 	    goto delrev_done;
6381 
6382 	if (before == NULL)
6383 	{
6384 	    /* We are deleting revisions from the head of the tree,
6385 	       so must create a new head. */
6386 	    diffbuf = NULL;
6387 	    bufsize = 0;
6388 	    get_file (afterfile, afterfile, "r", &diffbuf, &bufsize, &len);
6389 
6390 	    save_noexec = noexec;
6391 	    noexec = 0;
6392 	    if (unlink_file (afterfile) < 0)
6393 		error (0, errno, "cannot remove %s", afterfile);
6394 	    noexec = save_noexec;
6395 
6396 	    free (afterfile);
6397 	    afterfile = NULL;
6398 
6399 	    free (rcs->head);
6400 	    rcs->head = xstrdup (after);
6401 	}
6402 	else
6403 	{
6404 	    beforefile = cvs_temp_name();
6405 	    status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile,
6406 				   (RCSCHECKOUTPROC)0, NULL);
6407 	    if (status > 0)
6408 		goto delrev_done;
6409 
6410 	    outfile = cvs_temp_name();
6411 	    status = diff_exec (beforefile, afterfile, NULL, NULL, "-an", outfile);
6412 
6413 	    if (status == 2)
6414 	    {
6415 		/* Not sure we need this message; will diff_exec already
6416 		   have printed an error?  */
6417 		error (0, 0, "%s: could not diff", rcs->path);
6418 		status = 1;
6419 		goto delrev_done;
6420 	    }
6421 
6422 	    diffbuf = NULL;
6423 	    bufsize = 0;
6424 	    get_file (outfile, outfile, "r", &diffbuf, &bufsize, &len);
6425 	}
6426 
6427 	/* Save the new change text in after's delta node. */
6428 	nodep = findnode (rcs->versions, after);
6429 	revp = (RCSVers *) nodep->data;
6430 
6431 	assert (revp->text == NULL);
6432 
6433 	revp->text = (Deltatext *) xmalloc (sizeof (Deltatext));
6434 	memset ((Deltatext *) revp->text, 0, sizeof (Deltatext));
6435 	revp->text->version = xstrdup (revp->version);
6436 	revp->text->text = diffbuf;
6437 	revp->text->len = len;
6438 
6439 	/* If DIFFBUF is NULL, it means that OUTFILE is empty and that
6440 	   there are no differences between the two revisions.  In that
6441 	   case, we want to force RCS_copydeltas to write an empty string
6442 	   for the new change text (leaving the text field set NULL
6443 	   means "preserve the original change text for this delta," so
6444 	   we don't want that). */
6445 	if (revp->text->text == NULL)
6446 	    revp->text->text = xstrdup ("");
6447     }
6448 
6449     /* Walk through the revisions (again) to mark each one as
6450        outdated.  (FIXME: would it be safe to use the `dead' field for
6451        this?  Doubtful.) */
6452     for (next = rev1;
6453 	 next != NULL && (after == NULL || ! STREQ (next, after));
6454 	 next = revp->next)
6455     {
6456 	nodep = findnode (rcs->versions, next);
6457 	revp = (RCSVers *) nodep->data;
6458 	revp->outdated = 1;
6459     }
6460 
6461     /* Update delta links.  If BEFORE == NULL, we're changing the
6462        head of the tree and don't need to update any `next' links. */
6463     if (before != NULL)
6464     {
6465 	/* If REV1 is the first node on its branch, then BEFORE is its
6466 	   root node (on the trunk) and we have to update its branches
6467 	   list.  Otherwise, BEFORE is on the same branch as AFTER, and
6468 	   we can just change BEFORE's `next' field to point to AFTER.
6469 	   (This should be safe: since findnode manages its lists via
6470 	   the `hashnext' and `hashprev' fields, rather than `next' and
6471 	   `prev', mucking with `next' and `prev' should not corrupt the
6472 	   delta tree's internal structure.  Much. -twp) */
6473 
6474 	if (rev1 == NULL)
6475 	    /* beforep's ->next field already should be equal to after,
6476 	       which I think is always NULL in this case.  */
6477 	    ;
6478 	else if (STREQ (rev1, branchpoint))
6479 	{
6480 	    nodep = findnode (rcs->versions, before);
6481 	    revp = (RCSVers *) nodep->data;
6482 	    nodep = revp->branches->list->next;
6483 	    while (nodep != revp->branches->list &&
6484 		   ! STREQ (nodep->key, rev1))
6485 		nodep = nodep->next;
6486 	    assert (nodep != revp->branches->list);
6487 	    if (after == NULL)
6488 		delnode (nodep);
6489 	    else
6490 	    {
6491 		free (nodep->key);
6492 		nodep->key = xstrdup (after);
6493 	    }
6494 	}
6495 	else
6496 	{
6497 	    nodep = findnode (rcs->versions, before);
6498 	    beforep = (RCSVers *) nodep->data;
6499 	    free (beforep->next);
6500 	    beforep->next = xstrdup (after);
6501 	}
6502     }
6503 
6504     status = 0;
6505 
6506  delrev_done:
6507     if (rev1 != NULL)
6508 	free (rev1);
6509     if (rev2 != NULL)
6510 	free (rev2);
6511     if (branchpoint != NULL)
6512 	free (branchpoint);
6513     if (before != NULL)
6514 	free (before);
6515     if (after != NULL)
6516 	free (after);
6517 
6518     save_noexec = noexec;
6519     noexec = 0;
6520     if (beforefile != NULL)
6521     {
6522 	if (unlink_file (beforefile) < 0)
6523 	    error (0, errno, "cannot remove %s", beforefile);
6524 	free (beforefile);
6525     }
6526     if (afterfile != NULL)
6527     {
6528 	if (unlink_file (afterfile) < 0)
6529 	    error (0, errno, "cannot remove %s", afterfile);
6530 	free (afterfile);
6531     }
6532     if (outfile != NULL)
6533     {
6534 	if (unlink_file (outfile) < 0)
6535 	    error (0, errno, "cannot remove %s", outfile);
6536 	free (outfile);
6537     }
6538     noexec = save_noexec;
6539 
6540     return status;
6541 }
6542 
6543 /*
6544  * TRUE if there exists a symbolic tag "tag" in file.
6545  */
6546 int
RCS_exist_tag(rcs,tag)6547 RCS_exist_tag (rcs, tag)
6548     RCSNode *rcs;
6549     char *tag;
6550 {
6551 
6552     assert (rcs != NULL);
6553 
6554     if (findnode (RCS_symbols (rcs), tag))
6555     return 1;
6556     return 0;
6557 
6558 }
6559 
6560 /*
6561  * TRUE if RCS revision number "rev" exists.
6562  * This includes magic branch revisions, not found in rcs->versions,
6563  * but only in rcs->symbols, requiring a list walk to find them.
6564  * Take advantage of list walk callback function already used by
6565  * RCS_delete_revs, above.
6566  */
6567 int
RCS_exist_rev(rcs,rev)6568 RCS_exist_rev (rcs, rev)
6569     RCSNode *rcs;
6570     char *rev;
6571 {
6572 
6573     assert (rcs != NULL);
6574 
6575     if (rcs->flags & PARTIAL)
6576 	RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
6577 
6578     if (findnode(rcs->versions, rev) != 0)
6579 	return 1;
6580 
6581     if (walklist (RCS_symbols(rcs), findtag, rev) != 0)
6582 	return 1;
6583 
6584     return 0;
6585 
6586 }
6587 
6588 
6589 /* RCS_deltas and friends.  Processing of the deltas in RCS files.  */
6590 
6591 struct line
6592 {
6593     /* Text of this line.  Part of the same malloc'd block as the struct
6594        line itself (we probably should use the "struct hack" (char text[1])
6595        and save ourselves sizeof (char *) bytes).  Does not include \n;
6596        instead has_newline indicates the presence or absence of \n.  */
6597     char *text;
6598     /* Length of this line, not counting \n if has_newline is true.  */
6599     size_t len;
6600     /* Version in which it was introduced.  */
6601     RCSVers *vers;
6602     /* Nonzero if this line ends with \n.  This will always be true
6603        except possibly for the last line.  */
6604     int has_newline;
6605     /* Number of pointers to this struct line.  */
6606     int refcount;
6607 };
6608 
6609 struct linevector
6610 {
6611     /* How many lines in use for this linevector?  */
6612     unsigned int nlines;
6613     /* How many lines allocated for this linevector?  */
6614     unsigned int lines_alloced;
6615     /* Pointer to array containing a pointer to each line.  */
6616     struct line **vector;
6617 };
6618 
6619 static void linevector_init PROTO ((struct linevector *));
6620 
6621 /* Initialize *VEC to be a linevector with no lines.  */
6622 static void
linevector_init(vec)6623 linevector_init (vec)
6624     struct linevector *vec;
6625 {
6626     vec->lines_alloced = 0;
6627     vec->nlines = 0;
6628     vec->vector = NULL;
6629 }
6630 
6631 static int linevector_add PROTO ((struct linevector *vec, const char *text,
6632 				  size_t len, RCSVers *vers,
6633 				  unsigned int pos));
6634 
6635 /* Given some text TEXT, add each of its lines to VEC before line POS
6636    (where line 0 is the first line).  The last line in TEXT may or may
6637    not be \n terminated.
6638    Set the version for each of the new lines to VERS.  This
6639    function returns non-zero for success.  It returns zero if the line
6640    number is out of range.
6641 
6642    Each of the lines in TEXT are copied to space which is managed with
6643    the linevector (and freed by linevector_free).  So the caller doesn't
6644    need to keep TEXT around after the call to this function.  */
6645 static int
linevector_add(vec,text,len,vers,pos)6646 linevector_add (vec, text, len, vers, pos)
6647     struct linevector *vec;
6648     const char *text;
6649     size_t len;
6650     RCSVers *vers;
6651     unsigned int pos;
6652 {
6653     const char *textend;
6654     unsigned int i;
6655     unsigned int nnew;
6656     const char *p;
6657     const char *nextline_text;
6658     size_t nextline_len;
6659     int nextline_newline;
6660     struct line *q;
6661 
6662     if (len == 0)
6663 	return 1;
6664 
6665     textend = text + len;
6666 
6667     /* Count the number of lines we will need to add.  */
6668     nnew = 1;
6669     for (p = text; p < textend; ++p)
6670 	if (*p == '\n' && p + 1 < textend)
6671 	    ++nnew;
6672 
6673     /* Expand VEC->VECTOR if needed.  */
6674     if (vec->nlines + nnew >= vec->lines_alloced)
6675     {
6676 	if (vec->lines_alloced == 0)
6677 	    vec->lines_alloced = 10;
6678 	while (vec->nlines + nnew >= vec->lines_alloced)
6679 	    vec->lines_alloced *= 2;
6680 	vec->vector = xrealloc (vec->vector,
6681 				vec->lines_alloced * sizeof (*vec->vector));
6682     }
6683 
6684     /* Make room for the new lines in VEC->VECTOR.  */
6685     for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i)
6686 	vec->vector[i] = vec->vector[i - nnew];
6687 
6688     if (pos > vec->nlines)
6689 	return 0;
6690 
6691     /* Actually add the lines, to VEC->VECTOR.  */
6692     i = pos;
6693     nextline_text = text;
6694     nextline_newline = 0;
6695     for (p = text; p < textend; ++p)
6696 	if (*p == '\n')
6697 	{
6698 	    nextline_newline = 1;
6699 	    if (p + 1 == textend)
6700 		/* If there are no characters beyond the last newline, we
6701 		   don't consider it another line.  */
6702 		break;
6703 	    nextline_len = p - nextline_text;
6704 	    q = (struct line *) xmalloc (sizeof (struct line) + nextline_len);
6705 	    q->vers = vers;
6706 	    q->text = (char *)q + sizeof (struct line);
6707 	    q->len = nextline_len;
6708 	    q->has_newline = nextline_newline;
6709 	    q->refcount = 1;
6710 	    memcpy (q->text, nextline_text, nextline_len);
6711 	    vec->vector[i++] = q;
6712 
6713 	    nextline_text = (char *)p + 1;
6714 	    nextline_newline = 0;
6715 	}
6716     nextline_len = p - nextline_text;
6717     q = (struct line *) xmalloc (sizeof (struct line) + nextline_len);
6718     q->vers = vers;
6719     q->text = (char *)q + sizeof (struct line);
6720     q->len = nextline_len;
6721     q->has_newline = nextline_newline;
6722     q->refcount = 1;
6723     memcpy (q->text, nextline_text, nextline_len);
6724     vec->vector[i] = q;
6725 
6726     vec->nlines += nnew;
6727 
6728     return 1;
6729 }
6730 
6731 static void linevector_delete PROTO ((struct linevector *, unsigned int,
6732 				      unsigned int));
6733 
6734 /* Remove NLINES lines from VEC at position POS (where line 0 is the
6735    first line).  */
6736 static void
linevector_delete(vec,pos,nlines)6737 linevector_delete (vec, pos, nlines)
6738     struct linevector *vec;
6739     unsigned int pos;
6740     unsigned int nlines;
6741 {
6742     unsigned int i;
6743     unsigned int last;
6744 
6745     last = vec->nlines - nlines;
6746     for (i = pos; i < pos + nlines; ++i)
6747     {
6748 	if (--vec->vector[i]->refcount == 0)
6749 	    free (vec->vector[i]);
6750     }
6751     for (i = pos; i < last; ++i)
6752 	vec->vector[i] = vec->vector[i + nlines];
6753     vec->nlines -= nlines;
6754 }
6755 
6756 static void linevector_copy PROTO ((struct linevector *, struct linevector *));
6757 
6758 /* Copy FROM to TO, copying the vectors but not the lines pointed to.  */
6759 static void
linevector_copy(to,from)6760 linevector_copy (to, from)
6761     struct linevector *to;
6762     struct linevector *from;
6763 {
6764     unsigned int ln;
6765 
6766     for (ln = 0; ln < to->nlines; ++ln)
6767     {
6768 	if (--to->vector[ln]->refcount == 0)
6769 	    free (to->vector[ln]);
6770     }
6771     if (from->nlines > to->lines_alloced)
6772     {
6773 	to->lines_alloced = from->nlines;
6774 	to->vector = (struct line **)
6775 	    xrealloc (to->vector, to->lines_alloced * sizeof (*to->vector));
6776     }
6777     memcpy (to->vector, from->vector,
6778 	    from->nlines * sizeof (*to->vector));
6779     to->nlines = from->nlines;
6780     for (ln = 0; ln < to->nlines; ++ln)
6781 	++to->vector[ln]->refcount;
6782 }
6783 
6784 static void linevector_free PROTO ((struct linevector *));
6785 
6786 /* Free storage associated with linevector.  */
6787 static void
linevector_free(vec)6788 linevector_free (vec)
6789     struct linevector *vec;
6790 {
6791     unsigned int ln;
6792 
6793     if (vec->vector != NULL)
6794     {
6795 	for (ln = 0; ln < vec->nlines; ++ln)
6796 	    if (--vec->vector[ln]->refcount == 0)
6797 		free (vec->vector[ln]);
6798 
6799 	free (vec->vector);
6800     }
6801 }
6802 
6803 static char *month_printname PROTO ((char *));
6804 
6805 /* Given a textual string giving the month (1-12), terminated with any
6806    character not recognized by atoi, return the 3 character name to
6807    print it with.  I do not think it is a good idea to change these
6808    strings based on the locale; they are standard abbreviations (for
6809    example in rfc822 mail messages) which should be widely understood.
6810    Returns a pointer into static readonly storage.  */
6811 static char *
month_printname(month)6812 month_printname (month)
6813     char *month;
6814 {
6815     static const char *const months[] =
6816       {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
6817 	 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
6818     int mnum;
6819 
6820     mnum = atoi (month);
6821     if (mnum < 1 || mnum > 12)
6822 	return "???";
6823     return (char *)months[mnum - 1];
6824 }
6825 
6826 static int
6827 apply_rcs_changes PROTO ((struct linevector *, const char *, size_t,
6828 			  const char *, RCSVers *, RCSVers *));
6829 
6830 /* Apply changes to the line vector LINES.  DIFFBUF is a buffer of
6831    length DIFFLEN holding the change text from an RCS file (the output
6832    of diff -n).  NAME is used in error messages.  The VERS field of
6833    any line added is set to ADDVERS.  The VERS field of any line
6834    deleted is set to DELVERS, unless DELVERS is NULL, in which case
6835    the VERS field of deleted lines is unchanged.  The function returns
6836    non-zero if the change text is applied successfully.  It returns
6837    zero if the change text does not appear to apply to LINES (e.g., a
6838    line number is invalid).  If the change text is improperly
6839    formatted (e.g., it is not the output of diff -n), the function
6840    calls error with a status of 1, causing the program to exit.  */
6841 
6842 static int
apply_rcs_changes(lines,diffbuf,difflen,name,addvers,delvers)6843 apply_rcs_changes (lines, diffbuf, difflen, name, addvers, delvers)
6844      struct linevector *lines;
6845      const char *diffbuf;
6846      size_t difflen;
6847      const char *name;
6848      RCSVers *addvers;
6849      RCSVers *delvers;
6850 {
6851     const char *p;
6852     const char *q;
6853     int op;
6854     /* The RCS format throws us for a loop in that the deltafrags (if
6855        we define a deltafrag as an add or a delete) need to be applied
6856        in reverse order.  So we stick them into a linked list.  */
6857     struct deltafrag {
6858 	enum {FRAG_ADD, FRAG_DELETE} type;
6859 	unsigned long pos;
6860 	unsigned long nlines;
6861 	const char *new_lines;
6862 	size_t len;
6863 	struct deltafrag *next;
6864     };
6865     struct deltafrag *dfhead;
6866     struct deltafrag *df;
6867 
6868     dfhead = NULL;
6869     for (p = diffbuf; p != NULL && p < diffbuf + difflen; )
6870     {
6871 	op = *p++;
6872 	if (op != 'a' && op != 'd')
6873 	    /* Can't just skip over the deltafrag, because the value
6874 	       of op determines the syntax.  */
6875 	    error (1, 0, "unrecognized operation '\\x%x' in %s",
6876 		   op, name);
6877 	df = (struct deltafrag *) xmalloc (sizeof (struct deltafrag));
6878 	df->next = dfhead;
6879 	dfhead = df;
6880 	df->pos = strtoul (p, (char **) &q, 10);
6881 
6882 	if (p == q)
6883 	    error (1, 0, "number expected in %s", name);
6884 	p = q;
6885 	if (*p++ != ' ')
6886 	    error (1, 0, "space expected in %s", name);
6887 	df->nlines = strtoul (p, (char **) &q, 10);
6888 	if (p == q)
6889 	    error (1, 0, "number expected in %s", name);
6890 	p = q;
6891 	if (*p++ != '\012')
6892 	    error (1, 0, "linefeed expected in %s", name);
6893 
6894 	if (op == 'a')
6895 	{
6896 	    unsigned int i;
6897 
6898 	    df->type = FRAG_ADD;
6899 	    i = df->nlines;
6900 	    /* The text we want is the number of lines specified, or
6901 	       until the end of the value, whichever comes first (it
6902 	       will be the former except in the case where we are
6903 	       adding a line which does not end in newline).  */
6904 	    for (q = p; i != 0; ++q)
6905 		if (*q == '\n')
6906 		    --i;
6907 		else if (q == diffbuf + difflen)
6908 		{
6909 		    if (i != 1)
6910 			error (1, 0, "premature end of change in %s", name);
6911 		    else
6912 			break;
6913 		}
6914 
6915 	    /* Stash away a pointer to the text we are adding.  */
6916 	    df->new_lines = p;
6917 	    df->len = q - p;
6918 
6919 	    p = q;
6920 	}
6921 	else
6922 	{
6923 	    /* Correct for the fact that line numbers in RCS files
6924 	       start with 1.  */
6925 	    --df->pos;
6926 
6927 	    assert (op == 'd');
6928 	    df->type = FRAG_DELETE;
6929 	}
6930     }
6931 
6932     for (df = dfhead; df != NULL;)
6933     {
6934 	unsigned int ln;
6935 
6936 	switch (df->type)
6937 	{
6938 	case FRAG_ADD:
6939 	    if (! linevector_add (lines, df->new_lines, df->len, addvers,
6940 				  df->pos))
6941 		return 0;
6942 	    break;
6943 	case FRAG_DELETE:
6944 	    if (df->pos > lines->nlines
6945 		|| df->pos + df->nlines > lines->nlines)
6946 		return 0;
6947 	    if (delvers != NULL)
6948 		for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
6949 		    lines->vector[ln]->vers = delvers;
6950 	    linevector_delete (lines, df->pos, df->nlines);
6951 	    break;
6952 	}
6953 	df = df->next;
6954 	free (dfhead);
6955 	dfhead = df;
6956     }
6957 
6958     return 1;
6959 }
6960 
6961 /* Apply an RCS change text to a buffer.  The function name starts
6962    with rcs rather than RCS because this does not take an RCSNode
6963    argument.  NAME is used in error messages.  TEXTBUF is the text
6964    buffer to change, and TEXTLEN is the size.  DIFFBUF and DIFFLEN are
6965    the change buffer and size.  The new buffer is returned in *RETBUF
6966    and *RETLEN.  The new buffer is allocated by xmalloc.
6967 
6968    Return 1 for success.  On failure, call error and return 0.  */
6969 
6970 int
rcs_change_text(name,textbuf,textlen,diffbuf,difflen,retbuf,retlen)6971 rcs_change_text (name, textbuf, textlen, diffbuf, difflen, retbuf, retlen)
6972      const char *name;
6973      char *textbuf;
6974      size_t textlen;
6975      const char *diffbuf;
6976      size_t difflen;
6977      char **retbuf;
6978      size_t *retlen;
6979 {
6980     struct linevector lines;
6981     int ret;
6982 
6983     *retbuf = NULL;
6984     *retlen = 0;
6985 
6986     linevector_init (&lines);
6987 
6988     if (! linevector_add (&lines, textbuf, textlen, NULL, 0))
6989 	error (1, 0, "cannot initialize line vector");
6990 
6991     if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL))
6992     {
6993 	error (0, 0, "invalid change text in %s", name);
6994 	ret = 0;
6995     }
6996     else
6997     {
6998 	char *p;
6999 	size_t n;
7000 	unsigned int ln;
7001 
7002 	n = 0;
7003 	for (ln = 0; ln < lines.nlines; ++ln)
7004 	    /* 1 for \n */
7005 	    n += lines.vector[ln]->len + 1;
7006 
7007 	p = xmalloc (n);
7008 	*retbuf = p;
7009 
7010 	for (ln = 0; ln < lines.nlines; ++ln)
7011 	{
7012 	    memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len);
7013 	    p += lines.vector[ln]->len;
7014 	    if (lines.vector[ln]->has_newline)
7015 		*p++ = '\n';
7016 	}
7017 
7018 	*retlen = p - *retbuf;
7019 	assert (*retlen <= n);
7020 
7021 	ret = 1;
7022     }
7023 
7024     linevector_free (&lines);
7025 
7026     return ret;
7027 }
7028 
7029 /* Walk the deltas in RCS to get to revision VERSION.
7030 
7031    If OP is RCS_ANNOTATE, then write annotations using cvs_output.
7032 
7033    If OP is RCS_FETCH, then put the contents of VERSION into a
7034    newly-malloc'd array and put a pointer to it in *TEXT.  Each line
7035    is \n terminated; the caller is responsible for converting text
7036    files if desired.  The total length is put in *LEN.
7037 
7038    If FP is non-NULL, it should be a file descriptor open to the file
7039    RCS with file position pointing to the deltas.  We close the file
7040    when we are done.
7041 
7042    If LOG is non-NULL, then *LOG is set to the log message of VERSION,
7043    and *LOGLEN is set to the length of the log message.
7044 
7045    On error, give a fatal error.  */
7046 
7047 void
RCS_deltas(rcs,fp,rcsbuf,version,op,text,len,log,loglen)7048 RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen)
7049     RCSNode *rcs;
7050     FILE *fp;
7051     struct rcsbuffer *rcsbuf;
7052     char *version;
7053     enum rcs_delta_op op;
7054     char **text;
7055     size_t *len;
7056     char **log;
7057     size_t *loglen;
7058 {
7059     struct rcsbuffer rcsbuf_local;
7060     char *branchversion;
7061     char *cpversion;
7062     char *key;
7063     char *value;
7064     size_t vallen;
7065     RCSVers *vers;
7066     RCSVers *prev_vers;
7067     RCSVers *trunk_vers;
7068     char *next;
7069     int ishead, isnext, isversion, onbranch;
7070     Node *node;
7071     struct linevector headlines;
7072     struct linevector curlines;
7073     struct linevector trunklines;
7074     int foundhead;
7075 
7076     if (fp == NULL)
7077     {
7078 	rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local);
7079 	rcsbuf = &rcsbuf_local;
7080     }
7081 
7082     ishead = 1;
7083     vers = NULL;
7084     prev_vers = NULL;
7085     trunk_vers = NULL;
7086     next = NULL;
7087     onbranch = 0;
7088     foundhead = 0;
7089 
7090     linevector_init (&curlines);
7091     linevector_init (&headlines);
7092     linevector_init (&trunklines);
7093 
7094     /* We set BRANCHVERSION to the version we are currently looking
7095        for.  Initially, this is the version on the trunk from which
7096        VERSION branches off.  If VERSION is not a branch, then
7097        BRANCHVERSION is just VERSION.  */
7098     branchversion = xstrdup (version);
7099     cpversion = strchr (branchversion, '.');
7100     if (cpversion != NULL)
7101         cpversion = strchr (cpversion + 1, '.');
7102     if (cpversion != NULL)
7103         *cpversion = '\0';
7104 
7105     do {
7106 	if (! rcsbuf_getrevnum (rcsbuf, &key))
7107 	    error (1, 0, "unexpected EOF reading RCS file %s", rcs->path);
7108 
7109 	if (next != NULL && ! STREQ (next, key))
7110 	{
7111 	    /* This is not the next version we need.  It is a branch
7112                version which we want to ignore.  */
7113 	    isnext = 0;
7114 	    isversion = 0;
7115 	}
7116 	else
7117 	{
7118 	    isnext = 1;
7119 
7120 	    /* look up the revision */
7121 	    node = findnode (rcs->versions, key);
7122 	    if (node == NULL)
7123 	        error (1, 0,
7124 		       "mismatch in rcs file %s between deltas and deltatexts",
7125 		       rcs->path);
7126 
7127 	    /* Stash the previous version.  */
7128 	    prev_vers = vers;
7129 
7130 	    vers = (RCSVers *) node->data;
7131 	    next = vers->next;
7132 
7133 	    /* Compare key and trunkversion now, because key points to
7134 	       storage controlled by rcsbuf_getkey.  */
7135 	    if (STREQ (branchversion, key))
7136 	        isversion = 1;
7137 	    else
7138 	        isversion = 0;
7139 	}
7140 
7141 	while (1)
7142 	{
7143 	    if (! rcsbuf_getkey (rcsbuf, &key, &value))
7144 		error (1, 0, "%s does not appear to be a valid rcs file",
7145 		       rcs->path);
7146 
7147 	    if (log != NULL
7148 		&& isversion
7149 		&& STREQ (key, "log")
7150 		&& STREQ (branchversion, version))
7151 	    {
7152 		*log = rcsbuf_valcopy (rcsbuf, value, 0, loglen);
7153 	    }
7154 
7155 	    if (STREQ (key, "text"))
7156 	    {
7157 		rcsbuf_valpolish (rcsbuf, value, 0, &vallen);
7158 		if (ishead)
7159 		{
7160 		    if (! linevector_add (&curlines, value, vallen, NULL, 0))
7161 			error (1, 0, "invalid rcs file %s", rcs->path);
7162 
7163 		    ishead = 0;
7164 		}
7165 		else if (isnext)
7166 		{
7167 		    if (! apply_rcs_changes (&curlines, value, vallen,
7168 					     rcs->path,
7169 					     onbranch ? vers : NULL,
7170 					     onbranch ? NULL : prev_vers))
7171 			error (1, 0, "invalid change text in %s", rcs->path);
7172 		}
7173 		break;
7174 	    }
7175 	}
7176 
7177 	if (isversion)
7178 	{
7179 	    /* This is either the version we want, or it is the
7180                branchpoint to the version we want.  */
7181 	    if (STREQ (branchversion, version))
7182 	    {
7183 	        /* This is the version we want.  */
7184 		linevector_copy (&headlines, &curlines);
7185 		foundhead = 1;
7186 		if (onbranch)
7187 		{
7188 		    /* We have found this version by tracking up a
7189                        branch.  Restore back to the lines we saved
7190                        when we left the trunk, and continue tracking
7191                        down the trunk.  */
7192 		    onbranch = 0;
7193 		    vers = trunk_vers;
7194 		    next = vers->next;
7195 		    linevector_copy (&curlines, &trunklines);
7196 		    linevector_free (&trunklines);
7197 		    linevector_init (&trunklines);
7198 		}
7199 	    }
7200 	    else
7201 	    {
7202 	        Node *p;
7203 
7204 	        /* We need to look up the branch.  */
7205 	        onbranch = 1;
7206 
7207 		if (numdots (branchversion) < 2)
7208 		{
7209 		    unsigned int ln;
7210 
7211 		    /* We are leaving the trunk; save the current
7212                        lines so that we can restore them when we
7213                        continue tracking down the trunk.  */
7214 		    trunk_vers = vers;
7215 		    linevector_copy (&trunklines, &curlines);
7216 
7217 		    /* Reset the version information we have
7218                        accumulated so far.  It only applies to the
7219                        changes from the head to this version.  */
7220 		    for (ln = 0; ln < curlines.nlines; ++ln)
7221 		        curlines.vector[ln]->vers = NULL;
7222 		}
7223 
7224 		/* The next version we want is the entry on
7225                    VERS->branches which matches this branch.  For
7226                    example, suppose VERSION is 1.21.4.3 and
7227                    BRANCHVERSION was 1.21.  Then we look for an entry
7228                    starting with "1.21.4" and we'll put it (probably
7229                    1.21.4.1) in NEXT.  We'll advance BRANCHVERSION by
7230                    two dots (in this example, to 1.21.4.3).  */
7231 
7232 		if (vers->branches == NULL)
7233 		    error (1, 0, "missing expected branches in %s",
7234 			   rcs->path);
7235 		*cpversion = '.';
7236 		++cpversion;
7237 		cpversion = strchr (cpversion, '.');
7238 		if (cpversion == NULL)
7239 		    error (1, 0, "version number confusion in %s",
7240 			   rcs->path);
7241 		for (p = vers->branches->list->next;
7242 		     p != vers->branches->list;
7243 		     p = p->next)
7244 		    if (strncmp (p->key, branchversion,
7245 				 cpversion - branchversion) == 0)
7246 			break;
7247 		if (p == vers->branches->list)
7248 		    error (1, 0, "missing expected branch in %s",
7249 			   rcs->path);
7250 
7251 		next = p->key;
7252 
7253 		cpversion = strchr (cpversion + 1, '.');
7254 		if (cpversion != NULL)
7255 		    *cpversion = '\0';
7256 	    }
7257 	}
7258 	if (op == RCS_FETCH && foundhead)
7259 	    break;
7260     } while (next != NULL);
7261 
7262     free (branchversion);
7263 
7264     rcsbuf_cache (rcs, rcsbuf);
7265 
7266     if (! foundhead)
7267         error (1, 0, "could not find desired version %s in %s",
7268 	       version, rcs->path);
7269 
7270     /* Now print out or return the data we have just computed.  */
7271     switch (op)
7272     {
7273 	case RCS_ANNOTATE:
7274 	    {
7275 		unsigned int ln;
7276 
7277 		for (ln = 0; ln < headlines.nlines; ++ln)
7278 		{
7279 		    char *buf;
7280 		    /* Period which separates year from month in date.  */
7281 		    char *ym;
7282 		    /* Period which separates month from day in date.  */
7283 		    char *md;
7284 		    RCSVers *prvers;
7285 
7286 		    prvers = headlines.vector[ln]->vers;
7287 		    if (prvers == NULL)
7288 			prvers = vers;
7289 
7290 		    buf = xmalloc (strlen (prvers->version) + 24);
7291 		    sprintf (buf, "%-12s (%-8.8s ",
7292 			     prvers->version,
7293 			     prvers->author);
7294 		    cvs_output (buf, 0);
7295 		    free (buf);
7296 
7297 		    /* Now output the date.  */
7298 		    ym = strchr (prvers->date, '.');
7299 		    if (ym == NULL)
7300 		    {
7301 			/* ??- is an ANSI trigraph.  The ANSI way to
7302 			   avoid it is \? but some pre ANSI compilers
7303 			   complain about the unrecognized escape
7304 			   sequence.  Of course string concatenation
7305 			   ("??" "-???") is also an ANSI-ism.  Testing
7306 			   __STDC__ seems to be a can of worms, since
7307 			   compilers do all kinds of things with it.  */
7308 			cvs_output ("??", 0);
7309 			cvs_output ("-???", 0);
7310 			cvs_output ("-??", 0);
7311 		    }
7312 		    else
7313 		    {
7314 			md = strchr (ym + 1, '.');
7315 			if (md == NULL)
7316 			    cvs_output ("??", 0);
7317 			else
7318 			    cvs_output (md + 1, 2);
7319 
7320 			cvs_output ("-", 1);
7321 			cvs_output (month_printname (ym + 1), 0);
7322 			cvs_output ("-", 1);
7323 			/* Only output the last two digits of the year.  Our output
7324 			   lines are long enough as it is without printing the
7325 			   century.  */
7326 			cvs_output (ym - 2, 2);
7327 		    }
7328 		    cvs_output ("): ", 0);
7329 		    if (headlines.vector[ln]->len != 0)
7330 			cvs_output (headlines.vector[ln]->text,
7331 				    headlines.vector[ln]->len);
7332 		    cvs_output ("\n", 1);
7333 		}
7334 	    }
7335 	    break;
7336 	case RCS_FETCH:
7337 	    {
7338 		char *p;
7339 		size_t n;
7340 		unsigned int ln;
7341 
7342 		assert (text != NULL);
7343 		assert (len != NULL);
7344 
7345 		n = 0;
7346 		for (ln = 0; ln < headlines.nlines; ++ln)
7347 		    /* 1 for \n */
7348 		    n += headlines.vector[ln]->len + 1;
7349 		p = xmalloc (n);
7350 		*text = p;
7351 		for (ln = 0; ln < headlines.nlines; ++ln)
7352 		{
7353 		    memcpy (p, headlines.vector[ln]->text,
7354 			    headlines.vector[ln]->len);
7355 		    p += headlines.vector[ln]->len;
7356 		    if (headlines.vector[ln]->has_newline)
7357 			*p++ = '\n';
7358 		}
7359 		*len = p - *text;
7360 		assert (*len <= n);
7361 	    }
7362 	    break;
7363     }
7364 
7365     linevector_free (&curlines);
7366     linevector_free (&headlines);
7367     linevector_free (&trunklines);
7368 
7369     return;
7370 }
7371 
7372 /* Read the information for a single delta from the RCS buffer RCSBUF,
7373    whose name is RCSFILE.  *KEYP and *VALP are either NULL, or the
7374    first key/value pair to read, as set by rcsbuf_getkey. Return NULL
7375    if there are no more deltas.  Store the key/value pair which
7376    terminated the read in *KEYP and *VALP.  */
7377 
7378 static RCSVers *
getdelta(rcsbuf,rcsfile,keyp,valp)7379 getdelta (rcsbuf, rcsfile, keyp, valp)
7380     struct rcsbuffer *rcsbuf;
7381     char *rcsfile;
7382     char **keyp;
7383     char **valp;
7384 {
7385     RCSVers *vnode;
7386     char *key, *value, *cp;
7387     Node *kv;
7388 
7389     /* Get revision number if it wasn't passed in. This uses
7390        rcsbuf_getkey because it doesn't croak when encountering
7391        unexpected input.  As a result, we have to play unholy games
7392        with `key' and `value'. */
7393     if (*keyp != NULL)
7394     {
7395 	key = *keyp;
7396 	value = *valp;
7397     }
7398     else
7399     {
7400 	if (! rcsbuf_getkey (rcsbuf, &key, &value))
7401 	    error (1, 0, "%s: unexpected EOF", rcsfile);
7402     }
7403 
7404     /* Make sure that it is a revision number and not a cabbage
7405        or something. */
7406     for (cp = key;
7407 	 (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
7408 	 cp++)
7409 	/* do nothing */ ;
7410     /* Note that when comparing with RCSDATE, we are not massaging
7411        VALUE from the string found in the RCS file.  This is OK since
7412        we know exactly what to expect.  */
7413     if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0)
7414     {
7415 	*keyp = key;
7416 	*valp = value;
7417 	return NULL;
7418     }
7419 
7420     vnode = (RCSVers *) xmalloc (sizeof (RCSVers));
7421     memset (vnode, 0, sizeof (RCSVers));
7422 
7423     vnode->version = xstrdup (key);
7424 
7425     /* Grab the value of the date from value.  Note that we are not
7426        massaging VALUE from the string found in the RCS file.  */
7427     cp = value + (sizeof RCSDATE) - 1;	/* skip the "date" keyword */
7428     while (whitespace (*cp))		/* take space off front of value */
7429 	cp++;
7430 
7431     vnode->date = xstrdup (cp);
7432 
7433     /* Get author field.  */
7434     if (! rcsbuf_getkey (rcsbuf, &key, &value))
7435     {
7436 	error (1, 0, "unexpected end of file reading %s", rcsfile);
7437     }
7438     if (! STREQ (key, "author"))
7439 	error (1, 0, "\
7440 unable to parse %s; `author' not in the expected place", rcsfile);
7441     vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL);
7442 
7443     /* Get state field.  */
7444     if (! rcsbuf_getkey (rcsbuf, &key, &value))
7445     {
7446 	error (1, 0, "unexpected end of file reading %s", rcsfile);
7447     }
7448     if (! STREQ (key, "state"))
7449 	error (1, 0, "\
7450 unable to parse %s; `state' not in the expected place", rcsfile);
7451     vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL);
7452     /* The value is optional, according to rcsfile(5).  */
7453     if (value != NULL && STREQ (value, "dead"))
7454     {
7455 	vnode->dead = 1;
7456     }
7457 
7458     /* Note that "branches" and "next" are in fact mandatory, according
7459        to doc/RCSFILES.  */
7460 
7461     /* fill in the branch list (if any branches exist) */
7462     if (! rcsbuf_getkey (rcsbuf, &key, &value))
7463     {
7464 	error (1, 0, "unexpected end of file reading %s", rcsfile);
7465     }
7466     if (STREQ (key, RCSDESC))
7467     {
7468 	*keyp = key;
7469 	*valp = value;
7470 	/* Probably could/should be a fatal error.  */
7471 	error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile);
7472 	return vnode;
7473     }
7474     if (value != (char *) NULL)
7475     {
7476 	vnode->branches = getlist ();
7477 	/* Note that we are not massaging VALUE from the string found
7478            in the RCS file.  */
7479 	do_branches (vnode->branches, value);
7480     }
7481 
7482     /* fill in the next field if there is a next revision */
7483     if (! rcsbuf_getkey (rcsbuf, &key, &value))
7484     {
7485 	error (1, 0, "unexpected end of file reading %s", rcsfile);
7486     }
7487     if (STREQ (key, RCSDESC))
7488     {
7489 	*keyp = key;
7490 	*valp = value;
7491 	/* Probably could/should be a fatal error.  */
7492 	error (0, 0, "warning: 'next' keyword missing from %s", rcsfile);
7493 	return vnode;
7494     }
7495     if (value != (char *) NULL)
7496 	vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL);
7497 
7498     /*
7499      * XXX - this is where we put the symbolic link stuff???
7500      * (into newphrases in the deltas).
7501      */
7502     while (1)
7503     {
7504 	if (! rcsbuf_getkey (rcsbuf, &key, &value))
7505 	    error (1, 0, "unexpected end of file reading %s", rcsfile);
7506 
7507 	/* The `desc' keyword is the end of the deltas. */
7508 	if (strcmp (key, RCSDESC) == 0)
7509 	    break;
7510 
7511 #ifdef PRESERVE_PERMISSIONS_SUPPORT
7512 
7513 	/* The `hardlinks' value is a group of words, which must
7514 	   be parsed separately and added as a list to vnode->hardlinks. */
7515 	if (strcmp (key, "hardlinks") == 0)
7516 	{
7517 	    char *word;
7518 
7519 	    vnode->hardlinks = getlist();
7520 	    while ((word = rcsbuf_valword (rcsbuf, &value)) != NULL)
7521 	    {
7522 		Node *n = getnode();
7523 		n->key = word;
7524 		addnode (vnode->hardlinks, n);
7525 	    }
7526 	    continue;
7527 	}
7528 #endif
7529 
7530 	/* Enable use of repositories created by certain obsolete
7531 	   versions of CVS.  This code should remain indefinately;
7532 	   there is no procedure for converting old repositories, and
7533 	   checking for it is harmless.  */
7534 	if (STREQ (key, RCSDEAD))
7535 	{
7536 	    vnode->dead = 1;
7537 	    if (vnode->state != NULL)
7538 		free (vnode->state);
7539 	    vnode->state = xstrdup ("dead");
7540 	    continue;
7541 	}
7542 	/* if we have a new revision number, we're done with this delta */
7543 	for (cp = key;
7544 	     (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
7545 	     cp++)
7546 	    /* do nothing */ ;
7547 	/* Note that when comparing with RCSDATE, we are not massaging
7548 	   VALUE from the string found in the RCS file.  This is OK
7549 	   since we know exactly what to expect.  */
7550 	if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
7551 	    break;
7552 
7553 	/* At this point, key and value represent a user-defined field
7554 	   in the delta node. */
7555 	if (vnode->other_delta == NULL)
7556 	    vnode->other_delta = getlist ();
7557 	kv = getnode ();
7558 	kv->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
7559 	kv->key = xstrdup (key);
7560 	kv->data = rcsbuf_valcopy (rcsbuf, value, kv->type == RCSFIELD,
7561 				   (size_t *) NULL);
7562 	if (addnode (vnode->other_delta, kv) != 0)
7563 	{
7564 	    /* Complaining about duplicate keys in newphrases seems
7565 	       questionable, in that we don't know what they mean and
7566 	       doc/RCSFILES has no prohibition on several newphrases
7567 	       with the same key.  But we can't store more than one as
7568 	       long as we store them in a List *.  */
7569 	    error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
7570 		   key, rcsfile);
7571 	    freenode (kv);
7572 	}
7573     }
7574 
7575     /* Return the key which caused us to fail back to the caller.  */
7576     *keyp = key;
7577     *valp = value;
7578 
7579     return vnode;
7580 }
7581 
7582 static void
freedeltatext(d)7583 freedeltatext (d)
7584     Deltatext *d;
7585 {
7586     if (d->version != NULL)
7587 	free (d->version);
7588     if (d->log != NULL)
7589 	free (d->log);
7590     if (d->text != NULL)
7591 	free (d->text);
7592     if (d->other != (List *) NULL)
7593 	dellist (&d->other);
7594     free (d);
7595 }
7596 
7597 static Deltatext *
RCS_getdeltatext(rcs,fp,rcsbuf)7598 RCS_getdeltatext (rcs, fp, rcsbuf)
7599     RCSNode *rcs;
7600     FILE *fp;
7601     struct rcsbuffer *rcsbuf;
7602 {
7603     char *num;
7604     char *key, *value;
7605     Node *p;
7606     Deltatext *d;
7607 
7608     /* Get the revision number. */
7609     if (! rcsbuf_getrevnum (rcsbuf, &num))
7610     {
7611 	/* If num == NULL, it means we reached EOF naturally.  That's
7612 	   fine. */
7613 	if (num == NULL)
7614 	    return NULL;
7615 	else
7616 	    error (1, 0, "%s: unexpected EOF", rcs->path);
7617     }
7618 
7619     p = findnode (rcs->versions, num);
7620     if (p == NULL)
7621 	error (1, 0, "mismatch in rcs file %s between deltas and deltatexts",
7622 	       rcs->path);
7623 
7624     d = (Deltatext *) xmalloc (sizeof (Deltatext));
7625     d->version = xstrdup (num);
7626 
7627     /* Get the log message. */
7628     if (! rcsbuf_getkey (rcsbuf, &key, &value))
7629 	error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num);
7630     if (! STREQ (key, "log"))
7631 	error (1, 0, "%s, delta %s: expected `log', got `%s'",
7632 	       rcs->path, num, key);
7633     d->log = rcsbuf_valcopy (rcsbuf, value, 0, (size_t *) NULL);
7634 
7635     /* Get random newphrases. */
7636     d->other = getlist();
7637     while (1)
7638     {
7639 	if (! rcsbuf_getkey (rcsbuf, &key, &value))
7640 	    error (1, 0, "%s, delta %s: unexpected EOF", rcs->path, num);
7641 
7642 	if (STREQ (key, "text"))
7643 	    break;
7644 
7645 	p = getnode();
7646 	p->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
7647 	p->key = xstrdup (key);
7648 	p->data = rcsbuf_valcopy (rcsbuf, value, p->type == RCSFIELD,
7649 				  (size_t *) NULL);
7650 	if (addnode (d->other, p) < 0)
7651 	{
7652 	    error (0, 0, "warning: %s, delta %s: duplicate field `%s'",
7653 		   rcs->path, num, key);
7654 	}
7655     }
7656 
7657     /* Get the change text. We already know that this key is `text'. */
7658     d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len);
7659 
7660     return d;
7661 }
7662 
7663 /* RCS output functions, for writing RCS format files from RCSNode
7664    structures.
7665 
7666    For most of this work, RCS 5.7 uses an `aprintf' function which aborts
7667    program upon error.  Instead, these functions check the output status
7668    of the stream right before closing it, and aborts if an error condition
7669    is found.  The RCS solution is probably the better one: it produces
7670    more overhead, but will produce a clearer diagnostic in the case of
7671    catastrophic error.  In either case, however, the repository will probably
7672    not get corrupted. */
7673 
7674 static int
putsymbol_proc(symnode,fparg)7675 putsymbol_proc (symnode, fparg)
7676     Node *symnode;
7677     void *fparg;
7678 {
7679     FILE *fp = (FILE *) fparg;
7680 
7681     /* A fiddly optimization: this code used to just call fprintf, but
7682        in an old repository with hundreds of tags this can get called
7683        hundreds of thousands of times when doing a cvs tag.  Since
7684        tagging is a relatively common operation, and using putc and
7685        fputs is just as comprehensible, the change is worthwhile.  */
7686     putc ('\n', fp);
7687     putc ('\t', fp);
7688     fputs (symnode->key, fp);
7689     putc (':', fp);
7690     fputs (symnode->data, fp);
7691     return 0;
7692 }
7693 
7694 static int putlock_proc PROTO ((Node *, void *));
7695 
7696 /* putlock_proc is like putsymbol_proc, but key and data are reversed. */
7697 
7698 static int
putlock_proc(symnode,fp)7699 putlock_proc (symnode, fp)
7700     Node *symnode;
7701     void *fp;
7702 {
7703     return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->data, symnode->key);
7704 }
7705 
7706 static int
putrcsfield_proc(node,vfp)7707 putrcsfield_proc (node, vfp)
7708     Node *node;
7709     void *vfp;
7710 {
7711     FILE *fp = (FILE *) vfp;
7712 
7713     /* Some magic keys used internally by CVS start with `;'. Skip them. */
7714     if (node->key[0] == ';')
7715 	return 0;
7716 
7717     fprintf (fp, "\n%s\t", node->key);
7718     if (node->data != NULL)
7719     {
7720 	/* If the field's value contains evil characters,
7721 	   it must be stringified. */
7722 	/* FIXME: This does not quite get it right.  "7jk8f" is not a legal
7723 	   value for a value in a newpharse, according to doc/RCSFILES,
7724 	   because digits are not valid in an "id".  We might do OK by
7725 	   always writing strings (enclosed in @@).  Would be nice to
7726 	   explicitly mention this one way or another in doc/RCSFILES.
7727 	   A case where we are wrong in a much more clear-cut way is that
7728 	   we let through non-graphic characters such as whitespace and
7729 	   control characters.  */
7730 
7731 	if (node->type == RCSCMPFLD || strpbrk (node->data, "$,.:;@") == NULL)
7732 	    fputs (node->data, fp);
7733 	else
7734 	{
7735 	    putc ('@', fp);
7736 	    expand_at_signs (node->data, (off_t) strlen (node->data), fp);
7737 	    putc ('@', fp);
7738 	}
7739     }
7740 
7741     /* desc, log and text fields should not be terminated with semicolon;
7742        all other fields should be. */
7743     if (! STREQ (node->key, "desc") &&
7744 	! STREQ (node->key, "log") &&
7745 	! STREQ (node->key, "text"))
7746     {
7747 	putc (';', fp);
7748     }
7749     return 0;
7750 }
7751 
7752 #ifdef PRESERVE_PERMISSIONS_SUPPORT
7753 
7754 /* Save a filename in a `hardlinks' RCS field.  NODE->KEY will contain
7755    a full pathname, but currently only basenames are stored in the RCS
7756    node.  Assume that the filename includes nasty characters and
7757    @-escape it. */
7758 
7759 static int
puthardlink_proc(node,vfp)7760 puthardlink_proc (node, vfp)
7761     Node *node;
7762     void *vfp;
7763 {
7764     FILE *fp = (FILE *) vfp;
7765     char *basename = strrchr (node->key, '/');
7766 
7767     if (basename == NULL)
7768 	basename = node->key;
7769     else
7770 	++basename;
7771 
7772     putc ('\t', fp);
7773     putc ('@', fp);
7774     (void) expand_at_signs (basename, strlen (basename), fp);
7775     putc ('@', fp);
7776 
7777     return 0;
7778 }
7779 
7780 #endif
7781 
7782 /* Output the admin node for RCS into stream FP. */
7783 
7784 static void
RCS_putadmin(rcs,fp)7785 RCS_putadmin (rcs, fp)
7786     RCSNode *rcs;
7787     FILE *fp;
7788 {
7789     fprintf (fp, "%s\t%s;\n", RCSHEAD, rcs->head ? rcs->head : "");
7790     if (rcs->branch)
7791 	fprintf (fp, "%s\t%s;\n", RCSBRANCH, rcs->branch);
7792 
7793     fputs ("access", fp);
7794     if (rcs->access)
7795     {
7796 	char *p, *s;
7797 	s = xstrdup (rcs->access);
7798 	for (p = strtok (s, " \n\t"); p != NULL; p = strtok (NULL, " \n\t"))
7799 	    fprintf (fp, "\n\t%s", p);
7800 	free (s);
7801     }
7802     fputs (";\n", fp);
7803 
7804     fputs (RCSSYMBOLS, fp);
7805     /* If we haven't had to convert the symbols to a list yet, don't
7806        force a conversion now; just write out the string.  */
7807     if (rcs->symbols == NULL && rcs->symbols_data != NULL)
7808     {
7809 	fputs ("\n\t", fp);
7810 	fputs (rcs->symbols_data, fp);
7811     }
7812     else
7813 	walklist (RCS_symbols (rcs), putsymbol_proc, (void *) fp);
7814     fputs (";\n", fp);
7815 
7816     fputs ("locks", fp);
7817     if (rcs->locks_data)
7818 	fprintf (fp, "\t%s", rcs->locks_data);
7819     else if (rcs->locks)
7820 	walklist (rcs->locks, putlock_proc, (void *) fp);
7821     if (rcs->strict_locks)
7822 	fprintf (fp, "; strict");
7823     fputs (";\n", fp);
7824 
7825     if (rcs->comment)
7826     {
7827 	fprintf (fp, "comment\t@");
7828 	expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp);
7829 	fputs ("@;\n", fp);
7830     }
7831     if (rcs->expand && ! STREQ (rcs->expand, "kv"))
7832 	fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand);
7833 
7834     walklist (rcs->other, putrcsfield_proc, (void *) fp);
7835 
7836     putc ('\n', fp);
7837 }
7838 
7839 static void
putdelta(vers,fp)7840 putdelta (vers, fp)
7841     RCSVers *vers;
7842     FILE *fp;
7843 {
7844     Node *bp, *start;
7845 
7846     /* Skip if no revision was supplied, or if it is outdated (cvs admin -o) */
7847     if (vers == NULL || vers->outdated)
7848 	return;
7849 
7850     fprintf (fp, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
7851 	     vers->version,
7852 	     RCSDATE, vers->date,
7853 	     "author", vers->author,
7854 	     "state", vers->state ? vers->state : "");
7855 
7856     if (vers->branches != NULL)
7857     {
7858 	start = vers->branches->list;
7859 	for (bp = start->next; bp != start; bp = bp->next)
7860 	    fprintf (fp, "\n\t%s", bp->key);
7861     }
7862 
7863     fprintf (fp, ";\nnext\t%s;", vers->next ? vers->next : "");
7864 
7865     walklist (vers->other_delta, putrcsfield_proc, fp);
7866 
7867 #ifdef PRESERVE_PERMISSIONS_SUPPORT
7868     if (vers->hardlinks)
7869     {
7870 	fprintf (fp, "\nhardlinks");
7871 	walklist (vers->hardlinks, puthardlink_proc, fp);
7872 	putc (';', fp);
7873     }
7874 #endif
7875     putc ('\n', fp);
7876 }
7877 
7878 static void
RCS_putdtree(rcs,rev,fp)7879 RCS_putdtree (rcs, rev, fp)
7880     RCSNode *rcs;
7881     char *rev;
7882     FILE *fp;
7883 {
7884     RCSVers *versp;
7885     Node *p, *branch;
7886 
7887     if (rev == NULL)
7888 	return;
7889 
7890     /* Find the delta node for this revision. */
7891     p = findnode (rcs->versions, rev);
7892     if (p == NULL)
7893     {
7894         error (1, 0,
7895                "error parsing repository file %s, file may be corrupt.",
7896                rcs->path);
7897     }
7898 
7899     versp = (RCSVers *) p->data;
7900 
7901     /* Print the delta node and recurse on its `next' node.  This prints
7902        the trunk.  If there are any branches printed on this revision,
7903        print those trunks as well. */
7904     putdelta (versp, fp);
7905     RCS_putdtree (rcs, versp->next, fp);
7906     if (versp->branches != NULL)
7907     {
7908 	branch = versp->branches->list;
7909 	for (p = branch->next; p != branch; p = p->next)
7910 	    RCS_putdtree (rcs, p->key, fp);
7911     }
7912 }
7913 
7914 static void
RCS_putdesc(rcs,fp)7915 RCS_putdesc (rcs, fp)
7916     RCSNode *rcs;
7917     FILE *fp;
7918 {
7919     fprintf (fp, "\n\n%s\n@", RCSDESC);
7920     if (rcs->desc != NULL)
7921     {
7922 	off_t len = (off_t) strlen (rcs->desc);
7923 	if (len > 0)
7924 	{
7925 	    expand_at_signs (rcs->desc, len, fp);
7926 	    if (rcs->desc[len-1] != '\n')
7927 		putc ('\n', fp);
7928 	}
7929     }
7930     fputs ("@\n", fp);
7931 }
7932 
7933 static void
putdeltatext(fp,d)7934 putdeltatext (fp, d)
7935     FILE *fp;
7936     Deltatext *d;
7937 {
7938     fprintf (fp, "\n\n%s\nlog\n@", d->version);
7939     if (d->log != NULL)
7940     {
7941 	int loglen = strlen (d->log);
7942 	expand_at_signs (d->log, (off_t) loglen, fp);
7943 	if (d->log[loglen-1] != '\n')
7944 	    putc ('\n', fp);
7945     }
7946     putc ('@', fp);
7947 
7948     walklist (d->other, putrcsfield_proc, fp);
7949 
7950     fputs ("\ntext\n@", fp);
7951     if (d->text != NULL)
7952 	expand_at_signs (d->text, (off_t) d->len, fp);
7953     fputs ("@\n", fp);
7954 }
7955 
7956 /* TODO: the whole mechanism for updating deltas is kludgey... more
7957    sensible would be to supply all the necessary info in a `newdeltatext'
7958    field for RCSVers nodes. -twp */
7959 
7960 /* Copy delta text nodes from FIN to FOUT.  If NEWDTEXT is non-NULL, it
7961    is a new delta text node, and should be added to the tree at the
7962    node whose revision number is INSERTPT.  (Note that trunk nodes are
7963    written in decreasing order, and branch nodes are written in
7964    increasing order.) */
7965 
7966 static void
RCS_copydeltas(rcs,fin,rcsbufin,fout,newdtext,insertpt)7967 RCS_copydeltas (rcs, fin, rcsbufin, fout, newdtext, insertpt)
7968     RCSNode *rcs;
7969     FILE *fin;
7970     struct rcsbuffer *rcsbufin;
7971     FILE *fout;
7972     Deltatext *newdtext;
7973     char *insertpt;
7974 {
7975     int actions;
7976     RCSVers *dadmin;
7977     Node *np;
7978     int insertbefore, found;
7979     char *bufrest;
7980     int nls;
7981     size_t buflen;
7982     char buf[8192];
7983     int got;
7984 
7985     /* Count the number of versions for which we have to do some
7986        special operation.  */
7987     actions = walklist (rcs->versions, count_delta_actions, (void *) NULL);
7988 
7989     /* Make a note of whether NEWDTEXT should be inserted
7990        before or after its INSERTPT. */
7991     insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1);
7992 
7993     while (actions != 0 || newdtext != NULL)
7994     {
7995 	Deltatext *dtext;
7996 
7997 	dtext = RCS_getdeltatext (rcs, fin, rcsbufin);
7998 
7999 	/* We shouldn't hit EOF here, because that would imply that
8000            some action was not taken, or that we could not insert
8001            NEWDTEXT.  */
8002 	if (dtext == NULL)
8003 	    error (1, 0, "internal error: EOF too early in RCS_copydeltas");
8004 
8005 	found = (insertpt != NULL && STREQ (dtext->version, insertpt));
8006 	if (found && insertbefore)
8007 	{
8008 	    putdeltatext (fout, newdtext);
8009 	    newdtext = NULL;
8010 	    insertpt = NULL;
8011 	}
8012 
8013 	np = findnode (rcs->versions, dtext->version);
8014 	dadmin = (RCSVers *) np->data;
8015 
8016 	/* If this revision has been outdated, just skip it. */
8017 	if (dadmin->outdated)
8018 	{
8019 	    freedeltatext (dtext);
8020 	    --actions;
8021 	    continue;
8022 	}
8023 
8024 	/* Update the change text for this delta.  New change text
8025 	   data may come from cvs admin -m, cvs admin -o, or cvs ci. */
8026 	if (dadmin->text != NULL)
8027 	{
8028 	    if (dadmin->text->log != NULL || dadmin->text->text != NULL)
8029 		--actions;
8030 	    if (dadmin->text->log != NULL)
8031 	    {
8032 		free (dtext->log);
8033 		dtext->log = dadmin->text->log;
8034 		dadmin->text->log = NULL;
8035 	    }
8036 	    if (dadmin->text->text != NULL)
8037 	    {
8038 		free (dtext->text);
8039 		dtext->text = dadmin->text->text;
8040 		dtext->len = dadmin->text->len;
8041 		dadmin->text->text = NULL;
8042 	    }
8043 	}
8044 	putdeltatext (fout, dtext);
8045 	freedeltatext (dtext);
8046 
8047 	if (found && !insertbefore)
8048 	{
8049 	    putdeltatext (fout, newdtext);
8050 	    newdtext = NULL;
8051 	    insertpt = NULL;
8052 	}
8053     }
8054 
8055     /* Copy the rest of the file directly, without bothering to
8056        interpret it.  The caller will handle error checking by calling
8057        ferror.
8058 
8059        We just wrote a newline to the file, either in putdeltatext or
8060        in the caller.  However, we may not have read the corresponding
8061        newline from the file, because rcsbuf_getkey returns as soon as
8062        it finds the end of the '@' string for the desc or text key.
8063        Therefore, we may read three newlines when we should really
8064        only write two, and we check for that case here.  This is not
8065        an semantically important issue; we only do it to make our RCS
8066        files look traditional.  */
8067 
8068     nls = 3;
8069 
8070     rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen);
8071     if (buflen > 0)
8072     {
8073 	if (bufrest[0] != '\n'
8074 	    || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0)
8075 	{
8076 	    nls = 0;
8077 	}
8078 	else
8079 	{
8080 	    if (buflen < 3)
8081 		nls -= buflen;
8082 	    else
8083 	    {
8084 		++bufrest;
8085 		--buflen;
8086 		nls = 0;
8087 	    }
8088 	}
8089 
8090 	fwrite (bufrest, 1, buflen, fout);
8091     }
8092 
8093     while ((got = fread (buf, 1, sizeof buf, fin)) != 0)
8094     {
8095 	if (nls > 0
8096 	    && got >= nls
8097 	    && buf[0] == '\n'
8098 	    && strncmp (buf, "\n\n\n", nls) == 0)
8099 	{
8100 	    fwrite (buf + 1, 1, got - 1, fout);
8101 	}
8102 	else
8103 	{
8104 	    fwrite (buf, 1, got, fout);
8105 	}
8106 
8107 	nls = 0;
8108     }
8109 }
8110 
8111 /* A helper procedure for RCS_copydeltas.  This is called via walklist
8112    to count the number of RCS revisions for which some special action
8113    is required.  */
8114 
8115 static int
count_delta_actions(np,ignore)8116 count_delta_actions (np, ignore)
8117     Node *np;
8118     void *ignore;
8119 {
8120     RCSVers *dadmin;
8121 
8122     dadmin = (RCSVers *) np->data;
8123 
8124     if (dadmin->outdated)
8125 	return 1;
8126 
8127     if (dadmin->text != NULL
8128 	&& (dadmin->text->log != NULL || dadmin->text->text != NULL))
8129     {
8130 	return 1;
8131     }
8132 
8133     return 0;
8134 }
8135 
8136 /*
8137  * Clean up temporary files
8138  */
8139 RETSIGTYPE
rcs_cleanup()8140 rcs_cleanup ()
8141 {
8142     /* Note that the checks for existence_error are because we are
8143        called from a signal handler, so we don't know whether the
8144        files got created.  */
8145 
8146     /* FIXME: Do not perform buffered I/O from an interrupt handler like
8147        this (via error).  However, I'm leaving the error-calling code there
8148        in the hope that on the rare occasion the error call is actually made
8149        (e.g., a fluky I/O error or permissions problem prevents the deletion
8150        of a just-created file) reentrancy won't be an issue.  */
8151     if (rcs_lockfile != NULL)
8152     {
8153 	char *tmp = rcs_lockfile;
8154 	rcs_lockfile = NULL;
8155 	if (rcs_lockfd >= 0)
8156 	{
8157 	    if (close (rcs_lockfd) != 0)
8158 		error (0, errno, "error closing lock file %s", tmp);
8159 	    rcs_lockfd = -1;
8160 	}
8161 	if (unlink_file (tmp) < 0
8162 	    && !existence_error (errno))
8163 	    error (0, errno, "cannot remove %s", tmp);
8164     }
8165 }
8166 
8167 /* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style
8168    locking on the specified RCSFILE: for a file called `foo,v', open
8169    for writing a file called `,foo,'.
8170 
8171    Note that we what do here is quite different from what RCS does.
8172    RCS creates the ,foo, file before it reads the RCS file (if it
8173    knows that it will be writing later), so that it actually serves as
8174    a lock.  We don't; instead we rely on CVS writelocks.  This means
8175    that if someone is running RCS on the file at the same time they
8176    are running CVS on it, they might lose (we read the file,
8177    then RCS writes it, then we write it, clobbering the
8178    changes made by RCS).  I believe the current sentiment about this
8179    is "well, don't do that".
8180 
8181    A concern has been expressed about whether adopting the RCS
8182    strategy would slow us down.  I don't think so, since we need to
8183    write the ,foo, file anyway (unless perhaps if O_EXCL is slower or
8184    something).
8185 
8186    These do not perform quite the same function as the RCS -l option
8187    for locking files: they are intended to prevent competing RCS
8188    processes from stomping all over each other's laundry.  Hence,
8189    they are `internal' locking functions.
8190 
8191    If there is an error, give a fatal error; if we return we always
8192    return a non-NULL value.  */
8193 
8194 static FILE *
rcs_internal_lockfile(rcsfile)8195 rcs_internal_lockfile (rcsfile)
8196     char *rcsfile;
8197 {
8198     struct stat rstat;
8199     FILE *fp;
8200     static int first_call = 1;
8201 
8202     if (first_call)
8203     {
8204 	first_call = 0;
8205 	/* clean up if we get a signal */
8206 #ifdef SIGABRT
8207 	(void) SIG_register (SIGABRT, rcs_cleanup);
8208 #endif
8209 #ifdef SIGHUP
8210 	(void) SIG_register (SIGHUP, rcs_cleanup);
8211 #endif
8212 #ifdef SIGINT
8213 	(void) SIG_register (SIGINT, rcs_cleanup);
8214 #endif
8215 #ifdef SIGQUIT
8216 	(void) SIG_register (SIGQUIT, rcs_cleanup);
8217 #endif
8218 #ifdef SIGPIPE
8219 	(void) SIG_register (SIGPIPE, rcs_cleanup);
8220 #endif
8221 #ifdef SIGTERM
8222 	(void) SIG_register (SIGTERM, rcs_cleanup);
8223 #endif
8224     }
8225 
8226     /* Get the lock file name: `,file,' for RCS file `file,v'. */
8227     assert (rcs_lockfile == NULL);
8228     assert (rcs_lockfd < 0);
8229     rcs_lockfile = rcs_lockfilename (rcsfile);
8230 
8231     /* Use the existing RCS file mode, or read-only if this is a new
8232        file.  (Really, this is a lie -- if this is a new file,
8233        RCS_checkin uses the permissions from the working copy.  For
8234        actually creating the file, we use 0444 as a safe default mode.) */
8235     if (stat (rcsfile, &rstat) < 0)
8236     {
8237 	if (existence_error (errno))
8238 	    rstat.st_mode = S_IRUSR | S_IRGRP | S_IROTH;
8239 	else
8240 	    error (1, errno, "cannot stat %s", rcsfile);
8241     }
8242 
8243     /* Try to open exclusively.  POSIX.1 guarantees that O_EXCL|O_CREAT
8244        guarantees an exclusive open.  According to the RCS source, with
8245        NFS v2 we must also throw in O_TRUNC and use an open mask that makes
8246        the file unwriteable.  For extensive justification, see the comments for
8247        rcswriteopen() in rcsedit.c, in RCS 5.7.  This is kind of pointless
8248        in the CVS case; see comment at the start of this file concerning
8249        general ,foo, file strategy.
8250 
8251        There is some sentiment that with NFSv3 and such, that one can
8252        rely on O_EXCL these days.  This might be true for unix (I
8253        don't really know), but I am still pretty skeptical in the case
8254        of the non-unix systems.  */
8255     rcs_lockfd = open (rcs_lockfile,
8256 		       OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
8257 		       S_IRUSR | S_IRGRP | S_IROTH);
8258 
8259     if (rcs_lockfd < 0)
8260     {
8261 	error (1, errno, "could not open lock file `%s'", rcs_lockfile);
8262     }
8263 
8264     /* Force the file permissions, and return a stream object. */
8265     /* Because we change the modes later, we don't worry about
8266        this in the non-HAVE_FCHMOD case.  */
8267 #ifdef HAVE_FCHMOD
8268     if (fchmod (rcs_lockfd, rstat.st_mode) < 0)
8269 	error (1, errno, "cannot change mode for %s", rcs_lockfile);
8270 #endif
8271     fp = fdopen (rcs_lockfd, FOPEN_BINARY_WRITE);
8272     if (fp == NULL)
8273 	error (1, errno, "cannot fdopen %s", rcs_lockfile);
8274 
8275     return fp;
8276 }
8277 
8278 static void
rcs_internal_unlockfile(fp,rcsfile)8279 rcs_internal_unlockfile (fp, rcsfile)
8280     FILE *fp;
8281     char *rcsfile;
8282 {
8283     assert (rcs_lockfile != NULL);
8284     assert (rcs_lockfd >= 0);
8285 
8286     /* Abort if we could not write everything successfully to LOCKFILE.
8287        This is not a great error-handling mechanism, but should prevent
8288        corrupting the repository. */
8289 
8290     if (ferror (fp))
8291 	/* The only case in which using errno here would be meaningful
8292 	   is if we happen to have left errno unmolested since the call
8293 	   which produced the error (e.g. fprintf).  That is pretty
8294 	   fragile even if it happens to sometimes be true.  The real
8295 	   solution is to check each call to fprintf rather than waiting
8296 	   until the end like this.  */
8297 	error (1, 0, "error writing to lock file %s", rcs_lockfile);
8298     if (fclose (fp) == EOF)
8299 	error (1, errno, "error closing lock file %s", rcs_lockfile);
8300     rcs_lockfd = -1;
8301 
8302     rename_file (rcs_lockfile, rcsfile);
8303 
8304     {
8305 	/* Use a temporary to make sure there's no interval
8306 	   (after rcs_lockfile has been freed but before it's set to NULL)
8307 	   during which the signal handler's use of rcs_lockfile would
8308 	   reference freed memory.  */
8309 	char *tmp = rcs_lockfile;
8310 	rcs_lockfile = NULL;
8311 	free (tmp);
8312     }
8313 }
8314 
8315 static char *
rcs_lockfilename(rcsfile)8316 rcs_lockfilename (rcsfile)
8317     char *rcsfile;
8318 {
8319     char *lockfile, *lockp;
8320     char *rcsbase, *rcsp, *rcsend;
8321     int rcslen;
8322 
8323     /* Create the lockfile name. */
8324     rcslen = strlen (rcsfile);
8325     lockfile = (char *) xmalloc (rcslen + 10);
8326     rcsbase = last_component (rcsfile);
8327     rcsend = rcsfile + rcslen - sizeof(RCSEXT);
8328     for (lockp = lockfile, rcsp = rcsfile; rcsp < rcsbase; ++rcsp)
8329 	*lockp++ = *rcsp;
8330     *lockp++ = ',';
8331     while (rcsp <= rcsend)
8332 	*lockp++ = *rcsp++;
8333     *lockp++ = ',';
8334     *lockp = '\0';
8335 
8336     return lockfile;
8337 }
8338 
8339 /* Rewrite an RCS file.  The basic idea here is that the caller should
8340    first call RCS_reparsercsfile, then munge the data structures as
8341    desired (via RCS_delete_revs, RCS_settag, &c), then call RCS_rewrite.  */
8342 
8343 void
RCS_rewrite(rcs,newdtext,insertpt)8344 RCS_rewrite (rcs, newdtext, insertpt)
8345     RCSNode *rcs;
8346     Deltatext *newdtext;
8347     char *insertpt;
8348 {
8349     FILE *fin, *fout;
8350     struct rcsbuffer rcsbufin;
8351 
8352     if (noexec)
8353 	return;
8354 
8355     /* Make sure we're operating on an actual file and not a symlink.  */
8356     resolve_symlink (&(rcs->path));
8357 
8358     fout = rcs_internal_lockfile (rcs->path);
8359 
8360     RCS_putadmin (rcs, fout);
8361     RCS_putdtree (rcs, rcs->head, fout);
8362     RCS_putdesc (rcs, fout);
8363 
8364     /* Open the original RCS file and seek to the first delta text. */
8365     rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin);
8366 
8367     /* Update delta_pos to the current position in the output file.
8368        Do NOT move these statements: they must be done after fin has
8369        been positioned at the old delta_pos, but before any delta
8370        texts have been written to fout. */
8371     rcs->delta_pos = ftell (fout);
8372     if (rcs->delta_pos == -1)
8373 	error (1, errno, "cannot ftell in RCS file %s", rcs->path);
8374 
8375     RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt);
8376 
8377     /* We don't want to call rcsbuf_cache here, since we're about to
8378        delete the file.  */
8379     rcsbuf_close (&rcsbufin);
8380     if (ferror (fin))
8381 	/* The only case in which using errno here would be meaningful
8382 	   is if we happen to have left errno unmolested since the call
8383 	   which produced the error (e.g. fread).  That is pretty
8384 	   fragile even if it happens to sometimes be true.  The real
8385 	   solution is to make sure that all the code which reads
8386 	   from fin checks for errors itself (some does, some doesn't).  */
8387 	error (0, 0, "warning: when closing RCS file `%s'", rcs->path);
8388     if (fclose (fin) < 0)
8389 	error (0, errno, "warning: closing RCS file `%s'", rcs->path);
8390 
8391     rcs_internal_unlockfile (fout, rcs->path);
8392 }
8393 
8394 /* Abandon changes to an RCS file. */
8395 
8396 void
RCS_abandon(rcs)8397 RCS_abandon (rcs)
8398     RCSNode *rcs;
8399 {
8400     free_rcsnode_contents (rcs);
8401     rcs->symbols_data = NULL;
8402     rcs->expand = NULL;
8403     rcs->access = NULL;
8404     rcs->locks_data = NULL;
8405     rcs->comment = NULL;
8406     rcs->desc = NULL;
8407     rcs->flags |= PARTIAL;
8408 }
8409 
8410 /*
8411  * For a given file with full pathname PATH and revision number REV,
8412  * produce a file label suitable for passing to diff.  The default
8413  * file label as used by RCS 5.7 looks like this:
8414  *
8415  *	FILENAME <tab> YYYY/MM/DD <sp> HH:MM:SS <tab> REVNUM
8416  *
8417  * The date and time used are the revision's last checkin date and time.
8418  * If REV is NULL, use the working copy's mtime instead.
8419  *
8420  * /dev/null is not statted but assumed to have been created on the Epoch.
8421  * At least using the POSIX.2 definition of patch, this should cause creation
8422  * of files on platforms such as Windoze where the null IO device isn't named
8423  * /dev/null to be parsed by patch properly.
8424  */
8425 char *
make_file_label(path,rev,rcs)8426 make_file_label (path, rev, rcs)
8427     char *path;
8428     char *rev;
8429     RCSNode *rcs;
8430 {
8431     char datebuf[MAXDATELEN + 1];
8432     char *label;
8433 
8434     label = (char *) xmalloc (strlen (path)
8435 			      + (rev == NULL ? 0 : strlen (rev) + 1)
8436 			      + MAXDATELEN
8437 			      + 2);
8438 
8439     if (rev)
8440     {
8441 	char date[MAXDATELEN + 1];
8442 	/* revs cannot be attached to /dev/null ... duh. */
8443 	assert (strcmp(DEVNULL, path));
8444 	RCS_getrevtime (rcs, rev, datebuf, 0);
8445 	(void) date_to_internet (date, datebuf);
8446 	(void) sprintf (label, "-L%s\t%s\t%s", path, date, rev);
8447     }
8448     else
8449     {
8450 	struct stat sb;
8451 	struct tm *wm = NULL;
8452 
8453 	if (strcmp(DEVNULL, path))
8454 	{
8455 	    char *file = last_component (path);
8456 	    if (CVS_STAT (file, &sb) < 0)
8457 		error (0, 1, "could not get info for `%s'", path);
8458 	    else
8459 		wm = gmtime (&sb.st_mtime);
8460 	}
8461 	if (wm == NULL)
8462 	{
8463 	    time_t t = 0;
8464 	    wm = gmtime(&t);
8465 	}
8466 
8467 	(void) tm_to_internet (datebuf, wm);
8468 	(void) sprintf (label, "-L%s\t%s", path, datebuf);
8469     }
8470     return label;
8471 }
8472