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