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