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