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