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