Initial revision
[alioth/cvs.git] / src / rcs.c
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
17 #include "cvs.h"
18 #include "edit.h"
19 #include "hardlink.h"
20
21 /* These need to be source after cvs.h or HAVE_MMAP won't be set... */
22 #ifdef HAVE_MMAP
23 # include "getpagesize.h"
24 # include <sys/mman.h>
25
26 /* Define MAP_FILE when it isn't otherwise.  */
27 # ifndef MAP_FILE
28 #  define MAP_FILE 0
29 # endif
30 /* Define MAP_FAILED for old systems which neglect to.  */
31 # ifndef MAP_FAILED
32 #  define MAP_FAILED ((void *)-1)
33 # endif
34 #endif
35
36 /* The RCS -k options, and a set of enums that must match the array.
37    These come first so that we can use enum kflag in function
38    prototypes.  */
39 static const char *const kflags[] =
40   {"kv", "kvl", "k", "v", "o", "b", NULL};
41 enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B };
42
43 /* A structure we use to buffer the contents of an RCS file.  The
44    various fields are only referenced directly by the rcsbuf_*
45    functions.  We declare the struct here so that we can allocate it
46    on the stack, rather than in memory.  */
47
48 struct rcsbuffer
49 {
50     /* Points to the current position in the buffer.  */
51     char *ptr;
52     /* Points just after the last valid character in the buffer.  */
53     char *ptrend;
54     /* The file.  */
55     FILE *fp;
56     /* The name of the file, used for error messages.  */
57     const char *filename;
58     /* The starting file position of the data in the buffer.  */
59     unsigned long pos;
60     /* The length of the value.  */
61     size_t vlen;
62     /* Whether the value contains an '@' string.  If so, we can not
63        compress whitespace characters.  */
64     int at_string;
65     /* The number of embedded '@' characters in an '@' string.  If
66        this is non-zero, we must search the string for pairs of '@'
67        and convert them to a single '@'.  */
68     int embedded_at;
69 };
70
71 static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile);
72 static char *RCS_getdatebranch (RCSNode * rcs, const char *date,
73                                 const char *branch);
74 static void rcsbuf_open (struct rcsbuffer *, FILE *fp,
75                          const char *filename, unsigned long pos);
76 static void rcsbuf_close (struct rcsbuffer *);
77 static int rcsbuf_getkey (struct rcsbuffer *, char **keyp, char **valp);
78 static int rcsbuf_getrevnum (struct rcsbuffer *, char **revp);
79 static char *rcsbuf_fill (struct rcsbuffer *, char *ptr, char **keyp,
80                           char **valp);
81 static int rcsbuf_valcmp (struct rcsbuffer *);
82 static char *rcsbuf_valcopy (struct rcsbuffer *, char *val, int polish,
83                              size_t *lenp);
84 static void rcsbuf_valpolish (struct rcsbuffer *, char *val, int polish,
85                               size_t *lenp);
86 static void rcsbuf_valpolish_internal (struct rcsbuffer *, char *to,
87                                        const char *from, size_t *lenp);
88 static off_t rcsbuf_ftello (struct rcsbuffer *);
89 static void rcsbuf_get_buffered (struct rcsbuffer *, char **datap,
90                                  size_t *lenp);
91 static void rcsbuf_cache (RCSNode *, struct rcsbuffer *);
92 static void rcsbuf_cache_close (void);
93 static void rcsbuf_cache_open (RCSNode *, off_t, FILE **, struct rcsbuffer *);
94 static int checkmagic_proc (Node *p, void *closure);
95 static void do_branches (List * list, char *val);
96 static void do_symbols (List * list, char *val);
97 static void do_locks (List * list, char *val);
98 static void free_rcsnode_contents (RCSNode *);
99 static void free_rcsvers_contents (RCSVers *);
100 static void rcsvers_delproc (Node * p);
101 static char *translate_symtag (RCSNode *, const char *);
102 static char *RCS_addbranch (RCSNode *, const char *);
103 static char *truncate_revnum_in_place (char *);
104 static char *truncate_revnum (const char *);
105 static char *printable_date (const char *);
106 static char *escape_keyword_value (const char *, int *);
107 static void expand_keywords (RCSNode *, RCSVers *, const char *,
108                              const char *, size_t, enum kflag, char *,
109                              size_t, char **, size_t *);
110 static void cmp_file_buffer (void *, const char *, size_t);
111
112 /* Routines for reading, parsing and writing RCS files. */
113 static RCSVers *getdelta (struct rcsbuffer *, char *, char **, char **);
114 static Deltatext *RCS_getdeltatext (RCSNode *, FILE *, struct rcsbuffer *);
115 static void freedeltatext (Deltatext *);
116
117 static void RCS_putadmin (RCSNode *, FILE *);
118 static void RCS_putdtree (RCSNode *, char *, FILE *);
119 static void RCS_putdesc (RCSNode *, FILE *);
120 static void putdelta (RCSVers *, FILE *);
121 static int putrcsfield_proc (Node *, void *);
122 static int putsymbol_proc (Node *, void *);
123 static void RCS_copydeltas (RCSNode *, FILE *, struct rcsbuffer *, FILE *,
124                             Deltatext *, char *);
125 static int count_delta_actions (Node *, void *);
126 static void putdeltatext (FILE *, Deltatext *);
127
128 static FILE *rcs_internal_lockfile (char *);
129 static void rcs_internal_unlockfile (FILE *, char *);
130 static char *rcs_lockfilename (const char *);
131
132 /* The RCS file reading functions are called a lot, and they do some
133    string comparisons.  This macro speeds things up a bit by skipping
134    the function call when the first characters are different.  It
135    evaluates its arguments multiple times.  */
136 #define STREQ(a, b) (*(char *)(a) == *(char *)(b) && strcmp ((a), (b)) == 0)
137
138 static char * getfullCVSname (char *, char **);
139
140 /*
141  * We don't want to use isspace() from the C library because:
142  *
143  * 1. The definition of "whitespace" in RCS files includes ASCII
144  *    backspace, but the C locale doesn't.
145  * 2. isspace is an very expensive function call in some implementations
146  *    due to the addition of wide character support.
147  */
148 static const char spacetab[] = {
149         0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, /* 0x00 - 0x0f */
150         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
151         1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
152         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
153         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
154         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
155         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */
156         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
157         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
158         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
159         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
160         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
161         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
162         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
163         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
164         0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0  /* 0xf0 - 0xff */
165 };
166
167 #define whitespace(c)   (spacetab[(unsigned char)c] != 0)
168
169 static char *rcs_lockfile = NULL;
170 static int rcs_lockfd = -1;
171
172
173
174 /*
175  * char *
176  * locate_rcs ( const char* file, const char *repository , int *inattic )
177  *
178  * Find an RCS file in the repository, case insensitively when the cased name
179  * doesn't exist, we are running as the server, and a client has asked us to
180  * ignore case.
181  *
182  * Most parts of CVS will want to rely instead on RCS_parse which calls this
183  * function and is called by recurse.c which then puts the result in useful
184  * places like the rcs field of struct file_info.
185  *
186  * INPUTS
187  *
188  *  repository          the repository (including the directory)
189  *  file                the filename within that directory (without RCSEXT).
190  *  inattic             NULL or a pointer to the output boolean
191  *
192  * OUTPUTS
193  *
194  *  inattic             If this input was non-null, the destination will be
195  *                      set to true if the file was found in the attic or
196  *                      false if not.  If no RCS file is found, this value
197  *                      is undefined.
198  *
199  * RETURNS
200  *
201  *  a newly-malloc'd array containing the absolute pathname of the RCS
202  *  file that was found or NULL when none was found.
203  *
204  * ERRORS
205  *
206  *  errno can be set by the return value of the final call to
207  *  locate_file_in_dir().  This should resolve to the system's existence error
208  *  value (sometime ENOENT) if the Attic directory did not exist and ENOENT if
209  *  the Attic was found but no matching files were found in the Attic or its
210  *  parent.
211  */
212 static char *
213 locate_rcs (const char *repository, const char *file, int *inattic)
214 {
215     char *retval;
216
217     /* First, try to find the file as cased. */
218     retval = xmalloc (strlen (repository)
219                       + sizeof (CVSATTIC)
220                       + strlen (file)
221                       + sizeof (RCSEXT)
222                       + 3);
223     sprintf (retval, "%s/%s%s", repository, file, RCSEXT);
224     if (isreadable (retval))
225     {
226         if (inattic)
227             *inattic = 0;
228         return retval;
229     }
230     sprintf (retval, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
231     if (isreadable (retval))
232     {
233         if (inattic)
234             *inattic = 1;
235         return retval;
236     }
237     free (retval);
238
239     return NULL;
240 }
241
242
243
244 /* A few generic thoughts on error handling, in particular the
245    printing of unexpected characters that we find in the RCS file
246    (that is, why we use '\x%x' rather than %c or some such).
247
248    * Avoiding %c means we don't have to worry about what is printable
249    and other such stuff.  In error handling, often better to keep it
250    simple.
251
252    * Hex rather than decimal or octal because character set standards
253    tend to use hex.
254
255    * Saying "character 0x%x" might make it sound like we are printing
256    a file offset.  So we use '\x%x'.
257
258    * Would be nice to print the offset within the file, but I can
259    imagine various portability hassles (in particular, whether
260    unsigned long is always big enough to hold file offsets).  */
261
262 /* Parse an rcsfile given a user file name and a repository.  If there is
263    an error, we print an error message and return NULL.  If the file
264    does not exist, we return NULL without printing anything (I'm not
265    sure this allows the caller to do anything reasonable, but it is
266    the current behavior).  */
267 RCSNode *
268 RCS_parse (const char *file, const char *repos)
269 {
270     RCSNode *rcs;
271     FILE *fp;
272     RCSNode *retval = NULL;
273     char *rcsfile;
274     int inattic;
275
276     /* We're creating a new RCSNode, so there is no hope of finding it
277        in the cache.  */
278     rcsbuf_cache_close ();
279
280     if (!(rcsfile = locate_rcs (repos, file, &inattic)))
281     {
282         /* Handle the error cases */
283     }
284     else if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ))) 
285     {
286         rcs = RCS_parsercsfile_i (fp, rcsfile);
287         if (rcs)
288         {       
289             rcs->flags |= VALID;
290             if (inattic)
291                 rcs->flags |= INATTIC;
292         }
293
294         free (rcsfile);
295         retval = rcs;
296     }
297     else if (!existence_error (errno))
298     {
299         error (0, errno, "cannot open `%s'", rcsfile);
300         free (rcsfile);
301     }
302
303     return retval;
304 }
305
306
307
308 /*
309  * Parse a specific rcsfile.
310  */
311 RCSNode *
312 RCS_parsercsfile (const char *rcsfile)
313 {
314     FILE *fp;
315     RCSNode *rcs;
316
317     /* We're creating a new RCSNode, so there is no hope of finding it
318        in the cache.  */
319     rcsbuf_cache_close ();
320
321     /* open the rcsfile */
322     if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL)
323     {
324         error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
325         return NULL;
326     }
327
328     rcs = RCS_parsercsfile_i (fp, rcsfile);
329
330     return rcs;
331 }
332
333
334
335 /*
336  */ 
337 static RCSNode *
338 RCS_parsercsfile_i (FILE *fp, const char *rcsfile)
339 {
340     RCSNode *rdata;
341     struct rcsbuffer rcsbuf;
342     char *key, *value;
343
344     /* make a node */
345     rdata = xmalloc (sizeof (RCSNode));
346     memset (rdata, 0, sizeof (RCSNode));
347     rdata->refcount = 1;
348     rdata->path = xstrdup (rcsfile);
349     rdata->print_path = xstrdup (primary_root_inverse_translate (rcsfile));
350
351     /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header.
352
353        Most cvs operations on the main branch don't need any more
354        information.  Those that do call RCS_reparsercsfile to parse
355        the rest of the header and the deltas.  */
356
357     rcsbuf_open (&rcsbuf, fp, rcsfile, 0);
358
359     if (! rcsbuf_getkey (&rcsbuf, &key, &value))
360         goto l_error;
361     if (STREQ (key, RCSDESC))
362         goto l_error;
363
364     if (STREQ (RCSHEAD, key) && value != NULL)
365         rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
366
367     if (! rcsbuf_getkey (&rcsbuf, &key, &value))
368         goto l_error;
369     if (STREQ (key, RCSDESC))
370         goto l_error;
371
372     if (STREQ (RCSBRANCH, key) && value != NULL)
373     {
374         char *cp;
375
376         rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
377         if ((numdots (rdata->branch) & 1) != 0)
378         {
379             /* turn it into a branch if it's a revision */
380             cp = strrchr (rdata->branch, '.');
381             *cp = '\0';
382         }
383     }
384
385     /* Look ahead for expand, stopping when we see desc or a revision
386        number.  */
387     while (1)
388     {
389         char *cp;
390
391         if (STREQ (RCSEXPAND, key))
392         {
393             rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
394             break;
395         }
396
397         for (cp = key;
398              (isdigit ((unsigned char)*cp) || *cp == '.') && *cp != '\0';
399              cp++)
400             /* do nothing */ ;
401         if (*cp == '\0')
402             break;
403
404         if (STREQ (RCSDESC, key))
405             break;
406
407         if (! rcsbuf_getkey (&rcsbuf, &key, &value))
408             break;
409     }
410
411     rdata->flags |= PARTIAL;
412
413     rcsbuf_cache (rdata, &rcsbuf);
414
415     return rdata;
416
417 l_error:
418     error (0, 0, "`%s' does not appear to be a valid rcs file",
419            rcsfile);
420     rcsbuf_close (&rcsbuf);
421     freercsnode (&rdata);
422     fclose (fp);
423     return NULL;
424 }
425
426
427
428 /* Do the real work of parsing an RCS file.
429
430    On error, die with a fatal error; if it returns at all it was successful.
431
432    If PFP is NULL, close the file when done.  Otherwise, leave it open
433    and store the FILE * in *PFP.  */
434 void
435 RCS_reparsercsfile (RCSNode *rdata, FILE **pfp, struct rcsbuffer *rcsbufp)
436 {
437     FILE *fp;
438     char *rcsfile;
439     struct rcsbuffer rcsbuf;
440     Node *q, *kv;
441     RCSVers *vnode;
442     int gotkey;
443     char *cp;
444     char *key, *value;
445
446     assert (rdata != NULL);
447     rcsfile = rdata->path;
448
449     rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf);
450
451     /* make a node */
452     /* This probably shouldn't be done until later: if a file has an
453        empty revision tree (which is permissible), rdata->versions
454        should be NULL. -twp */
455     rdata->versions = getlist ();
456
457     /*
458      * process all the special header information, break out when we get to
459      * the first revision delta
460      */
461     gotkey = 0;
462     for (;;)
463     {
464         /* get the next key/value pair */
465         if (!gotkey)
466         {
467             if (! rcsbuf_getkey (&rcsbuf, &key, &value))
468             {
469                 error (1, 0, "`%s' does not appear to be a valid rcs file",
470                        rcsfile);
471             }
472         }
473
474         gotkey = 0;
475
476         /* Skip head, branch and expand tags; we already have them. */
477         if (STREQ (key, RCSHEAD)
478             || STREQ (key, RCSBRANCH)
479             || STREQ (key, RCSEXPAND))
480         {
481             continue;
482         }
483
484         if (STREQ (key, "access"))
485         {
486             if (value != NULL)
487             {
488                 /* We pass the POLISH parameter as 1 because
489                    RCS_addaccess expects nothing but spaces.  FIXME:
490                    It would be easy and more efficient to change
491                    RCS_addaccess.  */
492                 if (rdata->access)
493                 {
494                     error (0, 0,
495                            "Duplicate `access' keyword found in RCS file.");
496                     free (rdata->access);
497                 }
498                 rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
499             }
500             continue;
501         }
502
503         /* We always save lock information, so that we can handle
504            -kkvl correctly when checking out a file. */
505         if (STREQ (key, "locks"))
506         {
507             if (value != NULL)
508             {
509                 if (rdata->locks_data)
510                 {
511                     error (0, 0,
512                            "Duplicate `locks' keyword found in RCS file.");
513                     free (rdata->locks_data);
514                 }
515                 rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
516             }
517             if (! rcsbuf_getkey (&rcsbuf, &key, &value))
518             {
519                 error (1, 0, "premature end of file reading %s", rcsfile);
520             }
521             if (STREQ (key, "strict") && value == NULL)
522             {
523                 rdata->strict_locks = 1;
524             }
525             else
526                 gotkey = 1;
527             continue;
528         }
529
530         if (STREQ (RCSSYMBOLS, key))
531         {
532             if (value != NULL)
533             {
534                 if (rdata->symbols_data)
535                 {
536                     error (0, 0,
537                            "Duplicate `%s' keyword found in RCS file.",
538                            RCSSYMBOLS);
539                     free (rdata->symbols_data);
540                 }
541                 rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
542             }
543             continue;
544         }
545
546         /*
547          * check key for '.''s and digits (probably a rev) if it is a
548          * revision or `desc', we are done with the headers and are down to the
549          * revision deltas, so we break out of the loop
550          */
551         for (cp = key;
552              (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
553              cp++)
554              /* do nothing */ ;
555         /* Note that when comparing with RCSDATE, we are not massaging
556            VALUE from the string found in the RCS file.  This is OK
557            since we know exactly what to expect.  */
558         if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0)
559             break;
560
561         if (STREQ (key, RCSDESC))
562             break;
563
564         if (STREQ (key, "comment"))
565         {
566             if (rdata->comment)
567             {
568                 error (0, 0,
569                        "warning: duplicate key `%s' in RCS file `%s'",
570                        key, rcsfile);
571                 free (rdata->comment);
572             }
573             rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
574             continue;
575         }
576         if (rdata->other == NULL)
577             rdata->other = getlist ();
578         kv = getnode ();
579         kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
580         kv->key = xstrdup (key);
581         kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD, NULL);
582         if (addnode (rdata->other, kv) != 0)
583         {
584             error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
585                    key, rcsfile);
586             freenode (kv);
587         }
588
589         /* if we haven't grabbed it yet, we didn't want it */
590     }
591
592     /* We got out of the loop, so we have the first part of the first
593        revision delta in KEY (the revision) and VALUE (the date key
594        and its value).  This is what getdelta expects to receive.  */
595
596     while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL)
597     {
598         /* get the node */
599         q = getnode ();
600         q->type = RCSVERS;
601         q->delproc = rcsvers_delproc;
602         q->data = vnode;
603         q->key = vnode->version;
604
605         /* add the nodes to the list */
606         if (addnode (rdata->versions, q) != 0)
607         {
608 #if 0
609                 purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
610                          q->key, rcsfile);
611                 freenode (q);
612 #endif
613         }
614     }
615
616     /* Here KEY and VALUE are whatever caused getdelta to return NULL.  */
617
618     if (STREQ (key, RCSDESC))
619     {
620         if (rdata->desc != NULL)
621         {
622             error (0, 0,
623                    "warning: duplicate key `%s' in RCS file `%s'",
624                    key, rcsfile);
625             free (rdata->desc);
626         }
627         rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
628     }
629
630     rdata->delta_pos = rcsbuf_ftello (&rcsbuf);
631
632     if (pfp == NULL)
633         rcsbuf_cache (rdata, &rcsbuf);
634     else
635     {
636         *pfp = fp;
637         *rcsbufp = rcsbuf;
638     }
639     rdata->flags &= ~PARTIAL;
640 }
641
642
643
644 /* Move RCS into or out of the Attic, depending on TOATTIC.  If the
645    file is already in the desired place, return without doing
646    anything.  At some point may want to think about how this relates
647    to RCS_rewrite but that is a bit hairy (if one wants renames to be
648    atomic, or that kind of thing).  If there is an error, print a message
649    and return 1.  On success, return 0.  */
650 int
651 RCS_setattic (RCSNode *rcs, int toattic)
652 {
653     char *newpath;
654     const char *p;
655     char *q;
656
657     /* Some systems aren't going to let us rename an open file.  */
658     rcsbuf_cache_close ();
659
660     /* Could make the pathname computations in this file, and probably
661        in other parts of rcs.c too, easier if the REPOS and FILE
662        arguments to RCS_parse got stashed in the RCSNode.  */
663
664     if (toattic)
665     {
666         mode_t omask;
667
668         if (rcs->flags & INATTIC)
669             return 0;
670
671         /* Example: rcs->path is "/foo/bar/baz,v".  */
672         newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
673         p = last_component (rcs->path);
674         strncpy (newpath, rcs->path, p - rcs->path);
675         strcpy (newpath + (p - rcs->path), CVSATTIC);
676
677         /* Create the Attic directory if it doesn't exist.  */
678         omask = umask (cvsumask);
679         if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
680             error (0, errno, "cannot make directory %s", newpath);
681         (void) umask (omask);
682
683         strcat (newpath, "/");
684         strcat (newpath, p);
685
686         if (CVS_RENAME (rcs->path, newpath) < 0)
687         {
688             int save_errno = errno;
689
690             /* The checks for isreadable look awfully fishy, but
691                I'm going to leave them here for now until I
692                can think harder about whether they take care of
693                some cases which should be handled somehow.  */
694
695             if (isreadable (rcs->path) || !isreadable (newpath))
696             {
697                 error (0, save_errno, "cannot rename %s to %s",
698                        rcs->path, newpath);
699                 free (newpath);
700                 return 1;
701             }
702         }
703     }
704     else
705     {
706         if (!(rcs->flags & INATTIC))
707             return 0;
708
709         newpath = xmalloc (strlen (rcs->path));
710
711         /* Example: rcs->path is "/foo/bar/Attic/baz,v".  */
712         p = last_component (rcs->path);
713         strncpy (newpath, rcs->path, p - rcs->path - 1);
714         newpath[p - rcs->path - 1] = '\0';
715         q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
716         assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
717         strcpy (q, p);
718
719         if (CVS_RENAME (rcs->path, newpath) < 0)
720         {
721             error (0, errno, "failed to move `%s' out of the attic",
722                    rcs->path);
723             free (newpath);
724             return 1;
725         }
726     }
727
728     free (rcs->path);
729     rcs->path = newpath;
730
731     return 0;
732 }
733
734
735
736 /*
737  * Fully parse the RCS file.  Store all keyword/value pairs, fetch the
738  * log messages for each revision, and fetch add and delete counts for
739  * each revision (we could fetch the entire text for each revision,
740  * but the only caller, log_fileproc, doesn't need that information,
741  * so we don't waste the memory required to store it).  The add and
742  * delete counts are stored on the OTHER field of the RCSVERSNODE
743  * structure, under the names ";add" and ";delete", so that we don't
744  * waste the memory space of extra fields in RCSVERSNODE for code
745  * which doesn't need this information.
746  */
747 void
748 RCS_fully_parse (RCSNode *rcs)
749 {
750     FILE *fp;
751     struct rcsbuffer rcsbuf;
752
753     RCS_reparsercsfile (rcs, &fp, &rcsbuf);
754
755     while (1)
756     {
757         char *key, *value;
758         Node *vers;
759         RCSVers *vnode;
760
761         /* Rather than try to keep track of how much information we
762            have read, just read to the end of the file.  */
763         if (!rcsbuf_getrevnum (&rcsbuf, &key))
764             break;
765
766         vers = findnode (rcs->versions, key);
767         if (vers == NULL)
768             error (1, 0,
769                    "mismatch in rcs file %s between deltas and deltatexts (%s)",
770                    rcs->print_path, key);
771
772         vnode = vers->data;
773
774         while (rcsbuf_getkey (&rcsbuf, &key, &value))
775         {
776             if (!STREQ (key, "text"))
777             {
778                 Node *kv;
779
780                 if (vnode->other == NULL)
781                     vnode->other = getlist ();
782                 kv = getnode ();
783                 kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
784                 kv->key = xstrdup (key);
785                 kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
786                                            NULL);
787                 if (addnode (vnode->other, kv) != 0)
788                 {
789                     error (0, 0,
790                            "\
791 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
792                            key, vnode->version, rcs->print_path);
793                     freenode (kv);
794                 }
795
796                 continue;
797             }
798
799             if (!STREQ (vnode->version, rcs->head))
800             {
801                 unsigned long add, del;
802                 char buf[50];
803                 Node *kv;
804
805                 /* This is a change text.  Store the add and delete
806                    counts.  */
807                 add = 0;
808                 del = 0;
809                 if (value != NULL)
810                 {
811                     size_t vallen;
812                     const char *cp;
813
814                     rcsbuf_valpolish (&rcsbuf, value, 0, &vallen);
815                     cp = value;
816                     while (cp < value + vallen)
817                     {
818                         char op;
819                         unsigned long count;
820
821                         op = *cp++;
822                         if (op != 'a' && op  != 'd')
823                             error (1, 0, "\
824 unrecognized operation '\\x%x' in %s",
825                                    op, rcs->print_path);
826                         (void) strtoul (cp, (char **) &cp, 10);
827                         if (*cp++ != ' ')
828                             error (1, 0, "space expected in %s revision %s",
829                                    rcs->print_path, vnode->version);
830                         count = strtoul (cp, (char **) &cp, 10);
831                         if (*cp++ != '\012')
832                             error (1, 0, "linefeed expected in %s revision %s",
833                                    rcs->print_path, vnode->version);
834
835                         if (op == 'd')
836                             del += count;
837                         else
838                         {
839                             add += count;
840                             while (count != 0)
841                             {
842                                 if (*cp == '\012')
843                                     --count;
844                                 else if (cp == value + vallen)
845                                 {
846                                     if (count != 1)
847                                         error (1, 0, "\
848 premature end of value in %s revision %s",
849                                                rcs->print_path, vnode->version);
850                                     else
851                                         break;
852                                 }
853                                 ++cp;
854                             }
855                         }
856                     }
857                 }
858
859                 sprintf (buf, "%lu", add);
860                 kv = getnode ();
861                 kv->type = RCSFIELD;
862                 kv->key = xstrdup (";add");
863                 kv->data = xstrdup (buf);
864                 if (addnode (vnode->other, kv) != 0)
865                 {
866                     error (0, 0,
867                            "\
868 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
869                            key, vnode->version, rcs->print_path);
870                     freenode (kv);
871                 }
872
873                 sprintf (buf, "%lu", del);
874                 kv = getnode ();
875                 kv->type = RCSFIELD;
876                 kv->key = xstrdup (";delete");
877                 kv->data = xstrdup (buf);
878                 if (addnode (vnode->other, kv) != 0)
879                 {
880                     error (0, 0,
881                            "\
882 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
883                            key, vnode->version, rcs->print_path);
884                     freenode (kv);
885                 }
886             }
887
888             /* We have found the "text" key which ends the data for
889                this revision.  Break out of the loop and go on to the
890                next revision.  */
891             break;
892         }
893     }
894
895     rcsbuf_cache (rcs, &rcsbuf);
896 }
897
898
899
900 /*
901  * freercsnode - free up the info for an RCSNode
902  */
903 void
904 freercsnode (RCSNode **rnodep)
905 {
906     if (rnodep == NULL || *rnodep == NULL)
907         return;
908
909     ((*rnodep)->refcount)--;
910     if ((*rnodep)->refcount != 0)
911     {
912         *rnodep = NULL;
913         return;
914     }
915     free ((*rnodep)->path);
916     free ((*rnodep)->print_path);
917     if ((*rnodep)->head != NULL)
918         free ((*rnodep)->head);
919     if ((*rnodep)->branch != NULL)
920         free ((*rnodep)->branch);
921     free_rcsnode_contents (*rnodep);
922     free (*rnodep);
923     *rnodep = NULL;
924 }
925
926
927
928 /*
929  * free_rcsnode_contents - free up the contents of an RCSNode without
930  * freeing the node itself, or the file name, or the head, or the
931  * path.  This returns the RCSNode to the state it is in immediately
932  * after a call to RCS_parse.
933  */
934 static void
935 free_rcsnode_contents (RCSNode *rnode)
936 {
937     dellist (&rnode->versions);
938     if (rnode->symbols != NULL)
939         dellist (&rnode->symbols);
940     if (rnode->symbols_data != NULL)
941         free (rnode->symbols_data);
942     if (rnode->expand != NULL)
943         free (rnode->expand);
944     if (rnode->other != NULL)
945         dellist (&rnode->other);
946     if (rnode->access != NULL)
947         free (rnode->access);
948     if (rnode->locks_data != NULL)
949         free (rnode->locks_data);
950     if (rnode->locks != NULL)
951         dellist (&rnode->locks);
952     if (rnode->comment != NULL)
953         free (rnode->comment);
954     if (rnode->desc != NULL)
955         free (rnode->desc);
956 }
957
958
959
960 /* free_rcsvers_contents -- free up the contents of an RCSVers node,
961    but also free the pointer to the node itself. */
962 /* Note: The `hardlinks' list is *not* freed, since it is merely a
963    pointer into the `hardlist' structure (defined in hardlink.c), and
964    that structure is freed elsewhere in the program. */
965 static void
966 free_rcsvers_contents (RCSVers *rnode)
967 {
968     if (rnode->branches != NULL)
969         dellist (&rnode->branches);
970     if (rnode->date != NULL)
971         free (rnode->date);
972     if (rnode->next != NULL)
973         free (rnode->next);
974     if (rnode->author != NULL)
975         free (rnode->author);
976     if (rnode->state != NULL)
977         free (rnode->state);
978     if (rnode->other != NULL)
979         dellist (&rnode->other);
980     if (rnode->other_delta != NULL)
981         dellist (&rnode->other_delta);
982     if (rnode->text != NULL)
983         freedeltatext (rnode->text);
984     free (rnode);
985 }
986
987
988
989 /*
990  * rcsvers_delproc - free up an RCSVers type node
991  */
992 static void
993 rcsvers_delproc (Node *p)
994 {
995     free_rcsvers_contents (p->data);
996 }
997
998
999
1000 /* These functions retrieve keys and values from an RCS file using a
1001    buffer.  We use this somewhat complex approach because it turns out
1002    that for many common operations, CVS spends most of its time
1003    reading keys, so it's worth doing some fairly hairy optimization.  */
1004
1005 /* The number of bytes we try to read each time we need more data.  */
1006
1007 #define RCSBUF_BUFSIZE (8192)
1008
1009 /* The buffer we use to store data.  This grows as needed.  */
1010
1011 static char *rcsbuf_buffer = NULL;
1012 static size_t rcsbuf_buffer_size = 0;
1013
1014 /* Whether rcsbuf_buffer is in use.  This is used as a sanity check.  */
1015
1016 static int rcsbuf_inuse;
1017
1018 /* Set up to start gathering keys and values from an RCS file.  This
1019    initializes RCSBUF.  */
1020
1021 static void
1022 rcsbuf_open (struct rcsbuffer *rcsbuf, FILE *fp, const char *filename,
1023              long unsigned int pos)
1024 {
1025     if (rcsbuf_inuse)
1026         error (1, 0, "rcsbuf_open: internal error");
1027     rcsbuf_inuse = 1;
1028
1029 #ifdef HAVE_MMAP
1030     {
1031         /* When we have mmap, it is much more efficient to let the system do the
1032          * buffering and caching for us
1033          */
1034         struct stat fs;
1035         size_t mmap_off = 0;
1036
1037         if ( fstat (fileno(fp), &fs) < 0 )
1038             error ( 1, errno, "Could not stat RCS archive %s for mapping", filename );
1039
1040         if (pos)
1041         {
1042             size_t ps = getpagesize ();
1043             mmap_off = ( pos / ps ) * ps;
1044         }
1045
1046         /* Map private here since this particular buffer is read only */
1047         rcsbuf_buffer = mmap ( NULL, fs.st_size - mmap_off,
1048                                 PROT_READ | PROT_WRITE,
1049                                 MAP_PRIVATE, fileno(fp), mmap_off );
1050         if ( rcsbuf_buffer == NULL || rcsbuf_buffer == MAP_FAILED )
1051             error ( 1, errno, "Could not map memory to RCS archive %s", filename );
1052
1053         rcsbuf_buffer_size = fs.st_size - mmap_off;
1054         rcsbuf->ptr = rcsbuf_buffer + pos - mmap_off;
1055         rcsbuf->ptrend = rcsbuf_buffer + fs.st_size - mmap_off;
1056         rcsbuf->pos = mmap_off;
1057     }
1058 #else /* !HAVE_MMAP */
1059     if (rcsbuf_buffer_size < RCSBUF_BUFSIZE)
1060         expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE);
1061
1062     rcsbuf->ptr = rcsbuf_buffer;
1063     rcsbuf->ptrend = rcsbuf_buffer;
1064     rcsbuf->pos = pos;
1065 #endif /* HAVE_MMAP */
1066     rcsbuf->fp = fp;
1067     rcsbuf->filename = filename;
1068     rcsbuf->vlen = 0;
1069     rcsbuf->at_string = 0;
1070     rcsbuf->embedded_at = 0;
1071 }
1072
1073
1074
1075 /* Stop gathering keys from an RCS file.  */
1076 static void
1077 rcsbuf_close (struct rcsbuffer *rcsbuf)
1078 {
1079     if (! rcsbuf_inuse)
1080         error (1, 0, "rcsbuf_close: internal error");
1081 #ifdef HAVE_MMAP
1082     munmap ( rcsbuf_buffer, rcsbuf_buffer_size );
1083 #endif
1084     rcsbuf_inuse = 0;
1085 }
1086
1087
1088
1089 /* Read a key/value pair from an RCS file.  This sets *KEYP to point
1090    to the key, and *VALUEP to point to the value.  A missing or empty
1091    value is indicated by setting *VALUEP to NULL.
1092
1093    This function returns 1 on success, or 0 on EOF.  If there is an
1094    error reading the file, or an EOF in an unexpected location, it
1095    gives a fatal error.
1096
1097    This sets *KEYP and *VALUEP to point to storage managed by
1098    rcsbuf_getkey.  Moreover, *VALUEP has not been massaged from the
1099    RCS format: it may contain embedded whitespace and embedded '@'
1100    characters.  Call rcsbuf_valcopy or rcsbuf_valpolish to do
1101    appropriate massaging.  */
1102
1103 /* Note that the extreme hair in rcsbuf_getkey is because profiling
1104    statistics show that it was worth it. */
1105 static int
1106 rcsbuf_getkey (struct rcsbuffer *rcsbuf, char **keyp, char **valp)
1107 {
1108     register const char * const my_spacetab = spacetab;
1109     register char *ptr, *ptrend;
1110     char c;
1111
1112 #define my_whitespace(c)        (my_spacetab[(unsigned char)c] != 0)
1113
1114     rcsbuf->vlen = 0;
1115     rcsbuf->at_string = 0;
1116     rcsbuf->embedded_at = 0;
1117
1118     ptr = rcsbuf->ptr;
1119     ptrend = rcsbuf->ptrend;
1120
1121     /* Sanity check.  */
1122     assert (ptr >= rcsbuf_buffer && ptr <= rcsbuf_buffer + rcsbuf_buffer_size);
1123     assert (ptrend >= rcsbuf_buffer && ptrend <= rcsbuf_buffer + rcsbuf_buffer_size);
1124
1125 #ifndef HAVE_MMAP
1126     /* If the pointer is more than RCSBUF_BUFSIZE bytes into the
1127        buffer, move back to the start of the buffer.  This keeps the
1128        buffer from growing indefinitely.  */
1129     if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE)
1130     {
1131         int len;
1132
1133         len = ptrend - ptr;
1134
1135         /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes
1136            at a time, so we can't have more bytes than that past PTR.  */
1137         assert (len <= RCSBUF_BUFSIZE);
1138
1139         /* Update the POS field, which holds the file offset of the
1140            first byte in the RCSBUF_BUFFER buffer.  */
1141         rcsbuf->pos += ptr - rcsbuf_buffer;
1142
1143         memcpy (rcsbuf_buffer, ptr, len);
1144         ptr = rcsbuf_buffer;
1145         ptrend = ptr + len;
1146         rcsbuf->ptrend = ptrend;
1147     }
1148 #endif /* HAVE_MMAP */
1149
1150     /* Skip leading whitespace.  */
1151
1152     while (1)
1153     {
1154         if (ptr >= ptrend)
1155         {
1156             ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
1157             if (ptr == NULL)
1158                 return 0;
1159             ptrend = rcsbuf->ptrend;
1160         }
1161
1162         c = *ptr;
1163         if (! my_whitespace (c))
1164             break;
1165
1166         ++ptr;
1167     }
1168
1169     /* We've found the start of the key.  */
1170
1171     *keyp = ptr;
1172
1173     if (c != ';')
1174     {
1175         while (1)
1176         {
1177             ++ptr;
1178             if (ptr >= ptrend)
1179             {
1180                 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
1181                 if (ptr == NULL)
1182                     error (1, 0, "EOF in key in RCS file %s",
1183                            primary_root_inverse_translate (rcsbuf->filename));
1184                 ptrend = rcsbuf->ptrend;
1185             }
1186             c = *ptr;
1187             if (c == ';' || my_whitespace (c))
1188                 break;
1189         }
1190     }
1191
1192     /* Here *KEYP points to the key in the buffer, C is the character
1193        we found at the of the key, and PTR points to the location in
1194        the buffer where we found C.  We must set *PTR to \0 in order
1195        to terminate the key.  If the key ended with ';', then there is
1196        no value.  */
1197
1198     *ptr = '\0';
1199     ++ptr;
1200
1201     if (c == ';')
1202     {
1203         *valp = NULL;
1204         rcsbuf->ptr = ptr;
1205         return 1;
1206     }
1207
1208     /* C must be whitespace.  Skip whitespace between the key and the
1209        value.  If we find ';' now, there is no value.  */
1210
1211     while (1)
1212     {
1213         if (ptr >= ptrend)
1214         {
1215             ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
1216             if (ptr == NULL)
1217                 error (1, 0, "EOF while looking for value in RCS file %s",
1218                        primary_root_inverse_translate (rcsbuf->filename));
1219             ptrend = rcsbuf->ptrend;
1220         }
1221         c = *ptr;
1222         if (c == ';')
1223         {
1224             *valp = NULL;
1225             rcsbuf->ptr = ptr + 1;
1226             return 1;
1227         }
1228         if (! my_whitespace (c))
1229             break;
1230         ++ptr;
1231     }
1232
1233     /* Now PTR points to the start of the value, and C is the first
1234        character of the value.  */
1235
1236     if (c != '@')
1237         *valp = ptr;
1238     else
1239     {
1240         char *pat;
1241         size_t vlen;
1242
1243         /* Optimize the common case of a value composed of a single
1244            '@' string.  */
1245
1246         rcsbuf->at_string = 1;
1247
1248         ++ptr;
1249
1250         *valp = ptr;
1251
1252         while (1)
1253         {
1254             while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1255             {
1256                 /* Note that we pass PTREND as the PTR value to
1257                    rcsbuf_fill, so that we will wind up setting PTR to
1258                    the location corresponding to the old PTREND, so
1259                    that we don't search the same bytes again.  */
1260                 ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1261                 if (ptr == NULL)
1262                     error (1, 0,
1263                            "EOF while looking for end of string in RCS file %s",
1264                            primary_root_inverse_translate (rcsbuf->filename));
1265                 ptrend = rcsbuf->ptrend;
1266             }
1267
1268             /* Handle the special case of an '@' right at the end of
1269                the known bytes.  */
1270             if (pat + 1 >= ptrend)
1271             {
1272                 /* Note that we pass PAT, not PTR, here.  */
1273                 pat = rcsbuf_fill (rcsbuf, pat, keyp, valp);
1274                 if (pat == NULL)
1275                 {
1276                     /* EOF here is OK; it just means that the last
1277                        character of the file was an '@' terminating a
1278                        value for a key type which does not require a
1279                        trailing ';'.  */
1280                     pat = rcsbuf->ptrend - 1;
1281
1282                 }
1283                 ptrend = rcsbuf->ptrend;
1284
1285                 /* Note that the value of PTR is bogus here.  This is
1286                    OK, because we don't use it.  */
1287             }
1288
1289             if (pat + 1 >= ptrend || pat[1] != '@')
1290                 break;
1291
1292             /* We found an '@' pair in the string.  Keep looking.  */
1293             ++rcsbuf->embedded_at;
1294             ptr = pat + 2;
1295         }
1296
1297         /* Here PAT points to the final '@' in the string.  */
1298
1299         *pat = '\0';
1300
1301         vlen = pat - *valp;
1302         if (vlen == 0)
1303             *valp = NULL;
1304         rcsbuf->vlen = vlen;
1305
1306         ptr = pat + 1;
1307     }
1308
1309     /* Certain keywords only have a '@' string.  If there is no '@'
1310        string, then the old getrcskey function assumed that they had
1311        no value, and we do the same.  */
1312
1313     {
1314         char *k;
1315
1316         k = *keyp;
1317         if (STREQ (k, RCSDESC)
1318             || STREQ (k, "text")
1319             || STREQ (k, "log"))
1320         {
1321             if (c != '@')
1322                 *valp = NULL;
1323             rcsbuf->ptr = ptr;
1324             return 1;
1325         }
1326     }
1327
1328     /* If we've already gathered a '@' string, try to skip whitespace
1329        and find a ';'.  */
1330     if (c == '@')
1331     {
1332         while (1)
1333         {
1334             char n;
1335
1336             if (ptr >= ptrend)
1337             {
1338                 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1339                 if (ptr == NULL)
1340                     error (1, 0, "EOF in value in RCS file %s",
1341                            primary_root_inverse_translate (rcsbuf->filename));
1342                 ptrend = rcsbuf->ptrend;
1343             }
1344             n = *ptr;
1345             if (n == ';')
1346             {
1347                 /* We're done.  We already set everything up for this
1348                    case above.  */
1349                 rcsbuf->ptr = ptr + 1;
1350                 return 1;
1351             }
1352             if (! my_whitespace (n))
1353                 break;
1354             ++ptr;
1355         }
1356
1357         /* The value extends past the '@' string.  We need to undo the
1358            '@' stripping done in the default case above.  This
1359            case never happens in a plain RCS file, but it can happen
1360            if user defined phrases are used.  */
1361         ((*valp)--)[rcsbuf->vlen++] = '@';
1362     }
1363
1364     /* Here we have a value which is not a simple '@' string.  We need
1365        to gather up everything until the next ';', including any '@'
1366        strings.  *VALP points to the start of the value.  If
1367        RCSBUF->VLEN is not zero, then we have already read an '@'
1368        string, and PTR points to the data following the '@' string.
1369        Otherwise, PTR points to the start of the value.  */
1370
1371     while (1)
1372     {
1373         char *start, *psemi, *pat;
1374
1375         /* Find the ';' which must end the value.  */
1376         start = ptr;
1377         while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL)
1378         {
1379             int slen;
1380
1381             /* Note that we pass PTREND as the PTR value to
1382                rcsbuf_fill, so that we will wind up setting PTR to the
1383                location corresponding to the old PTREND, so that we
1384                don't search the same bytes again.  */
1385             slen = start - *valp;
1386             ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1387             if (ptr == NULL)
1388                 error (1, 0, "EOF in value in RCS file %s",
1389                        primary_root_inverse_translate (rcsbuf->filename));
1390             start = *valp + slen;
1391             ptrend = rcsbuf->ptrend;
1392         }
1393
1394         /* See if there are any '@' strings in the value.  */
1395         pat = memchr (start, '@', psemi - start);
1396
1397         if (pat == NULL)
1398         {
1399             size_t vlen;
1400
1401             /* We're done with the value.  Trim any trailing
1402                whitespace.  */
1403
1404             rcsbuf->ptr = psemi + 1;
1405
1406             start = *valp;
1407             while (psemi > start && my_whitespace (psemi[-1]))
1408                 --psemi;
1409             *psemi = '\0';
1410
1411             vlen = psemi - start;
1412             if (vlen == 0)
1413                 *valp = NULL;
1414             rcsbuf->vlen = vlen;
1415
1416             return 1;
1417         }
1418
1419         /* We found an '@' string in the value.  We set RCSBUF->AT_STRING
1420            and RCSBUF->EMBEDDED_AT to indicate that we won't be able to
1421            compress whitespace correctly for this type of value.
1422            Since this type of value never arises in a normal RCS file,
1423            this should not be a big deal.  It means that if anybody
1424            adds a phrase which can have both an '@' string and regular
1425            text, they will have to handle whitespace compression
1426            themselves.  */
1427
1428         rcsbuf->at_string = 1;
1429         rcsbuf->embedded_at = -1;
1430
1431         ptr = pat + 1;
1432
1433         while (1)
1434         {
1435             while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1436             {
1437                 /* Note that we pass PTREND as the PTR value to
1438                    rcsbuff_fill, so that we will wind up setting PTR
1439                    to the location corresponding to the old PTREND, so
1440                    that we don't search the same bytes again.  */
1441                 ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1442                 if (ptr == NULL)
1443                     error (1, 0,
1444                            "EOF while looking for end of string in RCS file %s",
1445                            primary_root_inverse_translate (rcsbuf->filename));
1446                 ptrend = rcsbuf->ptrend;
1447             }
1448
1449             /* Handle the special case of an '@' right at the end of
1450                the known bytes.  */
1451             if (pat + 1 >= ptrend)
1452             {
1453                 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1454                 if (ptr == NULL)
1455                     error (1, 0, "EOF in value in RCS file %s",
1456                            primary_root_inverse_translate (rcsbuf->filename));
1457                 ptrend = rcsbuf->ptrend;
1458             }
1459
1460             if (pat[1] != '@')
1461                 break;
1462
1463             /* We found an '@' pair in the string.  Keep looking.  */
1464             ptr = pat + 2;
1465         }
1466
1467         /* Here PAT points to the final '@' in the string.  */
1468         ptr = pat + 1;
1469     }
1470
1471 #undef my_whitespace
1472 }
1473
1474
1475
1476 /* Read an RCS revision number from an RCS file.  This sets *REVP to
1477    point to the revision number; it will point to space that is
1478    managed by the rcsbuf functions, and is only good until the next
1479    call to rcsbuf_getkey or rcsbuf_getrevnum.
1480
1481    This function returns 1 on success, or 0 on EOF.  If there is an
1482    error reading the file, or an EOF in an unexpected location, it
1483    gives a fatal error.  */
1484 static int
1485 rcsbuf_getrevnum (struct rcsbuffer *rcsbuf, char **revp)
1486 {
1487     char *ptr, *ptrend;
1488     char c;
1489
1490     ptr = rcsbuf->ptr;
1491     ptrend = rcsbuf->ptrend;
1492
1493     *revp = NULL;
1494
1495     /* Skip leading whitespace.  */
1496
1497     while (1)
1498     {
1499         if (ptr >= ptrend)
1500         {
1501             ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
1502             if (ptr == NULL)
1503                 return 0;
1504             ptrend = rcsbuf->ptrend;
1505         }
1506
1507         c = *ptr;
1508         if (! whitespace (c))
1509             break;
1510
1511         ++ptr;
1512     }
1513
1514     if (! isdigit ((unsigned char) c) && c != '.')
1515         error (1, 0,
1516                "\
1517 unexpected '\\x%x' reading revision number in RCS file %s",
1518                c, primary_root_inverse_translate (rcsbuf->filename));
1519
1520     *revp = ptr;
1521
1522     do
1523     {
1524         ++ptr;
1525         if (ptr >= ptrend)
1526         {
1527             ptr = rcsbuf_fill (rcsbuf, ptr, revp, NULL);
1528             if (ptr == NULL)
1529                 error (1, 0,
1530                        "unexpected EOF reading revision number in RCS file %s",
1531                        primary_root_inverse_translate (rcsbuf->filename));
1532             ptrend = rcsbuf->ptrend;
1533         }
1534
1535         c = *ptr;
1536     }
1537     while (isdigit ((unsigned char) c) || c == '.');
1538
1539     if (! whitespace (c))
1540         error (1, 0, "\
1541 unexpected '\\x%x' reading revision number in RCS file %s",
1542                c, primary_root_inverse_translate (rcsbuf->filename));
1543
1544     *ptr = '\0';
1545
1546     rcsbuf->ptr = ptr + 1;
1547
1548     return 1;
1549 }
1550
1551
1552
1553 /* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF,
1554    updating PTR and the PTREND field.  If KEYP and *KEYP are not NULL,
1555    then *KEYP points into the buffer, and must be adjusted if the
1556    buffer is changed.  Likewise for VALP.  Returns the new value of
1557    PTR, or NULL on error.  */
1558 static char *
1559 rcsbuf_fill (struct rcsbuffer *rcsbuf, char *ptr, char **keyp, char **valp)
1560 {
1561 #ifdef HAVE_MMAP
1562     return NULL;
1563 #else /* HAVE_MMAP */
1564     int got;
1565
1566     if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size)
1567     {
1568         int poff, peoff, koff, voff;
1569
1570         poff = ptr - rcsbuf_buffer;
1571         peoff = rcsbuf->ptrend - rcsbuf_buffer;
1572         koff = keyp == NULL ? 0 : *keyp - rcsbuf_buffer;
1573         voff = valp == NULL ? 0 : *valp - rcsbuf_buffer;
1574
1575         expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size,
1576                        rcsbuf_buffer_size + RCSBUF_BUFSIZE);
1577
1578         ptr = rcsbuf_buffer + poff;
1579         rcsbuf->ptrend = rcsbuf_buffer + peoff;
1580         if (keyp != NULL)
1581             *keyp = rcsbuf_buffer + koff;
1582         if (valp != NULL)
1583             *valp = rcsbuf_buffer + voff;
1584     }
1585
1586     got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp);
1587     if (got == 0)
1588     {
1589         if (ferror (rcsbuf->fp))
1590             error (1, errno, "cannot read %s", rcsbuf->filename);
1591         return NULL;
1592     }
1593
1594     rcsbuf->ptrend += got;
1595
1596     return ptr;
1597 #endif /* HAVE_MMAP */
1598 }
1599
1600
1601
1602 /* Test whether the last value returned by rcsbuf_getkey is a composite
1603    value or not. */
1604 static int
1605 rcsbuf_valcmp (struct rcsbuffer *rcsbuf)
1606 {
1607     return rcsbuf->at_string && rcsbuf->embedded_at < 0;
1608 }
1609
1610
1611
1612 /* Copy the value VAL returned by rcsbuf_getkey into a memory buffer,
1613    returning the memory buffer.  Polish the value like
1614    rcsbuf_valpolish, q.v.  */
1615 static char *
1616 rcsbuf_valcopy (struct rcsbuffer *rcsbuf, char *val, int polish, size_t *lenp)
1617 {
1618     size_t vlen;
1619     int embedded_at;
1620     char *ret;
1621
1622     if (val == NULL)
1623     {
1624         if (lenp != NULL)
1625             *lenp = 0;
1626         return NULL;
1627     }
1628
1629     vlen = rcsbuf->vlen;
1630     embedded_at = rcsbuf->embedded_at < 0 ? 0 : rcsbuf->embedded_at;
1631
1632     ret = xmalloc (vlen - embedded_at + 1);
1633
1634     if (rcsbuf->at_string ? embedded_at == 0 : ! polish)
1635     {
1636         /* No special action to take.  */
1637         memcpy (ret, val, vlen + 1);
1638         if (lenp != NULL)
1639             *lenp = vlen;
1640         return ret;
1641     }
1642
1643     rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp);
1644     return ret;
1645 }
1646
1647
1648
1649 /* Polish the value VAL returned by rcsbuf_getkey.  The POLISH
1650    parameter is non-zero if multiple embedded whitespace characters
1651    should be compressed into a single whitespace character.  Note that
1652    leading and trailing whitespace was already removed by
1653    rcsbuf_getkey.  Within an '@' string, pairs of '@' characters are
1654    compressed into a single '@' character regardless of the value of
1655    POLISH.  If LENP is not NULL, set *LENP to the length of the value.  */
1656 static void
1657 rcsbuf_valpolish (struct rcsbuffer *rcsbuf, char *val, int polish,
1658                   size_t *lenp)
1659 {
1660     if (val == NULL)
1661     {
1662         if (lenp != NULL)
1663             *lenp= 0;
1664         return;
1665     }
1666
1667     if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish)
1668     {
1669         /* No special action to take.  */
1670         if (lenp != NULL)
1671             *lenp = rcsbuf->vlen;
1672         return;
1673     }
1674
1675     rcsbuf_valpolish_internal (rcsbuf, val, val, lenp);
1676 }
1677
1678
1679
1680 /* Internal polishing routine, called from rcsbuf_valcopy and
1681    rcsbuf_valpolish.  */
1682 static void
1683 rcsbuf_valpolish_internal (struct rcsbuffer *rcsbuf, char *to,
1684                            const char *from, size_t *lenp)
1685 {
1686     size_t len;
1687
1688     len = rcsbuf->vlen;
1689
1690     if (! rcsbuf->at_string)
1691     {
1692         char *orig_to;
1693         size_t clen;
1694
1695         orig_to = to;
1696
1697         for (clen = len; clen > 0; ++from, --clen)
1698         {
1699             char c;
1700
1701             c = *from;
1702             if (whitespace (c))
1703             {
1704                 /* Note that we know that clen can not drop to zero
1705                    while we have whitespace, because we know there is
1706                    no trailing whitespace.  */
1707                 while (whitespace (from[1]))
1708                 {
1709                     ++from;
1710                     --clen;
1711                 }
1712                 c = ' ';
1713             }
1714             *to++ = c;
1715         }
1716
1717         *to = '\0';
1718
1719         if (lenp != NULL)
1720             *lenp = to - orig_to;
1721     }
1722     else
1723     {
1724         const char *orig_from;
1725         char *orig_to;
1726         int embedded_at;
1727         size_t clen;
1728
1729         orig_from = from;
1730         orig_to = to;
1731
1732         embedded_at = rcsbuf->embedded_at;
1733         assert (embedded_at > 0);
1734
1735         if (lenp != NULL)
1736             *lenp = len - embedded_at;
1737
1738         for (clen = len; clen > 0; ++from, --clen)
1739         {
1740             char c;
1741
1742             c = *from;
1743             *to++ = c;
1744             if (c == '@')
1745             {
1746                 ++from;
1747
1748                 /* Sanity check.
1749                  *
1750                  * FIXME: I restored this to an abort from an assert based on
1751                  * advice from Larry Jones that asserts should not be used to
1752                  * confirm the validity of an RCS file...  This leaves two
1753                  * issues here: 1) I am uncertain that the fact that we will
1754                  * only find double '@'s hasn't already been confirmed; and:
1755                  * 2) If this is the proper place to spot the error in the RCS
1756                  * file, then we should print a much clearer error here for the
1757                  * user!!!!!!!
1758                  *
1759                  *      - DRP
1760                  */
1761                 if (*from != '@' || clen == 0)
1762                     abort ();
1763
1764                 --clen;
1765
1766                 --embedded_at;
1767                 if (embedded_at == 0)
1768                 {
1769                     /* We've found all the embedded '@' characters.
1770                        We can just memcpy the rest of the buffer after
1771                        this '@' character.  */
1772                     if (orig_to != orig_from)
1773                         memcpy (to, from + 1, clen - 1);
1774                     else
1775                         memmove (to, from + 1, clen - 1);
1776                     from += clen;
1777                     to += clen - 1;
1778                     break;
1779                 }
1780             }
1781         }
1782
1783         /* Sanity check.  */
1784         assert (from == orig_from + len
1785             && to == orig_to + (len - rcsbuf->embedded_at));
1786
1787         *to = '\0';
1788     }
1789 }
1790
1791
1792
1793 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1794
1795 /* Copy the next word from the value VALP returned by rcsbuf_getkey into a
1796    memory buffer, updating VALP and returning the memory buffer.  Return
1797    NULL when there are no more words. */
1798
1799 static char *
1800 rcsbuf_valword (struct rcsbuffer *rcsbuf, char **valp)
1801 {
1802     register const char * const my_spacetab = spacetab;
1803     register char *ptr, *pat;
1804     char c;
1805
1806 # define my_whitespace(c)       (my_spacetab[(unsigned char)c] != 0)
1807
1808     if (*valp == NULL)
1809         return NULL;
1810
1811     for (ptr = *valp; my_whitespace (*ptr); ++ptr) ;
1812     if (*ptr == '\0')
1813     {
1814         assert (ptr - *valp == rcsbuf->vlen);
1815         *valp = NULL;
1816         rcsbuf->vlen = 0;
1817         return NULL;
1818     }
1819
1820     /* PTR now points to the start of a value.  Find out whether it is
1821        a num, an id, a string or a colon. */
1822     c = *ptr;
1823     if (c == ':')
1824     {
1825         rcsbuf->vlen -= ++ptr - *valp;
1826         *valp = ptr;
1827         return xstrdup (":");
1828     }
1829
1830     if (c == '@')
1831     {
1832         int embedded_at = 0;
1833         size_t vlen;
1834
1835         pat = ++ptr;
1836         while ((pat = strchr (pat, '@')) != NULL)
1837         {
1838             if (pat[1] != '@')
1839                 break;
1840             ++embedded_at;
1841             pat += 2;
1842         }
1843
1844         /* Here PAT points to the final '@' in the string.  */
1845         *pat++ = '\0';
1846         assert (rcsbuf->at_string);
1847         vlen = rcsbuf->vlen - (pat - *valp);
1848         rcsbuf->vlen = pat - ptr - 1;
1849         rcsbuf->embedded_at = embedded_at;
1850         ptr = rcsbuf_valcopy (rcsbuf, ptr, 0, NULL);
1851         *valp = pat;
1852         rcsbuf->vlen = vlen;
1853         if (strchr (pat, '@') == NULL)
1854             rcsbuf->at_string = 0;
1855         else
1856             rcsbuf->embedded_at = -1;
1857         return ptr;
1858     }
1859
1860     /* *PTR is neither `:', `;' nor `@', so it should be the start of a num
1861        or an id.  Make sure it is not another special character. */
1862     if (c == '$' || c == '.' || c == ',')
1863         error (1, 0, "invalid special character in RCS field in %s",
1864                primary_root_inverse_translate (rcsbuf->filename));
1865
1866     pat = ptr;
1867     while (1)
1868     {
1869         /* Legitimate ID characters are digits, dots and any `graphic
1870            printing character that is not a special.' This test ought
1871            to do the trick. */
1872         c = *++pat;
1873         if (!isprint ((unsigned char) c) ||
1874             c == ';' || c == '$' || c == ',' || c == '@' || c == ':')
1875             break;
1876     }
1877
1878     /* PAT points to the last non-id character in this word, and C is
1879        the character in its memory cell.  Check to make sure that it
1880        is a legitimate word delimiter -- whitespace or end. */
1881     if (c != '\0' && !my_whitespace (c))
1882         error (1, 0, "invalid special character in RCS field in %s",
1883                primary_root_inverse_translate (rcsbuf->filename));
1884
1885     *pat = '\0';
1886     rcsbuf->vlen -= pat - *valp;
1887     *valp = pat;
1888     return xstrdup (ptr);
1889
1890 # undef my_whitespace
1891 }
1892
1893 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
1894
1895
1896
1897 /* Return the current position of an rcsbuf.  */
1898 static off_t
1899 rcsbuf_ftello (struct rcsbuffer *rcsbuf)
1900 {
1901     return rcsbuf->pos + rcsbuf->ptr - rcsbuf_buffer;
1902 }
1903
1904
1905
1906 /* Return a pointer to any data buffered for RCSBUF, along with the
1907    length.  */
1908 static void
1909 rcsbuf_get_buffered (struct rcsbuffer *rcsbuf, char **datap, size_t *lenp)
1910 {
1911     *datap = rcsbuf->ptr;
1912     *lenp = rcsbuf->ptrend - rcsbuf->ptr;
1913 }
1914
1915
1916
1917 /* CVS optimizes by quickly reading some header information from a
1918    file.  If it decides it needs to do more with the file, it reopens
1919    it.  We speed that up here by maintaining a cache of a single open
1920    file, to save the time it takes to reopen the file in the common
1921    case.  */
1922 static RCSNode *cached_rcs;
1923 static struct rcsbuffer cached_rcsbuf;
1924
1925 /* Cache RCS and RCSBUF.  This takes responsibility for closing
1926    RCSBUF->FP.  */
1927 static void
1928 rcsbuf_cache (RCSNode *rcs, struct rcsbuffer *rcsbuf)
1929 {
1930     if (cached_rcs != NULL)
1931         rcsbuf_cache_close ();
1932     cached_rcs = rcs;
1933     ++rcs->refcount;
1934     cached_rcsbuf = *rcsbuf;
1935 }
1936
1937
1938
1939 /* If there is anything in the cache, close it.  */
1940 static void
1941 rcsbuf_cache_close (void)
1942 {
1943     if (cached_rcs != NULL)
1944     {
1945         rcsbuf_close (&cached_rcsbuf);
1946         if (fclose (cached_rcsbuf.fp) != 0)
1947             error (0, errno, "cannot close %s", cached_rcsbuf.filename);
1948         freercsnode (&cached_rcs);
1949         cached_rcs = NULL;
1950     }
1951 }
1952
1953
1954
1955 /* Open an rcsbuffer for RCS, getting it from the cache if possible.
1956    Set *FPP to the file, and *RCSBUFP to the rcsbuf.  The file should
1957    be put at position POS.  */
1958 static void
1959 rcsbuf_cache_open (RCSNode *rcs, off_t pos, FILE **pfp,
1960                    struct rcsbuffer *prcsbuf)
1961 {
1962 #ifndef HAVE_MMAP
1963     if (cached_rcs == rcs)
1964     {
1965         if (rcsbuf_ftello (&cached_rcsbuf) != pos)
1966         {
1967             if (fseeko (cached_rcsbuf.fp, pos, SEEK_SET) != 0)
1968                 error (1, 0, "cannot fseeko RCS file %s",
1969                        cached_rcsbuf.filename);
1970             cached_rcsbuf.ptr = rcsbuf_buffer;
1971             cached_rcsbuf.ptrend = rcsbuf_buffer;
1972             cached_rcsbuf.pos = pos;
1973         }
1974         *pfp = cached_rcsbuf.fp;
1975
1976         /* When RCS_parse opens a file using fopen_case, it frees the
1977            filename which we cached in CACHED_RCSBUF and stores a new
1978            file name in RCS->PATH.  We avoid problems here by always
1979            copying the filename over.  FIXME: This is hackish.  */
1980         cached_rcsbuf.filename = rcs->path;
1981
1982         *prcsbuf = cached_rcsbuf;
1983
1984         cached_rcs = NULL;
1985
1986         /* Removing RCS from the cache removes a reference to it.  */
1987         --rcs->refcount;
1988         if (rcs->refcount <= 0)
1989             error (1, 0, "rcsbuf_cache_open: internal error");
1990     }
1991     else
1992     {
1993 #endif /* ifndef HAVE_MMAP */
1994         /* FIXME:  If these routines can be rewritten to not write to the
1995          * rcs file buffer, there would be a considerably larger memory savings
1996          * from using mmap since the shared file would never need be copied to
1997          * process memory.
1998          *
1999          * If this happens, cached mmapped buffers would be usable, but don't
2000          * forget to make sure rcs->pos < pos here...
2001          */
2002         if (cached_rcs != NULL)
2003             rcsbuf_cache_close ();
2004
2005         *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ);
2006         if (*pfp == NULL)
2007             error (1, 0, "unable to reopen `%s'", rcs->path);
2008 #ifndef HAVE_MMAP
2009         if (pos != 0)
2010         {
2011             if (fseeko (*pfp, pos, SEEK_SET) != 0)
2012                 error (1, 0, "cannot fseeko RCS file %s", rcs->path);
2013         }
2014 #endif /* ifndef HAVE_MMAP */
2015         rcsbuf_open (prcsbuf, *pfp, rcs->path, pos);
2016 #ifndef HAVE_MMAP
2017     }
2018 #endif /* ifndef HAVE_MMAP */
2019 }
2020
2021
2022
2023 /*
2024  * process the symbols list of the rcs file
2025  */
2026 static void
2027 do_symbols (List *list, char *val)
2028 {
2029     Node *p;
2030     char *cp = val;
2031     char *tag, *rev;
2032
2033     assert (cp);
2034
2035     for (;;)
2036     {
2037         /* skip leading whitespace */
2038         while (whitespace (*cp))
2039             cp++;
2040
2041         /* if we got to the end, we are done */
2042         if (*cp == '\0')
2043             break;
2044
2045         /* split it up into tag and rev */
2046         tag = cp;
2047         cp = strchr (cp, ':');
2048         *cp++ = '\0';
2049         rev = cp;
2050         while (!whitespace (*cp) && *cp != '\0')
2051             cp++;
2052         if (*cp != '\0')
2053             *cp++ = '\0';
2054
2055         /* make a new node and add it to the list */
2056         p = getnode ();
2057         p->key = xstrdup (tag);
2058         p->data = xstrdup (rev);
2059         (void) addnode (list, p);
2060     }
2061 }
2062
2063
2064
2065 /*
2066  * process the locks list of the rcs file
2067  * Like do_symbols, but hash entries are keyed backwards: i.e.
2068  * an entry like `user:rev' is keyed on REV rather than on USER.
2069  */
2070 static void
2071 do_locks (List *list, char *val)
2072 {
2073     Node *p;
2074     char *cp = val;
2075     char *user, *rev;
2076
2077     assert (cp);
2078
2079     for (;;)
2080     {
2081         /* skip leading whitespace */
2082         while (whitespace (*cp))
2083             cp++;
2084
2085         /* if we got to the end, we are done */
2086         if (*cp == '\0')
2087             break;
2088
2089         /* split it up into user and rev */
2090         user = cp;
2091         cp = strchr (cp, ':');
2092         *cp++ = '\0';
2093         rev = cp;
2094         while (!whitespace (*cp) && *cp != '\0')
2095             cp++;
2096         if (*cp != '\0')
2097             *cp++ = '\0';
2098
2099         /* make a new node and add it to the list */
2100         p = getnode ();
2101         p->key = xstrdup (rev);
2102         p->data = xstrdup (user);
2103         (void) addnode (list, p);
2104     }
2105 }
2106
2107
2108
2109 /*
2110  * process the branches list of a revision delta
2111  */
2112 static void
2113 do_branches (List *list, char *val)
2114 {
2115     Node *p;
2116     char *cp = val;
2117     char *branch;
2118
2119     for (;;)
2120     {
2121         /* skip leading whitespace */
2122         while (whitespace (*cp))
2123             cp++;
2124
2125         /* if we got to the end, we are done */
2126         if (*cp == '\0')
2127             break;
2128
2129         /* find the end of this branch */
2130         branch = cp;
2131         while (!whitespace (*cp) && *cp != '\0')
2132             cp++;
2133         if (*cp != '\0')
2134             *cp++ = '\0';
2135
2136         /* make a new node and add it to the list */
2137         p = getnode ();
2138         p->key = xstrdup (branch);
2139         (void) addnode (list, p);
2140     }
2141 }
2142
2143
2144
2145 /*
2146  * Version Number
2147  * 
2148  * Returns the requested version number of the RCS file, satisfying tags and/or
2149  * dates, and walking branches, if necessary.
2150  * 
2151  * The result is returned; null-string if error.
2152  */
2153 char *
2154 RCS_getversion (RCSNode *rcs, const char *tag, const char *date,
2155                 int force_tag_match, int *simple_tag)
2156 {
2157     if (simple_tag != NULL)
2158         *simple_tag = 0;
2159
2160     /* make sure we have something to look at... */
2161     assert (rcs != NULL);
2162
2163     if (tag && date)
2164     {
2165         char *branch, *rev;
2166
2167         if (! RCS_nodeisbranch (rcs, tag))
2168         {
2169             /* We can't get a particular date if the tag is not a
2170                branch.  */
2171             return NULL;
2172         }
2173
2174         /* Work out the branch.  */
2175         if (! isdigit ((unsigned char) tag[0]))
2176             branch = RCS_whatbranch (rcs, tag);
2177         else
2178             branch = xstrdup (tag);
2179
2180         /* Fetch the revision of branch as of date.  */
2181         rev = RCS_getdatebranch (rcs, date, branch);
2182         free (branch);
2183         return rev;
2184     }
2185     else if (tag)
2186         return RCS_gettag (rcs, tag, force_tag_match, simple_tag);
2187     else if (date)
2188         return RCS_getdate (rcs, date, force_tag_match);
2189     else
2190         return RCS_head (rcs);
2191
2192 }
2193
2194
2195
2196 /*
2197  * Get existing revision number corresponding to tag or revision.
2198  * Similar to RCS_gettag but less interpretation imposed.
2199  * For example:
2200  * -- If tag designates a magic branch, RCS_tag2rev
2201  *    returns the magic branch number.
2202  * -- If tag is a branch tag, returns the branch number, not
2203  *    the revision of the head of the branch.
2204  * If tag or revision is not valid or does not exist in file,
2205  * return NULL.
2206  */
2207 char *
2208 RCS_tag2rev (RCSNode *rcs, char *tag)
2209 {
2210     char *rev, *pa, *pb;
2211     int i;
2212
2213     assert (rcs != NULL);
2214
2215     if (rcs->flags & PARTIAL)
2216         RCS_reparsercsfile (rcs, NULL, NULL);
2217
2218     /* If a valid revision, try to look it up */
2219     if ( RCS_valid_rev (tag) )
2220     {
2221         /* Make a copy so we can scribble on it */
2222         rev =  xstrdup (tag);
2223
2224         /* If revision exists, return the copy */
2225         if (RCS_exist_rev (rcs, tag))
2226             return rev;
2227
2228         /* Nope, none such. If tag is not a branch we're done. */ 
2229         i = numdots (rev);
2230         if ((i & 1) == 1 )
2231         {
2232             pa = strrchr (rev, '.');
2233             if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.')
2234             {
2235                 free (rev);
2236                 error (1, 0, "revision `%s' does not exist", tag);
2237             }
2238         }
2239
2240         /* Try for a real (that is, exists in the RCS deltas) branch
2241            (RCS_exist_rev just checks for real revisions and revisions
2242            which have tags pointing to them).  */
2243         pa = RCS_getbranch (rcs, rev, 1);
2244         if (pa != NULL)
2245         {
2246             free (pa);
2247             return rev;
2248         }
2249
2250        /* Tag is branch, but does not exist, try corresponding 
2251         * magic branch tag.
2252         *
2253         * FIXME: assumes all magic branches are of       
2254         * form "n.n.n ... .0.n".  I'll fix if somebody can
2255         * send me a method to get a magic branch tag with
2256         * the 0 in some other position -- <dan@gasboy.com>
2257         */ 
2258         pa = strrchr (rev, '.');
2259         if (!pa)
2260             /* This might happen, for instance, if an RCS file only contained
2261              * revisions 2.x and higher, and REV == "1".
2262              */
2263             error (1, 0, "revision `%s' does not exist", tag);
2264
2265         *pa++ = 0;
2266         pb = Xasprintf ("%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa);
2267         free (rev);
2268         rev = pb;
2269         if (RCS_exist_rev (rcs, rev))
2270             return rev;
2271         error (1, 0, "revision `%s' does not exist", tag);
2272     }
2273
2274
2275     RCS_check_tag (tag); /* exit if not a valid tag */
2276
2277     /* If tag is "HEAD", special case to get head RCS revision */
2278     if (tag && STREQ (tag, TAG_HEAD))
2279         return RCS_head (rcs);
2280
2281     /* If valid tag let translate_symtag say yea or nay. */
2282     rev = translate_symtag (rcs, tag);
2283
2284     if (rev)
2285         return rev;
2286
2287     /* Trust the caller to print warnings. */
2288     return NULL;
2289 }
2290
2291
2292
2293 /*
2294  * Find the revision for a specific tag.
2295  * If force_tag_match is set, return NULL if an exact match is not
2296  * possible otherwise return RCS_head ().  We are careful to look for
2297  * and handle "magic" revisions specially.
2298  * 
2299  * If the matched tag is a branch tag, find the head of the branch.
2300  * 
2301  * Returns pointer to newly malloc'd string, or NULL.
2302  */
2303 char *
2304 RCS_gettag (RCSNode *rcs, const char *symtag, int force_tag_match,
2305             int *simple_tag)
2306 {
2307     char *tag;
2308
2309     if (simple_tag != NULL)
2310         *simple_tag = 0;
2311
2312     /* make sure we have something to look at... */
2313     assert (rcs != NULL);
2314
2315     /* XXX this is probably not necessary, --jtc */
2316     if (rcs->flags & PARTIAL) 
2317         RCS_reparsercsfile (rcs, NULL, NULL);
2318
2319     /* If symtag is "HEAD", special case to get head RCS revision */
2320     if (symtag && STREQ (symtag, TAG_HEAD))
2321 #if 0 /* This #if 0 is only in the Cygnus code.  Why?  Death support?  */
2322         if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
2323             return NULL;        /* head request for removed file */
2324         else
2325 #endif
2326             return RCS_head (rcs);
2327
2328     if (!isdigit ((unsigned char) symtag[0]))
2329     {
2330         char *version;
2331
2332         /* If we got a symbolic tag, resolve it to a numeric */
2333         version = translate_symtag (rcs, symtag);
2334         if (version != NULL)
2335         {
2336             int dots;
2337             char *magic, *branch, *cp;
2338
2339             tag = version;
2340
2341             /*
2342              * If this is a magic revision, we turn it into either its
2343              * physical branch equivalent (if one exists) or into
2344              * its base revision, which we assume exists.
2345              */
2346             dots = numdots (tag);
2347             if (dots > 2 && (dots & 1) != 0)
2348             {
2349                 branch = strrchr (tag, '.');
2350                 cp = branch++ - 1;
2351                 while (*cp != '.')
2352                     cp--;
2353
2354                 /* see if we have .magic-branch. (".0.") */
2355                 magic = xmalloc (strlen (tag) + 1);
2356                 (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2357                 if (strncmp (magic, cp, strlen (magic)) == 0)
2358                 {
2359                     /* it's magic.  See if the branch exists */
2360                     *cp = '\0';         /* turn it into a revision */
2361                     (void) sprintf (magic, "%s.%s", tag, branch);
2362                     branch = RCS_getbranch (rcs, magic, 1);
2363                     free (magic);
2364                     if (branch != NULL)
2365                     {
2366                         free (tag);
2367                         return branch;
2368                     }
2369                     return tag;
2370                 }
2371                 free (magic);
2372             }
2373         }
2374         else
2375         {
2376             /* The tag wasn't there, so return the head or NULL */
2377             if (force_tag_match)
2378                 return NULL;
2379             else
2380                 return RCS_head (rcs);
2381         }
2382     }
2383     else
2384         tag = xstrdup (symtag);
2385
2386     /* tag is always allocated and numeric now.  */
2387
2388     /*
2389      * numeric tag processing:
2390      *          1) revision number - just return it
2391      *          2) branch number   - find head of branch
2392      */
2393
2394     /* strip trailing dots */
2395     while (tag[strlen (tag) - 1] == '.')
2396         tag[strlen (tag) - 1] = '\0';
2397
2398     if ((numdots (tag) & 1) == 0)
2399     {
2400         char *branch;
2401
2402         /* we have a branch tag, so we need to walk the branch */
2403         branch = RCS_getbranch (rcs, tag, force_tag_match);
2404         free (tag);
2405         return branch;
2406     }
2407     else
2408     {
2409         Node *p;
2410
2411         /* we have a revision tag, so make sure it exists */
2412         p = findnode (rcs->versions, tag);
2413         if (p != NULL)
2414         {
2415             /* We have found a numeric revision for the revision tag.
2416                To support expanding the RCS keyword Name, if
2417                SIMPLE_TAG is not NULL, tell the the caller that this
2418                is a simple tag which co will recognize.  FIXME: Are
2419                there other cases in which we should set this?  In
2420                particular, what if we expand RCS keywords internally
2421                without calling co?  */
2422             if (simple_tag != NULL)
2423                 *simple_tag = 1;
2424             return tag;
2425         }
2426         else
2427         {
2428             /* The revision wasn't there, so return the head or NULL */
2429             free (tag);
2430             if (force_tag_match)
2431                 return NULL;
2432             else
2433                 return RCS_head (rcs);
2434         }
2435     }
2436 }
2437
2438
2439
2440 /*
2441  * Return a "magic" revision as a virtual branch off of REV for the RCS file.
2442  * A "magic" revision is one which is unique in the RCS file.  By unique, I
2443  * mean we return a revision which:
2444  *      - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
2445  *      - has a revision component which is not an existing branch off REV
2446  *      - has a revision component which is not an existing magic revision
2447  *      - is an even-numbered revision, to avoid conflicts with vendor branches
2448  * The first point is what makes it "magic".
2449  *
2450  * As an example, if we pass in 1.37 as REV, we will look for an existing
2451  * branch called 1.37.2.  If it did not exist, we would look for an
2452  * existing symbolic tag with a numeric part equal to 1.37.0.2.  If that
2453  * didn't exist, then we know that the 1.37.2 branch can be reserved by
2454  * creating a symbolic tag with 1.37.0.2 as the numeric part.
2455  *
2456  * This allows us to fork development with very little overhead -- just a
2457  * symbolic tag is used in the RCS file.  When a commit is done, a physical
2458  * branch is dynamically created to hold the new revision.
2459  *
2460  * Note: We assume that REV is an RCS revision and not a branch number.
2461  */
2462 static char *check_rev;
2463 char *
2464 RCS_magicrev (RCSNode *rcs, char *rev)
2465 {
2466     int rev_num;
2467     char *xrev, *test_branch, *local_branch_num;
2468
2469     xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
2470     check_rev = xrev;
2471
2472     local_branch_num = getenv("CVS_LOCAL_BRANCH_NUM");
2473     if (local_branch_num)
2474     {
2475       rev_num = atoi(local_branch_num);
2476       if (rev_num < 2)
2477         rev_num = 2;
2478       else
2479         rev_num &= ~1;
2480     }
2481     else
2482       rev_num = 2;
2483
2484     /* only look at even numbered branches */
2485     for ( ; ; rev_num += 2)
2486     {
2487         /* see if the physical branch exists */
2488         (void) sprintf (xrev, "%s.%d", rev, rev_num);
2489         test_branch = RCS_getbranch (rcs, xrev, 1);
2490         if (test_branch != NULL)        /* it did, so keep looking */
2491         {
2492             free (test_branch);
2493             continue;
2494         }
2495
2496         /* now, create a "magic" revision */
2497         (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
2498
2499         /* walk the symbols list to see if a magic one already exists */
2500         if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0)
2501             continue;
2502
2503         /* we found a free magic branch.  Claim it as ours */
2504         return xrev;
2505     }
2506 }
2507
2508
2509
2510 /*
2511  * walklist proc to look for a match in the symbols list.
2512  * Returns 0 if the symbol does not match, 1 if it does.
2513  */
2514 static int
2515 checkmagic_proc (Node *p, void *closure)
2516 {
2517     if (STREQ (check_rev, p->data))
2518         return 1;
2519     else
2520         return 0;
2521 }
2522
2523
2524
2525 /*
2526  * Given an RCSNode, returns non-zero if the specified revision number 
2527  * or symbolic tag resolves to a "branch" within the rcs file.
2528  *
2529  * FIXME: this is the same as RCS_nodeisbranch except for the special 
2530  *        case for handling a null rcsnode.
2531  */
2532 int
2533 RCS_isbranch (RCSNode *rcs, const char *rev)
2534 {
2535     /* numeric revisions are easy -- even number of dots is a branch */
2536     if (isdigit ((unsigned char) *rev))
2537         return (numdots (rev) & 1) == 0;
2538
2539     /* assume a revision if you can't find the RCS info */
2540     if (rcs == NULL)
2541         return 0;
2542
2543     /* now, look for a match in the symbols list */
2544     return RCS_nodeisbranch (rcs, rev);
2545 }
2546
2547
2548
2549 /*
2550  * Given an RCSNode, returns non-zero if the specified revision number
2551  * or symbolic tag resolves to a "branch" within the rcs file.  We do
2552  * take into account any magic branches as well.
2553  */
2554 int
2555 RCS_nodeisbranch (RCSNode *rcs, const char *rev)
2556 {
2557     int dots;
2558     char *version;
2559
2560     assert (rcs != NULL);
2561
2562     /* numeric revisions are easy -- even number of dots is a branch */
2563     if (isdigit ((unsigned char) *rev))
2564         return (numdots (rev) & 1) == 0;
2565
2566     version = translate_symtag (rcs, rev);
2567     if (version == NULL)
2568         return 0;
2569     dots = numdots (version);
2570     if ((dots & 1) == 0)
2571     {
2572         free (version);
2573         return 1;
2574     }
2575
2576     /* got a symbolic tag match, but it's not a branch; see if it's magic */
2577     if (dots > 2)
2578     {
2579         char *magic;
2580         char *branch = strrchr (version, '.');
2581         char *cp = branch - 1;
2582         while (*cp != '.')
2583             cp--;
2584
2585         /* see if we have .magic-branch. (".0.") */
2586         magic = Xasprintf (".%d.", RCS_MAGIC_BRANCH);
2587         if (strncmp (magic, cp, strlen (magic)) == 0)
2588         {
2589             free (magic);
2590             free (version);
2591             return 1;
2592         }
2593         free (magic);
2594     }
2595     free (version);
2596     return 0;
2597 }
2598
2599
2600
2601 /*
2602  * Returns a pointer to malloc'ed memory which contains the branch
2603  * for the specified *symbolic* tag.  Magic branches are handled correctly.
2604  */
2605 char *
2606 RCS_whatbranch (RCSNode *rcs, const char *rev)
2607 {
2608     char *version;
2609     int dots;
2610
2611     /* assume no branch if you can't find the RCS info */
2612     if (rcs == NULL)
2613         return NULL;
2614
2615     /* now, look for a match in the symbols list */
2616     version = translate_symtag (rcs, rev);
2617     if (version == NULL)
2618         return NULL;
2619     dots = numdots (version);
2620     if ((dots & 1) == 0)
2621         return version;
2622
2623     /* got a symbolic tag match, but it's not a branch; see if it's magic */
2624     if (dots > 2)
2625     {
2626         char *magic;
2627         char *branch = strrchr (version, '.');
2628         char *cp = branch++ - 1;
2629         while (*cp != '.')
2630             cp--;
2631
2632         /* see if we have .magic-branch. (".0.") */
2633         magic = xmalloc (strlen (version) + 1);
2634         (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2635         if (strncmp (magic, cp, strlen (magic)) == 0)
2636         {
2637             /* yep.  it's magic.  now, construct the real branch */
2638             *cp = '\0';                 /* turn it into a revision */
2639             (void) sprintf (magic, "%s.%s", version, branch);
2640             free (version);
2641             return magic;
2642         }
2643         free (magic);
2644     }
2645     free (version);
2646     return NULL;
2647 }
2648
2649
2650
2651 /*
2652  * Get the head of the specified branch.  If the branch does not exist,
2653  * return NULL or RCS_head depending on force_tag_match.
2654  * Returns NULL or a newly malloc'd string.
2655  */
2656 char *
2657 RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match)
2658 {
2659     Node *p, *head;
2660     RCSVers *vn;
2661     char *xtag;
2662     char *nextvers;
2663     char *cp;
2664
2665     /* make sure we have something to look at... */
2666     assert (rcs != NULL);
2667
2668     if (rcs->flags & PARTIAL)
2669         RCS_reparsercsfile (rcs, NULL, NULL);
2670
2671     /* find out if the tag contains a dot, or is on the trunk */
2672     cp = strrchr (tag, '.');
2673
2674     /* trunk processing is the special case */
2675     if (cp == NULL)
2676     {
2677         xtag = Xasprintf ("%s.", tag);
2678         for (cp = rcs->head; cp != NULL;)
2679         {
2680             if (strncmp (xtag, cp, strlen (xtag)) == 0)
2681                 break;
2682             p = findnode (rcs->versions, cp);
2683             if (p == NULL)
2684             {
2685                 free (xtag);
2686                 if (force_tag_match)
2687                     return NULL;
2688                 else
2689                     return RCS_head (rcs);
2690             }
2691             vn = p->data;
2692             cp = vn->next;
2693         }
2694         free (xtag);
2695         if (cp == NULL)
2696         {
2697             if (force_tag_match)
2698                 return NULL;
2699             else
2700                 return RCS_head (rcs);
2701         }
2702         return xstrdup (cp);
2703     }
2704
2705     /* if it had a `.', terminate the string so we have the base revision */
2706     *cp = '\0';
2707
2708     /* look up the revision this branch is based on */
2709     p = findnode (rcs->versions, tag);
2710
2711     /* put the . back so we have the branch again */
2712     *cp = '.';
2713
2714     if (p == NULL)
2715     {
2716         /* if the base revision didn't exist, return head or NULL */
2717         if (force_tag_match)
2718             return NULL;
2719         else
2720             return RCS_head (rcs);
2721     }
2722
2723     /* find the first element of the branch we are looking for */
2724     vn = p->data;
2725     if (vn->branches == NULL)
2726         return NULL;
2727     xtag = Xasprintf ("%s.", tag);
2728     head = vn->branches->list;
2729     for (p = head->next; p != head; p = p->next)
2730         if (strncmp (p->key, xtag, strlen (xtag)) == 0)
2731             break;
2732     free (xtag);
2733
2734     if (p == head)
2735     {
2736         /* we didn't find a match so return head or NULL */
2737         if (force_tag_match)
2738             return NULL;
2739         else
2740             return RCS_head (rcs);
2741     }
2742
2743     /* now walk the next pointers of the branch */
2744     nextvers = p->key;
2745     do
2746     {
2747         p = findnode (rcs->versions, nextvers);
2748         if (p == NULL)
2749         {
2750             /* a link in the chain is missing - return head or NULL */
2751             if (force_tag_match)
2752                 return NULL;
2753             else
2754                 return RCS_head (rcs);
2755         }
2756         vn = p->data;
2757         nextvers = vn->next;
2758     } while (nextvers != NULL);
2759
2760     /* we have the version in our hand, so go for it */
2761     return xstrdup (vn->version);
2762 }
2763
2764
2765
2766 /* Returns the head of the branch which REV is on.  REV can be a
2767    branch tag or non-branch tag; symbolic or numeric.
2768
2769    Returns a newly malloc'd string.  Returns NULL if a symbolic name
2770    isn't found.  */
2771 char *
2772 RCS_branch_head (RCSNode *rcs, char *rev)
2773 {
2774     char *num;
2775     char *br;
2776     char *retval;
2777
2778     assert (rcs != NULL);
2779
2780     if (RCS_nodeisbranch (rcs, rev))
2781         return RCS_getbranch (rcs, rev, 1);
2782
2783     if (isdigit ((unsigned char) *rev))
2784         num = xstrdup (rev);
2785     else
2786     {
2787         num = translate_symtag (rcs, rev);
2788         if (num == NULL)
2789             return NULL;
2790     }
2791     br = truncate_revnum (num);
2792     retval = RCS_getbranch (rcs, br, 1);
2793     free (br);
2794     free (num);
2795     return retval;
2796 }
2797
2798
2799
2800 /* Get the branch point for a particular branch, that is the first
2801    revision on that branch.  For example, RCS_getbranchpoint (rcs,
2802    "1.3.2") will normally return "1.3.2.1".  TARGET may be either a
2803    branch number or a revision number; if a revnum, find the
2804    branchpoint of the branch to which TARGET belongs.
2805
2806    Return RCS_head if TARGET is on the trunk or if the root node could
2807    not be found (this is sort of backwards from our behavior on a branch;
2808    the rationale is that the return value is a revision from which you
2809    can start walking the next fields and end up at TARGET).
2810    Return NULL on error.  */
2811 static char *
2812 RCS_getbranchpoint (RCSNode *rcs, char *target)
2813 {
2814     char *branch, *bp;
2815     Node *vp;
2816     RCSVers *rev;
2817     int dots, isrevnum, brlen;
2818
2819     dots = numdots (target);
2820     isrevnum = dots & 1;
2821
2822     if (dots == 1)
2823         /* TARGET is a trunk revision; return rcs->head. */
2824         return RCS_head (rcs);
2825
2826     /* Get the revision number of the node at which TARGET's branch is
2827        rooted.  If TARGET is a branch number, lop off the last field;
2828        if it's a revision number, lop off the last *two* fields. */
2829     branch = xstrdup (target);
2830     bp = strrchr (branch, '.');
2831     if (bp == NULL)
2832         error (1, 0, "%s: confused revision number %s",
2833                rcs->print_path, target);
2834     if (isrevnum)
2835         while (*--bp != '.')
2836             ;
2837     *bp = '\0';
2838
2839     vp = findnode (rcs->versions, branch);
2840     if (vp == NULL)
2841     {   
2842         error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
2843         free (branch);
2844         return NULL;
2845     }
2846     rev = vp->data;
2847
2848     *bp++ = '.';
2849     while (*bp && *bp != '.')
2850         ++bp;
2851     brlen = bp - branch;
2852
2853     vp = rev->branches->list->next;
2854     while (vp != rev->branches->list)
2855     {
2856         /* BRANCH may be a genuine branch number, e.g. `1.1.3', or
2857            maybe a full revision number, e.g. `1.1.3.6'.  We have
2858            found our branch point if the first BRANCHLEN characters
2859            of the revision number match, *and* if the following
2860            character is a dot. */
2861         if (strncmp (vp->key, branch, brlen) == 0 && vp->key[brlen] == '.')
2862             break;
2863         vp = vp->next;
2864     }
2865
2866     free (branch);
2867     if (vp == rev->branches->list)
2868     {
2869         error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
2870         return NULL;
2871     }
2872     else
2873         return xstrdup (vp->key);
2874 }
2875
2876
2877
2878 /*
2879  * Get the head of the RCS file.  If branch is set, this is the head of the
2880  * branch, otherwise the real head.
2881  *
2882  * INPUTS
2883  *   rcs        The parsed rcs node information.
2884  *
2885  * RETURNS
2886  *   NULL when rcs->branch exists and cannot be found.
2887  *   A newly malloc'd string, otherwise.
2888  */
2889 char *
2890 RCS_head (RCSNode *rcs)
2891 {
2892     /* make sure we have something to look at... */
2893     assert (rcs);
2894
2895     /*
2896      * NOTE: we call getbranch with force_tag_match set to avoid any
2897      * possibility of recursion
2898      */
2899     if (rcs->branch)
2900         return RCS_getbranch (rcs, rcs->branch, 1);
2901     else
2902         return xstrdup (rcs->head);
2903 }
2904
2905
2906
2907 /*
2908  * Get the most recent revision, based on the supplied date, but use some
2909  * funky stuff and follow the vendor branch maybe
2910  */
2911 char *
2912 RCS_getdate (RCSNode *rcs, const char *date, int force_tag_match)
2913 {
2914     char *cur_rev = NULL;
2915     char *retval = NULL;
2916     Node *p;
2917     RCSVers *vers = NULL;
2918
2919     /* make sure we have something to look at... */
2920     assert (rcs != NULL);
2921
2922     if (rcs->flags & PARTIAL)
2923         RCS_reparsercsfile (rcs, NULL, NULL);
2924
2925     /* if the head is on a branch, try the branch first */
2926     if (rcs->branch != NULL)
2927     {
2928         retval = RCS_getdatebranch (rcs, date, rcs->branch);
2929         if (retval != NULL)
2930             return retval;
2931     }
2932
2933     /* otherwise if we have a trunk, try it */
2934     if (rcs->head)
2935     {
2936         p = findnode (rcs->versions, rcs->head);
2937         if (p == NULL)
2938         {
2939             error (0, 0, "%s: head revision %s doesn't exist", rcs->print_path,
2940                    rcs->head);
2941         }
2942         while (p != NULL)
2943         {
2944             /* if the date of this one is before date, take it */
2945             vers = p->data;
2946             if (RCS_datecmp (vers->date, date) <= 0)
2947             {
2948                 cur_rev = vers->version;
2949                 break;
2950             }
2951
2952             /* if there is a next version, find the node */
2953             if (vers->next != NULL)
2954                 p = findnode (rcs->versions, vers->next);
2955             else
2956                 p = NULL;
2957         }
2958     }
2959     else
2960         error (0, 0, "%s: no head revision", rcs->print_path);
2961
2962     /*
2963      * at this point, either we have the revision we want, or we have the
2964      * first revision on the trunk (1.1?) in our hands, or we've come up
2965      * completely empty
2966      */
2967
2968     /* if we found what we're looking for, and it's not 1.1 return it */
2969     if (cur_rev != NULL)
2970     {
2971         if (! STREQ (cur_rev, "1.1"))
2972             return xstrdup (cur_rev);
2973
2974         /* This is 1.1;  if the date of 1.1 is not the same as that for the
2975            1.1.1.1 version, then return 1.1.  This happens when the first
2976            version of a file is created by a regular cvs add and commit,
2977            and there is a subsequent cvs import of the same file.  */
2978         p = findnode (rcs->versions, "1.1.1.1");
2979         if (p)
2980         {
2981             char *date_1_1 = vers->date;
2982
2983             vers = p->data;
2984             if (RCS_datecmp (vers->date, date_1_1) != 0)
2985                 return xstrdup ("1.1");
2986         }
2987     }
2988
2989     /* look on the vendor branch */
2990     retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
2991
2992     /*
2993      * if we found a match, return it; otherwise, we return the first
2994      * revision on the trunk or NULL depending on force_tag_match and the
2995      * date of the first rev
2996      */
2997     if (retval != NULL)
2998         return retval;
2999
3000     if (vers && (!force_tag_match || RCS_datecmp (vers->date, date) <= 0))
3001         return xstrdup (vers->version);
3002     else
3003         return NULL;
3004 }
3005
3006
3007
3008 /*
3009  * Look up the last element on a branch that was put in before or on
3010  * the specified date and time (return the rev or NULL)
3011  */
3012 static char *
3013 RCS_getdatebranch (RCSNode *rcs, const char *date, const char *branch)
3014 {
3015     char *cur_rev = NULL;
3016     char *cp;
3017     char *xbranch, *xrev;
3018     Node *p;
3019     RCSVers *vers;
3020
3021     /* look up the first revision on the branch */
3022     xrev = xstrdup (branch);
3023     cp = strrchr (xrev, '.');
3024     if (cp == NULL)
3025     {
3026         free (xrev);
3027         return NULL;
3028     }
3029     *cp = '\0';                         /* turn it into a revision */
3030
3031     assert (rcs != NULL);
3032
3033     if (rcs->flags & PARTIAL)
3034         RCS_reparsercsfile (rcs, NULL, NULL);
3035
3036     p = findnode (rcs->versions, xrev);
3037     free (xrev);
3038     if (p == NULL)
3039         return NULL;
3040     vers = p->data;
3041
3042     /* Tentatively use this revision, if it is early enough.  */
3043     if (RCS_datecmp (vers->date, date) <= 0)
3044         cur_rev = vers->version;
3045
3046     /* If no branches list, return now.  This is what happens if the branch
3047        is a (magic) branch with no revisions yet.  */
3048     if (vers->branches == NULL)
3049         return xstrdup (cur_rev);
3050
3051     /* walk the branches list looking for the branch number */
3052     xbranch = Xasprintf ("%s.", branch);
3053     for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
3054         if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
3055             break;
3056     free (xbranch);
3057     if (p == vers->branches->list)
3058     {
3059         /* This is what happens if the branch is a (magic) branch with
3060            no revisions yet.  Similar to the case where vers->branches ==
3061            NULL, except here there was a another branch off the same
3062            branchpoint.  */
3063         return xstrdup (cur_rev);
3064     }
3065
3066     p = findnode (rcs->versions, p->key);
3067
3068     /* walk the next pointers until you find the end, or the date is too late */
3069     while (p != NULL)
3070     {
3071         vers = p->data;
3072         if (RCS_datecmp (vers->date, date) <= 0)
3073             cur_rev = vers->version;
3074         else
3075             break;
3076
3077         /* if there is a next version, find the node */
3078         if (vers->next != NULL)
3079             p = findnode (rcs->versions, vers->next);
3080         else
3081             p = NULL;
3082     }
3083
3084     /* Return whatever we found, which may be NULL.  */
3085     return xstrdup (cur_rev);
3086 }
3087
3088
3089
3090 /*
3091  * Compare two dates in RCS format. Beware the change in format on January 1,
3092  * 2000, when years go from 2-digit to full format.
3093  */
3094 int
3095 RCS_datecmp (const char *date1, const char *date2)
3096 {
3097     int length_diff = strlen (date1) - strlen (date2);
3098
3099     return length_diff ? length_diff : strcmp (date1, date2);
3100 }
3101
3102
3103
3104 /* Look up revision REV in RCS and return the date specified for the
3105    revision minus FUDGE seconds (FUDGE will generally be one, so that the
3106    logically previous revision will be found later, or zero, if we want
3107    the exact date).
3108
3109    The return value is the date being returned as a time_t, or (time_t)-1
3110    on error (previously was documented as zero on error; I haven't checked
3111    the callers to make sure that they really check for (time_t)-1, but
3112    the latter is what this function really returns).  If DATE is non-NULL,
3113    then it must point to MAXDATELEN characters, and we store the same
3114    return value there in DATEFORM format.  */
3115 time_t
3116 RCS_getrevtime (RCSNode *rcs, const char *rev, char *date, int fudge)
3117 {
3118     char *tdate;
3119     struct tm xtm, *ftm;
3120     struct timespec revdate;
3121     Node *p;
3122     RCSVers *vers;
3123
3124     /* make sure we have something to look at... */
3125     assert (rcs != NULL);
3126
3127     if (rcs->flags & PARTIAL)
3128         RCS_reparsercsfile (rcs, NULL, NULL);
3129
3130     /* look up the revision */
3131     p = findnode (rcs->versions, rev);
3132     if (p == NULL)
3133         return -1;
3134     vers = p->data;
3135
3136     /* split up the date */
3137     if (sscanf (vers->date, SDATEFORM, &xtm.tm_year, &xtm.tm_mon,
3138                 &xtm.tm_mday, &xtm.tm_hour, &xtm.tm_min, &xtm.tm_sec) != 6)
3139         error (1, 0, "%s: invalid date for revision %s (%s)", rcs->print_path,
3140                rev, vers->date);
3141
3142     /* If the year is from 1900 to 1999, RCS files contain only two
3143        digits, and sscanf gives us a year from 0-99.  If the year is
3144        2000+, RCS files contain all four digits and we subtract 1900,
3145        because the tm_year field should contain years since 1900.  */
3146
3147     if (xtm.tm_year >= 100 && xtm.tm_year < 2000)
3148         error (0, 0, "%s: non-standard date format for revision %s (%s)",
3149                rcs->print_path, rev, vers->date);
3150     if (xtm.tm_year >= 1900)
3151         xtm.tm_year -= 1900;
3152
3153     /* put the date in a form getdate can grok */
3154     tdate = Xasprintf ("%d-%d-%d %d:%d:%d -0000",
3155                        xtm.tm_year + 1900, xtm.tm_mon, xtm.tm_mday,
3156                        xtm.tm_hour, xtm.tm_min, xtm.tm_sec);
3157
3158     /* Turn it into seconds since the epoch.
3159      *
3160      * We use a struct timespec since that is what getdate requires, then
3161      * truncate the nanoseconds.
3162      */
3163     if (!get_date (&revdate, tdate, NULL))
3164     {
3165         free (tdate);
3166         return (time_t)-1;
3167     }
3168     free (tdate);
3169
3170     revdate.tv_sec -= fudge;    /* remove "fudge" seconds */
3171     if (date)
3172     {
3173         /* Put an appropriate string into `date', if we were given one. */
3174         ftm = gmtime (&revdate.tv_sec);
3175         (void) sprintf (date, DATEFORM,
3176                         ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
3177                         ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
3178                         ftm->tm_min, ftm->tm_sec);
3179     }
3180
3181     return revdate.tv_sec;
3182 }
3183
3184
3185
3186 List *
3187 RCS_getlocks (RCSNode *rcs)
3188 {
3189     assert(rcs != NULL);
3190
3191     if (rcs->flags & PARTIAL)
3192         RCS_reparsercsfile (rcs, NULL, NULL);
3193
3194     if (rcs->locks_data) {
3195         rcs->locks = getlist ();
3196         do_locks (rcs->locks, rcs->locks_data);
3197         free(rcs->locks_data);
3198         rcs->locks_data = NULL;
3199     }
3200
3201     return rcs->locks;
3202 }
3203
3204
3205
3206 List *
3207 RCS_symbols(RCSNode *rcs)
3208 {
3209     assert(rcs != NULL);
3210
3211     if (rcs->flags & PARTIAL)
3212         RCS_reparsercsfile (rcs, NULL, NULL);
3213
3214     if (rcs->symbols_data) {
3215         rcs->symbols = getlist ();
3216         do_symbols (rcs->symbols, rcs->symbols_data);
3217         free(rcs->symbols_data);
3218         rcs->symbols_data = NULL;
3219     }
3220
3221     return rcs->symbols;
3222 }
3223
3224
3225
3226 /*
3227  * Return the version associated with a particular symbolic tag.
3228  * Returns NULL or a newly malloc'd string.
3229  */
3230 static char *
3231 translate_symtag (RCSNode *rcs, const char *tag)
3232 {
3233     if (rcs->flags & PARTIAL)
3234         RCS_reparsercsfile (rcs, NULL, NULL);
3235
3236     if (rcs->symbols != NULL)
3237     {
3238         Node *p;
3239
3240         /* The symbols have already been converted into a list.  */
3241         p = findnode (rcs->symbols, tag);
3242         if (p == NULL)
3243             return NULL;
3244
3245         return xstrdup (p->data);
3246     }
3247
3248     if (rcs->symbols_data != NULL)
3249     {
3250         size_t len;
3251         char *cp, *last;
3252
3253         /* Look through the RCS symbols information.  This is like
3254            do_symbols, but we don't add the information to a list.  In
3255            most cases, we will only be called once for this file, so
3256            generating the list is unnecessary overhead.  */
3257
3258         len = strlen (tag);
3259         cp = rcs->symbols_data;
3260         /* Keeping track of LAST below isn't strictly necessary, now that tags
3261          * should be parsed for validity before they are accepted, but tags
3262          * with spaces used to cause the code below to loop indefintely, so
3263          * I have corrected for that.  Now, in the event that I missed
3264          * something, the server cannot be hung.  -DRP
3265          */
3266         last = NULL;
3267         while ((cp = strchr (cp, tag[0])) != NULL)
3268         {
3269             if (cp == last) break;
3270             if ((cp == rcs->symbols_data || whitespace (cp[-1]))
3271                 && strncmp (cp, tag, len) == 0
3272                 && cp[len] == ':')
3273             {
3274                 char *v, *r;
3275
3276                 /* We found the tag.  Return the version number.  */
3277
3278                 cp += len + 1;
3279                 v = cp;
3280                 while (! whitespace (*cp) && *cp != '\0')
3281                     ++cp;
3282                 r = xmalloc (cp - v + 1);
3283                 strncpy (r, v, cp - v);
3284                 r[cp - v] = '\0';
3285                 return r;
3286             }
3287
3288             while (! whitespace (*cp) && *cp != '\0')
3289                 ++cp;
3290             if (*cp == '\0')
3291                 break;
3292             last = cp;
3293         }
3294     }
3295
3296     return NULL;
3297 }
3298
3299
3300
3301 /*
3302  * The argument ARG is the getopt remainder of the -k option specified on the
3303  * command line.  This function returns malloc'ed space that can be used
3304  * directly in calls to RCS V5, with the -k flag munged correctly.
3305  */
3306 char *
3307 RCS_check_kflag (const char *arg)
3308 {
3309     static const char *const  keyword_usage[] =
3310     {
3311       "%s %s: invalid RCS keyword expansion mode\n",
3312       "Valid expansion modes include:\n",
3313       "   -kkv\tGenerate keywords using the default form.\n",
3314       "   -kkvl\tLike -kkv, except locker's name inserted.\n",
3315       "   -kk\tGenerate only keyword names in keyword strings.\n",
3316       "   -kv\tGenerate only keyword values in keyword strings.\n",
3317       "   -ko\tGenerate the old keyword string (no changes from checked in file).\n",
3318       "   -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n",
3319       "(Specify the --help global option for a list of other help options)\n",
3320       NULL,
3321     };
3322     char const *const *cpp = NULL;
3323
3324     if (arg)
3325     {
3326         for (cpp = kflags; *cpp != NULL; cpp++)
3327         {
3328             if (STREQ (arg, *cpp))
3329                 break;
3330         }
3331     }
3332
3333     if (arg == NULL || *cpp == NULL)
3334     {
3335         usage (keyword_usage);
3336     }
3337
3338     return Xasprintf ("-k%s", *cpp);
3339 }
3340
3341
3342
3343 /*
3344  * Do some consistency checks on the symbolic tag... These should equate
3345  * pretty close to what RCS checks, though I don't know for certain.
3346  */
3347 void
3348 RCS_check_tag (const char *tag)
3349 {
3350     char *invalid = "$,.:;@";           /* invalid RCS tag characters */
3351     const char *cp;
3352
3353     /*
3354      * The first character must be an alphabetic letter. The remaining
3355      * characters cannot be non-visible graphic characters, and must not be
3356      * in the set of "invalid" RCS identifier characters.
3357      */
3358     if (isalpha ((unsigned char) *tag))
3359     {
3360         for (cp = tag; *cp; cp++)
3361         {
3362             if (!isgraph ((unsigned char) *cp))
3363                 error (1, 0, "tag `%s' has non-visible graphic characters",
3364                        tag);
3365             if (strchr (invalid, *cp))
3366                 error (1, 0, "tag `%s' must not contain the characters `%s'",
3367                        tag, invalid);
3368         }
3369     }
3370     else
3371         error (1, 0, "tag `%s' must start with a letter", tag);
3372 }
3373
3374
3375
3376 /*
3377  * TRUE if argument has valid s