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