Sync usage with man page.
[netbsd-mini2440.git] / external / gpl2 / xcvs / dist / src / rcs.c
blob4c517a25469443e3f2ec51d894818beb6abad3f7
1 /*
2 * Copyright (C) 1986-2005 The Free Software Foundation, Inc.
4 * Portions Copyright (C) 1998-2005 Derek Price, Ximbiot <http://ximbiot.com>,
5 * and others.
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.
13 * The routines contained in this file do all the rcs file parsing and
14 * manipulation
17 #include "cvs.h"
18 #include "edit.h"
19 #include "hardlink.h"
21 /* These need to be source after cvs.h or HAVE_MMAP won't be set... */
22 #ifdef HAVE_MMAP
23 # include "getpagesize.h"
24 # include <sys/mman.h>
26 /* Define MAP_FILE when it isn't otherwise. */
27 # ifndef MAP_FILE
28 # define MAP_FILE 0
29 # endif
30 /* Define MAP_FAILED for old systems which neglect to. */
31 # ifndef MAP_FAILED
32 # define MAP_FAILED ((void *)-1)
33 # endif
34 #endif
36 /* The RCS -k options, and a set of enums that must match the array.
37 These come first so that we can use enum kflag in function
38 prototypes. */
39 static const char *const kflags[] =
40 {"kv", "kvl", "k", "v", "o", "b", NULL};
41 enum kflag { KFLAG_KV = 0, KFLAG_KVL, KFLAG_K, KFLAG_V, KFLAG_O, KFLAG_B };
43 /* A structure we use to buffer the contents of an RCS file. The
44 various fields are only referenced directly by the rcsbuf_*
45 functions. We declare the struct here so that we can allocate it
46 on the stack, rather than in memory. */
48 struct rcsbuffer
50 /* Points to the current position in the buffer. */
51 char *ptr;
52 /* Points just after the last valid character in the buffer. */
53 char *ptrend;
54 /* The file. */
55 FILE *fp;
56 /* The name of the file, used for error messages. */
57 const char *filename;
58 /* The starting file position of the data in the buffer. */
59 unsigned long pos;
60 /* The length of the value. */
61 size_t vlen;
62 /* Whether the value contains an '@' string. If so, we can not
63 compress whitespace characters. */
64 int at_string;
65 /* The number of embedded '@' characters in an '@' string. If
66 this is non-zero, we must search the string for pairs of '@'
67 and convert them to a single '@'. */
68 int embedded_at;
71 static RCSNode *RCS_parsercsfile_i (FILE * fp, const char *rcsfile);
72 static char *RCS_getdatebranch (RCSNode * rcs, const char *date,
73 const char *branch);
74 static void rcsbuf_open (struct rcsbuffer *, FILE *fp,
75 const char *filename, unsigned long pos);
76 static void rcsbuf_close (struct rcsbuffer *);
77 static int rcsbuf_getkey (struct rcsbuffer *, char **keyp, char **valp);
78 static int rcsbuf_getrevnum (struct rcsbuffer *, char **revp);
79 static char *rcsbuf_fill (struct rcsbuffer *, char *ptr, char **keyp,
80 char **valp);
81 static int rcsbuf_valcmp (struct rcsbuffer *);
82 static char *rcsbuf_valcopy (struct rcsbuffer *, char *val, int polish,
83 size_t *lenp);
84 static void rcsbuf_valpolish (struct rcsbuffer *, char *val, int polish,
85 size_t *lenp);
86 static void rcsbuf_valpolish_internal (struct rcsbuffer *, char *to,
87 const char *from, size_t *lenp);
88 static off_t rcsbuf_ftello (struct rcsbuffer *);
89 static void rcsbuf_get_buffered (struct rcsbuffer *, char **datap,
90 size_t *lenp);
91 static void rcsbuf_cache (RCSNode *, struct rcsbuffer *);
92 static void rcsbuf_cache_close (void);
93 static void rcsbuf_cache_open (RCSNode *, off_t, FILE **, struct rcsbuffer *);
94 static int checkmagic_proc (Node *p, void *closure);
95 static void do_branches (List * list, char *val);
96 static void do_symbols (List * list, char *val);
97 static void do_locks (List * list, char *val);
98 static void free_rcsnode_contents (RCSNode *);
99 static void free_rcsvers_contents (RCSVers *);
100 static void rcsvers_delproc (Node * p);
101 static char *translate_symtag (RCSNode *, const char *);
102 static char *RCS_addbranch (RCSNode *, const char *);
103 static char *truncate_revnum_in_place (char *);
104 static char *truncate_revnum (const char *);
105 static char *printable_date (const char *);
106 static char *escape_keyword_value (const char *, int *);
107 static void expand_keywords (RCSNode *, RCSVers *, const char *,
108 const char *, size_t, enum kflag, char *,
109 size_t, char **, size_t *);
110 static void cmp_file_buffer (void *, const char *, size_t);
112 /* Routines for reading, parsing and writing RCS files. */
113 static RCSVers *getdelta (struct rcsbuffer *, char *, char **, char **);
114 static Deltatext *RCS_getdeltatext (RCSNode *, FILE *, struct rcsbuffer *);
115 static void freedeltatext (Deltatext *);
117 static void RCS_putadmin (RCSNode *, FILE *);
118 static void RCS_putdtree (RCSNode *, char *, FILE *);
119 static void RCS_putdesc (RCSNode *, FILE *);
120 static void putdelta (RCSVers *, FILE *);
121 static int putrcsfield_proc (Node *, void *);
122 static int putsymbol_proc (Node *, void *);
123 static void RCS_copydeltas (RCSNode *, FILE *, struct rcsbuffer *, FILE *,
124 Deltatext *, char *);
125 static int count_delta_actions (Node *, void *);
126 static void putdeltatext (FILE *, Deltatext *);
128 static FILE *rcs_internal_lockfile (char *);
129 static void rcs_internal_unlockfile (FILE *, char *);
130 static char *rcs_lockfilename (const char *);
132 /* The RCS file reading functions are called a lot, and they do some
133 string comparisons. This macro speeds things up a bit by skipping
134 the function call when the first characters are different. It
135 evaluates its arguments multiple times. */
136 #define STREQ(a, b) (*(char *)(a) == *(char *)(b) && strcmp ((a), (b)) == 0)
138 static char * getfullCVSname (char *, char **);
141 * We don't want to use isspace() from the C library because:
143 * 1. The definition of "whitespace" in RCS files includes ASCII
144 * backspace, but the C locale doesn't.
145 * 2. isspace is an very expensive function call in some implementations
146 * due to the addition of wide character support.
148 static const char spacetab[] = {
149 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, /* 0x00 - 0x0f */
150 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x10 - 0x1f */
151 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20 - 0x2f */
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x30 - 0x3f */
153 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x40 - 0x4f */
154 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50 - 0x5f */
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x60 - 0x8f */
156 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70 - 0x7f */
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x80 - 0x8f */
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x90 - 0x9f */
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 - 0xaf */
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 - 0xbf */
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 - 0xcf */
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 - 0xdf */
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 - 0xef */
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */
167 #define whitespace(c) (spacetab[(unsigned char)c] != 0)
169 static char *rcs_lockfile = NULL;
170 static int rcs_lockfd = -1;
175 * char *
176 * locate_rcs ( const char* file, const char *repository , int *inattic )
178 * Find an RCS file in the repository, case insensitively when the cased name
179 * doesn't exist, we are running as the server, and a client has asked us to
180 * ignore case.
182 * Most parts of CVS will want to rely instead on RCS_parse which calls this
183 * function and is called by recurse.c which then puts the result in useful
184 * places like the rcs field of struct file_info.
186 * INPUTS
188 * repository the repository (including the directory)
189 * file the filename within that directory (without RCSEXT).
190 * inattic NULL or a pointer to the output boolean
192 * OUTPUTS
194 * inattic If this input was non-null, the destination will be
195 * set to true if the file was found in the attic or
196 * false if not. If no RCS file is found, this value
197 * is undefined.
199 * RETURNS
201 * a newly-malloc'd array containing the absolute pathname of the RCS
202 * file that was found or NULL when none was found.
204 * ERRORS
206 * errno can be set by the return value of the final call to
207 * locate_file_in_dir(). This should resolve to the system's existence error
208 * value (sometime ENOENT) if the Attic directory did not exist and ENOENT if
209 * the Attic was found but no matching files were found in the Attic or its
210 * parent.
212 static char *
213 locate_rcs (const char *repository, const char *file, int *inattic)
215 char *retval;
217 /* First, try to find the file as cased. */
218 retval = xmalloc (strlen (repository)
219 + sizeof (CVSATTIC)
220 + strlen (file)
221 + sizeof (RCSEXT)
222 + 3);
223 sprintf (retval, "%s/%s%s", repository, file, RCSEXT);
224 if (isreadable (retval))
226 if (inattic)
227 *inattic = 0;
228 return retval;
230 sprintf (retval, "%s/%s/%s%s", repository, CVSATTIC, file, RCSEXT);
231 if (isreadable (retval))
233 if (inattic)
234 *inattic = 1;
235 return retval;
237 free (retval);
239 return NULL;
244 /* A few generic thoughts on error handling, in particular the
245 printing of unexpected characters that we find in the RCS file
246 (that is, why we use '\x%x' rather than %c or some such).
248 * Avoiding %c means we don't have to worry about what is printable
249 and other such stuff. In error handling, often better to keep it
250 simple.
252 * Hex rather than decimal or octal because character set standards
253 tend to use hex.
255 * Saying "character 0x%x" might make it sound like we are printing
256 a file offset. So we use '\x%x'.
258 * Would be nice to print the offset within the file, but I can
259 imagine various portability hassles (in particular, whether
260 unsigned long is always big enough to hold file offsets). */
262 /* Parse an rcsfile given a user file name and a repository. If there is
263 an error, we print an error message and return NULL. If the file
264 does not exist, we return NULL without printing anything (I'm not
265 sure this allows the caller to do anything reasonable, but it is
266 the current behavior). */
267 RCSNode *
268 RCS_parse (const char *file, const char *repos)
270 RCSNode *rcs;
271 FILE *fp;
272 RCSNode *retval = NULL;
273 char *rcsfile;
274 int inattic;
276 /* We're creating a new RCSNode, so there is no hope of finding it
277 in the cache. */
278 rcsbuf_cache_close ();
280 if (!(rcsfile = locate_rcs (repos, file, &inattic)))
282 /* Handle the error cases */
284 else if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)))
286 rcs = RCS_parsercsfile_i (fp, rcsfile);
287 if (rcs)
289 rcs->flags |= VALID;
290 if (inattic)
291 rcs->flags |= INATTIC;
294 free (rcsfile);
295 retval = rcs;
297 else if (!existence_error (errno))
299 error (0, errno, "cannot open `%s'", rcsfile);
300 free (rcsfile);
303 return retval;
309 * Parse a specific rcsfile.
311 RCSNode *
312 RCS_parsercsfile (const char *rcsfile)
314 FILE *fp;
315 RCSNode *rcs;
317 /* We're creating a new RCSNode, so there is no hope of finding it
318 in the cache. */
319 rcsbuf_cache_close ();
321 /* open the rcsfile */
322 if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) == NULL)
324 error (0, errno, "Couldn't open rcs file `%s'", rcsfile);
325 return NULL;
328 rcs = RCS_parsercsfile_i (fp, rcsfile);
330 return rcs;
337 static RCSNode *
338 RCS_parsercsfile_i (FILE *fp, const char *rcsfile)
340 RCSNode *rdata;
341 struct rcsbuffer rcsbuf;
342 char *key, *value;
344 /* make a node */
345 rdata = xmalloc (sizeof (RCSNode));
346 memset (rdata, 0, sizeof (RCSNode));
347 rdata->refcount = 1;
348 rdata->path = xstrdup (rcsfile);
349 rdata->print_path = xstrdup (primary_root_inverse_translate (rcsfile));
351 /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header.
353 Most cvs operations on the main branch don't need any more
354 information. Those that do call RCS_reparsercsfile to parse
355 the rest of the header and the deltas. */
357 rcsbuf_open (&rcsbuf, fp, rcsfile, 0);
359 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
360 goto l_error;
361 if (STREQ (key, RCSDESC))
362 goto l_error;
364 if (STREQ (RCSHEAD, key) && value != NULL)
365 rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
367 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
368 goto l_error;
369 if (STREQ (key, RCSDESC))
370 goto l_error;
372 if (STREQ (RCSBRANCH, key) && value != NULL)
374 char *cp;
376 rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
377 if ((numdots (rdata->branch) & 1) != 0)
379 /* turn it into a branch if it's a revision */
380 cp = strrchr (rdata->branch, '.');
381 *cp = '\0';
385 /* Look ahead for expand, stopping when we see desc or a revision
386 number. */
387 while (1)
389 char *cp;
391 if (STREQ (RCSEXPAND, key))
393 rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
394 break;
397 for (cp = key;
398 (isdigit ((unsigned char)*cp) || *cp == '.') && *cp != '\0';
399 cp++)
400 /* do nothing */ ;
401 if (*cp == '\0')
402 break;
404 if (STREQ (RCSDESC, key))
405 break;
407 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
408 break;
411 rdata->flags |= PARTIAL;
413 rcsbuf_cache (rdata, &rcsbuf);
415 return rdata;
417 l_error:
418 error (0, 0, "`%s' does not appear to be a valid rcs file",
419 rcsfile);
420 rcsbuf_close (&rcsbuf);
421 freercsnode (&rdata);
422 fclose (fp);
423 return NULL;
428 /* Do the real work of parsing an RCS file.
430 On error, die with a fatal error; if it returns at all it was successful.
432 If PFP is NULL, close the file when done. Otherwise, leave it open
433 and store the FILE * in *PFP. */
434 void
435 RCS_reparsercsfile (RCSNode *rdata, FILE **pfp, struct rcsbuffer *rcsbufp)
437 FILE *fp;
438 char *rcsfile;
439 struct rcsbuffer rcsbuf;
440 Node *q, *kv;
441 RCSVers *vnode;
442 int gotkey;
443 char *cp;
444 char *key, *value;
446 assert (rdata != NULL);
447 rcsfile = rdata->path;
449 rcsbuf_cache_open (rdata, 0, &fp, &rcsbuf);
451 /* make a node */
452 /* This probably shouldn't be done until later: if a file has an
453 empty revision tree (which is permissible), rdata->versions
454 should be NULL. -twp */
455 rdata->versions = getlist ();
458 * process all the special header information, break out when we get to
459 * the first revision delta
461 gotkey = 0;
462 for (;;)
464 /* get the next key/value pair */
465 if (!gotkey)
467 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
469 error (1, 0, "`%s' does not appear to be a valid rcs file",
470 rcsfile);
474 gotkey = 0;
476 /* Skip head, branch and expand tags; we already have them. */
477 if (STREQ (key, RCSHEAD)
478 || STREQ (key, RCSBRANCH)
479 || STREQ (key, RCSEXPAND))
481 continue;
484 if (STREQ (key, "access"))
486 if (value != NULL)
488 /* We pass the POLISH parameter as 1 because
489 RCS_addaccess expects nothing but spaces. FIXME:
490 It would be easy and more efficient to change
491 RCS_addaccess. */
492 if (rdata->access)
494 error (0, 0,
495 "Duplicate `access' keyword found in RCS file.");
496 free (rdata->access);
498 rdata->access = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
500 continue;
503 /* We always save lock information, so that we can handle
504 -kkvl correctly when checking out a file. */
505 if (STREQ (key, "locks"))
507 if (value != NULL)
509 if (rdata->locks_data)
511 error (0, 0,
512 "Duplicate `locks' keyword found in RCS file.");
513 free (rdata->locks_data);
515 rdata->locks_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
517 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
519 error (1, 0, "premature end of file reading %s", rcsfile);
521 if (STREQ (key, "strict") && value == NULL)
523 rdata->strict_locks = 1;
525 else
526 gotkey = 1;
527 continue;
530 if (STREQ (RCSSYMBOLS, key))
532 if (value != NULL)
534 if (rdata->symbols_data)
536 error (0, 0,
537 "Duplicate `%s' keyword found in RCS file.",
538 RCSSYMBOLS);
539 free (rdata->symbols_data);
541 rdata->symbols_data = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
543 continue;
547 * check key for '.''s and digits (probably a rev) if it is a
548 * revision or `desc', we are done with the headers and are down to the
549 * revision deltas, so we break out of the loop
551 for (cp = key;
552 (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
553 cp++)
554 /* do nothing */ ;
555 /* Note that when comparing with RCSDATE, we are not massaging
556 VALUE from the string found in the RCS file. This is OK
557 since we know exactly what to expect. */
558 if (*cp == '\0' && strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) == 0)
559 break;
561 if (STREQ (key, RCSDESC))
562 break;
564 if (STREQ (key, "comment"))
566 if (rdata->comment)
568 error (0, 0,
569 "warning: duplicate key `%s' in RCS file `%s'",
570 key, rcsfile);
571 free (rdata->comment);
573 rdata->comment = rcsbuf_valcopy (&rcsbuf, value, 0, NULL);
574 continue;
576 if (rdata->other == NULL)
577 rdata->other = getlist ();
578 kv = getnode ();
579 kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
580 kv->key = xstrdup (key);
581 kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD, NULL);
582 if (addnode (rdata->other, kv) != 0)
584 error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
585 key, rcsfile);
586 freenode (kv);
589 /* if we haven't grabbed it yet, we didn't want it */
592 /* We got out of the loop, so we have the first part of the first
593 revision delta in KEY (the revision) and VALUE (the date key
594 and its value). This is what getdelta expects to receive. */
596 while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL)
598 /* get the node */
599 q = getnode ();
600 q->type = RCSVERS;
601 q->delproc = rcsvers_delproc;
602 q->data = vnode;
603 q->key = vnode->version;
605 /* add the nodes to the list */
606 if (addnode (rdata->versions, q) != 0)
608 #if 0
609 purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
610 q->key, rcsfile);
611 freenode (q);
612 #endif
616 /* Here KEY and VALUE are whatever caused getdelta to return NULL. */
618 if (STREQ (key, RCSDESC))
620 if (rdata->desc != NULL)
622 error (0, 0,
623 "warning: duplicate key `%s' in RCS file `%s'",
624 key, rcsfile);
625 free (rdata->desc);
627 rdata->desc = rcsbuf_valcopy (&rcsbuf, value, 1, NULL);
630 rdata->delta_pos = rcsbuf_ftello (&rcsbuf);
632 if (pfp == NULL)
633 rcsbuf_cache (rdata, &rcsbuf);
634 else
636 *pfp = fp;
637 *rcsbufp = rcsbuf;
639 rdata->flags &= ~PARTIAL;
644 /* Move RCS into or out of the Attic, depending on TOATTIC. If the
645 file is already in the desired place, return without doing
646 anything. At some point may want to think about how this relates
647 to RCS_rewrite but that is a bit hairy (if one wants renames to be
648 atomic, or that kind of thing). If there is an error, print a message
649 and return 1. On success, return 0. */
651 RCS_setattic (RCSNode *rcs, int toattic)
653 char *newpath;
654 const char *p;
655 char *q;
657 /* Some systems aren't going to let us rename an open file. */
658 rcsbuf_cache_close ();
660 /* Could make the pathname computations in this file, and probably
661 in other parts of rcs.c too, easier if the REPOS and FILE
662 arguments to RCS_parse got stashed in the RCSNode. */
664 if (toattic)
666 mode_t omask;
668 if (rcs->flags & INATTIC)
669 return 0;
671 /* Example: rcs->path is "/foo/bar/baz,v". */
672 newpath = xmalloc (strlen (rcs->path) + sizeof CVSATTIC + 5);
673 p = last_component (rcs->path);
674 strncpy (newpath, rcs->path, p - rcs->path);
675 strcpy (newpath + (p - rcs->path), CVSATTIC);
677 /* Create the Attic directory if it doesn't exist. */
678 omask = umask (cvsumask);
679 if (CVS_MKDIR (newpath, 0777) < 0 && errno != EEXIST)
680 error (0, errno, "cannot make directory %s", newpath);
681 (void) umask (omask);
683 strcat (newpath, "/");
684 strcat (newpath, p);
686 if (CVS_RENAME (rcs->path, newpath) < 0)
688 int save_errno = errno;
690 /* The checks for isreadable look awfully fishy, but
691 I'm going to leave them here for now until I
692 can think harder about whether they take care of
693 some cases which should be handled somehow. */
695 if (isreadable (rcs->path) || !isreadable (newpath))
697 error (0, save_errno, "cannot rename %s to %s",
698 rcs->path, newpath);
699 free (newpath);
700 return 1;
704 else
706 if (!(rcs->flags & INATTIC))
707 return 0;
709 newpath = xmalloc (strlen (rcs->path));
711 /* Example: rcs->path is "/foo/bar/Attic/baz,v". */
712 p = last_component (rcs->path);
713 strncpy (newpath, rcs->path, p - rcs->path - 1);
714 newpath[p - rcs->path - 1] = '\0';
715 q = newpath + (p - rcs->path - 1) - (sizeof CVSATTIC - 1);
716 assert (strncmp (q, CVSATTIC, sizeof CVSATTIC - 1) == 0);
717 strcpy (q, p);
719 if (CVS_RENAME (rcs->path, newpath) < 0)
721 error (0, errno, "failed to move `%s' out of the attic",
722 rcs->path);
723 free (newpath);
724 return 1;
728 free (rcs->path);
729 rcs->path = newpath;
731 return 0;
737 * Fully parse the RCS file. Store all keyword/value pairs, fetch the
738 * log messages for each revision, and fetch add and delete counts for
739 * each revision (we could fetch the entire text for each revision,
740 * but the only caller, log_fileproc, doesn't need that information,
741 * so we don't waste the memory required to store it). The add and
742 * delete counts are stored on the OTHER field of the RCSVERSNODE
743 * structure, under the names ";add" and ";delete", so that we don't
744 * waste the memory space of extra fields in RCSVERSNODE for code
745 * which doesn't need this information.
747 void
748 RCS_fully_parse (RCSNode *rcs)
750 FILE *fp;
751 struct rcsbuffer rcsbuf;
753 RCS_reparsercsfile (rcs, &fp, &rcsbuf);
755 while (1)
757 char *key, *value;
758 Node *vers;
759 RCSVers *vnode;
761 /* Rather than try to keep track of how much information we
762 have read, just read to the end of the file. */
763 if (!rcsbuf_getrevnum (&rcsbuf, &key))
764 break;
766 vers = findnode (rcs->versions, key);
767 if (vers == NULL)
768 error (1, 0,
769 "mismatch in rcs file %s between deltas and deltatexts (%s)",
770 rcs->print_path, key);
772 vnode = vers->data;
774 while (rcsbuf_getkey (&rcsbuf, &key, &value))
776 if (!STREQ (key, "text"))
778 Node *kv;
780 if (vnode->other == NULL)
781 vnode->other = getlist ();
782 kv = getnode ();
783 kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
784 kv->key = xstrdup (key);
785 kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
786 NULL);
787 if (addnode (vnode->other, kv) != 0)
789 error (0, 0,
791 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
792 key, vnode->version, rcs->print_path);
793 freenode (kv);
796 continue;
799 if (!STREQ (vnode->version, rcs->head))
801 unsigned long add, del;
802 char buf[50];
803 Node *kv;
805 /* This is a change text. Store the add and delete
806 counts. */
807 add = 0;
808 del = 0;
809 if (value != NULL)
811 size_t vallen;
812 const char *cp;
814 rcsbuf_valpolish (&rcsbuf, value, 0, &vallen);
815 cp = value;
816 while (cp < value + vallen)
818 char op;
819 unsigned long count;
821 op = *cp++;
822 if (op != 'a' && op != 'd')
823 error (1, 0, "\
824 unrecognized operation '\\x%x' in %s",
825 op, rcs->print_path);
826 (void) strtoul (cp, (char **) &cp, 10);
827 if (*cp++ != ' ')
828 error (1, 0, "space expected in %s revision %s",
829 rcs->print_path, vnode->version);
830 count = strtoul (cp, (char **) &cp, 10);
831 if (*cp++ != '\012')
832 error (1, 0, "linefeed expected in %s revision %s",
833 rcs->print_path, vnode->version);
835 if (op == 'd')
836 del += count;
837 else
839 add += count;
840 while (count != 0)
842 if (*cp == '\012')
843 --count;
844 else if (cp == value + vallen)
846 if (count != 1)
847 error (1, 0, "\
848 premature end of value in %s revision %s",
849 rcs->print_path, vnode->version);
850 else
851 break;
853 ++cp;
859 sprintf (buf, "%lu", add);
860 kv = getnode ();
861 kv->type = RCSFIELD;
862 kv->key = xstrdup (";add");
863 kv->data = xstrdup (buf);
864 if (addnode (vnode->other, kv) != 0)
866 error (0, 0,
868 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
869 key, vnode->version, rcs->print_path);
870 freenode (kv);
873 sprintf (buf, "%lu", del);
874 kv = getnode ();
875 kv->type = RCSFIELD;
876 kv->key = xstrdup (";delete");
877 kv->data = xstrdup (buf);
878 if (addnode (vnode->other, kv) != 0)
880 error (0, 0,
882 warning: duplicate key `%s' in version `%s' of RCS file `%s'",
883 key, vnode->version, rcs->print_path);
884 freenode (kv);
888 /* We have found the "text" key which ends the data for
889 this revision. Break out of the loop and go on to the
890 next revision. */
891 break;
895 rcsbuf_cache (rcs, &rcsbuf);
901 * freercsnode - free up the info for an RCSNode
903 void
904 freercsnode (RCSNode **rnodep)
906 if (rnodep == NULL || *rnodep == NULL)
907 return;
909 ((*rnodep)->refcount)--;
910 if ((*rnodep)->refcount != 0)
912 *rnodep = NULL;
913 return;
915 free ((*rnodep)->path);
916 free ((*rnodep)->print_path);
917 if ((*rnodep)->head != NULL)
918 free ((*rnodep)->head);
919 if ((*rnodep)->branch != NULL)
920 free ((*rnodep)->branch);
921 free_rcsnode_contents (*rnodep);
922 free (*rnodep);
923 *rnodep = NULL;
929 * free_rcsnode_contents - free up the contents of an RCSNode without
930 * freeing the node itself, or the file name, or the head, or the
931 * path. This returns the RCSNode to the state it is in immediately
932 * after a call to RCS_parse.
934 static void
935 free_rcsnode_contents (RCSNode *rnode)
937 dellist (&rnode->versions);
938 if (rnode->symbols != NULL)
939 dellist (&rnode->symbols);
940 if (rnode->symbols_data != NULL)
941 free (rnode->symbols_data);
942 if (rnode->expand != NULL)
943 free (rnode->expand);
944 if (rnode->other != NULL)
945 dellist (&rnode->other);
946 if (rnode->access != NULL)
947 free (rnode->access);
948 if (rnode->locks_data != NULL)
949 free (rnode->locks_data);
950 if (rnode->locks != NULL)
951 dellist (&rnode->locks);
952 if (rnode->comment != NULL)
953 free (rnode->comment);
954 if (rnode->desc != NULL)
955 free (rnode->desc);
960 /* free_rcsvers_contents -- free up the contents of an RCSVers node,
961 but also free the pointer to the node itself. */
962 /* Note: The `hardlinks' list is *not* freed, since it is merely a
963 pointer into the `hardlist' structure (defined in hardlink.c), and
964 that structure is freed elsewhere in the program. */
965 static void
966 free_rcsvers_contents (RCSVers *rnode)
968 if (rnode->branches != NULL)
969 dellist (&rnode->branches);
970 if (rnode->date != NULL)
971 free (rnode->date);
972 if (rnode->next != NULL)
973 free (rnode->next);
974 if (rnode->author != NULL)
975 free (rnode->author);
976 if (rnode->state != NULL)
977 free (rnode->state);
978 if (rnode->other != NULL)
979 dellist (&rnode->other);
980 if (rnode->other_delta != NULL)
981 dellist (&rnode->other_delta);
982 if (rnode->text != NULL)
983 freedeltatext (rnode->text);
984 free (rnode);
990 * rcsvers_delproc - free up an RCSVers type node
992 static void
993 rcsvers_delproc (Node *p)
995 free_rcsvers_contents (p->data);
1000 /* These functions retrieve keys and values from an RCS file using a
1001 buffer. We use this somewhat complex approach because it turns out
1002 that for many common operations, CVS spends most of its time
1003 reading keys, so it's worth doing some fairly hairy optimization. */
1005 /* The number of bytes we try to read each time we need more data. */
1007 #define RCSBUF_BUFSIZE (8192)
1009 /* The buffer we use to store data. This grows as needed. */
1011 static char *rcsbuf_buffer = NULL;
1012 static size_t rcsbuf_buffer_size = 0;
1014 /* Whether rcsbuf_buffer is in use. This is used as a sanity check. */
1016 static int rcsbuf_inuse;
1018 /* Set up to start gathering keys and values from an RCS file. This
1019 initializes RCSBUF. */
1021 static void
1022 rcsbuf_open (struct rcsbuffer *rcsbuf, FILE *fp, const char *filename,
1023 long unsigned int pos)
1025 if (rcsbuf_inuse)
1026 error (1, 0, "rcsbuf_open: internal error");
1027 rcsbuf_inuse = 1;
1029 #ifdef HAVE_MMAP
1031 /* When we have mmap, it is much more efficient to let the system do the
1032 * buffering and caching for us
1034 struct stat fs;
1035 size_t mmap_off = 0;
1037 if ( fstat (fileno(fp), &fs) < 0 )
1038 error ( 1, errno, "Could not stat RCS archive %s for mapping", filename );
1040 if (pos)
1042 size_t ps = getpagesize ();
1043 mmap_off = ( pos / ps ) * ps;
1046 /* Map private here since this particular buffer is read only */
1047 rcsbuf_buffer = mmap ( NULL, fs.st_size - mmap_off,
1048 PROT_READ | PROT_WRITE,
1049 MAP_PRIVATE, fileno(fp), mmap_off );
1050 if ( rcsbuf_buffer == NULL || rcsbuf_buffer == MAP_FAILED )
1051 error ( 1, errno, "Could not map memory to RCS archive %s", filename );
1053 rcsbuf_buffer_size = fs.st_size - mmap_off;
1054 rcsbuf->ptr = rcsbuf_buffer + pos - mmap_off;
1055 rcsbuf->ptrend = rcsbuf_buffer + fs.st_size - mmap_off;
1056 rcsbuf->pos = mmap_off;
1058 #else /* !HAVE_MMAP */
1059 if (rcsbuf_buffer_size < RCSBUF_BUFSIZE)
1060 expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size, RCSBUF_BUFSIZE);
1062 rcsbuf->ptr = rcsbuf_buffer;
1063 rcsbuf->ptrend = rcsbuf_buffer;
1064 rcsbuf->pos = pos;
1065 #endif /* HAVE_MMAP */
1066 rcsbuf->fp = fp;
1067 rcsbuf->filename = filename;
1068 rcsbuf->vlen = 0;
1069 rcsbuf->at_string = 0;
1070 rcsbuf->embedded_at = 0;
1075 /* Stop gathering keys from an RCS file. */
1076 static void
1077 rcsbuf_close (struct rcsbuffer *rcsbuf)
1079 if (! rcsbuf_inuse)
1080 error (1, 0, "rcsbuf_close: internal error");
1081 #ifdef HAVE_MMAP
1082 munmap ( rcsbuf_buffer, rcsbuf_buffer_size );
1083 #endif
1084 rcsbuf_inuse = 0;
1089 /* Read a key/value pair from an RCS file. This sets *KEYP to point
1090 to the key, and *VALUEP to point to the value. A missing or empty
1091 value is indicated by setting *VALUEP to NULL.
1093 This function returns 1 on success, or 0 on EOF. If there is an
1094 error reading the file, or an EOF in an unexpected location, it
1095 gives a fatal error.
1097 This sets *KEYP and *VALUEP to point to storage managed by
1098 rcsbuf_getkey. Moreover, *VALUEP has not been massaged from the
1099 RCS format: it may contain embedded whitespace and embedded '@'
1100 characters. Call rcsbuf_valcopy or rcsbuf_valpolish to do
1101 appropriate massaging. */
1103 /* Note that the extreme hair in rcsbuf_getkey is because profiling
1104 statistics show that it was worth it. */
1105 static int
1106 rcsbuf_getkey (struct rcsbuffer *rcsbuf, char **keyp, char **valp)
1108 register const char * const my_spacetab = spacetab;
1109 register char *ptr, *ptrend;
1110 char c;
1112 #define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0)
1114 rcsbuf->vlen = 0;
1115 rcsbuf->at_string = 0;
1116 rcsbuf->embedded_at = 0;
1118 ptr = rcsbuf->ptr;
1119 ptrend = rcsbuf->ptrend;
1121 /* Sanity check. */
1122 assert (ptr >= rcsbuf_buffer && ptr <= rcsbuf_buffer + rcsbuf_buffer_size);
1123 assert (ptrend >= rcsbuf_buffer && ptrend <= rcsbuf_buffer + rcsbuf_buffer_size);
1125 #ifndef HAVE_MMAP
1126 /* If the pointer is more than RCSBUF_BUFSIZE bytes into the
1127 buffer, move back to the start of the buffer. This keeps the
1128 buffer from growing indefinitely. */
1129 if (ptr - rcsbuf_buffer >= RCSBUF_BUFSIZE)
1131 int len;
1133 len = ptrend - ptr;
1135 /* Sanity check: we don't read more than RCSBUF_BUFSIZE bytes
1136 at a time, so we can't have more bytes than that past PTR. */
1137 assert (len <= RCSBUF_BUFSIZE);
1139 /* Update the POS field, which holds the file offset of the
1140 first byte in the RCSBUF_BUFFER buffer. */
1141 rcsbuf->pos += ptr - rcsbuf_buffer;
1143 memcpy (rcsbuf_buffer, ptr, len);
1144 ptr = rcsbuf_buffer;
1145 ptrend = ptr + len;
1146 rcsbuf->ptrend = ptrend;
1148 #endif /* HAVE_MMAP */
1150 /* Skip leading whitespace. */
1152 while (1)
1154 if (ptr >= ptrend)
1156 ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
1157 if (ptr == NULL)
1158 return 0;
1159 ptrend = rcsbuf->ptrend;
1162 c = *ptr;
1163 if (! my_whitespace (c))
1164 break;
1166 ++ptr;
1169 /* We've found the start of the key. */
1171 *keyp = ptr;
1173 if (c != ';')
1175 while (1)
1177 ++ptr;
1178 if (ptr >= ptrend)
1180 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
1181 if (ptr == NULL)
1182 error (1, 0, "EOF in key in RCS file %s",
1183 primary_root_inverse_translate (rcsbuf->filename));
1184 ptrend = rcsbuf->ptrend;
1186 c = *ptr;
1187 if (c == ';' || my_whitespace (c))
1188 break;
1192 /* Here *KEYP points to the key in the buffer, C is the character
1193 we found at the of the key, and PTR points to the location in
1194 the buffer where we found C. We must set *PTR to \0 in order
1195 to terminate the key. If the key ended with ';', then there is
1196 no value. */
1198 *ptr = '\0';
1199 ++ptr;
1201 if (c == ';')
1203 *valp = NULL;
1204 rcsbuf->ptr = ptr;
1205 return 1;
1208 /* C must be whitespace. Skip whitespace between the key and the
1209 value. If we find ';' now, there is no value. */
1211 while (1)
1213 if (ptr >= ptrend)
1215 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, NULL);
1216 if (ptr == NULL)
1217 error (1, 0, "EOF while looking for value in RCS file %s",
1218 primary_root_inverse_translate (rcsbuf->filename));
1219 ptrend = rcsbuf->ptrend;
1221 c = *ptr;
1222 if (c == ';')
1224 *valp = NULL;
1225 rcsbuf->ptr = ptr + 1;
1226 return 1;
1228 if (! my_whitespace (c))
1229 break;
1230 ++ptr;
1233 /* Now PTR points to the start of the value, and C is the first
1234 character of the value. */
1236 if (c != '@')
1237 *valp = ptr;
1238 else
1240 char *pat;
1241 size_t vlen;
1243 /* Optimize the common case of a value composed of a single
1244 '@' string. */
1246 rcsbuf->at_string = 1;
1248 ++ptr;
1250 *valp = ptr;
1252 while (1)
1254 while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1256 /* Note that we pass PTREND as the PTR value to
1257 rcsbuf_fill, so that we will wind up setting PTR to
1258 the location corresponding to the old PTREND, so
1259 that we don't search the same bytes again. */
1260 ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1261 if (ptr == NULL)
1262 error (1, 0,
1263 "EOF while looking for end of string in RCS file %s",
1264 primary_root_inverse_translate (rcsbuf->filename));
1265 ptrend = rcsbuf->ptrend;
1268 /* Handle the special case of an '@' right at the end of
1269 the known bytes. */
1270 if (pat + 1 >= ptrend)
1272 /* Note that we pass PAT, not PTR, here. */
1273 pat = rcsbuf_fill (rcsbuf, pat, keyp, valp);
1274 if (pat == NULL)
1276 /* EOF here is OK; it just means that the last
1277 character of the file was an '@' terminating a
1278 value for a key type which does not require a
1279 trailing ';'. */
1280 pat = rcsbuf->ptrend - 1;
1283 ptrend = rcsbuf->ptrend;
1285 /* Note that the value of PTR is bogus here. This is
1286 OK, because we don't use it. */
1289 if (pat + 1 >= ptrend || pat[1] != '@')
1290 break;
1292 /* We found an '@' pair in the string. Keep looking. */
1293 ++rcsbuf->embedded_at;
1294 ptr = pat + 2;
1297 /* Here PAT points to the final '@' in the string. */
1299 *pat = '\0';
1301 vlen = pat - *valp;
1302 if (vlen == 0)
1303 *valp = NULL;
1304 rcsbuf->vlen = vlen;
1306 ptr = pat + 1;
1309 /* Certain keywords only have a '@' string. If there is no '@'
1310 string, then the old getrcskey function assumed that they had
1311 no value, and we do the same. */
1314 char *k;
1316 k = *keyp;
1317 if (STREQ (k, RCSDESC)
1318 || STREQ (k, "text")
1319 || STREQ (k, "log"))
1321 if (c != '@')
1322 *valp = NULL;
1323 rcsbuf->ptr = ptr;
1324 return 1;
1328 /* If we've already gathered a '@' string, try to skip whitespace
1329 and find a ';'. */
1330 if (c == '@')
1332 while (1)
1334 char n;
1336 if (ptr >= ptrend)
1338 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1339 if (ptr == NULL)
1340 error (1, 0, "EOF in value in RCS file %s",
1341 primary_root_inverse_translate (rcsbuf->filename));
1342 ptrend = rcsbuf->ptrend;
1344 n = *ptr;
1345 if (n == ';')
1347 /* We're done. We already set everything up for this
1348 case above. */
1349 rcsbuf->ptr = ptr + 1;
1350 return 1;
1352 if (! my_whitespace (n))
1353 break;
1354 ++ptr;
1357 /* The value extends past the '@' string. We need to undo the
1358 '@' stripping done in the default case above. This
1359 case never happens in a plain RCS file, but it can happen
1360 if user defined phrases are used. */
1361 ((*valp)--)[rcsbuf->vlen++] = '@';
1364 /* Here we have a value which is not a simple '@' string. We need
1365 to gather up everything until the next ';', including any '@'
1366 strings. *VALP points to the start of the value. If
1367 RCSBUF->VLEN is not zero, then we have already read an '@'
1368 string, and PTR points to the data following the '@' string.
1369 Otherwise, PTR points to the start of the value. */
1371 while (1)
1373 char *start, *psemi, *pat;
1375 /* Find the ';' which must end the value. */
1376 start = ptr;
1377 while ((psemi = memchr (ptr, ';', ptrend - ptr)) == NULL)
1379 int slen;
1381 /* Note that we pass PTREND as the PTR value to
1382 rcsbuf_fill, so that we will wind up setting PTR to the
1383 location corresponding to the old PTREND, so that we
1384 don't search the same bytes again. */
1385 slen = start - *valp;
1386 ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1387 if (ptr == NULL)
1388 error (1, 0, "EOF in value in RCS file %s",
1389 primary_root_inverse_translate (rcsbuf->filename));
1390 start = *valp + slen;
1391 ptrend = rcsbuf->ptrend;
1394 /* See if there are any '@' strings in the value. */
1395 pat = memchr (start, '@', psemi - start);
1397 if (pat == NULL)
1399 size_t vlen;
1401 /* We're done with the value. Trim any trailing
1402 whitespace. */
1404 rcsbuf->ptr = psemi + 1;
1406 start = *valp;
1407 while (psemi > start && my_whitespace (psemi[-1]))
1408 --psemi;
1409 *psemi = '\0';
1411 vlen = psemi - start;
1412 if (vlen == 0)
1413 *valp = NULL;
1414 rcsbuf->vlen = vlen;
1416 return 1;
1419 /* We found an '@' string in the value. We set RCSBUF->AT_STRING
1420 and RCSBUF->EMBEDDED_AT to indicate that we won't be able to
1421 compress whitespace correctly for this type of value.
1422 Since this type of value never arises in a normal RCS file,
1423 this should not be a big deal. It means that if anybody
1424 adds a phrase which can have both an '@' string and regular
1425 text, they will have to handle whitespace compression
1426 themselves. */
1428 rcsbuf->at_string = 1;
1429 rcsbuf->embedded_at = -1;
1431 ptr = pat + 1;
1433 while (1)
1435 while ((pat = memchr (ptr, '@', ptrend - ptr)) == NULL)
1437 /* Note that we pass PTREND as the PTR value to
1438 rcsbuff_fill, so that we will wind up setting PTR
1439 to the location corresponding to the old PTREND, so
1440 that we don't search the same bytes again. */
1441 ptr = rcsbuf_fill (rcsbuf, ptrend, keyp, valp);
1442 if (ptr == NULL)
1443 error (1, 0,
1444 "EOF while looking for end of string in RCS file %s",
1445 primary_root_inverse_translate (rcsbuf->filename));
1446 ptrend = rcsbuf->ptrend;
1449 /* Handle the special case of an '@' right at the end of
1450 the known bytes. */
1451 if (pat + 1 >= ptrend)
1453 ptr = rcsbuf_fill (rcsbuf, ptr, keyp, valp);
1454 if (ptr == NULL)
1455 error (1, 0, "EOF in value in RCS file %s",
1456 primary_root_inverse_translate (rcsbuf->filename));
1457 ptrend = rcsbuf->ptrend;
1460 if (pat[1] != '@')
1461 break;
1463 /* We found an '@' pair in the string. Keep looking. */
1464 ptr = pat + 2;
1467 /* Here PAT points to the final '@' in the string. */
1468 ptr = pat + 1;
1471 #undef my_whitespace
1476 /* Read an RCS revision number from an RCS file. This sets *REVP to
1477 point to the revision number; it will point to space that is
1478 managed by the rcsbuf functions, and is only good until the next
1479 call to rcsbuf_getkey or rcsbuf_getrevnum.
1481 This function returns 1 on success, or 0 on EOF. If there is an
1482 error reading the file, or an EOF in an unexpected location, it
1483 gives a fatal error. */
1484 static int
1485 rcsbuf_getrevnum (struct rcsbuffer *rcsbuf, char **revp)
1487 char *ptr, *ptrend;
1488 char c;
1490 ptr = rcsbuf->ptr;
1491 ptrend = rcsbuf->ptrend;
1493 *revp = NULL;
1495 /* Skip leading whitespace. */
1497 while (1)
1499 if (ptr >= ptrend)
1501 ptr = rcsbuf_fill (rcsbuf, ptr, NULL, NULL);
1502 if (ptr == NULL)
1503 return 0;
1504 ptrend = rcsbuf->ptrend;
1507 c = *ptr;
1508 if (! whitespace (c))
1509 break;
1511 ++ptr;
1514 if (! isdigit ((unsigned char) c) && c != '.')
1515 error (1, 0,
1517 unexpected '\\x%x' reading revision number in RCS file %s",
1518 c, primary_root_inverse_translate (rcsbuf->filename));
1520 *revp = ptr;
1524 ++ptr;
1525 if (ptr >= ptrend)
1527 ptr = rcsbuf_fill (rcsbuf, ptr, revp, NULL);
1528 if (ptr == NULL)
1529 error (1, 0,
1530 "unexpected EOF reading revision number in RCS file %s",
1531 primary_root_inverse_translate (rcsbuf->filename));
1532 ptrend = rcsbuf->ptrend;
1535 c = *ptr;
1537 while (isdigit ((unsigned char) c) || c == '.');
1539 if (! whitespace (c))
1540 error (1, 0, "\
1541 unexpected '\\x%x' reading revision number in RCS file %s",
1542 c, primary_root_inverse_translate (rcsbuf->filename));
1544 *ptr = '\0';
1546 rcsbuf->ptr = ptr + 1;
1548 return 1;
1553 /* Fill RCSBUF_BUFFER with bytes from the file associated with RCSBUF,
1554 updating PTR and the PTREND field. If KEYP and *KEYP are not NULL,
1555 then *KEYP points into the buffer, and must be adjusted if the
1556 buffer is changed. Likewise for VALP. Returns the new value of
1557 PTR, or NULL on error. */
1558 static char *
1559 rcsbuf_fill (struct rcsbuffer *rcsbuf, char *ptr, char **keyp, char **valp)
1561 #ifdef HAVE_MMAP
1562 return NULL;
1563 #else /* HAVE_MMAP */
1564 int got;
1566 if (rcsbuf->ptrend - rcsbuf_buffer + RCSBUF_BUFSIZE > rcsbuf_buffer_size)
1568 int poff, peoff, koff, voff;
1570 poff = ptr - rcsbuf_buffer;
1571 peoff = rcsbuf->ptrend - rcsbuf_buffer;
1572 koff = keyp == NULL ? 0 : *keyp - rcsbuf_buffer;
1573 voff = valp == NULL ? 0 : *valp - rcsbuf_buffer;
1575 expand_string (&rcsbuf_buffer, &rcsbuf_buffer_size,
1576 rcsbuf_buffer_size + RCSBUF_BUFSIZE);
1578 ptr = rcsbuf_buffer + poff;
1579 rcsbuf->ptrend = rcsbuf_buffer + peoff;
1580 if (keyp != NULL)
1581 *keyp = rcsbuf_buffer + koff;
1582 if (valp != NULL)
1583 *valp = rcsbuf_buffer + voff;
1586 got = fread (rcsbuf->ptrend, 1, RCSBUF_BUFSIZE, rcsbuf->fp);
1587 if (got == 0)
1589 if (ferror (rcsbuf->fp))
1590 error (1, errno, "cannot read %s", rcsbuf->filename);
1591 return NULL;
1594 rcsbuf->ptrend += got;
1596 return ptr;
1597 #endif /* HAVE_MMAP */
1602 /* Test whether the last value returned by rcsbuf_getkey is a composite
1603 value or not. */
1604 static int
1605 rcsbuf_valcmp (struct rcsbuffer *rcsbuf)
1607 return rcsbuf->at_string && rcsbuf->embedded_at < 0;
1612 /* Copy the value VAL returned by rcsbuf_getkey into a memory buffer,
1613 returning the memory buffer. Polish the value like
1614 rcsbuf_valpolish, q.v. */
1615 static char *
1616 rcsbuf_valcopy (struct rcsbuffer *rcsbuf, char *val, int polish, size_t *lenp)
1618 size_t vlen;
1619 int embedded_at;
1620 char *ret;
1622 if (val == NULL)
1624 if (lenp != NULL)
1625 *lenp = 0;
1626 return NULL;
1629 vlen = rcsbuf->vlen;
1630 embedded_at = rcsbuf->embedded_at < 0 ? 0 : rcsbuf->embedded_at;
1632 ret = xmalloc (vlen - embedded_at + 1);
1634 if (rcsbuf->at_string ? embedded_at == 0 : ! polish)
1636 /* No special action to take. */
1637 memcpy (ret, val, vlen + 1);
1638 if (lenp != NULL)
1639 *lenp = vlen;
1640 return ret;
1643 rcsbuf_valpolish_internal (rcsbuf, ret, val, lenp);
1644 return ret;
1649 /* Polish the value VAL returned by rcsbuf_getkey. The POLISH
1650 parameter is non-zero if multiple embedded whitespace characters
1651 should be compressed into a single whitespace character. Note that
1652 leading and trailing whitespace was already removed by
1653 rcsbuf_getkey. Within an '@' string, pairs of '@' characters are
1654 compressed into a single '@' character regardless of the value of
1655 POLISH. If LENP is not NULL, set *LENP to the length of the value. */
1656 static void
1657 rcsbuf_valpolish (struct rcsbuffer *rcsbuf, char *val, int polish,
1658 size_t *lenp)
1660 if (val == NULL)
1662 if (lenp != NULL)
1663 *lenp= 0;
1664 return;
1667 if (rcsbuf->at_string ? rcsbuf->embedded_at == 0 : ! polish)
1669 /* No special action to take. */
1670 if (lenp != NULL)
1671 *lenp = rcsbuf->vlen;
1672 return;
1675 rcsbuf_valpolish_internal (rcsbuf, val, val, lenp);
1680 /* Internal polishing routine, called from rcsbuf_valcopy and
1681 rcsbuf_valpolish. */
1682 static void
1683 rcsbuf_valpolish_internal (struct rcsbuffer *rcsbuf, char *to,
1684 const char *from, size_t *lenp)
1686 size_t len;
1688 len = rcsbuf->vlen;
1690 if (! rcsbuf->at_string)
1692 char *orig_to;
1693 size_t clen;
1695 orig_to = to;
1697 for (clen = len; clen > 0; ++from, --clen)
1699 char c;
1701 c = *from;
1702 if (whitespace (c))
1704 /* Note that we know that clen can not drop to zero
1705 while we have whitespace, because we know there is
1706 no trailing whitespace. */
1707 while (whitespace (from[1]))
1709 ++from;
1710 --clen;
1712 c = ' ';
1714 *to++ = c;
1717 *to = '\0';
1719 if (lenp != NULL)
1720 *lenp = to - orig_to;
1722 else
1724 const char *orig_from;
1725 char *orig_to;
1726 int embedded_at;
1727 size_t clen;
1729 orig_from = from;
1730 orig_to = to;
1732 embedded_at = rcsbuf->embedded_at;
1733 assert (embedded_at > 0);
1735 if (lenp != NULL)
1736 *lenp = len - embedded_at;
1738 for (clen = len; clen > 0; ++from, --clen)
1740 char c;
1742 c = *from;
1743 *to++ = c;
1744 if (c == '@')
1746 ++from;
1748 /* Sanity check.
1750 * FIXME: I restored this to an abort from an assert based on
1751 * advice from Larry Jones that asserts should not be used to
1752 * confirm the validity of an RCS file... This leaves two
1753 * issues here: 1) I am uncertain that the fact that we will
1754 * only find double '@'s hasn't already been confirmed; and:
1755 * 2) If this is the proper place to spot the error in the RCS
1756 * file, then we should print a much clearer error here for the
1757 * user!!!!!!!
1759 * - DRP
1761 if (*from != '@' || clen == 0)
1762 abort ();
1764 --clen;
1766 --embedded_at;
1767 if (embedded_at == 0)
1769 /* We've found all the embedded '@' characters.
1770 We can just memcpy the rest of the buffer after
1771 this '@' character. */
1772 if (orig_to != orig_from)
1773 memcpy (to, from + 1, clen - 1);
1774 else
1775 memmove (to, from + 1, clen - 1);
1776 from += clen;
1777 to += clen - 1;
1778 break;
1783 /* Sanity check. */
1784 assert (from == orig_from + len
1785 && to == orig_to + (len - rcsbuf->embedded_at));
1787 *to = '\0';
1793 #ifdef PRESERVE_PERMISSIONS_SUPPORT
1795 /* Copy the next word from the value VALP returned by rcsbuf_getkey into a
1796 memory buffer, updating VALP and returning the memory buffer. Return
1797 NULL when there are no more words. */
1799 static char *
1800 rcsbuf_valword (struct rcsbuffer *rcsbuf, char **valp)
1802 register const char * const my_spacetab = spacetab;
1803 register char *ptr, *pat;
1804 char c;
1806 # define my_whitespace(c) (my_spacetab[(unsigned char)c] != 0)
1808 if (*valp == NULL)
1809 return NULL;
1811 for (ptr = *valp; my_whitespace (*ptr); ++ptr) ;
1812 if (*ptr == '\0')
1814 assert (ptr - *valp == rcsbuf->vlen);
1815 *valp = NULL;
1816 rcsbuf->vlen = 0;
1817 return NULL;
1820 /* PTR now points to the start of a value. Find out whether it is
1821 a num, an id, a string or a colon. */
1822 c = *ptr;
1823 if (c == ':')
1825 rcsbuf->vlen -= ++ptr - *valp;
1826 *valp = ptr;
1827 return xstrdup (":");
1830 if (c == '@')
1832 int embedded_at = 0;
1833 size_t vlen;
1835 pat = ++ptr;
1836 while ((pat = strchr (pat, '@')) != NULL)
1838 if (pat[1] != '@')
1839 break;
1840 ++embedded_at;
1841 pat += 2;
1844 /* Here PAT points to the final '@' in the string. */
1845 *pat++ = '\0';
1846 assert (rcsbuf->at_string);
1847 vlen = rcsbuf->vlen - (pat - *valp);
1848 rcsbuf->vlen = pat - ptr - 1;
1849 rcsbuf->embedded_at = embedded_at;
1850 ptr = rcsbuf_valcopy (rcsbuf, ptr, 0, NULL);
1851 *valp = pat;
1852 rcsbuf->vlen = vlen;
1853 if (strchr (pat, '@') == NULL)
1854 rcsbuf->at_string = 0;
1855 else
1856 rcsbuf->embedded_at = -1;
1857 return ptr;
1860 /* *PTR is neither `:', `;' nor `@', so it should be the start of a num
1861 or an id. Make sure it is not another special character. */
1862 if (c == '$' || c == '.' || c == ',')
1863 error (1, 0, "invalid special character in RCS field in %s",
1864 primary_root_inverse_translate (rcsbuf->filename));
1866 pat = ptr;
1867 while (1)
1869 /* Legitimate ID characters are digits, dots and any `graphic
1870 printing character that is not a special.' This test ought
1871 to do the trick. */
1872 c = *++pat;
1873 if (!isprint ((unsigned char) c) ||
1874 c == ';' || c == '$' || c == ',' || c == '@' || c == ':')
1875 break;
1878 /* PAT points to the last non-id character in this word, and C is
1879 the character in its memory cell. Check to make sure that it
1880 is a legitimate word delimiter -- whitespace or end. */
1881 if (c != '\0' && !my_whitespace (c))
1882 error (1, 0, "invalid special character in RCS field in %s",
1883 primary_root_inverse_translate (rcsbuf->filename));
1885 *pat = '\0';
1886 rcsbuf->vlen -= pat - *valp;
1887 *valp = pat;
1888 return xstrdup (ptr);
1890 # undef my_whitespace
1893 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
1897 /* Return the current position of an rcsbuf. */
1898 static off_t
1899 rcsbuf_ftello (struct rcsbuffer *rcsbuf)
1901 return rcsbuf->pos + rcsbuf->ptr - rcsbuf_buffer;
1906 /* Return a pointer to any data buffered for RCSBUF, along with the
1907 length. */
1908 static void
1909 rcsbuf_get_buffered (struct rcsbuffer *rcsbuf, char **datap, size_t *lenp)
1911 *datap = rcsbuf->ptr;
1912 *lenp = rcsbuf->ptrend - rcsbuf->ptr;
1917 /* CVS optimizes by quickly reading some header information from a
1918 file. If it decides it needs to do more with the file, it reopens
1919 it. We speed that up here by maintaining a cache of a single open
1920 file, to save the time it takes to reopen the file in the common
1921 case. */
1922 static RCSNode *cached_rcs;
1923 static struct rcsbuffer cached_rcsbuf;
1925 /* Cache RCS and RCSBUF. This takes responsibility for closing
1926 RCSBUF->FP. */
1927 static void
1928 rcsbuf_cache (RCSNode *rcs, struct rcsbuffer *rcsbuf)
1930 if (cached_rcs != NULL)
1931 rcsbuf_cache_close ();
1932 cached_rcs = rcs;
1933 ++rcs->refcount;
1934 cached_rcsbuf = *rcsbuf;
1939 /* If there is anything in the cache, close it. */
1940 static void
1941 rcsbuf_cache_close (void)
1943 if (cached_rcs != NULL)
1945 rcsbuf_close (&cached_rcsbuf);
1946 if (fclose (cached_rcsbuf.fp) != 0)
1947 error (0, errno, "cannot close %s", cached_rcsbuf.filename);
1948 freercsnode (&cached_rcs);
1949 cached_rcs = NULL;
1955 /* Open an rcsbuffer for RCS, getting it from the cache if possible.
1956 Set *FPP to the file, and *RCSBUFP to the rcsbuf. The file should
1957 be put at position POS. */
1958 static void
1959 rcsbuf_cache_open (RCSNode *rcs, off_t pos, FILE **pfp,
1960 struct rcsbuffer *prcsbuf)
1962 #ifndef HAVE_MMAP
1963 if (cached_rcs == rcs)
1965 if (rcsbuf_ftello (&cached_rcsbuf) != pos)
1967 if (fseeko (cached_rcsbuf.fp, pos, SEEK_SET) != 0)
1968 error (1, 0, "cannot fseeko RCS file %s",
1969 cached_rcsbuf.filename);
1970 cached_rcsbuf.ptr = rcsbuf_buffer;
1971 cached_rcsbuf.ptrend = rcsbuf_buffer;
1972 cached_rcsbuf.pos = pos;
1974 *pfp = cached_rcsbuf.fp;
1976 /* When RCS_parse opens a file using fopen_case, it frees the
1977 filename which we cached in CACHED_RCSBUF and stores a new
1978 file name in RCS->PATH. We avoid problems here by always
1979 copying the filename over. FIXME: This is hackish. */
1980 cached_rcsbuf.filename = rcs->path;
1982 *prcsbuf = cached_rcsbuf;
1984 cached_rcs = NULL;
1986 /* Removing RCS from the cache removes a reference to it. */
1987 --rcs->refcount;
1988 if (rcs->refcount <= 0)
1989 error (1, 0, "rcsbuf_cache_open: internal error");
1991 else
1993 #endif /* ifndef HAVE_MMAP */
1994 /* FIXME: If these routines can be rewritten to not write to the
1995 * rcs file buffer, there would be a considerably larger memory savings
1996 * from using mmap since the shared file would never need be copied to
1997 * process memory.
1999 * If this happens, cached mmapped buffers would be usable, but don't
2000 * forget to make sure rcs->pos < pos here...
2002 if (cached_rcs != NULL)
2003 rcsbuf_cache_close ();
2005 *pfp = CVS_FOPEN (rcs->path, FOPEN_BINARY_READ);
2006 if (*pfp == NULL)
2007 error (1, 0, "unable to reopen `%s'", rcs->path);
2008 #ifndef HAVE_MMAP
2009 if (pos != 0)
2011 if (fseeko (*pfp, pos, SEEK_SET) != 0)
2012 error (1, 0, "cannot fseeko RCS file %s", rcs->path);
2014 #endif /* ifndef HAVE_MMAP */
2015 rcsbuf_open (prcsbuf, *pfp, rcs->path, pos);
2016 #ifndef HAVE_MMAP
2018 #endif /* ifndef HAVE_MMAP */
2024 * process the symbols list of the rcs file
2026 static void
2027 do_symbols (List *list, char *val)
2029 Node *p;
2030 char *cp = val;
2031 char *tag, *rev;
2033 assert (cp);
2035 for (;;)
2037 /* skip leading whitespace */
2038 while (whitespace (*cp))
2039 cp++;
2041 /* if we got to the end, we are done */
2042 if (*cp == '\0')
2043 break;
2045 /* split it up into tag and rev */
2046 tag = cp;
2047 cp = strchr (cp, ':');
2048 *cp++ = '\0';
2049 rev = cp;
2050 while (!whitespace (*cp) && *cp != '\0')
2051 cp++;
2052 if (*cp != '\0')
2053 *cp++ = '\0';
2055 /* make a new node and add it to the list */
2056 p = getnode ();
2057 p->key = xstrdup (tag);
2058 p->data = xstrdup (rev);
2059 (void) addnode (list, p);
2066 * process the locks list of the rcs file
2067 * Like do_symbols, but hash entries are keyed backwards: i.e.
2068 * an entry like `user:rev' is keyed on REV rather than on USER.
2070 static void
2071 do_locks (List *list, char *val)
2073 Node *p;
2074 char *cp = val;
2075 char *user, *rev;
2077 assert (cp);
2079 for (;;)
2081 /* skip leading whitespace */
2082 while (whitespace (*cp))
2083 cp++;
2085 /* if we got to the end, we are done */
2086 if (*cp == '\0')
2087 break;
2089 /* split it up into user and rev */
2090 user = cp;
2091 cp = strchr (cp, ':');
2092 *cp++ = '\0';
2093 rev = cp;
2094 while (!whitespace (*cp) && *cp != '\0')
2095 cp++;
2096 if (*cp != '\0')
2097 *cp++ = '\0';
2099 /* make a new node and add it to the list */
2100 p = getnode ();
2101 p->key = xstrdup (rev);
2102 p->data = xstrdup (user);
2103 (void) addnode (list, p);
2110 * process the branches list of a revision delta
2112 static void
2113 do_branches (List *list, char *val)
2115 Node *p;
2116 char *cp = val;
2117 char *branch;
2119 for (;;)
2121 /* skip leading whitespace */
2122 while (whitespace (*cp))
2123 cp++;
2125 /* if we got to the end, we are done */
2126 if (*cp == '\0')
2127 break;
2129 /* find the end of this branch */
2130 branch = cp;
2131 while (!whitespace (*cp) && *cp != '\0')
2132 cp++;
2133 if (*cp != '\0')
2134 *cp++ = '\0';
2136 /* make a new node and add it to the list */
2137 p = getnode ();
2138 p->key = xstrdup (branch);
2139 (void) addnode (list, p);
2146 * Version Number
2148 * Returns the requested version number of the RCS file, satisfying tags and/or
2149 * dates, and walking branches, if necessary.
2151 * The result is returned; null-string if error.
2153 char *
2154 RCS_getversion (RCSNode *rcs, const char *tag, const char *date,
2155 int force_tag_match, int *simple_tag)
2157 if (simple_tag != NULL)
2158 *simple_tag = 0;
2160 /* make sure we have something to look at... */
2161 assert (rcs != NULL);
2163 if (tag && date)
2165 char *branch, *rev;
2167 if (! RCS_nodeisbranch (rcs, tag))
2169 /* We can't get a particular date if the tag is not a
2170 branch. */
2171 return NULL;
2174 /* Work out the branch. */
2175 if (! isdigit ((unsigned char) tag[0]))
2176 branch = RCS_whatbranch (rcs, tag);
2177 else
2178 branch = xstrdup (tag);
2180 /* Fetch the revision of branch as of date. */
2181 rev = RCS_getdatebranch (rcs, date, branch);
2182 free (branch);
2183 return rev;
2185 else if (tag)
2186 return RCS_gettag (rcs, tag, force_tag_match, simple_tag);
2187 else if (date)
2188 return RCS_getdate (rcs, date, force_tag_match);
2189 else
2190 return RCS_head (rcs);
2197 * Get existing revision number corresponding to tag or revision.
2198 * Similar to RCS_gettag but less interpretation imposed.
2199 * For example:
2200 * -- If tag designates a magic branch, RCS_tag2rev
2201 * returns the magic branch number.
2202 * -- If tag is a branch tag, returns the branch number, not
2203 * the revision of the head of the branch.
2204 * If tag or revision is not valid or does not exist in file,
2205 * return NULL.
2207 char *
2208 RCS_tag2rev (RCSNode *rcs, char *tag)
2210 char *rev, *pa, *pb;
2211 int i;
2213 assert (rcs != NULL);
2215 if (rcs->flags & PARTIAL)
2216 RCS_reparsercsfile (rcs, NULL, NULL);
2218 /* If a valid revision, try to look it up */
2219 if ( RCS_valid_rev (tag) )
2221 /* Make a copy so we can scribble on it */
2222 rev = xstrdup (tag);
2224 /* If revision exists, return the copy */
2225 if (RCS_exist_rev (rcs, tag))
2226 return rev;
2228 /* Nope, none such. If tag is not a branch we're done. */
2229 i = numdots (rev);
2230 if ((i & 1) == 1 )
2232 pa = strrchr (rev, '.');
2233 if (i == 1 || *(pa-1) != RCS_MAGIC_BRANCH || *(pa-2) != '.')
2235 free (rev);
2236 error (1, 0, "revision `%s' does not exist", tag);
2240 /* Try for a real (that is, exists in the RCS deltas) branch
2241 (RCS_exist_rev just checks for real revisions and revisions
2242 which have tags pointing to them). */
2243 pa = RCS_getbranch (rcs, rev, 1);
2244 if (pa != NULL)
2246 free (pa);
2247 return rev;
2250 /* Tag is branch, but does not exist, try corresponding
2251 * magic branch tag.
2253 * FIXME: assumes all magic branches are of
2254 * form "n.n.n ... .0.n". I'll fix if somebody can
2255 * send me a method to get a magic branch tag with
2256 * the 0 in some other position -- <dan@gasboy.com>
2258 pa = strrchr (rev, '.');
2259 if (!pa)
2260 /* This might happen, for instance, if an RCS file only contained
2261 * revisions 2.x and higher, and REV == "1".
2263 error (1, 0, "revision `%s' does not exist", tag);
2265 *pa++ = 0;
2266 pb = Xasprintf ("%s.%d.%s", rev, RCS_MAGIC_BRANCH, pa);
2267 free (rev);
2268 rev = pb;
2269 if (RCS_exist_rev (rcs, rev))
2270 return rev;
2271 error (1, 0, "revision `%s' does not exist", tag);
2275 RCS_check_tag (tag); /* exit if not a valid tag */
2277 /* If tag is "HEAD", special case to get head RCS revision */
2278 if (tag && STREQ (tag, TAG_HEAD))
2279 return RCS_head (rcs);
2281 /* If valid tag let translate_symtag say yea or nay. */
2282 rev = translate_symtag (rcs, tag);
2284 if (rev)
2285 return rev;
2287 /* Trust the caller to print warnings. */
2288 return NULL;
2294 * Find the revision for a specific tag.
2295 * If force_tag_match is set, return NULL if an exact match is not
2296 * possible otherwise return RCS_head (). We are careful to look for
2297 * and handle "magic" revisions specially.
2299 * If the matched tag is a branch tag, find the head of the branch.
2301 * Returns pointer to newly malloc'd string, or NULL.
2303 char *
2304 RCS_gettag (RCSNode *rcs, const char *symtag, int force_tag_match,
2305 int *simple_tag)
2307 char *tag;
2309 if (simple_tag != NULL)
2310 *simple_tag = 0;
2312 /* make sure we have something to look at... */
2313 assert (rcs != NULL);
2315 /* XXX this is probably not necessary, --jtc */
2316 if (rcs->flags & PARTIAL)
2317 RCS_reparsercsfile (rcs, NULL, NULL);
2319 /* If symtag is "HEAD", special case to get head RCS revision */
2320 if (symtag && STREQ (symtag, TAG_HEAD))
2321 #if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */
2322 if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
2323 return NULL; /* head request for removed file */
2324 else
2325 #endif
2326 return RCS_head (rcs);
2328 if (!isdigit ((unsigned char) symtag[0]))
2330 char *version;
2332 /* If we got a symbolic tag, resolve it to a numeric */
2333 version = translate_symtag (rcs, symtag);
2334 if (version != NULL)
2336 int dots;
2337 char *magic, *branch, *cp;
2339 tag = version;
2342 * If this is a magic revision, we turn it into either its
2343 * physical branch equivalent (if one exists) or into
2344 * its base revision, which we assume exists.
2346 dots = numdots (tag);
2347 if (dots > 2 && (dots & 1) != 0)
2349 branch = strrchr (tag, '.');
2350 cp = branch++ - 1;
2351 while (*cp != '.')
2352 cp--;
2354 /* see if we have .magic-branch. (".0.") */
2355 magic = xmalloc (strlen (tag) + 1);
2356 (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2357 if (strncmp (magic, cp, strlen (magic)) == 0)
2359 /* it's magic. See if the branch exists */
2360 *cp = '\0'; /* turn it into a revision */
2361 (void) sprintf (magic, "%s.%s", tag, branch);
2362 branch = RCS_getbranch (rcs, magic, 1);
2363 free (magic);
2364 if (branch != NULL)
2366 free (tag);
2367 return branch;
2369 return tag;
2371 free (magic);
2374 else
2376 /* The tag wasn't there, so return the head or NULL */
2377 if (force_tag_match)
2378 return NULL;
2379 else
2380 return RCS_head (rcs);
2383 else
2384 tag = xstrdup (symtag);
2386 /* tag is always allocated and numeric now. */
2389 * numeric tag processing:
2390 * 1) revision number - just return it
2391 * 2) branch number - find head of branch
2394 /* strip trailing dots */
2395 while (tag[strlen (tag) - 1] == '.')
2396 tag[strlen (tag) - 1] = '\0';
2398 if ((numdots (tag) & 1) == 0)
2400 char *branch;
2402 /* we have a branch tag, so we need to walk the branch */
2403 branch = RCS_getbranch (rcs, tag, force_tag_match);
2404 free (tag);
2405 return branch;
2407 else
2409 Node *p;
2411 /* we have a revision tag, so make sure it exists */
2412 p = findnode (rcs->versions, tag);
2413 if (p != NULL)
2415 /* We have found a numeric revision for the revision tag.
2416 To support expanding the RCS keyword Name, if
2417 SIMPLE_TAG is not NULL, tell the the caller that this
2418 is a simple tag which co will recognize. FIXME: Are
2419 there other cases in which we should set this? In
2420 particular, what if we expand RCS keywords internally
2421 without calling co? */
2422 if (simple_tag != NULL)
2423 *simple_tag = 1;
2424 return tag;
2426 else
2428 /* The revision wasn't there, so return the head or NULL */
2429 free (tag);
2430 if (force_tag_match)
2431 return NULL;
2432 else
2433 return RCS_head (rcs);
2441 * Return a "magic" revision as a virtual branch off of REV for the RCS file.
2442 * A "magic" revision is one which is unique in the RCS file. By unique, I
2443 * mean we return a revision which:
2444 * - has a branch of 0 (see rcs.h RCS_MAGIC_BRANCH)
2445 * - has a revision component which is not an existing branch off REV
2446 * - has a revision component which is not an existing magic revision
2447 * - is an even-numbered revision, to avoid conflicts with vendor branches
2448 * The first point is what makes it "magic".
2450 * As an example, if we pass in 1.37 as REV, we will look for an existing
2451 * branch called 1.37.2. If it did not exist, we would look for an
2452 * existing symbolic tag with a numeric part equal to 1.37.0.2. If that
2453 * didn't exist, then we know that the 1.37.2 branch can be reserved by
2454 * creating a symbolic tag with 1.37.0.2 as the numeric part.
2456 * This allows us to fork development with very little overhead -- just a
2457 * symbolic tag is used in the RCS file. When a commit is done, a physical
2458 * branch is dynamically created to hold the new revision.
2460 * Note: We assume that REV is an RCS revision and not a branch number.
2462 static char *check_rev;
2463 char *
2464 RCS_magicrev (RCSNode *rcs, char *rev)
2466 int rev_num;
2467 char *xrev, *test_branch, *local_branch_num;
2469 xrev = xmalloc (strlen (rev) + 14); /* enough for .0.number */
2470 check_rev = xrev;
2472 local_branch_num = getenv("CVS_LOCAL_BRANCH_NUM");
2473 if (local_branch_num)
2475 rev_num = atoi(local_branch_num);
2476 if (rev_num < 2)
2477 rev_num = 2;
2478 else
2479 rev_num &= ~1;
2481 else
2482 rev_num = 2;
2484 /* only look at even numbered branches */
2485 for ( ; ; rev_num += 2)
2487 /* see if the physical branch exists */
2488 (void) sprintf (xrev, "%s.%d", rev, rev_num);
2489 test_branch = RCS_getbranch (rcs, xrev, 1);
2490 if (test_branch != NULL) /* it did, so keep looking */
2492 free (test_branch);
2493 continue;
2496 /* now, create a "magic" revision */
2497 (void) sprintf (xrev, "%s.%d.%d", rev, RCS_MAGIC_BRANCH, rev_num);
2499 /* walk the symbols list to see if a magic one already exists */
2500 if (walklist (RCS_symbols(rcs), checkmagic_proc, NULL) != 0)
2501 continue;
2503 /* we found a free magic branch. Claim it as ours */
2504 return xrev;
2511 * walklist proc to look for a match in the symbols list.
2512 * Returns 0 if the symbol does not match, 1 if it does.
2514 static int
2515 checkmagic_proc (Node *p, void *closure)
2517 if (STREQ (check_rev, p->data))
2518 return 1;
2519 else
2520 return 0;
2526 * Given an RCSNode, returns non-zero if the specified revision number
2527 * or symbolic tag resolves to a "branch" within the rcs file.
2529 * FIXME: this is the same as RCS_nodeisbranch except for the special
2530 * case for handling a null rcsnode.
2533 RCS_isbranch (RCSNode *rcs, const char *rev)
2535 /* numeric revisions are easy -- even number of dots is a branch */
2536 if (isdigit ((unsigned char) *rev))
2537 return (numdots (rev) & 1) == 0;
2539 /* assume a revision if you can't find the RCS info */
2540 if (rcs == NULL)
2541 return 0;
2543 /* now, look for a match in the symbols list */
2544 return RCS_nodeisbranch (rcs, rev);
2550 * Given an RCSNode, returns non-zero if the specified revision number
2551 * or symbolic tag resolves to a "branch" within the rcs file. We do
2552 * take into account any magic branches as well.
2555 RCS_nodeisbranch (RCSNode *rcs, const char *rev)
2557 int dots;
2558 char *version;
2560 assert (rcs != NULL);
2562 /* numeric revisions are easy -- even number of dots is a branch */
2563 if (isdigit ((unsigned char) *rev))
2564 return (numdots (rev) & 1) == 0;
2566 version = translate_symtag (rcs, rev);
2567 if (version == NULL)
2568 return 0;
2569 dots = numdots (version);
2570 if ((dots & 1) == 0)
2572 free (version);
2573 return 1;
2576 /* got a symbolic tag match, but it's not a branch; see if it's magic */
2577 if (dots > 2)
2579 char *magic;
2580 char *branch = strrchr (version, '.');
2581 char *cp = branch - 1;
2582 while (*cp != '.')
2583 cp--;
2585 /* see if we have .magic-branch. (".0.") */
2586 magic = Xasprintf (".%d.", RCS_MAGIC_BRANCH);
2587 if (strncmp (magic, cp, strlen (magic)) == 0)
2589 free (magic);
2590 free (version);
2591 return 1;
2593 free (magic);
2595 free (version);
2596 return 0;
2602 * Returns a pointer to malloc'ed memory which contains the branch
2603 * for the specified *symbolic* tag. Magic branches are handled correctly.
2605 char *
2606 RCS_whatbranch (RCSNode *rcs, const char *rev)
2608 char *version;
2609 int dots;
2611 /* assume no branch if you can't find the RCS info */
2612 if (rcs == NULL)
2613 return NULL;
2615 /* now, look for a match in the symbols list */
2616 version = translate_symtag (rcs, rev);
2617 if (version == NULL)
2618 return NULL;
2619 dots = numdots (version);
2620 if ((dots & 1) == 0)
2621 return version;
2623 /* got a symbolic tag match, but it's not a branch; see if it's magic */
2624 if (dots > 2)
2626 char *magic;
2627 char *branch = strrchr (version, '.');
2628 char *cp = branch++ - 1;
2629 while (*cp != '.')
2630 cp--;
2632 /* see if we have .magic-branch. (".0.") */
2633 magic = xmalloc (strlen (version) + 1);
2634 (void) sprintf (magic, ".%d.", RCS_MAGIC_BRANCH);
2635 if (strncmp (magic, cp, strlen (magic)) == 0)
2637 /* yep. it's magic. now, construct the real branch */
2638 *cp = '\0'; /* turn it into a revision */
2639 (void) sprintf (magic, "%s.%s", version, branch);
2640 free (version);
2641 return magic;
2643 free (magic);
2645 free (version);
2646 return NULL;
2652 * Get the head of the specified branch. If the branch does not exist,
2653 * return NULL or RCS_head depending on force_tag_match.
2654 * Returns NULL or a newly malloc'd string.
2656 char *
2657 RCS_getbranch (RCSNode *rcs, const char *tag, int force_tag_match)
2659 Node *p, *head;
2660 RCSVers *vn;
2661 char *xtag;
2662 char *nextvers;
2663 char *cp;
2665 /* make sure we have something to look at... */
2666 assert (rcs != NULL);
2668 if (rcs->flags & PARTIAL)
2669 RCS_reparsercsfile (rcs, NULL, NULL);
2671 /* find out if the tag contains a dot, or is on the trunk */
2672 cp = strrchr (tag, '.');
2674 /* trunk processing is the special case */
2675 if (cp == NULL)
2677 xtag = Xasprintf ("%s.", tag);
2678 for (cp = rcs->head; cp != NULL;)
2680 if (strncmp (xtag, cp, strlen (xtag)) == 0)
2681 break;
2682 p = findnode (rcs->versions, cp);
2683 if (p == NULL)
2685 free (xtag);
2686 if (force_tag_match)
2687 return NULL;
2688 else
2689 return RCS_head (rcs);
2691 vn = p->data;
2692 cp = vn->next;
2694 free (xtag);
2695 if (cp == NULL)
2697 if (force_tag_match)
2698 return NULL;
2699 else
2700 return RCS_head (rcs);
2702 return xstrdup (cp);
2705 /* if it had a `.', terminate the string so we have the base revision */
2706 *cp = '\0';
2708 /* look up the revision this branch is based on */
2709 p = findnode (rcs->versions, tag);
2711 /* put the . back so we have the branch again */
2712 *cp = '.';
2714 if (p == NULL)
2716 /* if the base revision didn't exist, return head or NULL */
2717 if (force_tag_match)
2718 return NULL;
2719 else
2720 return RCS_head (rcs);
2723 /* find the first element of the branch we are looking for */
2724 vn = p->data;
2725 if (vn->branches == NULL)
2726 return NULL;
2727 xtag = Xasprintf ("%s.", tag);
2728 head = vn->branches->list;
2729 for (p = head->next; p != head; p = p->next)
2730 if (strncmp (p->key, xtag, strlen (xtag)) == 0)
2731 break;
2732 free (xtag);
2734 if (p == head)
2736 /* we didn't find a match so return head or NULL */
2737 if (force_tag_match)
2738 return NULL;
2739 else
2740 return RCS_head (rcs);
2743 /* now walk the next pointers of the branch */
2744 nextvers = p->key;
2747 p = findnode (rcs->versions, nextvers);
2748 if (p == NULL)
2750 /* a link in the chain is missing - return head or NULL */
2751 if (force_tag_match)
2752 return NULL;
2753 else
2754 return RCS_head (rcs);
2756 vn = p->data;
2757 nextvers = vn->next;
2758 } while (nextvers != NULL);
2760 /* we have the version in our hand, so go for it */
2761 return xstrdup (vn->version);
2766 /* Returns the head of the branch which REV is on. REV can be a
2767 branch tag or non-branch tag; symbolic or numeric.
2769 Returns a newly malloc'd string. Returns NULL if a symbolic name
2770 isn't found. */
2771 char *
2772 RCS_branch_head (RCSNode *rcs, char *rev)
2774 char *num;
2775 char *br;
2776 char *retval;
2778 assert (rcs != NULL);
2780 if (RCS_nodeisbranch (rcs, rev))
2781 return RCS_getbranch (rcs, rev, 1);
2783 if (isdigit ((unsigned char) *rev))
2784 num = xstrdup (rev);
2785 else
2787 num = translate_symtag (rcs, rev);
2788 if (num == NULL)
2789 return NULL;
2791 br = truncate_revnum (num);
2792 retval = RCS_getbranch (rcs, br, 1);
2793 free (br);
2794 free (num);
2795 return retval;
2800 /* Get the branch point for a particular branch, that is the first
2801 revision on that branch. For example, RCS_getbranchpoint (rcs,
2802 "1.3.2") will normally return "1.3.2.1". TARGET may be either a
2803 branch number or a revision number; if a revnum, find the
2804 branchpoint of the branch to which TARGET belongs.
2806 Return RCS_head if TARGET is on the trunk or if the root node could
2807 not be found (this is sort of backwards from our behavior on a branch;
2808 the rationale is that the return value is a revision from which you
2809 can start walking the next fields and end up at TARGET).
2810 Return NULL on error. */
2811 static char *
2812 RCS_getbranchpoint (RCSNode *rcs, char *target)
2814 char *branch, *bp;
2815 Node *vp;
2816 RCSVers *rev;
2817 int dots, isrevnum, brlen;
2819 dots = numdots (target);
2820 isrevnum = dots & 1;
2822 if (dots == 1)
2823 /* TARGET is a trunk revision; return rcs->head. */
2824 return RCS_head (rcs);
2826 /* Get the revision number of the node at which TARGET's branch is
2827 rooted. If TARGET is a branch number, lop off the last field;
2828 if it's a revision number, lop off the last *two* fields. */
2829 branch = xstrdup (target);
2830 bp = strrchr (branch, '.');
2831 if (bp == NULL)
2832 error (1, 0, "%s: confused revision number %s",
2833 rcs->print_path, target);
2834 if (isrevnum)
2835 while (*--bp != '.')
2837 *bp = '\0';
2839 vp = findnode (rcs->versions, branch);
2840 if (vp == NULL)
2842 error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
2843 free (branch);
2844 return NULL;
2846 rev = vp->data;
2848 *bp++ = '.';
2849 while (*bp && *bp != '.')
2850 ++bp;
2851 brlen = bp - branch;
2853 vp = rev->branches->list->next;
2854 while (vp != rev->branches->list)
2856 /* BRANCH may be a genuine branch number, e.g. `1.1.3', or
2857 maybe a full revision number, e.g. `1.1.3.6'. We have
2858 found our branch point if the first BRANCHLEN characters
2859 of the revision number match, *and* if the following
2860 character is a dot. */
2861 if (strncmp (vp->key, branch, brlen) == 0 && vp->key[brlen] == '.')
2862 break;
2863 vp = vp->next;
2866 free (branch);
2867 if (vp == rev->branches->list)
2869 error (0, 0, "%s: can't find branch point %s", rcs->print_path, target);
2870 return NULL;
2872 else
2873 return xstrdup (vp->key);
2879 * Get the head of the RCS file. If branch is set, this is the head of the
2880 * branch, otherwise the real head.
2882 * INPUTS
2883 * rcs The parsed rcs node information.
2885 * RETURNS
2886 * NULL when rcs->branch exists and cannot be found.
2887 * A newly malloc'd string, otherwise.
2889 char *
2890 RCS_head (RCSNode *rcs)
2892 /* make sure we have something to look at... */
2893 assert (rcs);
2896 * NOTE: we call getbranch with force_tag_match set to avoid any
2897 * possibility of recursion
2899 if (rcs->branch)
2900 return RCS_getbranch (rcs, rcs->branch, 1);
2901 else
2902 return xstrdup (rcs->head);
2908 * Get the most recent revision, based on the supplied date, but use some
2909 * funky stuff and follow the vendor branch maybe
2911 char *
2912 RCS_getdate (RCSNode *rcs, const char *date, int force_tag_match)
2914 char *cur_rev = NULL;
2915 char *retval = NULL;
2916 Node *p;
2917 RCSVers *vers = NULL;
2919 /* make sure we have something to look at... */
2920 assert (rcs != NULL);
2922 if (rcs->flags & PARTIAL)
2923 RCS_reparsercsfile (rcs, NULL, NULL);
2925 /* if the head is on a branch, try the branch first */
2926 if (rcs->branch != NULL)
2928 retval = RCS_getdatebranch (rcs, date, rcs->branch);
2929 if (retval != NULL)
2930 return retval;
2933 /* otherwise if we have a trunk, try it */
2934 if (rcs->head)
2936 p = findnode (rcs->versions, rcs->head);
2937 if (p == NULL)
2939 error (0, 0, "%s: head revision %s doesn't exist", rcs->print_path,
2940 rcs->head);
2942 while (p != NULL)
2944 /* if the date of this one is before date, take it */
2945 vers = p->data;
2946 if (RCS_datecmp (vers->date, date) <= 0)
2948 cur_rev = vers->version;
2949 break;
2952 /* if there is a next version, find the node */
2953 if (vers->next != NULL)
2954 p = findnode (rcs->versions, vers->next);
2955 else
2956 p = NULL;
2959 else
2960 error (0, 0, "%s: no head revision", rcs->print_path);
2963 * at this point, either we have the revision we want, or we have the
2964 * first revision on the trunk (1.1?) in our hands, or we've come up
2965 * completely empty
2968 /* if we found what we're looking for, and it's not 1.1 return it */
2969 if (cur_rev != NULL)
2971 if (! STREQ (cur_rev, "1.1"))
2972 return xstrdup (cur_rev);
2974 /* This is 1.1; if the date of 1.1 is not the same as that for the
2975 1.1.1.1 version, then return 1.1. This happens when the first
2976 version of a file is created by a regular cvs add and commit,
2977 and there is a subsequent cvs import of the same file. */
2978 p = findnode (rcs->versions, "1.1.1.1");
2979 if (p)
2981 char *date_1_1 = vers->date;
2983 vers = p->data;
2984 if (RCS_datecmp (vers->date, date_1_1) != 0)
2985 return xstrdup ("1.1");
2989 /* look on the vendor branch */
2990 retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
2993 * if we found a match, return it; otherwise, we return the first
2994 * revision on the trunk or NULL depending on force_tag_match and the
2995 * date of the first rev
2997 if (retval != NULL)
2998 return retval;
3000 if (vers && (!force_tag_match || RCS_datecmp (vers->date, date) <= 0))
3001 return xstrdup (vers->version);
3002 else
3003 return NULL;
3009 * Look up the last element on a branch that was put in before or on
3010 * the specified date and time (return the rev or NULL)
3012 static char *
3013 RCS_getdatebranch (RCSNode *rcs, const char *date, const char *branch)
3015 char *cur_rev = NULL;
3016 char *cp;
3017 char *xbranch, *xrev;
3018 Node *p;
3019 RCSVers *vers;
3021 /* look up the first revision on the branch */
3022 xrev = xstrdup (branch);
3023 cp = strrchr (xrev, '.');
3024 if (cp == NULL)
3026 free (xrev);
3027 return NULL;
3029 *cp = '\0'; /* turn it into a revision */
3031 assert (rcs != NULL);
3033 if (rcs->flags & PARTIAL)
3034 RCS_reparsercsfile (rcs, NULL, NULL);
3036 p = findnode (rcs->versions, xrev);
3037 free (xrev);
3038 if (p == NULL)
3039 return NULL;
3040 vers = p->data;
3042 /* Tentatively use this revision, if it is early enough. */
3043 if (RCS_datecmp (vers->date, date) <= 0)
3044 cur_rev = vers->version;
3046 /* If no branches list, return now. This is what happens if the branch
3047 is a (magic) branch with no revisions yet. */
3048 if (vers->branches == NULL)
3049 return xstrdup (cur_rev);
3051 /* walk the branches list looking for the branch number */
3052 xbranch = Xasprintf ("%s.", branch);
3053 for (p = vers->branches->list->next; p != vers->branches->list; p = p->next)
3054 if (strncmp (p->key, xbranch, strlen (xbranch)) == 0)
3055 break;
3056 free (xbranch);
3057 if (p == vers->branches->list)
3059 /* This is what happens if the branch is a (magic) branch with
3060 no revisions yet. Similar to the case where vers->branches ==
3061 NULL, except here there was a another branch off the same
3062 branchpoint. */
3063 return xstrdup (cur_rev);
3066 p = findnode (rcs->versions, p->key);
3068 /* walk the next pointers until you find the end, or the date is too late */
3069 while (p != NULL)
3071 vers = p->data;
3072 if (RCS_datecmp (vers->date, date) <= 0)
3073 cur_rev = vers->version;
3074 else
3075 break;
3077 /* if there is a next version, find the node */
3078 if (vers->next != NULL)
3079 p = findnode (rcs->versions, vers->next);
3080 else
3081 p = NULL;
3084 /* Return whatever we found, which may be NULL. */
3085 return xstrdup (cur_rev);
3091 * Compare two dates in RCS format. Beware the change in format on January 1,
3092 * 2000, when years go from 2-digit to full format.
3095 RCS_datecmp (const char *date1, const char *date2)
3097 int length_diff = strlen (date1) - strlen (date2);
3099 return length_diff ? length_diff : strcmp (date1, date2);
3104 /* Look up revision REV in RCS and return the date specified for the
3105 revision minus FUDGE seconds (FUDGE will generally be one, so that the
3106 logically previous revision will be found later, or zero, if we want
3107 the exact date).
3109 The return value is the date being returned as a time_t, or (time_t)-1
3110 on error (previously was documented as zero on error; I haven't checked
3111 the callers to make sure that they really check for (time_t)-1, but
3112 the latter is what this function really returns). If DATE is non-NULL,
3113 then it must point to MAXDATELEN characters, and we store the same
3114 return value there in DATEFORM format. */
3115 time_t
3116 RCS_getrevtime (RCSNode *rcs, const char *rev, char *date, int fudge)
3118 char *tdate;
3119 struct tm xtm, *ftm;
3120 struct timespec revdate;
3121 Node *p;
3122 RCSVers *vers;
3124 /* make sure we have something to look at... */
3125 assert (rcs != NULL);
3127 if (rcs->flags & PARTIAL)
3128 RCS_reparsercsfile (rcs, NULL, NULL);
3130 /* look up the revision */
3131 p = findnode (rcs->versions, rev);
3132 if (p == NULL)
3133 return -1;
3134 vers = p->data;
3136 /* split up the date */
3137 if (sscanf (vers->date, SDATEFORM, &xtm.tm_year, &xtm.tm_mon,
3138 &xtm.tm_mday, &xtm.tm_hour, &xtm.tm_min, &xtm.tm_sec) != 6)
3139 error (1, 0, "%s: invalid date for revision %s (%s)", rcs->print_path,
3140 rev, vers->date);
3142 /* If the year is from 1900 to 1999, RCS files contain only two
3143 digits, and sscanf gives us a year from 0-99. If the year is
3144 2000+, RCS files contain all four digits and we subtract 1900,
3145 because the tm_year field should contain years since 1900. */
3147 if (xtm.tm_year >= 100 && xtm.tm_year < 2000)
3148 error (0, 0, "%s: non-standard date format for revision %s (%s)",
3149 rcs->print_path, rev, vers->date);
3150 if (xtm.tm_year >= 1900)
3151 xtm.tm_year -= 1900;
3153 /* put the date in a form getdate can grok */
3154 tdate = Xasprintf ("%d-%d-%d %d:%d:%d -0000",
3155 xtm.tm_year + 1900, xtm.tm_mon, xtm.tm_mday,
3156 xtm.tm_hour, xtm.tm_min, xtm.tm_sec);
3158 /* Turn it into seconds since the epoch.
3160 * We use a struct timespec since that is what getdate requires, then
3161 * truncate the nanoseconds.
3163 if (!get_date (&revdate, tdate, NULL))
3165 free (tdate);
3166 return (time_t)-1;
3168 free (tdate);
3170 revdate.tv_sec -= fudge; /* remove "fudge" seconds */
3171 if (date)
3173 /* Put an appropriate string into `date', if we were given one. */
3174 ftm = gmtime (&revdate.tv_sec);
3175 (void) sprintf (date, DATEFORM,
3176 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
3177 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
3178 ftm->tm_min, ftm->tm_sec);
3181 return revdate.tv_sec;
3186 List *
3187 RCS_getlocks (RCSNode *rcs)
3189 assert(rcs != NULL);
3191 if (rcs->flags & PARTIAL)
3192 RCS_reparsercsfile (rcs, NULL, NULL);
3194 if (rcs->locks_data) {
3195 rcs->locks = getlist ();
3196 do_locks (rcs->locks, rcs->locks_data);
3197 free(rcs->locks_data);
3198 rcs->locks_data = NULL;
3201 return rcs->locks;
3206 List *
3207 RCS_symbols(RCSNode *rcs)
3209 assert(rcs != NULL);
3211 if (rcs->flags & PARTIAL)
3212 RCS_reparsercsfile (rcs, NULL, NULL);
3214 if (rcs->symbols_data) {
3215 rcs->symbols = getlist ();
3216 do_symbols (rcs->symbols, rcs->symbols_data);
3217 free(rcs->symbols_data);
3218 rcs->symbols_data = NULL;
3221 return rcs->symbols;
3227 * Return the version associated with a particular symbolic tag.
3228 * Returns NULL or a newly malloc'd string.
3230 static char *
3231 translate_symtag (RCSNode *rcs, const char *tag)
3233 if (rcs->flags & PARTIAL)
3234 RCS_reparsercsfile (rcs, NULL, NULL);
3236 if (rcs->symbols != NULL)
3238 Node *p;
3240 /* The symbols have already been converted into a list. */
3241 p = findnode (rcs->symbols, tag);
3242 if (p == NULL)
3243 return NULL;
3245 return xstrdup (p->data);
3248 if (rcs->symbols_data != NULL)
3250 size_t len;
3251 char *cp, *last;
3253 /* Look through the RCS symbols information. This is like
3254 do_symbols, but we don't add the information to a list. In
3255 most cases, we will only be called once for this file, so
3256 generating the list is unnecessary overhead. */
3258 len = strlen (tag);
3259 cp = rcs->symbols_data;
3260 /* Keeping track of LAST below isn't strictly necessary, now that tags
3261 * should be parsed for validity before they are accepted, but tags
3262 * with spaces used to cause the code below to loop indefintely, so
3263 * I have corrected for that. Now, in the event that I missed
3264 * something, the server cannot be hung. -DRP
3266 last = NULL;
3267 while ((cp = strchr (cp, tag[0])) != NULL)
3269 if (cp == last) break;
3270 if ((cp == rcs->symbols_data || whitespace (cp[-1]))
3271 && strncmp (cp, tag, len) == 0
3272 && cp[len] == ':')
3274 char *v, *r;
3276 /* We found the tag. Return the version number. */
3278 cp += len + 1;
3279 v = cp;
3280 while (! whitespace (*cp) && *cp != '\0')
3281 ++cp;
3282 r = xmalloc (cp - v + 1);
3283 strncpy (r, v, cp - v);
3284 r[cp - v] = '\0';
3285 return r;
3288 while (! whitespace (*cp) && *cp != '\0')
3289 ++cp;
3290 if (*cp == '\0')
3291 break;
3292 last = cp;
3296 return NULL;
3302 * The argument ARG is the getopt remainder of the -k option specified on the
3303 * command line. This function returns malloc'ed space that can be used
3304 * directly in calls to RCS V5, with the -k flag munged correctly.
3306 char *
3307 RCS_check_kflag (const char *arg)
3309 static const char *const keyword_usage[] =
3311 "%s %s: invalid RCS keyword expansion mode\n",
3312 "Valid expansion modes include:\n",
3313 " -kkv\tGenerate keywords using the default form.\n",
3314 " -kkvl\tLike -kkv, except locker's name inserted.\n",
3315 " -kk\tGenerate only keyword names in keyword strings.\n",
3316 " -kv\tGenerate only keyword values in keyword strings.\n",
3317 " -ko\tGenerate the old keyword string (no changes from checked in file).\n",
3318 " -kb\tGenerate binary file unmodified (merges not allowed) (RCS 5.7).\n",
3319 "(Specify the --help global option for a list of other help options)\n",
3320 NULL,
3322 char const *const *cpp = NULL;
3324 if (arg)
3326 for (cpp = kflags; *cpp != NULL; cpp++)
3328 if (STREQ (arg, *cpp))
3329 break;
3333 if (arg == NULL || *cpp == NULL)
3335 usage (keyword_usage);
3338 return Xasprintf ("-k%s", *cpp);
3344 * Do some consistency checks on the symbolic tag... These should equate
3345 * pretty close to what RCS checks, though I don't know for certain.
3347 void
3348 RCS_check_tag (const char *tag)
3350 char *invalid = "$,.:;@"; /* invalid RCS tag characters */
3351 const char *cp;
3354 * The first character must be an alphabetic letter. The remaining
3355 * characters cannot be non-visible graphic characters, and must not be
3356 * in the set of "invalid" RCS identifier characters.
3358 if (isalpha ((unsigned char) *tag))
3360 for (cp = tag; *cp; cp++)
3362 if (!isgraph ((unsigned char) *cp))
3363 error (1, 0, "tag `%s' has non-visible graphic characters",
3364 tag);
3365 if (strchr (invalid, *cp))
3366 error (1, 0, "tag `%s' must not contain the characters `%s'",
3367 tag, invalid);
3370 else
3371 error (1, 0, "tag `%s' must start with a letter", tag);
3377 * TRUE if argument has valid syntax for an RCS revision or
3378 * branch number. All characters must be digits or dots, first
3379 * and last characters must be digits, and no two consecutive
3380 * characters may be dots.
3382 * Intended for classifying things, so this function doesn't
3383 * call error.
3385 int
3386 RCS_valid_rev (const char *rev)
3388 char last, c;
3389 last = *rev++;
3390 if (!isdigit ((unsigned char) last))
3391 return 0;
3392 while ((c = *rev++)) /* Extra parens placate -Wall gcc option */
3394 if (c == '.')
3396 if (last == '.')
3397 return 0;
3398 continue;
3400 last = c;
3401 if (!isdigit ((unsigned char) c))
3402 return 0;
3404 if (!isdigit ((unsigned char) last))
3405 return 0;
3406 return 1;
3412 * Return true if RCS revision with TAG is a dead revision.
3415 RCS_isdead (RCSNode *rcs, const char *tag)
3417 Node *p;
3418 RCSVers *version;
3420 if (rcs->flags & PARTIAL)
3421 RCS_reparsercsfile (rcs, NULL, NULL);
3423 p = findnode (rcs->versions, tag);
3424 if (p == NULL)
3425 return 0;
3427 version = p->data;
3428 return version->dead;
3433 /* Return the RCS keyword expansion mode. For example "b" for binary.
3434 Returns a pointer into storage which is allocated and freed along with
3435 the rest of the RCS information; the caller should not modify this
3436 storage. Returns NULL if the RCS file does not specify a keyword
3437 expansion mode; for all other errors, die with a fatal error. */
3438 char *
3439 RCS_getexpand (RCSNode *rcs)
3441 /* Since RCS_parsercsfile_i now reads expand, don't need to worry
3442 about RCS_reparsercsfile. */
3443 assert (rcs != NULL);
3444 return rcs->expand;
3449 /* Set keyword expansion mode to EXPAND. For example "b" for binary. */
3450 void
3451 RCS_setexpand (RCSNode *rcs, const char *expand)
3453 /* Since RCS_parsercsfile_i now reads expand, don't need to worry
3454 about RCS_reparsercsfile. */
3455 assert (rcs != NULL);
3456 if (rcs->expand != NULL)
3457 free (rcs->expand);
3458 rcs->expand = xstrdup (expand);
3463 /* RCS keywords, and a matching enum. */
3464 enum keyword
3466 KEYWORD_AUTHOR = 0,
3467 KEYWORD_DATE,
3468 KEYWORD_CVSHEADER,
3469 KEYWORD_HEADER,
3470 KEYWORD_ID,
3471 KEYWORD_LOCKER,
3472 KEYWORD_LOG,
3473 KEYWORD_NAME,
3474 KEYWORD_RCSFILE,
3475 KEYWORD_REVISION,
3476 KEYWORD_SOURCE,
3477 KEYWORD_STATE,
3478 KEYWORD_LOCALID
3480 struct rcs_keyword
3482 const char *string;
3483 size_t len;
3484 enum keyword expandto;
3485 bool expandit;
3490 static inline struct rcs_keyword *
3491 new_keywords (void)
3493 struct rcs_keyword *new;
3494 new = xcalloc (KEYWORD_LOCALID + 2, sizeof (struct rcs_keyword));
3496 #define KEYWORD_INIT(k, i, s) \
3497 k[i].string = s; \
3498 k[i].len = sizeof s - 1; \
3499 k[i].expandto = i; \
3500 k[i].expandit = true
3502 KEYWORD_INIT (new, KEYWORD_AUTHOR, "Author");
3503 KEYWORD_INIT (new, KEYWORD_DATE, "Date");
3504 KEYWORD_INIT (new, KEYWORD_CVSHEADER, "CVSHeader");
3505 KEYWORD_INIT (new, KEYWORD_HEADER, "Header");
3506 KEYWORD_INIT (new, KEYWORD_ID, "Id");
3507 KEYWORD_INIT (new, KEYWORD_LOCKER, "Locker");
3508 KEYWORD_INIT (new, KEYWORD_LOG, "Log");
3509 KEYWORD_INIT (new, KEYWORD_NAME, "Name");
3510 KEYWORD_INIT (new, KEYWORD_RCSFILE, "RCSfile");
3511 KEYWORD_INIT (new, KEYWORD_REVISION, "Revision");
3512 KEYWORD_INIT (new, KEYWORD_SOURCE, "Source");
3513 KEYWORD_INIT (new, KEYWORD_STATE, "State");
3514 new[KEYWORD_LOCALID].string = NULL;
3515 new[KEYWORD_LOCALID].len = 0;
3516 new[KEYWORD_LOCALID].expandto = KEYWORD_LOCALID;
3517 new[KEYWORD_LOCALID].expandit = false;
3519 return new;
3524 void
3525 free_keywords (void *keywords)
3527 free (keywords);
3532 /* Convert an RCS date string into a readable string. This is like
3533 the RCS date2str function. */
3534 static char *
3535 printable_date (const char *rcs_date)
3537 int year, mon, mday, hour, min, sec;
3538 char buf[100];
3540 (void) sscanf (rcs_date, SDATEFORM, &year, &mon, &mday, &hour, &min,
3541 &sec);
3542 if (year < 1900)
3543 year += 1900;
3544 sprintf (buf, "%04d/%02d/%02d %02d:%02d:%02d", year, mon, mday,
3545 hour, min, sec);
3546 return xstrdup (buf);
3551 /* Escape the characters in a string so that it can be included in an
3552 RCS value. */
3553 static char *
3554 escape_keyword_value (const char *value, int *free_value)
3556 char *ret, *t;
3557 const char *s;
3559 for (s = value; *s != '\0'; s++)
3561 char c;
3563 c = *s;
3564 if (c == '\t'
3565 || c == '\n'
3566 || c == '\\'
3567 || c == ' '
3568 || c == '$')
3570 break;
3574 if (*s == '\0')
3576 *free_value = 0;
3577 return (char *) value;
3580 ret = xmalloc (strlen (value) * 4 + 1);
3581 *free_value = 1;
3583 for (s = value, t = ret; *s != '\0'; s++, t++)
3585 switch (*s)
3587 default:
3588 *t = *s;
3589 break;
3590 case '\t':
3591 *t++ = '\\';
3592 *t = 't';
3593 break;
3594 case '\n':
3595 *t++ = '\\';
3596 *t = 'n';
3597 break;
3598 case '\\':
3599 *t++ = '\\';
3600 *t = '\\';
3601 break;
3602 case ' ':
3603 *t++ = '\\';
3604 *t++ = '0';
3605 *t++ = '4';
3606 *t = '0';
3607 break;
3608 case '$':
3609 *t++ = '\\';
3610 *t++ = '0';
3611 *t++ = '4';
3612 *t = '4';
3613 break;
3617 *t = '\0';
3619 return ret;
3624 /* Expand RCS keywords in the memory buffer BUF of length LEN. This
3625 applies to file RCS and version VERS. If NAME is not NULL, and is
3626 not a numeric revision, then it is the symbolic tag used for the
3627 checkout. EXPAND indicates how to expand the keywords. This
3628 function sets *RETBUF and *RETLEN to the new buffer and length.
3629 This function may modify the buffer BUF. If BUF != *RETBUF, then
3630 RETBUF is a newly allocated buffer. */
3631 static void
3632 expand_keywords (RCSNode *rcs, RCSVers *ver, const char *name, const char *log,
3633 size_t loglen, enum kflag expand, char *buf, size_t len,
3634 char **retbuf, size_t *retlen)
3636 struct expand_buffer
3638 struct expand_buffer *next;
3639 char *data;
3640 size_t len;
3641 int free_data;
3642 } *ebufs = NULL;
3643 struct expand_buffer *ebuf_last = NULL;
3644 size_t ebuf_len = 0;
3645 char *locker;
3646 char *srch, *srch_next;
3647 size_t srch_len;
3648 const struct rcs_keyword *keywords;
3650 if (!config /* For `cvs init', config may not be set. */
3651 ||expand == KFLAG_O || expand == KFLAG_B)
3653 *retbuf = buf;
3654 *retlen = len;
3655 return;
3658 if (!config->keywords) config->keywords = new_keywords ();
3659 keywords = config->keywords;
3661 /* If we are using -kkvl, dig out the locker information if any. */
3662 locker = NULL;
3663 if (expand == KFLAG_KVL)
3665 Node *lock;
3666 lock = findnode (RCS_getlocks(rcs), ver->version);
3667 if (lock != NULL)
3668 locker = xstrdup (lock->data);
3671 /* RCS keywords look like $STRING$ or $STRING: VALUE$. */
3672 srch = buf;
3673 srch_len = len;
3674 while ((srch_next = memchr (srch, '$', srch_len)) != NULL)
3676 char *s, *send;
3677 size_t slen;
3678 const struct rcs_keyword *keyword;
3679 char *value;
3680 int free_value;
3681 char *sub;
3682 size_t sublen;
3684 srch_len -= (srch_next + 1) - srch;
3685 srch = srch_next + 1;
3687 /* Look for the first non alphabetic character after the '$'. */
3688 send = srch + srch_len;
3689 for (s = srch; s < send; s++)
3690 if (! isalpha ((unsigned char) *s))
3691 break;
3693 /* If the first non alphabetic character is not '$' or ':',
3694 then this is not an RCS keyword. */
3695 if (s == send || (*s != '$' && *s != ':'))
3696 continue;
3698 /* See if this is one of the keywords. */
3699 slen = s - srch;
3700 for (keyword = keywords; keyword->string != NULL; keyword++)
3702 if (keyword->expandit
3703 && keyword->len == slen
3704 && strncmp (keyword->string, srch, slen) == 0)
3706 break;
3709 if (keyword->string == NULL)
3710 continue;
3712 /* If the keyword ends with a ':', then the old value consists
3713 of the characters up to the next '$'. If there is no '$'
3714 before the end of the line, though, then this wasn't an RCS
3715 keyword after all. */
3716 if (*s == ':')
3718 for (; s < send; s++)
3719 if (*s == '$' || *s == '\n')
3720 break;
3721 if (s == send || *s != '$')
3722 continue;
3725 /* At this point we must replace the string from SRCH to S
3726 with the expansion of the keyword KW. */
3728 /* Get the value to use. */
3729 free_value = 0;
3730 if (expand == KFLAG_K)
3731 value = NULL;
3732 else
3734 switch (keyword->expandto)
3736 default:
3737 assert (!"unreached");
3739 case KEYWORD_AUTHOR:
3740 value = ver->author;
3741 break;
3743 case KEYWORD_DATE:
3744 value = printable_date (ver->date);
3745 free_value = 1;
3746 break;
3748 case KEYWORD_CVSHEADER:
3749 case KEYWORD_HEADER:
3750 case KEYWORD_ID:
3751 case KEYWORD_LOCALID:
3753 const char *path;
3754 int free_path;
3755 char *date;
3756 char *old_path;
3758 old_path = NULL;
3759 if (keyword->expandto == KEYWORD_HEADER)
3760 path = rcs->print_path;
3761 else if (keyword->expandto == KEYWORD_CVSHEADER)
3762 path = getfullCVSname (rcs->print_path, &old_path);
3763 else
3764 path = last_component (rcs->print_path);
3765 path = escape_keyword_value (path, &free_path);
3766 date = printable_date (ver->date);
3767 value = Xasprintf ("%s %s %s %s %s%s%s",
3768 path, ver->version, date, ver->author,
3769 ver->state,
3770 locker != NULL ? " " : "",
3771 locker != NULL ? locker : "");
3772 if (free_path)
3773 /* If free_path is set then we know we allocated path
3774 * and we can discard the const.
3776 free ((char *)path);
3777 if (old_path)
3778 free (old_path);
3779 free (date);
3780 free_value = 1;
3782 break;
3784 case KEYWORD_LOCKER:
3785 value = locker;
3786 break;
3788 case KEYWORD_LOG:
3789 case KEYWORD_RCSFILE:
3790 value = escape_keyword_value (last_component (rcs->print_path),
3791 &free_value);
3792 break;
3794 case KEYWORD_NAME:
3795 if (name != NULL && ! isdigit ((unsigned char) *name))
3796 value = (char *) name;
3797 else
3798 value = NULL;
3799 break;
3801 case KEYWORD_REVISION:
3802 value = ver->version;
3803 break;
3805 case KEYWORD_SOURCE:
3806 value = escape_keyword_value (rcs->print_path, &free_value);
3807 break;
3809 case KEYWORD_STATE:
3810 value = ver->state;
3811 break;
3815 sub = xmalloc (keyword->len
3816 + (value == NULL ? 0 : strlen (value))
3817 + 10);
3818 if (expand == KFLAG_V)
3820 /* Decrement SRCH and increment S to remove the $
3821 characters. */
3822 --srch;
3823 ++srch_len;
3824 ++s;
3825 sublen = 0;
3827 else
3829 strcpy (sub, keyword->string);
3830 sublen = strlen (keyword->string);
3831 if (expand != KFLAG_K)
3833 sub[sublen] = ':';
3834 sub[sublen + 1] = ' ';
3835 sublen += 2;
3838 if (value != NULL)
3840 strcpy (sub + sublen, value);
3841 sublen += strlen (value);
3843 if (expand != KFLAG_V && expand != KFLAG_K)
3845 sub[sublen] = ' ';
3846 ++sublen;
3847 sub[sublen] = '\0';
3850 if (free_value)
3851 free (value);
3853 /* The Log keyword requires special handling. This behaviour
3854 is taken from RCS 5.7. The special log message is what RCS
3855 uses for ci -k. */
3856 if (keyword->expandto == KEYWORD_LOG
3857 && (sizeof "checked in with -k by " <= loglen
3858 || log == NULL
3859 || strncmp (log, "checked in with -k by ",
3860 sizeof "checked in with -k by " - 1) != 0))
3862 char *start;
3863 char *leader;
3864 size_t leader_len, leader_sp_len;
3865 const char *logend;
3866 const char *snl;
3867 int cnl;
3868 char *date;
3869 const char *sl;
3871 /* We are going to insert the trailing $ ourselves, before
3872 the log message, so we must remove it from S, if we
3873 haven't done so already. */
3874 if (expand != KFLAG_V)
3875 ++s;
3877 /* CVS never has empty log messages, but old RCS files might. */
3878 if (log == NULL)
3879 log = "";
3881 /* Find the start of the line. */
3882 start = srch;
3883 leader_len = 0;
3884 while (start > buf && start[-1] != '\n'
3885 && leader_len <= xsum (config->MaxCommentLeaderLength,
3886 expand != KFLAG_V ? 1 : 0))
3888 --start;
3889 ++leader_len;
3892 if (expand != KFLAG_V)
3893 /* When automagically determined and !KFLAG_V, we wish to avoid
3894 * including the leading `$' of the Log keyword in our leader.
3896 --leader_len;
3898 /* If the automagically determined leader exceeds the limit set in
3899 * CVSROOT/config, try to use a fallback.
3901 if (leader_len > config->MaxCommentLeaderLength)
3903 if (config->UseArchiveCommentLeader && rcs->comment)
3905 leader = xstrdup (rcs->comment);
3906 leader_len = strlen (rcs->comment);
3908 else
3910 error (0, 0,
3911 "Skipping `$" "Log$' keyword due to excessive comment leader.");
3912 continue;
3915 else /* leader_len <= config->MaxCommentLeaderLength */
3917 /* Copy the start of the line to use as a comment leader. */
3918 leader = xmalloc (leader_len);
3919 memcpy (leader, start, leader_len);
3922 leader_sp_len = leader_len;
3923 while (leader_sp_len > 0 && isspace (leader[leader_sp_len - 1]))
3924 --leader_sp_len;
3926 /* RCS does some checking for an old style of Log here,
3927 but we don't bother. RCS issues a warning if it
3928 changes anything. */
3930 /* Count the number of newlines in the log message so that
3931 we know how many copies of the leader we will need. */
3932 cnl = 0;
3933 logend = log + loglen;
3934 for (snl = log; snl < logend; snl++)
3935 if (*snl == '\n')
3936 ++cnl;
3938 /* If the log message did not end in a newline, increment
3939 * the newline count so we have space for the extra leader.
3940 * Failure to do so results in a buffer overrun.
3942 if (loglen && snl[-1] != '\n')
3943 ++cnl;
3945 date = printable_date (ver->date);
3946 sub = xrealloc (sub,
3947 (sublen
3948 + sizeof "Revision"
3949 + strlen (ver->version)
3950 + strlen (date)
3951 + strlen (ver->author)
3952 + loglen
3953 /* Use CNL + 2 below: One leader for each log
3954 * line, plus the Revision/Author/Date line,
3955 * plus a trailing blank line.
3957 + (cnl + 2) * leader_len
3958 + 20));
3959 if (expand != KFLAG_V)
3961 sub[sublen] = '$';
3962 ++sublen;
3964 sub[sublen] = '\n';
3965 ++sublen;
3966 memcpy (sub + sublen, leader, leader_len);
3967 sublen += leader_len;
3968 sprintf (sub + sublen, "Revision %s %s %s\n",
3969 ver->version, date, ver->author);
3970 sublen += strlen (sub + sublen);
3971 free (date);
3973 sl = log;
3974 while (sl < logend)
3976 if (*sl == '\n')
3978 memcpy (sub + sublen, leader, leader_sp_len);
3979 sublen += leader_sp_len;
3980 sub[sublen] = '\n';
3981 ++sublen;
3982 ++sl;
3984 else
3986 const char *slnl;
3988 memcpy (sub + sublen, leader, leader_len);
3989 sublen += leader_len;
3990 for (slnl = sl; slnl < logend && *slnl != '\n'; ++slnl)
3992 if (slnl < logend)
3993 ++slnl;
3994 memcpy (sub + sublen, sl, slnl - sl);
3995 sublen += slnl - sl;
3996 if (slnl == logend && slnl[-1] != '\n')
3998 /* There was no EOL at the end of the log message. Add
3999 * one.
4001 sub[sublen] = '\n';
4002 ++sublen;
4004 sl = slnl;
4008 memcpy (sub + sublen, leader, leader_sp_len);
4009 sublen += leader_sp_len;
4011 free (leader);
4014 /* Now SUB contains a string which is to replace the string
4015 from SRCH to S. SUBLEN is the length of SUB. */
4017 if (srch + sublen == s)
4019 memcpy (srch, sub, sublen);
4020 free (sub);
4022 else
4024 struct expand_buffer *ebuf;
4026 /* We need to change the size of the buffer. We build a
4027 list of expand_buffer structures. Each expand_buffer
4028 structure represents a portion of the final output. We
4029 concatenate them back into a single buffer when we are
4030 done. This minimizes the number of potentially large
4031 buffer copies we must do. */
4033 if (ebufs == NULL)
4035 ebufs = xmalloc (sizeof *ebuf);
4036 ebufs->next = NULL;
4037 ebufs->data = buf;
4038 ebufs->free_data = 0;
4039 ebuf_len = srch - buf;
4040 ebufs->len = ebuf_len;
4041 ebuf_last = ebufs;
4043 else
4045 assert (srch >= ebuf_last->data);
4046 assert (srch <= ebuf_last->data + ebuf_last->len);
4047 ebuf_len -= ebuf_last->len - (srch - ebuf_last->data);
4048 ebuf_last->len = srch - ebuf_last->data;
4051 ebuf = xmalloc (sizeof *ebuf);
4052 ebuf->data = sub;
4053 ebuf->len = sublen;
4054 ebuf->free_data = 1;
4055 ebuf->next = NULL;
4056 ebuf_last->next = ebuf;
4057 ebuf_last = ebuf;
4058 ebuf_len += sublen;
4060 ebuf = xmalloc (sizeof *ebuf);
4061 ebuf->data = s;
4062 ebuf->len = srch_len - (s - srch);
4063 ebuf->free_data = 0;
4064 ebuf->next = NULL;
4065 ebuf_last->next = ebuf;
4066 ebuf_last = ebuf;
4067 ebuf_len += srch_len - (s - srch);
4070 srch_len -= (s - srch);
4071 srch = s;
4074 if (locker != NULL)
4075 free (locker);
4077 if (ebufs == NULL)
4079 *retbuf = buf;
4080 *retlen = len;
4082 else
4084 char *ret;
4086 ret = xmalloc (ebuf_len);
4087 *retbuf = ret;
4088 *retlen = ebuf_len;
4089 while (ebufs != NULL)
4091 struct expand_buffer *next;
4093 memcpy (ret, ebufs->data, ebufs->len);
4094 ret += ebufs->len;
4095 if (ebufs->free_data)
4096 free (ebufs->data);
4097 next = ebufs->next;
4098 free (ebufs);
4099 ebufs = next;
4106 /* Check out a revision from an RCS file.
4108 If PFN is not NULL, then ignore WORKFILE and SOUT. Call PFN zero
4109 or more times with the contents of the file. CALLERDAT is passed,
4110 uninterpreted, to PFN. (The current code will always call PFN
4111 exactly once for a non empty file; however, the current code
4112 assumes that it can hold the entire file contents in memory, which
4113 is not a good assumption, and might change in the future).
4115 Otherwise, if WORKFILE is not NULL, check out the revision to
4116 WORKFILE. However, if WORKFILE is not NULL, and noexec is set,
4117 then don't do anything.
4119 Otherwise, if WORKFILE is NULL, check out the revision to SOUT. If
4120 SOUT is RUN_TTY, then write the contents of the revision to
4121 standard output. When using SOUT, the output is generally a
4122 temporary file; don't bother to get the file modes correct. When
4123 NOEXEC is set, WORKFILEs are not written but SOUTs are.
4125 REV is the numeric revision to check out. It may be NULL, which
4126 means to check out the head of the default branch.
4128 If NAMETAG is not NULL, and is not a numeric revision, then it is
4129 the tag that should be used when expanding the RCS Name keyword.
4131 OPTIONS is a string such as "-kb" or "-kv" for keyword expansion
4132 options. It may be NULL to use the default expansion mode of the
4133 file, typically "-kkv".
4135 On an error which prevented checking out the file, either print a
4136 nonfatal error and return 1, or give a fatal error. On success,
4137 return 0. */
4139 /* This function mimics the behavior of `rcs co' almost exactly. The
4140 chief difference is in its support for preserving file ownership,
4141 permissions, and special files across checkin and checkout -- see
4142 comments in RCS_checkin for some issues about this. -twp */
4144 RCS_checkout (RCSNode *rcs, const char *workfile, const char *rev,
4145 const char *nametag, const char *options, const char *sout,
4146 RCSCHECKOUTPROC pfn, void *callerdat)
4148 int free_rev = 0;
4149 enum kflag expand;
4150 FILE *fp,
4151 *ofp = NULL; /* Initialize since -Wall doesn't understand that
4152 * error (1, ...) does not return.
4154 struct stat sb;
4155 struct rcsbuffer rcsbuf;
4156 char *key;
4157 char *value;
4158 size_t len;
4159 int free_value = 0;
4160 char *log = NULL;
4161 size_t loglen = 0;
4162 Node *vp = NULL;
4163 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4164 uid_t rcs_owner = (uid_t) -1;
4165 gid_t rcs_group = (gid_t) -1;
4166 mode_t rcs_mode;
4167 int change_rcs_owner_or_group = 0;
4168 int change_rcs_mode = 0;
4169 int special_file = 0;
4170 unsigned long devnum_long;
4171 dev_t devnum = 0;
4172 #endif
4174 TRACE (TRACE_FUNCTION, "RCS_checkout (%s, %s, %s, %s, %s)",
4175 rcs->path,
4176 rev != NULL ? rev : "",
4177 nametag != NULL ? nametag : "",
4178 options != NULL ? options : "",
4179 (pfn != NULL ? "(function)"
4180 : (workfile != NULL ? workfile
4181 : (sout != RUN_TTY ? sout
4182 : "(stdout)"))));
4184 assert (rev == NULL || isdigit ((unsigned char) *rev));
4186 if (noexec && !server_active && workfile != NULL)
4187 return 0;
4189 assert (sout == RUN_TTY || workfile == NULL);
4190 assert (pfn == NULL || (sout == RUN_TTY && workfile == NULL));
4192 /* Some callers, such as Checkin or remove_file, will pass us a
4193 branch. */
4194 if (rev != NULL && (numdots (rev) & 1) == 0)
4196 rev = RCS_getbranch (rcs, rev, 1);
4197 if (rev == NULL)
4198 error (1, 0, "internal error: bad branch tag in checkout");
4199 free_rev = 1;
4202 if (rev == NULL || STREQ (rev, rcs->head))
4204 int gothead;
4206 /* We want the head revision. Try to read it directly. */
4208 if (rcs->flags & PARTIAL)
4209 RCS_reparsercsfile (rcs, &fp, &rcsbuf);
4210 else
4211 rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf);
4213 gothead = 0;
4214 if (! rcsbuf_getrevnum (&rcsbuf, &key))
4215 error (1, 0, "unexpected EOF reading %s", rcs->print_path);
4216 while (rcsbuf_getkey (&rcsbuf, &key, &value))
4218 if (STREQ (key, "log"))
4220 if (log)
4222 error (0, 0,
4223 "Duplicate log keyword found for head revision in RCS file.");
4224 free (log);
4226 log = rcsbuf_valcopy (&rcsbuf, value, 0, &loglen);
4228 else if (STREQ (key, "text"))
4230 gothead = 1;
4231 break;
4235 if (! gothead)
4237 error (0, 0, "internal error: cannot find head text");
4238 if (free_rev)
4239 /* It's okay to discard the const when free_rev is set, because
4240 * we know we allocated it in this function.
4242 free ((char *)rev);
4243 return 1;
4246 rcsbuf_valpolish (&rcsbuf, value, 0, &len);
4248 if (fstat (fileno (fp), &sb) < 0)
4249 error (1, errno, "cannot fstat %s", rcs->path);
4251 rcsbuf_cache (rcs, &rcsbuf);
4253 else
4255 struct rcsbuffer *rcsbufp;
4257 /* It isn't the head revision of the trunk. We'll need to
4258 walk through the deltas. */
4260 fp = NULL;
4261 if (rcs->flags & PARTIAL)
4262 RCS_reparsercsfile (rcs, &fp, &rcsbuf);
4264 if (fp == NULL)
4266 /* If RCS_deltas didn't close the file, we could use fstat
4267 here too. Probably should change it thusly.... */
4268 if (stat (rcs->path, &sb) < 0)
4269 error (1, errno, "cannot stat %s", rcs->path);
4270 rcsbufp = NULL;
4272 else
4274 if (fstat (fileno (fp), &sb) < 0)
4275 error (1, errno, "cannot fstat %s", rcs->path);
4276 rcsbufp = &rcsbuf;
4279 RCS_deltas (rcs, fp, rcsbufp, rev, RCS_FETCH, &value, &len,
4280 &log, &loglen);
4281 free_value = 1;
4284 /* If OPTIONS is NULL or the empty string, then the old code would
4285 invoke the RCS co program with no -k option, which means that
4286 co would use the string we have stored in rcs->expand. */
4287 if ((options == NULL || options[0] == '\0') && rcs->expand == NULL)
4288 expand = KFLAG_KV;
4289 else
4291 const char *ouroptions;
4292 const char * const *cpp;
4294 if (options != NULL && options[0] != '\0')
4296 assert (options[0] == '-' && options[1] == 'k');
4297 ouroptions = options + 2;
4299 else
4300 ouroptions = rcs->expand;
4302 for (cpp = kflags; *cpp != NULL; cpp++)
4303 if (STREQ (*cpp, ouroptions))
4304 break;
4306 if (*cpp != NULL)
4307 expand = (enum kflag) (cpp - kflags);
4308 else
4310 error (0, 0,
4311 "internal error: unsupported substitution string -k%s",
4312 ouroptions);
4313 expand = KFLAG_KV;
4317 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4318 /* Handle special files and permissions, if that is desired. */
4319 if (preserve_perms)
4321 RCSVers *vers;
4322 Node *info;
4324 vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4325 if (vp == NULL)
4326 error (1, 0, "internal error: no revision information for %s",
4327 rev == NULL ? rcs->head : rev);
4328 vers = vp->data;
4330 /* First we look for symlinks, which are simplest to handle. */
4331 info = findnode (vers->other_delta, "symlink");
4332 if (info != NULL)
4334 char *dest;
4336 if (pfn != NULL || (workfile == NULL && sout == RUN_TTY))
4337 error (1, 0, "symbolic link %s:%s cannot be piped",
4338 rcs->path, vers->version);
4339 if (workfile == NULL)
4340 dest = sout;
4341 else
4342 dest = workfile;
4344 /* Remove `dest', just in case. It's okay to get ENOENT here,
4345 since we just want the file not to be there. (TODO: decide
4346 whether it should be considered an error for `dest' to exist
4347 at this point. If so, the unlink call should be removed and
4348 `symlink' should signal the error. -twp) */
4349 if (CVS_UNLINK (dest) < 0 && !existence_error (errno))
4350 error (1, errno, "cannot remove %s", dest);
4351 if (symlink (info->data, dest) < 0)
4352 error (1, errno, "cannot create symbolic link from %s to %s",
4353 dest, (char *)info->data);
4354 if (free_value)
4355 free (value);
4356 if (free_rev)
4357 /* It's okay to discard the const when free_rev is set, because
4358 * we know we allocated it in this function.
4360 free ((char *)rev);
4361 return 0;
4364 /* Next, we look at this file's hardlinks field, and see whether
4365 it is linked to any other file that has been checked out.
4366 If so, we don't do anything else -- just link it to that file.
4368 If we are checking out a file to a pipe or temporary storage,
4369 none of this should matter. Hence the `workfile != NULL'
4370 wrapper around the whole thing. -twp */
4372 if (workfile != NULL)
4374 List *links = vers->hardlinks;
4375 if (links != NULL)
4377 Node *uptodate_link;
4379 /* For each file in the hardlinks field, check to see
4380 if it exists, and if so, if it has been checked out
4381 this iteration. When walklist returns, uptodate_link
4382 should point to a hardlist node representing a file
4383 in `links' which has recently been checked out, or
4384 NULL if no file in `links' has yet been checked out. */
4386 uptodate_link = NULL;
4387 (void) walklist (links, find_checkedout_proc, &uptodate_link);
4388 dellist (&links);
4390 /* If we've found a file that `workfile' is supposed to be
4391 linked to, and it has been checked out since CVS was
4392 invoked, then simply link workfile to that file and return.
4394 If one of these conditions is not met, then
4395 workfile is the first one in its hardlink group to
4396 be checked out, and we must continue with a full
4397 checkout. */
4399 if (uptodate_link != NULL)
4401 struct hardlink_info *hlinfo = uptodate_link->data;
4403 if (link (uptodate_link->key, workfile) < 0)
4404 error (1, errno, "cannot link %s to %s",
4405 workfile, uptodate_link->key);
4406 hlinfo->checked_out = 1; /* probably unnecessary */
4407 if (free_value)
4408 free (value);
4409 if (free_rev)
4410 /* It's okay to discard the const when free_rev is set,
4411 * because we know we allocated it in this function.
4413 free ((char *)rev);
4414 return 0;
4419 info = findnode (vers->other_delta, "owner");
4420 if (info != NULL)
4422 change_rcs_owner_or_group = 1;
4423 rcs_owner = (uid_t) strtoul (info->data, NULL, 10);
4425 info = findnode (vers->other_delta, "group");
4426 if (info != NULL)
4428 change_rcs_owner_or_group = 1;
4429 rcs_group = (gid_t) strtoul (info->data, NULL, 10);
4431 info = findnode (vers->other_delta, "permissions");
4432 if (info != NULL)
4434 change_rcs_mode = 1;
4435 rcs_mode = (mode_t) strtoul (info->data, NULL, 8);
4437 info = findnode (vers->other_delta, "special");
4438 if (info != NULL)
4440 /* If the size of `devtype' changes, fix the sscanf call also */
4441 char devtype[16];
4443 if (sscanf (info->data, "%15s %lu",
4444 devtype, &devnum_long) < 2)
4445 error (1, 0, "%s:%s has bad `special' newphrase %s",
4446 workfile, vers->version, (char *)info->data);
4447 devnum = devnum_long;
4448 if (STREQ (devtype, "character"))
4449 special_file = S_IFCHR;
4450 else if (STREQ (devtype, "block"))
4451 special_file = S_IFBLK;
4452 else
4453 error (0, 0, "%s is a special file of unsupported type `%s'",
4454 workfile, (char *)info->data);
4457 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
4459 if (expand != KFLAG_O && expand != KFLAG_B)
4461 char *newvalue;
4463 /* Don't fetch the delta node again if we already have it. */
4464 if (vp == NULL)
4466 vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4467 if (vp == NULL)
4468 error (1, 0, "internal error: no revision information for %s",
4469 rev == NULL ? rcs->head : rev);
4472 expand_keywords (rcs, vp->data, nametag, log, loglen,
4473 expand, value, len, &newvalue, &len);
4475 if (newvalue != value)
4477 if (free_value)
4478 free (value);
4479 value = newvalue;
4480 free_value = 1;
4484 if (free_rev)
4485 /* It's okay to discard the const when free_rev is set, because
4486 * we know we allocated it in this function.
4488 free ((char *)rev);
4490 if (log != NULL)
4492 free (log);
4493 log = NULL;
4496 if (pfn != NULL)
4498 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4499 if (special_file)
4500 error (1, 0, "special file %s cannot be piped to anything",
4501 rcs->path);
4502 #endif
4503 /* The PFN interface is very simple to implement right now, as
4504 we always have the entire file in memory. */
4505 if (len != 0)
4506 pfn (callerdat, value, len);
4508 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4509 else if (special_file)
4511 # ifdef HAVE_MKNOD
4512 char *dest;
4514 /* Can send either to WORKFILE or to SOUT, as long as SOUT is
4515 not RUN_TTY. */
4516 dest = workfile;
4517 if (dest == NULL)
4519 if (sout == RUN_TTY)
4520 error (1, 0, "special file %s cannot be written to stdout",
4521 rcs->path);
4522 dest = sout;
4525 /* Unlink `dest', just in case. It's okay if this provokes a
4526 ENOENT error. */
4527 if (CVS_UNLINK (dest) < 0 && existence_error (errno))
4528 error (1, errno, "cannot remove %s", dest);
4529 if (mknod (dest, special_file, devnum) < 0)
4530 error (1, errno, "could not create special file %s",
4531 dest);
4532 # else
4533 error (1, 0,
4534 "cannot create %s: unable to create special files on this system",
4535 workfile);
4536 # endif
4538 #endif
4539 else
4541 /* Not a special file: write to WORKFILE or SOUT. */
4542 if (workfile == NULL)
4544 if (sout == RUN_TTY)
4545 ofp = stdout;
4546 else
4548 /* Symbolic links should be removed before replacement, so that
4549 `fopen' doesn't follow the link and open the wrong file. */
4550 if (islink (sout))
4551 if (unlink_file (sout) < 0)
4552 error (1, errno, "cannot remove %s", sout);
4553 ofp = CVS_FOPEN (sout, expand == KFLAG_B ? "wb" : "w");
4554 if (ofp == NULL)
4555 error (1, errno, "cannot open %s", sout);
4558 else
4560 /* Output is supposed to go to WORKFILE, so we should open that
4561 file. Symbolic links should be removed first (see above). */
4562 if (islink (workfile))
4563 if (unlink_file (workfile) < 0)
4564 error (1, errno, "cannot remove %s", workfile);
4566 ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
4568 /* If the open failed because the existing workfile was not
4569 writable, try to chmod the file and retry the open. */
4570 if (ofp == NULL && errno == EACCES
4571 && isfile (workfile) && !iswritable (workfile))
4573 xchmod (workfile, 1);
4574 ofp = CVS_FOPEN (workfile, expand == KFLAG_B ? "wb" : "w");
4577 if (ofp == NULL)
4579 error (0, errno, "cannot open %s", workfile);
4580 if (free_value)
4581 free (value);
4582 return 1;
4586 if (workfile == NULL && sout == RUN_TTY)
4588 if (expand == KFLAG_B)
4589 cvs_output_binary (value, len);
4590 else
4592 /* cvs_output requires the caller to check for zero
4593 length. */
4594 if (len > 0)
4595 cvs_output (value, len);
4598 else
4600 /* NT 4.0 is said to have trouble writing 2099999 bytes
4601 (for example) in a single fwrite. So break it down
4602 (there is no need to be writing that much at once
4603 anyway; it is possible that LARGEST_FWRITE should be
4604 somewhat larger for good performance, but for testing I
4605 want to start with a small value until/unless a bigger
4606 one proves useful). */
4607 #define LARGEST_FWRITE 8192
4608 size_t nleft = len;
4609 size_t nstep = (len < LARGEST_FWRITE ? len : LARGEST_FWRITE);
4610 char *p = value;
4612 while (nleft > 0)
4614 if (fwrite (p, 1, nstep, ofp) != nstep)
4616 error (0, errno, "cannot write %s",
4617 (workfile != NULL
4618 ? workfile
4619 : (sout != RUN_TTY ? sout : "stdout")));
4620 if (free_value)
4621 free (value);
4622 return 1;
4624 p += nstep;
4625 nleft -= nstep;
4626 if (nleft < nstep)
4627 nstep = nleft;
4632 if (free_value)
4633 free (value);
4635 if (workfile != NULL)
4637 int ret;
4639 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4640 if (!special_file && fclose (ofp) < 0)
4642 error (0, errno, "cannot close %s", workfile);
4643 return 1;
4646 if (change_rcs_owner_or_group)
4648 if (chown (workfile, rcs_owner, rcs_group) < 0)
4649 error (0, errno, "could not change owner or group of %s",
4650 workfile);
4653 ret = chmod (workfile,
4654 change_rcs_mode
4655 ? rcs_mode
4656 : sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
4657 #else
4658 if (fclose (ofp) < 0)
4660 error (0, errno, "cannot close %s", workfile);
4661 return 1;
4664 ret = chmod (workfile,
4665 sb.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH));
4666 #endif
4667 if (ret < 0)
4669 error (0, errno, "cannot change mode of file %s",
4670 workfile);
4673 else if (sout != RUN_TTY)
4675 if (
4676 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4677 !special_file &&
4678 #endif
4679 fclose (ofp) < 0)
4681 error (0, errno, "cannot close %s", sout);
4682 return 1;
4686 #ifdef PRESERVE_PERMISSIONS_SUPPORT
4687 /* If we are in the business of preserving hardlinks, then
4688 mark this file as having been checked out. */
4689 if (preserve_perms && workfile != NULL)
4690 update_hardlink_info (workfile);
4691 #endif
4693 return 0;
4698 /* Find the delta currently locked by the user. From the `ci' man page:
4700 "If rev is omitted, ci tries to derive the new revision
4701 number from the caller's last lock. If the caller has
4702 locked the tip revision of a branch, the new revision is
4703 appended to that branch. The new revision number is
4704 obtained by incrementing the tip revision number. If the
4705 caller locked a non-tip revision, a new branch is started
4706 at that revision by incrementing the highest branch number
4707 at that revision. The default initial branch and level
4708 numbers are 1.
4710 If rev is omitted and the caller has no lock, but owns the
4711 file and locking is not set to strict, then the revision
4712 is appended to the default branch (normally the trunk; see
4713 the -b option of rcs(1))."
4715 RCS_findlock_or_tip finds the unique revision locked by the caller
4716 and returns its delta node. If the caller has not locked any
4717 revisions (and is permitted to commit to an unlocked delta, as
4718 described above), return the tip of the default branch. */
4719 static RCSVers *
4720 RCS_findlock_or_tip (RCSNode *rcs)
4722 char *user = getcaller();
4723 Node *lock, *p;
4724 List *locklist;
4726 /* Find unique delta locked by caller. This code is very similar
4727 to the code in RCS_unlock -- perhaps it could be abstracted
4728 into a RCS_findlock function. */
4729 locklist = RCS_getlocks (rcs);
4730 lock = NULL;
4731 for (p = locklist->list->next; p != locklist->list; p = p->next)
4733 if (STREQ (p->data, user))
4735 if (lock != NULL)
4737 error (0, 0, "\
4738 %s: multiple revisions locked by %s; please specify one", rcs->print_path, user);
4739 return NULL;
4741 lock = p;
4745 if (lock != NULL)
4747 /* Found an old lock, but check that the revision still exists. */
4748 p = findnode (rcs->versions, lock->key);
4749 if (p == NULL)
4751 error (0, 0, "%s: can't unlock nonexistent revision %s",
4752 rcs->print_path,
4753 lock->key);
4754 return NULL;
4756 return p->data;
4759 /* No existing lock. The RCS rule is that this is an error unless
4760 locking is nonstrict AND the file is owned by the current
4761 user. Trying to determine the latter is a portability nightmare
4762 in the face of NT, VMS, AFS, and other systems with non-unix-like
4763 ideas of users and owners. In the case of CVS, we should never get
4764 here (as long as the traditional behavior of making sure to call
4765 RCS_lock persists). Anyway, we skip the RCS error checks
4766 and just return the default branch or head. The reasoning is that
4767 those error checks are to make users lock before a checkin, and we do
4768 that in other ways if at all anyway (e.g. rcslock.pl). */
4770 p = findnode (rcs->versions, RCS_getbranch (rcs, rcs->branch, 0));
4771 if (!p)
4773 error (0, 0, "RCS file `%s' does not contain its default revision.",
4774 rcs->path);
4775 return NULL;
4778 return p->data;
4783 /* Revision number string, R, must contain a `.'.
4784 Return a newly-malloc'd copy of the prefix of R up
4785 to but not including the final `.'. */
4786 static char *
4787 truncate_revnum (const char *r)
4789 size_t len;
4790 char *new_r;
4791 char *dot = strrchr (r, '.');
4793 assert (dot);
4794 len = dot - r;
4795 new_r = xmalloc (len + 1);
4796 memcpy (new_r, r, len);
4797 *(new_r + len) = '\0';
4798 return new_r;
4803 /* Revision number string, R, must contain a `.'.
4804 R must be writable. Replace the rightmost `.' in R with
4805 the NUL byte and return a pointer to that NUL byte. */
4806 static char *
4807 truncate_revnum_in_place (char *r)
4809 char *dot = strrchr (r, '.');
4810 assert (dot);
4811 *dot = '\0';
4812 return dot;
4817 /* Revision number strings, R and S, must each contain a `.'.
4818 R and S must be writable and must have the same number of dots.
4819 Truncate R and S for the comparison, then restored them to their
4820 original state.
4821 Return the result (see compare_revnums) of comparing R and S
4822 ignoring differences in any component after the rightmost `.'. */
4823 static int
4824 compare_truncated_revnums (char *r, char *s)
4826 char *r_dot = truncate_revnum_in_place (r);
4827 char *s_dot = truncate_revnum_in_place (s);
4828 int cmp;
4830 assert (numdots (r) == numdots (s));
4832 cmp = compare_revnums (r, s);
4834 *r_dot = '.';
4835 *s_dot = '.';
4837 return cmp;
4842 /* Return a malloc'd copy of the string representing the highest branch
4843 number on BRANCHNODE. If there are no branches on BRANCHNODE, return NULL.
4844 FIXME: isn't the max rev always the last one?
4845 If so, we don't even need a loop. */
4846 static char *
4847 max_rev (const RCSVers *branchnode)
4849 Node *head;
4850 Node *bp;
4851 char *max;
4853 if (branchnode->branches == NULL)
4855 return NULL;
4858 max = NULL;
4859 head = branchnode->branches->list;
4860 for (bp = head->next; bp != head; bp = bp->next)
4862 if (max == NULL || compare_truncated_revnums (max, bp->key) < 0)
4864 max = bp->key;
4867 assert (max);
4869 return truncate_revnum (max);
4874 /* Create BRANCH in RCS's delta tree. BRANCH may be either a branch
4875 number or a revision number. In the former case, create the branch
4876 with the specified number; in the latter case, create a new branch
4877 rooted at node BRANCH with a higher branch number than any others.
4878 Return the number of the tip node on the new branch. */
4879 static char *
4880 RCS_addbranch (RCSNode *rcs, const char *branch)
4882 char *branchpoint, *newrevnum;
4883 Node *nodep, *bp;
4884 Node *marker;
4885 RCSVers *branchnode;
4887 assert (branch);
4889 /* Append to end by default. */
4890 marker = NULL;
4892 branchpoint = xstrdup (branch);
4893 if ((numdots (branchpoint) & 1) == 0)
4895 truncate_revnum_in_place (branchpoint);
4898 /* Find the branch rooted at BRANCHPOINT. */
4899 nodep = findnode (rcs->versions, branchpoint);
4900 if (nodep == NULL)
4902 error (0, 0, "%s: can't find branch point %s", rcs->print_path, branchpoint);
4903 free (branchpoint);
4904 return NULL;
4906 free (branchpoint);
4907 branchnode = nodep->data;
4909 /* If BRANCH was a full branch number, make sure it is higher than MAX. */
4910 if ((numdots (branch) & 1) == 1)
4912 if (branchnode->branches == NULL)
4914 /* We have to create the first branch on this node, which means
4915 appending ".2" to the revision number. */
4916 newrevnum = Xasprintf ("%s.2", branch);
4918 else
4920 char *max = max_rev (branchnode);
4921 assert (max);
4922 newrevnum = increment_revnum (max);
4923 free (max);
4926 else
4928 newrevnum = xstrdup (branch);
4930 if (branchnode->branches != NULL)
4932 Node *head;
4933 Node *bp;
4935 /* Find the position of this new branch in the sorted list
4936 of branches. */
4937 head = branchnode->branches->list;
4938 for (bp = head->next; bp != head; bp = bp->next)
4940 char *dot;
4941 int found_pos;
4943 /* The existing list must be sorted on increasing revnum. */
4944 assert (bp->next == head
4945 || compare_truncated_revnums (bp->key,
4946 bp->next->key) < 0);
4947 dot = truncate_revnum_in_place (bp->key);
4948 found_pos = (compare_revnums (branch, bp->key) < 0);
4949 *dot = '.';
4951 if (found_pos)
4953 break;
4956 marker = bp;
4960 newrevnum = xrealloc (newrevnum, strlen (newrevnum) + 3);
4961 strcat (newrevnum, ".1");
4963 /* Add this new revision number to BRANCHPOINT's branches list. */
4964 if (branchnode->branches == NULL)
4965 branchnode->branches = getlist();
4966 bp = getnode();
4967 bp->key = xstrdup (newrevnum);
4969 /* Append to the end of the list by default, that is, just before
4970 the header node, `list'. */
4971 if (marker == NULL)
4972 marker = branchnode->branches->list;
4975 int fail;
4976 fail = insert_before (branchnode->branches, marker, bp);
4977 assert (!fail);
4980 return newrevnum;
4985 /* Check in to RCSFILE with revision REV (which must be greater than
4986 the largest revision) and message MESSAGE (which is checked for
4987 validity). If FLAGS & RCS_FLAGS_DEAD, check in a dead revision.
4988 If FLAGS & RCS_FLAGS_QUIET, tell ci to be quiet. If FLAGS &
4989 RCS_FLAGS_MODTIME, use the working file's modification time for the
4990 checkin time. WORKFILE is the working file to check in from, or
4991 NULL to use the usual RCS rules for deriving it from the RCSFILE.
4992 If FLAGS & RCS_FLAGS_KEEPFILE, don't unlink the working file;
4993 unlinking the working file is standard RCS behavior, but is rarely
4994 appropriate for CVS.
4996 UPDATE_DIR is used to print the path for the file. This argument is
4997 unnecessary when FLAGS & RCS_FLAGS_QUIET since the path won't be printed
4998 anyhow.
5000 This function should almost exactly mimic the behavior of `rcs ci'. The
5001 principal point of difference is the support here for preserving file
5002 ownership and permissions in the delta nodes. This is not a clean
5003 solution -- precisely because it diverges from RCS's behavior -- but
5004 it doesn't seem feasible to do this anywhere else in the code. [-twp]
5006 Return value is -1 for error (and errno is set to indicate the
5007 error), positive for error (and an error message has been printed),
5008 or zero for success. */
5010 RCS_checkin (RCSNode *rcs, const char *update_dir, const char *workfile_in,
5011 const char *message, const char *rev, time_t citime, int flags)
5013 RCSVers *delta, *commitpt;
5014 Deltatext *dtext;
5015 Node *nodep;
5016 char *tmpfile, *changefile;
5017 int dargc = 0;
5018 size_t darg_allocated = 0;
5019 char **dargv = NULL;
5020 size_t bufsize;
5021 int status, checkin_quiet;
5022 struct tm *ftm;
5023 time_t modtime;
5024 int adding_branch = 0;
5025 char *workfile = xstrdup (workfile_in);
5026 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5027 struct stat sb;
5028 #endif
5029 Node *np;
5031 commitpt = NULL;
5033 if (rcs->flags & PARTIAL)
5034 RCS_reparsercsfile (rcs, NULL, NULL);
5036 /* Get basename of working file. Is there a library function to
5037 do this? I couldn't find one. -twp */
5038 if (workfile == NULL)
5040 char *p;
5041 int extlen = strlen (RCSEXT);
5042 assert (rcs->path);
5043 workfile = xstrdup (last_component (rcs->path));
5044 p = workfile + (strlen (workfile) - extlen);
5045 assert (strncmp (p, RCSEXT, extlen) == 0);
5046 *p = '\0';
5049 /* If the filename is a symbolic link, follow it and replace it
5050 with the destination of the link. We need to do this before
5051 calling rcs_internal_lockfile, or else we won't put the lock in
5052 the right place. */
5053 resolve_symlink (&(rcs->path));
5055 checkin_quiet = flags & RCS_FLAGS_QUIET;
5056 if (!(checkin_quiet || really_quiet))
5058 cvs_output (rcs->path, 0);
5059 cvs_output (" <-- ", 7);
5060 if (update_dir && strlen (update_dir))
5062 cvs_output (update_dir, 0);
5063 cvs_output ("/", 1);
5065 cvs_output (workfile, 0);
5066 cvs_output ("\n", 1);
5069 /* Create new delta node. */
5070 delta = xmalloc (sizeof (RCSVers));
5071 memset (delta, 0, sizeof (RCSVers));
5072 delta->author = xstrdup (getcaller ());
5073 if (flags & RCS_FLAGS_MODTIME)
5075 struct stat ws;
5076 if (stat (workfile, &ws) < 0)
5078 error (1, errno, "cannot stat %s", workfile);
5080 modtime = ws.st_mtime;
5082 else if (flags & RCS_FLAGS_USETIME)
5083 modtime = citime;
5084 else
5085 (void) time (&modtime);
5086 ftm = gmtime (&modtime);
5087 delta->date = Xasprintf (DATEFORM,
5088 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
5089 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
5090 ftm->tm_min, ftm->tm_sec);
5091 if (flags & RCS_FLAGS_DEAD)
5093 delta->state = xstrdup (RCSDEAD);
5094 delta->dead = 1;
5096 else
5097 delta->state = xstrdup ("Exp");
5099 delta->other_delta = getlist();
5101 /* save the commit ID */
5102 np = getnode();
5103 np->type = RCSFIELD;
5104 np->key = xstrdup ("commitid");
5105 np->data = xstrdup(global_session_id);
5106 addnode (delta->other_delta, np);
5109 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5110 /* If permissions should be preserved on this project, then
5111 save the permission info. */
5112 if (preserve_perms)
5114 Node *np;
5115 char buf[64]; /* static buffer should be safe: see usage. -twp */
5117 delta->other_delta = getlist();
5119 if (lstat (workfile, &sb) < 0)
5120 error (1, errno, "cannot lstat %s", workfile);
5122 if (S_ISLNK (sb.st_mode))
5124 np = getnode();
5125 np->type = RCSFIELD;
5126 np->key = xstrdup ("symlink");
5127 np->data = Xreadlink (workfile, sb.st_size);
5128 addnode (delta->other_delta, np);
5130 else
5132 (void) sprintf (buf, "%u", sb.st_uid);
5133 np = getnode();
5134 np->type = RCSFIELD;
5135 np->key = xstrdup ("owner");
5136 np->data = xstrdup (buf);
5137 addnode (delta->other_delta, np);
5139 (void) sprintf (buf, "%u", sb.st_gid);
5140 np = getnode();
5141 np->type = RCSFIELD;
5142 np->key = xstrdup ("group");
5143 np->data = xstrdup (buf);
5144 addnode (delta->other_delta, np);
5146 (void) sprintf (buf, "%o", sb.st_mode & 07777);
5147 np = getnode();
5148 np->type = RCSFIELD;
5149 np->key = xstrdup ("permissions");
5150 np->data = xstrdup (buf);
5151 addnode (delta->other_delta, np);
5153 /* Save device number. */
5154 switch (sb.st_mode & S_IFMT)
5156 case S_IFREG: break;
5157 case S_IFCHR:
5158 case S_IFBLK:
5159 # ifdef HAVE_STRUCT_STAT_ST_RDEV
5160 np = getnode();
5161 np->type = RCSFIELD;
5162 np->key = xstrdup ("special");
5163 sprintf (buf, "%s %lu",
5164 ((sb.st_mode & S_IFMT) == S_IFCHR
5165 ? "character" : "block"),
5166 (unsigned long) sb.st_rdev);
5167 np->data = xstrdup (buf);
5168 addnode (delta->other_delta, np);
5169 # else
5170 error (0, 0,
5171 "can't preserve %s: unable to save device files on this system",
5172 workfile);
5173 # endif
5174 break;
5176 default:
5177 error (0, 0, "special file %s has unknown type", workfile);
5180 /* Save hardlinks. */
5181 delta->hardlinks = list_linked_files_on_disk (workfile);
5184 #endif
5186 /* Create a new deltatext node. */
5187 dtext = xmalloc (sizeof (Deltatext));
5188 memset (dtext, 0, sizeof (Deltatext));
5190 dtext->log = make_message_rcsvalid (message);
5192 /* If the delta tree is empty, then there's nothing to link the
5193 new delta into. So make a new delta tree, snarf the working
5194 file contents, and just write the new RCS file. */
5195 if (rcs->head == NULL)
5197 char *newrev;
5198 FILE *fout;
5200 /* Figure out what the first revision number should be. */
5201 if (rev == NULL || *rev == '\0')
5202 newrev = xstrdup ("1.1");
5203 else if (numdots (rev) == 0)
5205 newrev = Xasprintf ("%s.1", rev);
5207 else
5208 newrev = xstrdup (rev);
5210 /* Don't need to xstrdup NEWREV because it's already dynamic, and
5211 not used for anything else. (Don't need to free it, either.) */
5212 rcs->head = newrev;
5213 delta->version = xstrdup (newrev);
5214 nodep = getnode();
5215 nodep->type = RCSVERS;
5216 nodep->delproc = rcsvers_delproc;
5217 nodep->data = delta;
5218 nodep->key = delta->version;
5219 (void) addnode (rcs->versions, nodep);
5221 dtext->version = xstrdup (newrev);
5222 bufsize = 0;
5223 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5224 if (preserve_perms && !S_ISREG (sb.st_mode))
5225 /* Pretend file is empty. */
5226 bufsize = 0;
5227 else
5228 #endif
5229 get_file (workfile, workfile,
5230 rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5231 &dtext->text, &bufsize, &dtext->len);
5233 if (!(checkin_quiet || really_quiet))
5235 cvs_output ("initial revision: ", 0);
5236 cvs_output (rcs->head, 0);
5237 cvs_output ("\n", 1);
5240 /* We are probably about to invalidate any cached file. */
5241 rcsbuf_cache_close ();
5243 fout = rcs_internal_lockfile (rcs->path);
5244 RCS_putadmin (rcs, fout);
5245 RCS_putdtree (rcs, rcs->head, fout);
5246 RCS_putdesc (rcs, fout);
5247 rcs->delta_pos = ftello (fout);
5248 if (rcs->delta_pos == -1)
5249 error (1, errno, "cannot ftello for %s", rcs->path);
5250 putdeltatext (fout, dtext);
5251 rcs_internal_unlockfile (fout, rcs->path);
5253 if ((flags & RCS_FLAGS_KEEPFILE) == 0)
5255 if (unlink_file (workfile) < 0)
5256 /* FIXME-update-dir: message does not include update_dir. */
5257 error (0, errno, "cannot remove %s", workfile);
5260 status = 0;
5261 goto checkin_done;
5264 /* Derive a new revision number. From the `ci' man page:
5266 "If rev is a revision number, it must be higher than the
5267 latest one on the branch to which rev belongs, or must
5268 start a new branch.
5270 If rev is a branch rather than a revision number, the new
5271 revision is appended to that branch. The level number is
5272 obtained by incrementing the tip revision number of that
5273 branch. If rev indicates a non-existing branch, that
5274 branch is created with the initial revision numbered
5275 rev.1."
5277 RCS_findlock_or_tip handles the case where REV is omitted.
5278 RCS 5.7 also permits REV to be "$" or to begin with a dot, but
5279 we do not address those cases -- every routine that calls
5280 RCS_checkin passes it a numeric revision. */
5282 if (rev == NULL || *rev == '\0')
5284 /* Figure out where the commit point is by looking for locks.
5285 If the commit point is at the tip of a branch (or is the
5286 head of the delta tree), then increment its revision number
5287 to obtain the new revnum. Otherwise, start a new
5288 branch. */
5289 commitpt = RCS_findlock_or_tip (rcs);
5290 if (commitpt == NULL)
5292 status = 1;
5293 goto checkin_done;
5295 else if (commitpt->next == NULL
5296 || STREQ (commitpt->version, rcs->head))
5297 delta->version = increment_revnum (commitpt->version);
5298 else
5299 delta->version = RCS_addbranch (rcs, commitpt->version);
5301 else
5303 /* REV is either a revision number or a branch number. Find the
5304 tip of the target branch. */
5305 char *branch, *tip, *newrev, *p;
5306 int dots, isrevnum;
5308 assert (isdigit ((unsigned char) *rev));
5310 newrev = xstrdup (rev);
5311 dots = numdots (newrev);
5312 isrevnum = dots & 1;
5314 branch = xstrdup (rev);
5315 if (isrevnum)
5317 p = strrchr (branch, '.');
5318 *p = '\0';
5321 /* Find the tip of the target branch. If we got a one- or two-digit
5322 revision number, this will be the head of the tree. Exception:
5323 if rev is a single-field revision equal to the branch number of
5324 the trunk (usually "1") then we want to treat it like an ordinary
5325 branch revision. */
5326 if (dots == 0)
5328 tip = xstrdup (rcs->head);
5329 if (atoi (tip) != atoi (branch))
5331 newrev = xrealloc (newrev, strlen (newrev) + 3);
5332 strcat (newrev, ".1");
5333 dots = isrevnum = 1;
5336 else if (dots == 1)
5337 tip = xstrdup (rcs->head);
5338 else
5339 tip = RCS_getbranch (rcs, branch, 1);
5341 /* If the branch does not exist, and we were supplied an exact
5342 revision number, signal an error. Otherwise, if we were
5343 given only a branch number, create it and set COMMITPT to
5344 the branch point. */
5345 if (tip == NULL)
5347 if (isrevnum)
5349 error (0, 0, "%s: can't find branch point %s",
5350 rcs->print_path, branch);
5351 free (branch);
5352 free (newrev);
5353 status = 1;
5354 goto checkin_done;
5356 delta->version = RCS_addbranch (rcs, branch);
5357 if (!delta->version)
5359 free (branch);
5360 free (newrev);
5361 status = 1;
5362 goto checkin_done;
5364 adding_branch = 1;
5365 p = strrchr (branch, '.');
5366 *p = '\0';
5367 tip = xstrdup (branch);
5369 else
5371 if (isrevnum)
5373 /* NEWREV must be higher than TIP. */
5374 if (compare_revnums (tip, newrev) >= 0)
5376 error (0, 0,
5377 "%s: revision %s too low; must be higher than %s",
5378 rcs->print_path,
5379 newrev, tip);
5380 free (branch);
5381 free (newrev);
5382 free (tip);
5383 status = 1;
5384 goto checkin_done;
5386 delta->version = xstrdup (newrev);
5388 else
5389 /* Just increment the tip number to get the new revision. */
5390 delta->version = increment_revnum (tip);
5393 nodep = findnode (rcs->versions, tip);
5394 commitpt = nodep->data;
5396 free (branch);
5397 free (newrev);
5398 free (tip);
5401 assert (delta->version != NULL);
5403 /* If COMMITPT is locked by us, break the lock. If it's locked
5404 by someone else, signal an error. */
5405 nodep = findnode (RCS_getlocks (rcs), commitpt->version);
5406 if (nodep != NULL)
5408 if (! STREQ (nodep->data, delta->author))
5410 /* If we are adding a branch, then leave the old lock around.
5411 That is sensible in the sense that when adding a branch,
5412 we don't need to use the lock to tell us where to check
5413 in. It is fishy in the sense that if it is our own lock,
5414 we break it. However, this is the RCS 5.7 behavior (at
5415 the end of addbranch in ci.c in RCS 5.7, it calls
5416 removelock only if it is our own lock, not someone
5417 else's). */
5419 if (!adding_branch)
5421 error (0, 0, "%s: revision %s locked by %s",
5422 rcs->print_path,
5423 nodep->key, (char *)nodep->data);
5424 status = 1;
5425 goto checkin_done;
5428 else
5429 delnode (nodep);
5432 dtext->version = xstrdup (delta->version);
5434 /* Obtain the change text for the new delta. If DELTA is to be the
5435 new head of the tree, then its change text should be the contents
5436 of the working file, and LEAFNODE's change text should be a diff.
5437 Else, DELTA's change text should be a diff between LEAFNODE and
5438 the working file. */
5440 tmpfile = cvs_temp_name();
5441 status = RCS_checkout (rcs, NULL, commitpt->version, NULL,
5442 ((rcs->expand != NULL
5443 && STREQ (rcs->expand, "b"))
5444 ? "-kb"
5445 : "-ko"),
5446 tmpfile,
5447 NULL, NULL);
5448 if (status != 0)
5449 error (1, 0,
5450 "could not check out revision %s of `%s'",
5451 commitpt->version, rcs->print_path);
5453 bufsize = 0;
5454 changefile = cvs_temp_name();
5456 /* Diff options should include --binary if the RCS file has -kb set
5457 in its `expand' field. */
5458 run_add_arg_p (&dargc, &darg_allocated, &dargv, "-a");
5459 run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
5460 if (rcs->expand != NULL && STREQ (rcs->expand, "b"))
5461 run_add_arg_p (&dargc, &darg_allocated, &dargv, "--binary");
5463 if (STREQ (commitpt->version, rcs->head) &&
5464 numdots (delta->version) == 1)
5466 /* If this revision is being inserted on the trunk, the change text
5467 for the new delta should be the contents of the working file ... */
5468 bufsize = 0;
5469 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5470 if (preserve_perms && !S_ISREG (sb.st_mode))
5471 /* Pretend file is empty. */
5473 else
5474 #endif
5475 get_file (workfile, workfile,
5476 rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5477 &dtext->text, &bufsize, &dtext->len);
5479 /* ... and the change text for the old delta should be a diff. */
5480 commitpt->text = xmalloc (sizeof (Deltatext));
5481 memset (commitpt->text, 0, sizeof (Deltatext));
5483 bufsize = 0;
5484 switch (diff_exec (workfile, tmpfile, NULL, NULL,
5485 dargc, dargv, changefile))
5487 case 0:
5488 case 1:
5489 break;
5490 case -1:
5491 /* FIXME-update-dir: message does not include update_dir. */
5492 error (1, errno, "error diffing %s", workfile);
5493 break;
5494 default:
5495 /* FIXME-update-dir: message does not include update_dir. */
5496 error (1, 0, "error diffing %s", workfile);
5497 break;
5500 /* OK, the text file case here is really dumb. Logically
5501 speaking we want diff to read the files in text mode,
5502 convert them to the canonical form found in RCS files
5503 (which, we hope at least, is independent of OS--always
5504 bare linefeeds), and then work with change texts in that
5505 format. However, diff_exec both generates change
5506 texts and produces output for user purposes (e.g. patch.c),
5507 and there is no way to distinguish between the two cases.
5508 So we actually implement the text file case by writing the
5509 change text as a text file, then reading it as a text file.
5510 This should cause no harm, but doesn't strike me as
5511 immensely clean. */
5512 get_file (changefile, changefile,
5513 rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5514 &commitpt->text->text, &bufsize, &commitpt->text->len);
5516 /* If COMMITPT->TEXT->TEXT is NULL, it means that CHANGEFILE
5517 was empty and that there are no differences between revisions.
5518 In that event, we want to force RCS_rewrite to write an empty
5519 string for COMMITPT's change text. Leaving the change text
5520 field set NULL won't work, since that means "preserve the original
5521 change text for this delta." */
5522 if (commitpt->text->text == NULL)
5524 commitpt->text->text = xstrdup ("");
5525 commitpt->text->len = 0;
5528 else
5530 /* This file is not being inserted at the head, but on a side
5531 branch somewhere. Make a diff from the previous revision
5532 to the working file. */
5533 switch (diff_exec (tmpfile, workfile, NULL, NULL,
5534 dargc, dargv, changefile))
5536 case 0:
5537 case 1:
5538 break;
5539 case -1:
5540 /* FIXME-update-dir: message does not include update_dir. */
5541 error (1, errno, "error diffing %s", workfile);
5542 break;
5543 default:
5544 /* FIXME-update-dir: message does not include update_dir. */
5545 error (1, 0, "error diffing %s", workfile);
5546 break;
5548 /* See the comment above, at the other get_file invocation,
5549 regarding binary vs. text. */
5550 get_file (changefile, changefile,
5551 rcs->expand != NULL && STREQ (rcs->expand, "b") ? "rb" : "r",
5552 &dtext->text, &bufsize,
5553 &dtext->len);
5554 if (dtext->text == NULL)
5556 dtext->text = xstrdup ("");
5557 dtext->len = 0;
5561 run_arg_free_p (dargc, dargv);
5562 free (dargv);
5564 /* Update DELTA linkage. It is important not to do this before
5565 the very end of RCS_checkin; if an error arises that forces
5566 us to abort checking in, we must not have malformed deltas
5567 partially linked into the tree.
5569 If DELTA and COMMITPT are on different branches, do nothing --
5570 DELTA is linked to the tree through COMMITPT->BRANCHES, and we
5571 don't want to change `next' pointers.
5573 Otherwise, if the nodes are both on the trunk, link DELTA to
5574 COMMITPT; otherwise, link COMMITPT to DELTA. */
5576 if (numdots (commitpt->version) == numdots (delta->version))
5578 if (STREQ (commitpt->version, rcs->head))
5580 delta->next = rcs->head;
5581 rcs->head = xstrdup (delta->version);
5583 else
5584 commitpt->next = xstrdup (delta->version);
5587 /* Add DELTA to RCS->VERSIONS. */
5588 if (rcs->versions == NULL)
5589 rcs->versions = getlist();
5590 nodep = getnode();
5591 nodep->type = RCSVERS;
5592 nodep->delproc = rcsvers_delproc;
5593 nodep->data = delta;
5594 nodep->key = delta->version;
5595 (void) addnode (rcs->versions, nodep);
5597 /* Write the new RCS file, inserting the new delta at COMMITPT. */
5598 if (!(checkin_quiet || really_quiet))
5600 cvs_output ("new revision: ", 14);
5601 cvs_output (delta->version, 0);
5602 cvs_output ("; previous revision: ", 21);
5603 cvs_output (commitpt->version, 0);
5604 cvs_output ("\n", 1);
5607 RCS_rewrite (rcs, dtext, commitpt->version);
5609 if ((flags & RCS_FLAGS_KEEPFILE) == 0)
5611 if (unlink_file (workfile) < 0)
5612 /* FIXME-update-dir: message does not include update_dir. */
5613 error (1, errno, "cannot remove %s", workfile);
5615 if (unlink_file (tmpfile) < 0)
5616 error (0, errno, "cannot remove %s", tmpfile);
5617 free (tmpfile);
5618 if (unlink_file (changefile) < 0)
5619 error (0, errno, "cannot remove %s", changefile);
5620 free (changefile);
5622 checkin_done:
5623 free (workfile);
5625 if (commitpt != NULL && commitpt->text != NULL)
5627 freedeltatext (commitpt->text);
5628 commitpt->text = NULL;
5631 freedeltatext (dtext);
5632 if (status != 0)
5634 /* If delta has not been added to a List, then freeing the Node key
5635 * won't free delta->version.
5637 if (delta->version) free (delta->version);
5638 free_rcsvers_contents (delta);
5641 return status;
5646 /* This structure is passed between RCS_cmp_file and cmp_file_buffer. */
5647 struct cmp_file_data
5649 const char *filename;
5650 FILE *fp;
5651 int different;
5654 /* Compare the contents of revision REV1 of RCS file RCS with the
5655 contents of REV2 if given, otherwise, compare with the contents of
5656 the file FILENAME. OPTIONS is a string for the keyword
5657 expansion options. Return 0 if the contents of the revision are
5658 the same as the contents of the file, 1 if they are different. */
5660 RCS_cmp_file (RCSNode *rcs, const char *rev1, char **rev1_cache,
5661 const char *rev2, const char *options, const char *filename)
5663 int binary;
5665 TRACE (TRACE_FUNCTION, "RCS_cmp_file( %s, %s, %s, %s, %s )",
5666 rcs->path ? rcs->path : "(null)",
5667 rev1 ? rev1 : "(null)", rev2 ? rev2 : "(null)",
5668 options ? options : "(null)", filename ? filename : "(null)");
5670 if (options != NULL && options[0] != '\0')
5671 binary = STREQ (options, "-kb");
5672 else
5674 char *expand;
5676 expand = RCS_getexpand (rcs);
5677 if (expand != NULL && STREQ (expand, "b"))
5678 binary = 1;
5679 else
5680 binary = 0;
5683 #ifdef PRESERVE_PERMISSIONS_SUPPORT
5684 /* If CVS is to deal properly with special files (when
5685 PreservePermissions is on), the best way is to check out the
5686 revision to a temporary file and call `xcmp' on the two disk
5687 files. xcmp needs to handle non-regular files properly anyway,
5688 so calling it simplifies RCS_cmp_file. We *could* just yank
5689 the delta node out of the version tree and look for device
5690 numbers, but writing to disk and calling xcmp is a better
5691 abstraction (therefore probably more robust). -twp */
5693 if (preserve_perms)
5695 char *tmp;
5696 int retcode;
5698 tmp = cvs_temp_name();
5699 retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL);
5700 if (retcode != 0)
5701 return 1;
5703 retcode = xcmp (tmp, filename);
5704 if (CVS_UNLINK (tmp) < 0)
5705 error (0, errno, "cannot remove %s", tmp);
5706 free (tmp);
5707 return retcode;
5709 else
5710 #endif
5712 FILE *fp;
5713 struct cmp_file_data data;
5714 const char *use_file1;
5715 char *tmpfile = NULL;
5717 if (rev2 != NULL)
5719 /* Open & cache rev1 */
5720 tmpfile = cvs_temp_name();
5721 if (RCS_checkout (rcs, NULL, rev1, NULL, options, tmpfile,
5722 NULL, NULL))
5723 error (1, errno,
5724 "cannot check out revision %s of %s",
5725 rev1, rcs->print_path);
5726 use_file1 = tmpfile;
5727 if (rev1_cache != NULL)
5728 *rev1_cache = tmpfile;
5730 else
5731 use_file1 = filename;
5733 fp = CVS_FOPEN (use_file1, binary ? FOPEN_BINARY_READ : "r");
5734 if (fp == NULL)
5735 /* FIXME-update-dir: should include update_dir in message. */
5736 error (1, errno, "cannot open file %s for comparing", use_file1);
5738 data.filename = use_file1;
5739 data.fp = fp;
5740 data.different = 0;
5742 if (RCS_checkout (rcs, NULL, rev2 ? rev2 : rev1, NULL, options,
5743 RUN_TTY, cmp_file_buffer, &data ))
5744 error (1, errno,
5745 "cannot check out revision %s of %s",
5746 rev2 ? rev2 : rev1, rcs->print_path);
5748 /* If we have not yet found a difference, make sure that we are at
5749 the end of the file. */
5750 if (!data.different)
5752 if (getc (fp) != EOF)
5753 data.different = 1;
5756 fclose (fp);
5757 if (rev1_cache == NULL && tmpfile)
5759 if (CVS_UNLINK (tmpfile ) < 0)
5760 error (0, errno, "cannot remove %s", tmpfile);
5761 free (tmpfile);
5764 return data.different;
5770 /* This is a subroutine of RCS_cmp_file. It is passed to
5771 RCS_checkout. */
5772 #define CMP_BUF_SIZE (8 * 1024)
5774 static void
5775 cmp_file_buffer (void *callerdat, const char *buffer, size_t len)
5777 struct cmp_file_data *data = callerdat;
5778 char *filebuf;
5780 /* If we've already found a difference, we don't need to check
5781 further. */
5782 if (data->different)
5783 return;
5785 filebuf = xmalloc (len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len);
5787 while (len > 0)
5789 size_t checklen;
5791 checklen = len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len;
5792 if (fread (filebuf, 1, checklen, data->fp) != checklen)
5794 if (ferror (data->fp))
5795 error (1, errno, "cannot read file %s for comparing",
5796 data->filename);
5797 data->different = 1;
5798 free (filebuf);
5799 return;
5802 if (memcmp (filebuf, buffer, checklen) != 0)
5804 data->different = 1;
5805 free (filebuf);
5806 return;
5809 buffer += checklen;
5810 len -= checklen;
5813 free (filebuf);
5818 /* For RCS file RCS, make symbolic tag TAG point to revision REV.
5819 This validates that TAG is OK for a user to use. Return value is
5820 -1 for error (and errno is set to indicate the error), positive for
5821 error (and an error message has been printed), or zero for success. */
5823 RCS_settag (RCSNode *rcs, const char *tag, const char *rev)
5825 List *symbols;
5826 Node *node;
5828 if (rcs->flags & PARTIAL)
5829 RCS_reparsercsfile (rcs, NULL, NULL);
5831 /* FIXME: This check should be moved to RCS_check_tag. There is no
5832 reason for it to be here. */
5833 if (STREQ (tag, TAG_BASE)
5834 || STREQ (tag, TAG_HEAD))
5836 /* Print the name of the tag might be considered redundant
5837 with the caller, which also prints it. Perhaps this helps
5838 clarify why the tag name is considered reserved, I don't
5839 know. */
5840 error (0, 0, "Attempt to add reserved tag name %s", tag);
5841 return 1;
5844 /* A revision number of NULL means use the head or default branch.
5845 If rev is not NULL, it may be a symbolic tag or branch number;
5846 expand it to the correct numeric revision or branch head. */
5847 if (rev == NULL)
5848 rev = rcs->branch ? rcs->branch : rcs->head;
5850 /* At this point rcs->symbol_data may not have been parsed.
5851 Calling RCS_symbols will force it to be parsed into a list
5852 which we can easily manipulate. */
5853 symbols = RCS_symbols (rcs);
5854 if (symbols == NULL)
5856 symbols = getlist ();
5857 rcs->symbols = symbols;
5859 node = findnode (symbols, tag);
5860 if (node != NULL)
5862 free (node->data);
5863 node->data = xstrdup (rev);
5865 else
5867 node = getnode ();
5868 node->key = xstrdup (tag);
5869 node->data = xstrdup (rev);
5870 (void)addnode_at_front (symbols, node);
5873 return 0;
5878 /* Delete the symbolic tag TAG from the RCS file RCS. Return 0 if
5879 the tag was found (and removed), or 1 if it was not present. (In
5880 either case, the tag will no longer be in RCS->SYMBOLS.) */
5882 RCS_deltag (RCSNode *rcs, const char *tag)
5884 List *symbols;
5885 Node *node;
5886 if (rcs->flags & PARTIAL)
5887 RCS_reparsercsfile (rcs, NULL, NULL);
5889 symbols = RCS_symbols (rcs);
5890 if (symbols == NULL)
5891 return 1;
5893 node = findnode (symbols, tag);
5894 if (node == NULL)
5895 return 1;
5897 delnode (node);
5899 return 0;
5904 /* Set the default branch of RCS to REV. */
5906 RCS_setbranch (RCSNode *rcs, const char *rev)
5908 if (rcs->flags & PARTIAL)
5909 RCS_reparsercsfile (rcs, NULL, NULL);
5911 if (rev && ! *rev)
5912 rev = NULL;
5914 if (rev == NULL && rcs->branch == NULL)
5915 return 0;
5916 if (rev != NULL && rcs->branch != NULL && STREQ (rev, rcs->branch))
5917 return 0;
5919 if (rcs->branch != NULL)
5920 free (rcs->branch);
5921 rcs->branch = xstrdup (rev);
5923 return 0;
5928 /* Lock revision REV. LOCK_QUIET is 1 to suppress output. FIXME:
5929 Most of the callers only call us because RCS_checkin still tends to
5930 like a lock (a relic of old behavior inherited from the RCS ci
5931 program). If we clean this up, only "cvs admin -l" will still need
5932 to call RCS_lock. */
5934 /* FIXME-twp: if a lock owned by someone else is broken, should this
5935 send mail to the lock owner? Prompt user? It seems like such an
5936 obscure situation for CVS as almost not worth worrying much
5937 about. */
5939 RCS_lock (RCSNode *rcs, const char *rev, int lock_quiet)
5941 List *locks;
5942 Node *p;
5943 char *user;
5944 char *xrev = NULL;
5946 if (rcs->flags & PARTIAL)
5947 RCS_reparsercsfile (rcs, NULL, NULL);
5949 locks = RCS_getlocks (rcs);
5950 if (locks == NULL)
5951 locks = rcs->locks = getlist();
5952 user = getcaller();
5954 /* A revision number of NULL means lock the head or default branch. */
5955 if (rev == NULL)
5956 xrev = RCS_head (rcs);
5957 else
5958 xrev = RCS_gettag (rcs, rev, 1, NULL);
5960 /* Make sure that the desired revision exists. Technically,
5961 we can update the locks list without even checking this,
5962 but RCS 5.7 did this. And it can't hurt. */
5963 if (xrev == NULL || findnode (rcs->versions, xrev) == NULL)
5965 if (!lock_quiet)
5966 error (0, 0, "%s: revision %s absent", rcs->print_path, rev);
5967 free (xrev);
5968 return 1;
5971 /* Is this rev already locked? */
5972 p = findnode (locks, xrev);
5973 if (p != NULL)
5975 if (STREQ (p->data, user))
5977 /* We already own the lock on this revision, so do nothing. */
5978 free (xrev);
5979 return 0;
5982 #if 0
5983 /* Well, first of all, "rev" below should be "xrev" to avoid
5984 core dumps. But more importantly, should we really be
5985 breaking the lock unconditionally? What CVS 1.9 does (via
5986 RCS) is to prompt "Revision 1.1 is already locked by fred.
5987 Do you want to break the lock? [ny](n): ". Well, we don't
5988 want to interact with the user (certainly not at the
5989 server/protocol level, and probably not in the command-line
5990 client), but isn't it more sensible to give an error and
5991 let the user run "cvs admin -u" if they want to break the
5992 lock? */
5994 /* Break the lock. */
5995 if (!lock_quiet)
5997 cvs_output (rev, 0);
5998 cvs_output (" unlocked\n", 0);
6000 delnode (p);
6001 #else
6002 error (1, 0, "Revision %s is already locked by %s",
6003 xrev, (char *)p->data);
6004 #endif
6007 /* Create a new lock. */
6008 p = getnode();
6009 p->key = xrev; /* already xstrdupped */
6010 p->data = xstrdup (getcaller());
6011 (void)addnode_at_front (locks, p);
6013 if (!lock_quiet)
6015 cvs_output (xrev, 0);
6016 cvs_output (" locked\n", 0);
6019 return 0;
6024 /* Unlock revision REV. UNLOCK_QUIET is 1 to suppress output. FIXME:
6025 Like RCS_lock, this can become a no-op if we do the checkin
6026 ourselves.
6028 If REV is not null and is locked by someone else, break their
6029 lock and notify them. It is an open issue whether RCS_unlock
6030 queries the user about whether or not to break the lock. */
6032 RCS_unlock (RCSNode *rcs, char *rev, int unlock_quiet)
6034 Node *lock;
6035 List *locks;
6036 char *user;
6037 char *xrev = NULL;
6039 user = getcaller();
6040 if (rcs->flags & PARTIAL)
6041 RCS_reparsercsfile (rcs, NULL, NULL);
6043 /* If rev is NULL, unlock the revision held by the caller; if more
6044 than one, make the user specify the revision explicitly. This
6045 differs from RCS which unlocks the latest revision (first in
6046 rcs->locks) held by the caller. */
6047 if (rev == NULL)
6049 Node *p;
6051 /* No-ops: attempts to unlock an empty tree or an unlocked file. */
6052 if (rcs->head == NULL)
6054 if (!unlock_quiet)
6055 cvs_outerr ("can't unlock an empty tree\n", 0);
6056 return 0;
6059 locks = RCS_getlocks (rcs);
6060 if (locks == NULL)
6062 if (!unlock_quiet)
6063 cvs_outerr ("No locks are set.\n", 0);
6064 return 0;
6067 lock = NULL;
6068 for (p = locks->list->next; p != locks->list; p = p->next)
6070 if (STREQ (p->data, user))
6072 if (lock != NULL)
6074 if (!unlock_quiet)
6075 error (0, 0, "\
6076 %s: multiple revisions locked by %s; please specify one", rcs->print_path, user);
6077 return 1;
6079 lock = p;
6082 if (lock == NULL)
6084 if (!unlock_quiet)
6085 error (0, 0, "No locks are set for %s.\n", user);
6086 return 0; /* no lock found, ergo nothing to do */
6088 xrev = xstrdup (lock->key);
6090 else
6092 xrev = RCS_gettag (rcs, rev, 1, NULL);
6093 if (xrev == NULL)
6095 error (0, 0, "%s: revision %s absent", rcs->print_path, rev);
6096 return 1;
6100 lock = findnode (RCS_getlocks (rcs), xrev);
6101 if (lock == NULL)
6103 /* This revision isn't locked. */
6104 free (xrev);
6105 return 0;
6108 if (! STREQ (lock->data, user))
6110 /* If the revision is locked by someone else, notify
6111 them. Note that this shouldn't ever happen if RCS_unlock
6112 is called with a NULL revision, since that means "whatever
6113 revision is currently locked by the caller." */
6114 char *repos, *workfile;
6115 if (!unlock_quiet)
6116 error (0, 0, "\
6117 %s: revision %s locked by %s; breaking lock", rcs->print_path, xrev,
6118 (char *)lock->data);
6119 repos = xstrdup (rcs->path);
6120 workfile = strrchr (repos, '/');
6121 *workfile++ = '\0';
6122 notify_do ('C', workfile, NULL, user, NULL, NULL, repos);
6123 free (repos);
6126 delnode (lock);
6127 if (!unlock_quiet)
6129 cvs_output (xrev, 0);
6130 cvs_output (" unlocked\n", 0);
6133 free (xrev);
6134 return 0;
6139 /* Add USER to the access list of RCS. Do nothing if already present.
6140 FIXME-twp: check syntax of USER to make sure it's a valid id. */
6142 void
6143 RCS_addaccess (RCSNode *rcs, char *user)
6145 char *access, *a;
6147 if (rcs->flags & PARTIAL)
6148 RCS_reparsercsfile (rcs, NULL, NULL);
6150 if (rcs->access == NULL)
6151 rcs->access = xstrdup (user);
6152 else
6154 access = xstrdup (rcs->access);
6155 for (a = strtok (access, " "); a != NULL; a = strtok (NULL, " "))
6157 if (STREQ (a, user))
6159 free (access);
6160 return;
6163 free (access);
6164 rcs->access = xrealloc (rcs->access,
6165 strlen (rcs->access) + strlen (user) + 2);
6166 strcat (rcs->access, " ");
6167 strcat (rcs->access, user);
6173 /* Remove USER from the access list of RCS. */
6174 void
6175 RCS_delaccess (RCSNode *rcs, char *user)
6177 char *p, *s;
6178 int ulen;
6180 if (rcs->flags & PARTIAL)
6181 RCS_reparsercsfile (rcs, NULL, NULL);
6183 if (rcs->access == NULL)
6184 return;
6186 if (user == NULL)
6188 free (rcs->access);
6189 rcs->access = NULL;
6190 return;
6193 p = rcs->access;
6194 ulen = strlen (user);
6195 while (p != NULL)
6197 if (strncmp (p, user, ulen) == 0 && (p[ulen] == '\0' || p[ulen] == ' '))
6198 break;
6199 p = strchr (p, ' ');
6200 if (p != NULL)
6201 ++p;
6204 if (p == NULL)
6205 return;
6207 s = p + ulen;
6208 while (*s != '\0')
6209 *p++ = *s++;
6210 *p = '\0';
6215 char *
6216 RCS_getaccess (RCSNode *rcs)
6218 if (rcs->flags & PARTIAL)
6219 RCS_reparsercsfile (rcs, NULL, NULL);
6221 return rcs->access;
6226 /* Return a nonzero value if the revision specified by ARG is found. */
6227 static int
6228 findtag (Node *node, void *arg)
6230 char *rev = arg;
6232 if (STREQ (node->data, rev))
6233 return 1;
6234 else
6235 return 0;
6240 /* Delete revisions between REV1 and REV2. The changes between the two
6241 revisions must be collapsed, and the result stored in the revision
6242 immediately preceding the lower one. Return 0 for successful completion,
6243 1 otherwise.
6245 Solution: check out the revision preceding REV1 and the revision
6246 following REV2. Use call_diff to find aggregate diffs between
6247 these two revisions, and replace the delta text for the latter one
6248 with the new aggregate diff. Alternatively, we could write a
6249 function that takes two change texts and combines them to produce a
6250 new change text, without checking out any revs or calling diff. It
6251 would be hairy, but so, so cool.
6253 If INCLUSIVE is set, then TAG1 and TAG2, if non-NULL, tell us to
6254 delete that revision as well (cvs admin -o tag1:tag2). If clear,
6255 delete up to but not including that revision (cvs admin -o tag1::tag2).
6256 This does not affect TAG1 or TAG2 being NULL; the meaning of the start
6257 point in ::tag2 and :tag2 is the same and likewise for end points. */
6259 RCS_delete_revs (RCSNode *rcs, char *tag1, char *tag2, int inclusive)
6261 char *next;
6262 Node *nodep;
6263 RCSVers *revp = NULL;
6264 RCSVers *beforep;
6265 int status, found;
6266 int save_noexec;
6268 char *branchpoint = NULL;
6269 char *rev1 = NULL;
6270 char *rev2 = NULL;
6271 int rev1_inclusive = inclusive;
6272 int rev2_inclusive = inclusive;
6273 char *before = NULL;
6274 char *after = NULL;
6275 char *beforefile = NULL;
6276 char *afterfile = NULL;
6277 char *outfile = NULL;
6279 if (tag1 == NULL && tag2 == NULL)
6280 return 0;
6282 /* Assume error status until everything is finished. */
6283 status = 1;
6285 /* Make sure both revisions exist. */
6286 if (tag1 != NULL)
6288 rev1 = RCS_gettag (rcs, tag1, 1, NULL);
6289 if (rev1 == NULL || (nodep = findnode (rcs->versions, rev1)) == NULL)
6291 error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path, tag1);
6292 goto delrev_done;
6295 if (tag2 != NULL)
6297 rev2 = RCS_gettag (rcs, tag2, 1, NULL);
6298 if (rev2 == NULL || (nodep = findnode (rcs->versions, rev2)) == NULL)
6300 error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path, tag2);
6301 goto delrev_done;
6305 /* If rev1 is on the trunk and rev2 is NULL, rev2 should be
6306 RCS->HEAD. (*Not* RCS_head(rcs), which may return rcs->branch
6307 instead.) We need to check this special case early, in order
6308 to make sure that rev1 and rev2 get ordered correctly. */
6309 if (rev2 == NULL && numdots (rev1) == 1)
6311 rev2 = xstrdup (rcs->head);
6312 rev2_inclusive = 1;
6315 if (rev2 == NULL)
6316 rev2_inclusive = 1;
6318 if (rev1 != NULL && rev2 != NULL)
6320 /* A range consisting of a branch number means the latest revision
6321 on that branch. */
6322 if (RCS_isbranch (rcs, rev1) && STREQ (rev1, rev2))
6324 char *tmp = RCS_getbranch (rcs, rev1, 0);
6325 free (rev1);
6326 free (rev2);
6327 rev1 = rev2 = tmp;
6329 else
6331 /* Make sure REV1 and REV2 are ordered correctly (in the
6332 same order as the next field). For revisions on the
6333 trunk, REV1 should be higher than REV2; for branches,
6334 REV1 should be lower. */
6335 /* Shouldn't we just be giving an error in the case where
6336 the user specifies the revisions in the wrong order
6337 (that is, always swap on the trunk, never swap on a
6338 branch, in the non-error cases)? It is not at all
6339 clear to me that users who specify -o 1.4:1.2 really
6340 meant to type -o 1.2:1.4, and the out of order usage
6341 has never been documented, either by cvs.texinfo or
6342 rcs(1). */
6343 char *temp;
6344 int temp_inclusive;
6345 if (numdots (rev1) == 1)
6347 if (compare_revnums (rev1, rev2) <= 0)
6349 temp = rev2;
6350 rev2 = rev1;
6351 rev1 = temp;
6353 temp_inclusive = rev2_inclusive;
6354 rev2_inclusive = rev1_inclusive;
6355 rev1_inclusive = temp_inclusive;
6358 else if (compare_revnums (rev1, rev2) > 0)
6360 temp = rev2;
6361 rev2 = rev1;
6362 rev1 = temp;
6364 temp_inclusive = rev2_inclusive;
6365 rev2_inclusive = rev1_inclusive;
6366 rev1_inclusive = temp_inclusive;
6371 /* Basically the same thing; make sure that the ordering is what we
6372 need. */
6373 if (rev1 == NULL)
6375 assert (rev2 != NULL);
6376 if (numdots (rev2) == 1)
6378 /* Swap rev1 and rev2. */
6379 int temp_inclusive;
6381 rev1 = rev2;
6382 rev2 = NULL;
6384 temp_inclusive = rev2_inclusive;
6385 rev2_inclusive = rev1_inclusive;
6386 rev1_inclusive = temp_inclusive;
6390 /* Put the revision number preceding the first one to delete into
6391 BEFORE (where "preceding" means according to the next field).
6392 If the first revision to delete is the first revision on its
6393 branch (e.g. 1.3.2.1), BEFORE should be the node on the trunk
6394 at which the branch is rooted. If the first revision to delete
6395 is the head revision of the trunk, set BEFORE to NULL.
6397 Note that because BEFORE may not be on the same branch as REV1,
6398 it is not very handy for navigating the revision tree. It's
6399 most useful just for checking out the revision preceding REV1. */
6400 before = NULL;
6401 branchpoint = RCS_getbranchpoint (rcs, rev1 != NULL ? rev1 : rev2);
6402 if (rev1 == NULL)
6404 rev1 = xstrdup (branchpoint);
6405 if (numdots (branchpoint) > 1)
6407 char *bp;
6408 bp = strrchr (branchpoint, '.');
6409 while (*--bp != '.')
6411 *bp = '\0';
6412 /* Note that this is exclusive, always, because the inclusive
6413 flag doesn't affect the meaning when rev1 == NULL. */
6414 before = xstrdup (branchpoint);
6415 *bp = '.';
6418 else if (! STREQ (rev1, branchpoint))
6420 /* Walk deltas from BRANCHPOINT on, looking for REV1. */
6421 nodep = findnode (rcs->versions, branchpoint);
6422 revp = nodep->data;
6423 while (revp->next != NULL && ! STREQ (revp->next, rev1))
6425 revp = nodep->data;
6426 nodep = findnode (rcs->versions, revp->next);
6428 if (revp->next == NULL)
6430 error (0, 0, "%s: Revision %s doesn't exist.", rcs->print_path, rev1);
6431 goto delrev_done;
6433 if (rev1_inclusive)
6434 before = xstrdup (revp->version);
6435 else
6437 before = rev1;
6438 nodep = findnode (rcs->versions, before);
6439 rev1 = xstrdup (((RCSVers *)nodep->data)->next);
6442 else if (!rev1_inclusive)
6444 before = rev1;
6445 nodep = findnode (rcs->versions, before);
6446 rev1 = xstrdup (((RCSVers *)nodep->data)->next);
6448 else if (numdots (branchpoint) > 1)
6450 /* Example: rev1 is "1.3.2.1", branchpoint is "1.3.2.1".
6451 Set before to "1.3". */
6452 char *bp;
6453 bp = strrchr (branchpoint, '.');
6454 while (*--bp != '.')
6456 *bp = '\0';
6457 before = xstrdup (branchpoint);
6458 *bp = '.';
6461 /* If any revision between REV1 and REV2 is locked or is a branch point,
6462 we can't delete that revision and must abort. */
6463 after = NULL;
6464 next = rev1;
6465 found = 0;
6466 while (!found && next != NULL)
6468 nodep = findnode (rcs->versions, next);
6469 revp = nodep->data;
6471 if (rev2 != NULL)
6472 found = STREQ (revp->version, rev2);
6473 next = revp->next;
6475 if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL)
6477 if (findnode (RCS_getlocks (rcs), revp->version))
6479 error (0, 0, "%s: can't remove locked revision %s",
6480 rcs->print_path,
6481 revp->version);
6482 goto delrev_done;
6484 if (revp->branches != NULL)
6486 error (0, 0, "%s: can't remove branch point %s",
6487 rcs->print_path,
6488 revp->version);
6489 goto delrev_done;
6492 /* Doing this only for the :: syntax is for compatibility.
6493 See cvs.texinfo for somewhat more discussion. */
6494 if (!inclusive
6495 && walklist (RCS_symbols (rcs), findtag, revp->version))
6497 /* We don't print which file this happens to on the theory
6498 that the caller will print the name of the file in a
6499 more useful fashion (fullname not rcs->path). */
6500 error (0, 0, "cannot remove revision %s because it has tags",
6501 revp->version);
6502 goto delrev_done;
6505 /* It's misleading to print the `deleting revision' output
6506 here, since we may not actually delete these revisions.
6507 But that's how RCS does it. Bleah. Someday this should be
6508 moved to the point where the revs are actually marked for
6509 deletion. -twp */
6510 cvs_output ("deleting revision ", 0);
6511 cvs_output (revp->version, 0);
6512 cvs_output ("\n", 1);
6516 if (rev2 == NULL)
6518 else if (found)
6520 if (rev2_inclusive)
6521 after = xstrdup (next);
6522 else
6523 after = xstrdup (revp->version);
6525 else if (!inclusive)
6527 /* In the case of an empty range, for example 1.2::1.2 or
6528 1.2::1.3, we want to just do nothing. */
6529 status = 0;
6530 goto delrev_done;
6532 else
6534 /* This looks fishy in the cases where tag1 == NULL or tag2 == NULL.
6535 Are those cases really impossible? */
6536 assert (tag1 != NULL);
6537 assert (tag2 != NULL);
6539 error (0, 0, "%s: invalid revision range %s:%s", rcs->print_path,
6540 tag1, tag2);
6541 goto delrev_done;
6544 if (after == NULL && before == NULL)
6546 /* The user is trying to delete all revisions. While an
6547 RCS file without revisions makes sense to RCS (e.g. the
6548 state after "rcs -i"), CVS has never been able to cope with
6549 it. So at least for now we just make this an error.
6551 We don't include rcs->path in the message since "cvs admin"
6552 already printed "RCS file:" and the name. */
6553 error (1, 0, "attempt to delete all revisions");
6556 /* The conditionals at this point get really hairy. Here is the
6557 general idea:
6559 IF before != NULL and after == NULL
6560 THEN don't check out any revisions, just delete them
6561 IF before == NULL and after != NULL
6562 THEN only check out after's revision, and use it for the new deltatext
6563 ELSE
6564 check out both revisions and diff -n them. This could use
6565 RCS_exec_rcsdiff with some changes, like being able
6566 to suppress diagnostic messages and to direct output. */
6568 if (after != NULL)
6570 char *diffbuf;
6571 size_t bufsize, len;
6573 #if defined (WOE32) && !defined (__CYGWIN32__)
6574 /* FIXME: This is an awful kludge, but at least until I have
6575 time to work on it a little more and test it, I'd rather
6576 give a fatal error than corrupt the file. I think that we
6577 need to use "-kb" and "--binary" and "rb" to get_file
6578 (probably can do it always, not just for binary files, if
6579 we are consistent between the RCS_checkout and the diff). */
6581 char *expand = RCS_getexpand (rcs);
6582 if (expand != NULL && STREQ (expand, "b"))
6583 error (1, 0,
6584 "admin -o not implemented yet for binary on this system");
6586 #endif /* WOE32 */
6588 afterfile = cvs_temp_name();
6589 status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
6590 NULL, NULL);
6591 if (status > 0)
6592 goto delrev_done;
6594 if (before == NULL)
6596 /* We are deleting revisions from the head of the tree,
6597 so must create a new head. */
6598 diffbuf = NULL;
6599 bufsize = 0;
6600 get_file (afterfile, afterfile, "r", &diffbuf, &bufsize, &len);
6602 save_noexec = noexec;
6603 noexec = 0;
6604 if (unlink_file (afterfile) < 0)
6605 error (0, errno, "cannot remove %s", afterfile);
6606 noexec = save_noexec;
6608 free (afterfile);
6609 afterfile = NULL;
6611 free (rcs->head);
6612 rcs->head = xstrdup (after);
6614 else
6616 int dargc = 0;
6617 size_t darg_allocated = 0;
6618 char **dargv = NULL;
6620 beforefile = cvs_temp_name();
6621 status = RCS_checkout (rcs, NULL, before, NULL, "-ko", beforefile,
6622 NULL, NULL);
6623 if (status > 0)
6624 goto delrev_done;
6626 outfile = cvs_temp_name();
6627 run_add_arg_p (&dargc, &darg_allocated, &dargv, "-a");
6628 run_add_arg_p (&dargc, &darg_allocated, &dargv, "-n");
6629 status = diff_exec (beforefile, afterfile, NULL, NULL,
6630 dargc, dargv, outfile);
6631 run_arg_free_p (dargc, dargv);
6632 free (dargv);
6634 if (status == 2)
6636 /* Not sure we need this message; will diff_exec already
6637 have printed an error? */
6638 error (0, 0, "%s: could not diff", rcs->print_path);
6639 status = 1;
6640 goto delrev_done;
6643 diffbuf = NULL;
6644 bufsize = 0;
6645 get_file (outfile, outfile, "r", &diffbuf, &bufsize, &len);
6648 /* Save the new change text in after's delta node. */
6649 nodep = findnode (rcs->versions, after);
6650 revp = nodep->data;
6652 assert (revp->text == NULL);
6654 revp->text = xmalloc (sizeof (Deltatext));
6655 memset (revp->text, 0, sizeof (Deltatext));
6656 revp->text->version = xstrdup (revp->version);
6657 revp->text->text = diffbuf;
6658 revp->text->len = len;
6660 /* If DIFFBUF is NULL, it means that OUTFILE is empty and that
6661 there are no differences between the two revisions. In that
6662 case, we want to force RCS_copydeltas to write an empty string
6663 for the new change text (leaving the text field set NULL
6664 means "preserve the original change text for this delta," so
6665 we don't want that). */
6666 if (revp->text->text == NULL)
6667 revp->text->text = xstrdup ("");
6670 /* Walk through the revisions (again) to mark each one as
6671 outdated. (FIXME: would it be safe to use the `dead' field for
6672 this? Doubtful.) */
6673 for (next = rev1;
6674 next != NULL && (after == NULL || ! STREQ (next, after));
6675 next = revp->next)
6677 nodep = findnode (rcs->versions, next);
6678 revp = nodep->data;
6679 revp->outdated = 1;
6682 /* Update delta links. If BEFORE == NULL, we're changing the
6683 head of the tree and don't need to update any `next' links. */
6684 if (before != NULL)
6686 /* If REV1 is the first node on its branch, then BEFORE is its
6687 root node (on the trunk) and we have to update its branches
6688 list. Otherwise, BEFORE is on the same branch as AFTER, and
6689 we can just change BEFORE's `next' field to point to AFTER.
6690 (This should be safe: since findnode manages its lists via
6691 the `hashnext' and `hashprev' fields, rather than `next' and
6692 `prev', mucking with `next' and `prev' should not corrupt the
6693 delta tree's internal structure. Much. -twp) */
6695 if (rev1 == NULL)
6696 /* beforep's ->next field already should be equal to after,
6697 which I think is always NULL in this case. */
6699 else if (STREQ (rev1, branchpoint))
6701 nodep = findnode (rcs->versions, before);
6702 revp = nodep->data;
6703 nodep = revp->branches->list->next;
6704 while (nodep != revp->branches->list &&
6705 ! STREQ (nodep->key, rev1))
6706 nodep = nodep->next;
6707 assert (nodep != revp->branches->list);
6708 if (after == NULL)
6709 delnode (nodep);
6710 else
6712 free (nodep->key);
6713 nodep->key = xstrdup (after);
6716 else
6718 nodep = findnode (rcs->versions, before);
6719 beforep = nodep->data;
6720 free (beforep->next);
6721 beforep->next = xstrdup (after);
6725 status = 0;
6727 delrev_done:
6728 if (rev1 != NULL)
6729 free (rev1);
6730 if (rev2 && rev2 != rev1)
6731 free (rev2);
6732 if (branchpoint != NULL)
6733 free (branchpoint);
6734 if (before != NULL)
6735 free (before);
6736 if (after != NULL)
6737 free (after);
6739 save_noexec = noexec;
6740 noexec = 0;
6741 if (beforefile != NULL)
6743 if (unlink_file (beforefile) < 0)
6744 error (0, errno, "cannot remove %s", beforefile);
6745 free (beforefile);
6747 if (afterfile != NULL)
6749 if (unlink_file (afterfile) < 0)
6750 error (0, errno, "cannot remove %s", afterfile);
6751 free (afterfile);
6753 if (outfile != NULL)
6755 if (unlink_file (outfile) < 0)
6756 error (0, errno, "cannot remove %s", outfile);
6757 free (outfile);
6759 noexec = save_noexec;
6761 return status;
6767 * TRUE if there exists a symbolic tag "tag" in file.
6769 int
6770 RCS_exist_tag (RCSNode *rcs, char *tag)
6773 assert (rcs != NULL);
6775 if (findnode (RCS_symbols (rcs), tag))
6776 return 1;
6777 return 0;
6784 * TRUE if RCS revision number "rev" exists.
6785 * This includes magic branch revisions, not found in rcs->versions,
6786 * but only in rcs->symbols, requiring a list walk to find them.
6787 * Take advantage of list walk callback function already used by
6788 * RCS_delete_revs, above.
6791 RCS_exist_rev (RCSNode *rcs, char *rev)
6794 assert (rcs != NULL);
6796 if (rcs->flags & PARTIAL)
6797 RCS_reparsercsfile (rcs, NULL, NULL);
6799 if (findnode(rcs->versions, rev) != 0)
6800 return 1;
6802 if (walklist (RCS_symbols(rcs), findtag, rev) != 0)
6803 return 1;
6805 return 0;
6812 /* RCS_deltas and friends. Processing of the deltas in RCS files. */
6813 struct line
6815 /* Text of this line. Part of the same malloc'd block as the struct
6816 line itself (we probably should use the "struct hack" (char text[1])
6817 and save ourselves sizeof (char *) bytes). Does not include \n;
6818 instead has_newline indicates the presence or absence of \n. */
6819 char *text;
6820 /* Length of this line, not counting \n if has_newline is true. */
6821 size_t len;
6822 /* Version in which it was introduced. */
6823 RCSVers *vers;
6824 /* Nonzero if this line ends with \n. This will always be true
6825 except possibly for the last line. */
6826 int has_newline;
6827 /* Number of pointers to this struct line. */
6828 int refcount;
6831 struct linevector
6833 /* How many lines in use for this linevector? */
6834 unsigned int nlines;
6835 /* How many lines allocated for this linevector? */
6836 unsigned int lines_alloced;
6837 /* Pointer to array containing a pointer to each line. */
6838 struct line **vector;
6843 /* Initialize *VEC to be a linevector with no lines. */
6844 static void
6845 linevector_init (struct linevector *vec)
6847 vec->lines_alloced = 0;
6848 vec->nlines = 0;
6849 vec->vector = NULL;
6854 /* Given some text TEXT, add each of its lines to VEC before line POS
6855 (where line 0 is the first line). The last line in TEXT may or may
6856 not be \n terminated.
6857 Set the version for each of the new lines to VERS. This
6858 function returns non-zero for success. It returns zero if the line
6859 number is out of range.
6861 Each of the lines in TEXT are copied to space which is managed with
6862 the linevector (and freed by linevector_free). So the caller doesn't
6863 need to keep TEXT around after the call to this function. */
6864 static int
6865 linevector_add (struct linevector *vec, const char *text, size_t len,
6866 RCSVers *vers, unsigned int pos)
6868 const char *textend;
6869 unsigned int i;
6870 unsigned int nnew;
6871 const char *p;
6872 const char *nextline_text;
6873 size_t nextline_len;
6874 int nextline_newline;
6875 struct line *q;
6877 if (len == 0)
6878 return 1;
6880 textend = text + len;
6882 /* Count the number of lines we will need to add. */
6883 nnew = 1;
6884 for (p = text; p < textend; ++p)
6885 if (*p == '\n' && p + 1 < textend)
6886 ++nnew;
6888 /* Expand VEC->VECTOR if needed. */
6889 if (vec->nlines + nnew >= vec->lines_alloced)
6891 if (vec->lines_alloced == 0)
6892 vec->lines_alloced = 10;
6893 while (vec->nlines + nnew >= vec->lines_alloced)
6894 vec->lines_alloced *= 2;
6895 vec->vector = xnrealloc (vec->vector,
6896 vec->lines_alloced, sizeof (*vec->vector));
6899 /* Make room for the new lines in VEC->VECTOR. */
6900 for (i = vec->nlines + nnew - 1; i >= pos + nnew; --i)
6901 vec->vector[i] = vec->vector[i - nnew];
6903 if (pos > vec->nlines)
6904 return 0;
6906 /* Actually add the lines, to VEC->VECTOR. */
6907 i = pos;
6908 nextline_text = text;
6909 nextline_newline = 0;
6910 for (p = text; p < textend; ++p)
6911 if (*p == '\n')
6913 nextline_newline = 1;
6914 if (p + 1 == textend)
6915 /* If there are no characters beyond the last newline, we
6916 don't consider it another line. */
6917 break;
6918 nextline_len = p - nextline_text;
6919 q = xmalloc (sizeof (struct line) + nextline_len);
6920 q->vers = vers;
6921 q->text = (char *)q + sizeof (struct line);
6922 q->len = nextline_len;
6923 q->has_newline = nextline_newline;
6924 q->refcount = 1;
6925 memcpy (q->text, nextline_text, nextline_len);
6926 vec->vector[i++] = q;
6928 nextline_text = (char *)p + 1;
6929 nextline_newline = 0;
6931 nextline_len = p - nextline_text;
6932 q = xmalloc (sizeof (struct line) + nextline_len);
6933 q->vers = vers;
6934 q->text = (char *)q + sizeof (struct line);
6935 q->len = nextline_len;
6936 q->has_newline = nextline_newline;
6937 q->refcount = 1;
6938 memcpy (q->text, nextline_text, nextline_len);
6939 vec->vector[i] = q;
6941 vec->nlines += nnew;
6943 return 1;
6948 /* Remove NLINES lines from VEC at position POS (where line 0 is the
6949 first line). */
6950 static void
6951 linevector_delete (struct linevector *vec, unsigned int pos,
6952 unsigned int nlines)
6954 unsigned int i;
6955 unsigned int last;
6957 last = vec->nlines - nlines;
6958 for (i = pos; i < pos + nlines; ++i)
6960 if (--vec->vector[i]->refcount == 0)
6961 free (vec->vector[i]);
6963 for (i = pos; i < last; ++i)
6964 vec->vector[i] = vec->vector[i + nlines];
6965 vec->nlines -= nlines;
6970 /* Copy FROM to TO, copying the vectors but not the lines pointed to. */
6971 static void
6972 linevector_copy (struct linevector *to, struct linevector *from)
6974 unsigned int ln;
6976 for (ln = 0; ln < to->nlines; ++ln)
6978 if (--to->vector[ln]->refcount == 0)
6979 free (to->vector[ln]);
6981 if (from->nlines > to->lines_alloced)
6983 if (to->lines_alloced == 0)
6984 to->lines_alloced = 10;
6985 while (from->nlines > to->lines_alloced)
6986 to->lines_alloced *= 2;
6987 to->vector = xnrealloc (to->vector,
6988 to->lines_alloced,
6989 sizeof (*to->vector));
6991 memcpy (to->vector, from->vector,
6992 /* XXX: wrong int cast to avoid gcc warning */
6993 xtimes ((int)from->nlines, sizeof (*to->vector)));
6994 to->nlines = from->nlines;
6995 for (ln = 0; ln < to->nlines; ++ln)
6996 ++to->vector[ln]->refcount;
7001 /* Free storage associated with linevector. */
7002 static void
7003 linevector_free (struct linevector *vec)
7005 unsigned int ln;
7007 if (vec->vector != NULL)
7009 for (ln = 0; ln < vec->nlines; ++ln)
7010 if (--vec->vector[ln]->refcount == 0)
7011 free (vec->vector[ln]);
7013 free (vec->vector);
7019 /* Given a textual string giving the month (1-12), terminated with any
7020 character not recognized by atoi, return the 3 character name to
7021 print it with. I do not think it is a good idea to change these
7022 strings based on the locale; they are standard abbreviations (for
7023 example in rfc822 mail messages) which should be widely understood.
7024 Returns a pointer into static readonly storage. */
7025 static const char *
7026 month_printname (const char *month)
7028 static const char *const months[] =
7029 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
7030 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
7031 int mnum;
7033 mnum = atoi (month);
7034 if (mnum < 1 || mnum > 12)
7035 return "???";
7036 return months[mnum - 1];
7041 /* Apply changes to the line vector LINES. DIFFBUF is a buffer of
7042 length DIFFLEN holding the change text from an RCS file (the output
7043 of diff -n). NAME is used in error messages. The VERS field of
7044 any line added is set to ADDVERS. The VERS field of any line
7045 deleted is set to DELVERS, unless DELVERS is NULL, in which case
7046 the VERS field of deleted lines is unchanged. The function returns
7047 non-zero if the change text is applied successfully. It returns
7048 zero if the change text does not appear to apply to LINES (e.g., a
7049 line number is invalid). If the change text is improperly
7050 formatted (e.g., it is not the output of diff -n), the function
7051 calls error with a status of 1, causing the program to exit. */
7052 static int
7053 apply_rcs_changes (struct linevector *lines, const char *diffbuf,
7054 size_t difflen, const char *name, RCSVers *addvers,
7055 RCSVers *delvers)
7057 const char *p;
7058 const char *q;
7059 int op;
7060 /* The RCS format throws us for a loop in that the deltafrags (if
7061 we define a deltafrag as an add or a delete) need to be applied
7062 in reverse order. So we stick them into a linked list. */
7063 struct deltafrag {
7064 enum {FRAG_ADD, FRAG_DELETE} type;
7065 unsigned long pos;
7066 unsigned long nlines;
7067 const char *new_lines;
7068 size_t len;
7069 struct deltafrag *next;
7071 struct deltafrag *dfhead;
7072 struct deltafrag *df;
7073 int err;
7075 dfhead = NULL;
7076 for (p = diffbuf; p != NULL && p < diffbuf + difflen; )
7078 op = *p++;
7079 if (op != 'a' && op != 'd')
7080 /* Can't just skip over the deltafrag, because the value
7081 of op determines the syntax. */
7082 error (1, 0, "unrecognized operation '\\x%x' in %s",
7083 op, name);
7084 df = xmalloc (sizeof (struct deltafrag));
7085 df->next = dfhead;
7086 dfhead = df;
7087 df->pos = strtoul (p, (char **) &q, 10);
7089 if (p == q)
7090 error (1, 0, "number expected in %s", name);
7091 p = q;
7092 if (*p++ != ' ')
7093 error (1, 0, "space expected in %s", name);
7094 df->nlines = strtoul (p, (char **) &q, 10);
7095 if (p == q)
7096 error (1, 0, "number expected in %s", name);
7097 p = q;
7098 if (*p++ != '\012')
7099 error (1, 0, "linefeed expected in %s", name);
7101 if (op == 'a')
7103 unsigned int i;
7105 df->type = FRAG_ADD;
7106 i = df->nlines;
7107 /* The text we want is the number of lines specified, or
7108 until the end of the value, whichever comes first (it
7109 will be the former except in the case where we are
7110 adding a line which does not end in newline). */
7111 for (q = p; i != 0; ++q)
7112 if (*q == '\n')
7113 --i;
7114 else if (q == diffbuf + difflen)
7116 if (i != 1)
7117 error (1, 0, "premature end of change in %s", name);
7118 else
7119 break;
7122 /* Stash away a pointer to the text we are adding. */
7123 df->new_lines = p;
7124 df->len = q - p;
7126 p = q;
7128 else
7130 /* Correct for the fact that line numbers in RCS files
7131 start with 1. */
7132 --df->pos;
7134 assert (op == 'd');
7135 df->type = FRAG_DELETE;
7139 err = 0;
7140 for (df = dfhead; df != NULL;)
7142 unsigned int ln;
7144 /* Once an error is encountered, just free the rest of the list and
7145 * return.
7147 if (!err)
7148 switch (df->type)
7150 case FRAG_ADD:
7151 if (! linevector_add (lines, df->new_lines, df->len, addvers,
7152 df->pos))
7153 err = 1;
7154 break;
7155 case FRAG_DELETE:
7156 if (df->pos > lines->nlines
7157 || df->pos + df->nlines > lines->nlines)
7158 return 0;
7159 if (delvers != NULL)
7160 for (ln = df->pos; ln < df->pos + df->nlines; ++ln)
7161 lines->vector[ln]->vers = delvers;
7162 linevector_delete (lines, df->pos, df->nlines);
7163 break;
7166 df = df->next;
7167 free (dfhead);
7168 dfhead = df;
7171 return !err;
7176 /* Apply an RCS change text to a buffer. The function name starts
7177 with rcs rather than RCS because this does not take an RCSNode
7178 argument. NAME is used in error messages. TEXTBUF is the text
7179 buffer to change, and TEXTLEN is the size. DIFFBUF and DIFFLEN are
7180 the change buffer and size. The new buffer is returned in *RETBUF
7181 and *RETLEN. The new buffer is allocated by xmalloc.
7183 Return 1 for success. On failure, call error and return 0. */
7185 rcs_change_text (const char *name, char *textbuf, size_t textlen,
7186 const char *diffbuf, size_t difflen, char **retbuf,
7187 size_t *retlen)
7189 struct linevector lines;
7190 int ret;
7192 *retbuf = NULL;
7193 *retlen = 0;
7195 linevector_init (&lines);
7197 if (! linevector_add (&lines, textbuf, textlen, NULL, 0))
7198 error (1, 0, "cannot initialize line vector");
7200 if (! apply_rcs_changes (&lines, diffbuf, difflen, name, NULL, NULL))
7202 error (0, 0, "invalid change text in %s", name);
7203 ret = 0;
7205 else
7207 char *p;
7208 size_t n;
7209 unsigned int ln;
7211 n = 0;
7212 for (ln = 0; ln < lines.nlines; ++ln)
7213 /* 1 for \n */
7214 n += lines.vector[ln]->len + 1;
7216 p = xmalloc (n);
7217 *retbuf = p;
7219 for (ln = 0; ln < lines.nlines; ++ln)
7221 memcpy (p, lines.vector[ln]->text, lines.vector[ln]->len);
7222 p += lines.vector[ln]->len;
7223 if (lines.vector[ln]->has_newline)
7224 *p++ = '\n';
7227 *retlen = p - *retbuf;
7228 assert (*retlen <= n);
7230 ret = 1;
7233 linevector_free (&lines);
7235 return ret;
7240 /* Walk the deltas in RCS to get to revision VERSION.
7242 If OP is RCS_ANNOTATE, then write annotations using cvs_output.
7244 If OP is RCS_FETCH, then put the contents of VERSION into a
7245 newly-malloc'd array and put a pointer to it in *TEXT. Each line
7246 is \n terminated; the caller is responsible for converting text
7247 files if desired. The total length is put in *LEN.
7249 If FP is non-NULL, it should be a file descriptor open to the file
7250 RCS with file position pointing to the deltas. We close the file
7251 when we are done.
7253 If LOG is non-NULL, then *LOG is set to the log message of VERSION,
7254 and *LOGLEN is set to the length of the log message.
7256 On error, give a fatal error. */
7257 void
7258 RCS_deltas (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf,
7259 const char *version, enum rcs_delta_op op, char **text,
7260 size_t *len, char **log, size_t *loglen)
7262 struct rcsbuffer rcsbuf_local;
7263 char *branchversion;
7264 char *cpversion;
7265 char *key;
7266 char *value;
7267 size_t vallen;
7268 RCSVers *vers;
7269 RCSVers *prev_vers;
7270 RCSVers *trunk_vers;
7271 char *next;
7272 int ishead, isnext, isversion, onbranch;
7273 Node *node;
7274 struct linevector headlines;
7275 struct linevector curlines;
7276 struct linevector trunklines;
7277 int foundhead;
7279 assert (version);
7281 if (fp == NULL)
7283 rcsbuf_cache_open (rcs, rcs->delta_pos, &fp, &rcsbuf_local);
7284 rcsbuf = &rcsbuf_local;
7287 if (log) *log = NULL;
7289 ishead = 1;
7290 vers = NULL;
7291 prev_vers = NULL;
7292 trunk_vers = NULL;
7293 next = NULL;
7294 onbranch = 0;
7295 foundhead = 0;
7297 linevector_init (&curlines);
7298 linevector_init (&headlines);
7299 linevector_init (&trunklines);
7301 /* We set BRANCHVERSION to the version we are currently looking
7302 for. Initially, this is the version on the trunk from which
7303 VERSION branches off. If VERSION is not a branch, then
7304 BRANCHVERSION is just VERSION. */
7305 branchversion = xstrdup (version);
7306 cpversion = strchr (branchversion, '.');
7307 if (cpversion != NULL)
7308 cpversion = strchr (cpversion + 1, '.');
7309 if (cpversion != NULL)
7310 *cpversion = '\0';
7312 do {
7313 if (! rcsbuf_getrevnum (rcsbuf, &key))
7314 error (1, 0, "unexpected EOF reading RCS file %s", rcs->print_path);
7316 if (next != NULL && ! STREQ (next, key))
7318 /* This is not the next version we need. It is a branch
7319 version which we want to ignore. */
7320 isnext = 0;
7321 isversion = 0;
7323 else
7325 isnext = 1;
7327 /* look up the revision */
7328 node = findnode (rcs->versions, key);
7329 if (node == NULL)
7330 error (1, 0,
7331 "mismatch in rcs file %s between deltas and deltatexts (%s)",
7332 rcs->print_path, key);
7334 /* Stash the previous version. */
7335 prev_vers = vers;
7337 vers = node->data;
7338 next = vers->next;
7340 /* Compare key and trunkversion now, because key points to
7341 storage controlled by rcsbuf_getkey. */
7342 if (STREQ (branchversion, key))
7343 isversion = 1;
7344 else
7345 isversion = 0;
7348 while (1)
7350 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7351 error (1, 0, "%s does not appear to be a valid rcs file",
7352 rcs->print_path);
7354 if (log != NULL
7355 && isversion
7356 && STREQ (key, "log")
7357 && STREQ (branchversion, version))
7359 if (*log != NULL)
7361 error (0, 0, "Duplicate `log' keyword in RCS file (`%s').",
7362 rcs->print_path);
7363 free (*log);
7365 *log = rcsbuf_valcopy (rcsbuf, value, 0, loglen);
7368 if (STREQ (key, "text"))
7370 rcsbuf_valpolish (rcsbuf, value, 0, &vallen);
7371 if (ishead)
7373 if (! linevector_add (&curlines, value, vallen, NULL, 0))
7374 error (1, 0, "invalid rcs file %s", rcs->print_path);
7376 ishead = 0;
7378 else if (isnext)
7380 if (! apply_rcs_changes (&curlines, value, vallen,
7381 rcs->path,
7382 onbranch ? vers : NULL,
7383 onbranch ? NULL : prev_vers))
7384 error (1, 0, "invalid change text in %s", rcs->print_path);
7386 break;
7390 if (isversion)
7392 /* This is either the version we want, or it is the
7393 branchpoint to the version we want. */
7394 if (STREQ (branchversion, version))
7396 /* This is the version we want. */
7397 linevector_copy (&headlines, &curlines);
7398 foundhead = 1;
7399 if (onbranch)
7401 /* We have found this version by tracking up a
7402 branch. Restore back to the lines we saved
7403 when we left the trunk, and continue tracking
7404 down the trunk. */
7405 onbranch = 0;
7406 vers = trunk_vers;
7407 next = vers->next;
7408 linevector_copy (&curlines, &trunklines);
7411 else
7413 Node *p;
7415 /* We need to look up the branch. */
7416 onbranch = 1;
7418 if (numdots (branchversion) < 2)
7420 unsigned int ln;
7422 /* We are leaving the trunk; save the current
7423 lines so that we can restore them when we
7424 continue tracking down the trunk. */
7425 trunk_vers = vers;
7426 linevector_copy (&trunklines, &curlines);
7428 /* Reset the version information we have
7429 accumulated so far. It only applies to the
7430 changes from the head to this version. */
7431 for (ln = 0; ln < curlines.nlines; ++ln)
7432 curlines.vector[ln]->vers = NULL;
7435 /* The next version we want is the entry on
7436 VERS->branches which matches this branch. For
7437 example, suppose VERSION is 1.21.4.3 and
7438 BRANCHVERSION was 1.21. Then we look for an entry
7439 starting with "1.21.4" and we'll put it (probably
7440 1.21.4.1) in NEXT. We'll advance BRANCHVERSION by
7441 two dots (in this example, to 1.21.4.3). */
7443 if (vers->branches == NULL)
7444 error (1, 0, "missing expected branches in %s",
7445 rcs->print_path);
7446 if (!cpversion)
7447 error (1, 0, "Invalid revision number in `%s'.",
7448 rcs->print_path);
7449 *cpversion = '.';
7450 ++cpversion;
7451 cpversion = strchr (cpversion, '.');
7452 if (cpversion == NULL)
7453 error (1, 0, "version number confusion in %s",
7454 rcs->print_path);
7455 for (p = vers->branches->list->next;
7456 p != vers->branches->list;
7457 p = p->next)
7458 if (strncmp (p->key, branchversion,
7459 cpversion - branchversion) == 0)
7460 break;
7461 if (p == vers->branches->list)
7462 error (1, 0, "missing expected branch in %s",
7463 rcs->print_path);
7465 next = p->key;
7467 cpversion = strchr (cpversion + 1, '.');
7468 if (cpversion != NULL)
7469 *cpversion = '\0';
7472 if (op == RCS_FETCH && foundhead)
7473 break;
7474 } while (next != NULL);
7476 free (branchversion);
7478 rcsbuf_cache (rcs, rcsbuf);
7480 if (! foundhead)
7481 error (1, 0, "could not find desired version %s in %s",
7482 version, rcs->print_path);
7484 /* Now print out or return the data we have just computed. */
7485 switch (op)
7487 case RCS_ANNOTATE:
7489 unsigned int ln;
7491 for (ln = 0; ln < headlines.nlines; ++ln)
7493 char *buf;
7494 /* Period which separates year from month in date. */
7495 char *ym;
7496 /* Period which separates month from day in date. */
7497 char *md;
7498 RCSVers *prvers;
7500 prvers = headlines.vector[ln]->vers;
7501 if (prvers == NULL)
7502 prvers = vers;
7504 buf = xmalloc (strlen (prvers->version) + 24);
7505 sprintf (buf, "%-12s (%-8.8s ",
7506 prvers->version,
7507 prvers->author);
7508 cvs_output (buf, 0);
7509 free (buf);
7511 /* Now output the date. */
7512 ym = strchr (prvers->date, '.');
7513 if (ym == NULL)
7515 cvs_output ("??", 0);
7516 cvs_output ("-???", 0);
7517 cvs_output ("-??", 0);
7519 else
7521 md = strchr (ym + 1, '.');
7522 if (md == NULL)
7523 cvs_output ("??", 0);
7524 else
7525 cvs_output (md + 1, 2);
7527 cvs_output ("-", 1);
7528 cvs_output (month_printname (ym + 1), 0);
7529 cvs_output ("-", 1);
7530 /* Only output the last two digits of the year. Our output
7531 lines are long enough as it is without printing the
7532 century. */
7533 cvs_output (ym - 2, 2);
7535 cvs_output ("): ", 0);
7536 if (headlines.vector[ln]->len != 0)
7537 cvs_output (headlines.vector[ln]->text,
7538 headlines.vector[ln]->len);
7539 cvs_output ("\n", 1);
7542 break;
7543 case RCS_FETCH:
7545 char *p;
7546 size_t n;
7547 unsigned int ln;
7549 assert (text != NULL);
7550 assert (len != NULL);
7552 n = 0;
7553 for (ln = 0; ln < headlines.nlines; ++ln)
7554 /* 1 for \n */
7555 n += headlines.vector[ln]->len + 1;
7556 p = xmalloc (n);
7557 *text = p;
7558 for (ln = 0; ln < headlines.nlines; ++ln)
7560 memcpy (p, headlines.vector[ln]->text,
7561 headlines.vector[ln]->len);
7562 p += headlines.vector[ln]->len;
7563 if (headlines.vector[ln]->has_newline)
7564 *p++ = '\n';
7566 *len = p - *text;
7567 assert (*len <= n);
7569 break;
7572 linevector_free (&curlines);
7573 linevector_free (&headlines);
7574 linevector_free (&trunklines);
7576 return;
7581 /* Read the information for a single delta from the RCS buffer RCSBUF,
7582 whose name is RCSFILE. *KEYP and *VALP are either NULL, or the
7583 first key/value pair to read, as set by rcsbuf_getkey. Return NULL
7584 if there are no more deltas. Store the key/value pair which
7585 terminated the read in *KEYP and *VALP. */
7586 static RCSVers *
7587 getdelta (struct rcsbuffer *rcsbuf, char *rcsfile, char **keyp, char **valp)
7589 RCSVers *vnode;
7590 char *key, *value, *cp;
7591 Node *kv;
7593 /* Get revision number if it wasn't passed in. This uses
7594 rcsbuf_getkey because it doesn't croak when encountering
7595 unexpected input. As a result, we have to play unholy games
7596 with `key' and `value'. */
7597 if (*keyp != NULL)
7599 key = *keyp;
7600 value = *valp;
7602 else
7604 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7605 error (1, 0, "%s: unexpected EOF", rcsfile);
7608 /* Make sure that it is a revision number and not a cabbage
7609 or something. */
7610 for (cp = key;
7611 (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
7612 cp++)
7613 /* do nothing */ ;
7614 /* Note that when comparing with RCSDATE, we are not massaging
7615 VALUE from the string found in the RCS file. This is OK since
7616 we know exactly what to expect. */
7617 if (*cp != '\0' || strncmp (RCSDATE, value, (sizeof RCSDATE) - 1) != 0)
7619 *keyp = key;
7620 *valp = value;
7621 return NULL;
7624 vnode = xmalloc (sizeof (RCSVers));
7625 memset (vnode, 0, sizeof (RCSVers));
7627 vnode->version = xstrdup (key);
7629 /* Grab the value of the date from value. Note that we are not
7630 massaging VALUE from the string found in the RCS file. */
7631 cp = value + (sizeof RCSDATE) - 1; /* skip the "date" keyword */
7632 while (whitespace (*cp)) /* take space off front of value */
7633 cp++;
7635 vnode->date = xstrdup (cp);
7637 /* Get author field. */
7638 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7640 error (1, 0, "unexpected end of file reading %s", rcsfile);
7642 if (! STREQ (key, "author"))
7643 error (1, 0, "\
7644 unable to parse %s; `author' not in the expected place", rcsfile);
7645 vnode->author = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
7647 /* Get state field. */
7648 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7650 error (1, 0, "unexpected end of file reading %s", rcsfile);
7652 if (! STREQ (key, "state"))
7653 error (1, 0, "\
7654 unable to parse %s; `state' not in the expected place", rcsfile);
7655 vnode->state = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
7656 /* The value is optional, according to rcsfile(5). */
7657 if (value != NULL && STREQ (value, RCSDEAD))
7659 vnode->dead = 1;
7662 /* Note that "branches" and "next" are in fact mandatory, according
7663 to doc/RCSFILES. */
7665 /* fill in the branch list (if any branches exist) */
7666 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7668 error (1, 0, "unexpected end of file reading %s", rcsfile);
7670 if (STREQ (key, RCSDESC))
7672 *keyp = key;
7673 *valp = value;
7674 /* Probably could/should be a fatal error. */
7675 error (0, 0, "warning: 'branches' keyword missing from %s", rcsfile);
7676 return vnode;
7678 if (value != NULL)
7680 vnode->branches = getlist ();
7681 /* Note that we are not massaging VALUE from the string found
7682 in the RCS file. */
7683 do_branches (vnode->branches, value);
7686 /* fill in the next field if there is a next revision */
7687 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7689 error (1, 0, "unexpected end of file reading %s", rcsfile);
7691 if (STREQ (key, RCSDESC))
7693 *keyp = key;
7694 *valp = value;
7695 /* Probably could/should be a fatal error. */
7696 error (0, 0, "warning: 'next' keyword missing from %s", rcsfile);
7697 return vnode;
7699 if (value != NULL)
7700 vnode->next = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
7703 * XXX - this is where we put the symbolic link stuff???
7704 * (into newphrases in the deltas).
7706 while (1)
7708 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7709 error (1, 0, "unexpected end of file reading %s", rcsfile);
7711 /* The `desc' keyword is the end of the deltas. */
7712 if (strcmp (key, RCSDESC) == 0)
7713 break;
7715 #ifdef PRESERVE_PERMISSIONS_SUPPORT
7717 /* The `hardlinks' value is a group of words, which must
7718 be parsed separately and added as a list to vnode->hardlinks. */
7719 if (strcmp (key, "hardlinks") == 0)
7721 char *word;
7723 vnode->hardlinks = getlist();
7724 while ((word = rcsbuf_valword (rcsbuf, &value)) != NULL)
7726 Node *n = getnode();
7727 n->key = word;
7728 addnode (vnode->hardlinks, n);
7730 continue;
7732 #endif
7734 /* Enable use of repositories created by certain obsolete
7735 versions of CVS. This code should remain indefinately;
7736 there is no procedure for converting old repositories, and
7737 checking for it is harmless. */
7738 if (STREQ (key, RCSDEAD))
7740 vnode->dead = 1;
7741 if (vnode->state != NULL)
7742 free (vnode->state);
7743 vnode->state = xstrdup (RCSDEAD);
7744 continue;
7746 /* if we have a new revision number, we're done with this delta */
7747 for (cp = key;
7748 (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
7749 cp++)
7750 /* do nothing */ ;
7751 /* Note that when comparing with RCSDATE, we are not massaging
7752 VALUE from the string found in the RCS file. This is OK
7753 since we know exactly what to expect. */
7754 if (*cp == '\0' && strncmp (RCSDATE, value, strlen (RCSDATE)) == 0)
7755 break;
7757 /* At this point, key and value represent a user-defined field
7758 in the delta node. */
7759 if (vnode->other_delta == NULL)
7760 vnode->other_delta = getlist ();
7761 kv = getnode ();
7762 kv->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
7763 kv->key = xstrdup (key);
7764 kv->data = rcsbuf_valcopy (rcsbuf, value, kv->type == RCSFIELD, NULL);
7765 if (addnode (vnode->other_delta, kv) != 0)
7767 /* Complaining about duplicate keys in newphrases seems
7768 questionable, in that we don't know what they mean and
7769 doc/RCSFILES has no prohibition on several newphrases
7770 with the same key. But we can't store more than one as
7771 long as we store them in a List *. */
7772 error (0, 0, "warning: duplicate key `%s' in RCS file `%s'",
7773 key, rcsfile);
7774 freenode (kv);
7778 /* Return the key which caused us to fail back to the caller. */
7779 *keyp = key;
7780 *valp = value;
7782 return vnode;
7787 static void
7788 freedeltatext (Deltatext *d)
7790 if (d->version != NULL)
7791 free (d->version);
7792 if (d->log != NULL)
7793 free (d->log);
7794 if (d->text != NULL)
7795 free (d->text);
7796 if (d->other != NULL)
7797 dellist (&d->other);
7798 free (d);
7801 static Deltatext *
7802 RCS_getdeltatext (RCSNode *rcs, FILE *fp, struct rcsbuffer *rcsbuf)
7804 char *num;
7805 char *key, *value;
7806 Node *p;
7807 Deltatext *d;
7809 /* Get the revision number. */
7810 if (! rcsbuf_getrevnum (rcsbuf, &num))
7812 /* If num == NULL, it means we reached EOF naturally. That's
7813 fine. */
7814 if (num == NULL)
7815 return NULL;
7816 else
7817 error (1, 0, "%s: unexpected EOF", rcs->print_path);
7820 p = findnode (rcs->versions, num);
7821 if (p == NULL)
7822 error (1, 0, "mismatch in rcs file %s between deltas and deltatexts (%s)",
7823 rcs->print_path, num);
7825 d = xmalloc (sizeof (Deltatext));
7826 d->version = xstrdup (num);
7828 /* Get the log message. */
7829 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7830 error (1, 0, "%s, delta %s: unexpected EOF", rcs->print_path, num);
7831 if (! STREQ (key, "log"))
7832 error (1, 0, "%s, delta %s: expected `log', got `%s'",
7833 rcs->print_path, num, key);
7834 d->log = rcsbuf_valcopy (rcsbuf, value, 0, NULL);
7836 /* Get random newphrases. */
7837 d->other = getlist();
7838 while (1)
7840 if (! rcsbuf_getkey (rcsbuf, &key, &value))
7841 error (1, 0, "%s, delta %s: unexpected EOF", rcs->print_path, num);
7843 if (STREQ (key, "text"))
7844 break;
7846 p = getnode();
7847 p->type = rcsbuf_valcmp (rcsbuf) ? RCSCMPFLD : RCSFIELD;
7848 p->key = xstrdup (key);
7849 p->data = rcsbuf_valcopy (rcsbuf, value, p->type == RCSFIELD, NULL);
7850 if (addnode (d->other, p) < 0)
7852 error (0, 0, "warning: %s, delta %s: duplicate field `%s'",
7853 rcs->print_path, num, key);
7857 /* Get the change text. We already know that this key is `text'. */
7858 d->text = rcsbuf_valcopy (rcsbuf, value, 0, &d->len);
7860 return d;
7865 /* RCS output functions, for writing RCS format files from RCSNode
7866 structures.
7868 For most of this work, RCS 5.7 uses an `aprintf' function which aborts
7869 program upon error. Instead, these functions check the output status
7870 of the stream right before closing it, and aborts if an error condition
7871 is found. The RCS solution is probably the better one: it produces
7872 more overhead, but will produce a clearer diagnostic in the case of
7873 catastrophic error. In either case, however, the repository will probably
7874 not get corrupted. */
7875 static int
7876 putsymbol_proc (Node *symnode, void *fparg)
7878 FILE *fp = fparg;
7880 /* A fiddly optimization: this code used to just call fprintf, but
7881 in an old repository with hundreds of tags this can get called
7882 hundreds of thousands of times when doing a cvs tag. Since
7883 tagging is a relatively common operation, and using putc and
7884 fputs is just as comprehensible, the change is worthwhile. */
7885 putc ('\n', fp);
7886 putc ('\t', fp);
7887 fputs (symnode->key, fp);
7888 putc (':', fp);
7889 fputs (symnode->data, fp);
7890 return 0;
7895 /* putlock_proc is like putsymbol_proc, but key and data are reversed. */
7896 static int
7897 putlock_proc (Node *symnode, void *fp)
7899 return fprintf (fp, "\n\t%s:%s", (char *)symnode->data, symnode->key);
7904 static int
7905 putrcsfield_proc (Node *node, void *vfp)
7907 FILE *fp = vfp;
7909 /* Some magic keys used internally by CVS start with `;'. Skip them. */
7910 if (node->key[0] == ';')
7911 return 0;
7913 fprintf (fp, "\n%s\t", node->key);
7914 if (node->data != NULL)
7916 /* If the field's value contains evil characters,
7917 it must be stringified. */
7918 /* FIXME: This does not quite get it right. "7jk8f" is not a valid
7919 value for a value in a newpharse, according to doc/RCSFILES,
7920 because digits are not valid in an "id". We might do OK by
7921 always writing strings (enclosed in @@). Would be nice to
7922 explicitly mention this one way or another in doc/RCSFILES.
7923 A case where we are wrong in a much more clear-cut way is that
7924 we let through non-graphic characters such as whitespace and
7925 control characters. */
7927 if (node->type == RCSCMPFLD || strpbrk (node->data, "$,.:;@") == NULL)
7928 fputs (node->data, fp);
7929 else
7931 putc ('@', fp);
7932 expand_at_signs (node->data, (off_t) strlen (node->data), fp);
7933 putc ('@', fp);
7937 /* desc, log and text fields should not be terminated with semicolon;
7938 all other fields should be. */
7939 if (! STREQ (node->key, "desc") &&
7940 ! STREQ (node->key, "log") &&
7941 ! STREQ (node->key, "text"))
7943 putc (';', fp);
7945 return 0;
7950 #ifdef PRESERVE_PERMISSIONS_SUPPORT
7952 /* Save a filename in a `hardlinks' RCS field. NODE->KEY will contain
7953 a full pathname, but currently only basenames are stored in the RCS
7954 node. Assume that the filename includes nasty characters and
7955 @-escape it. */
7957 static int
7958 puthardlink_proc (node, vfp)
7959 Node *node;
7960 void *vfp;
7962 FILE *fp = vfp;
7963 char *basename = strrchr (node->key, '/');
7965 if (basename == NULL)
7966 basename = node->key;
7967 else
7968 ++basename;
7970 putc ('\t', fp);
7971 putc ('@', fp);
7972 (void) expand_at_signs (basename, strlen (basename), fp);
7973 putc ('@', fp);
7975 return 0;
7978 #endif /* PRESERVE_PERMISSIONS_SUPPORT */
7982 /* Output the admin node for RCS into stream FP. */
7983 static void
7984 RCS_putadmin (RCSNode *rcs, FILE *fp)
7986 fprintf (fp, "%s\t%s;\n", RCSHEAD, rcs->head ? rcs->head : "");
7987 if (rcs->branch)
7988 fprintf (fp, "%s\t%s;\n", RCSBRANCH, rcs->branch);
7990 fputs ("access", fp);
7991 if (rcs->access)
7993 char *p, *s;
7994 s = xstrdup (rcs->access);
7995 for (p = strtok (s, " \n\t"); p != NULL; p = strtok (NULL, " \n\t"))
7996 fprintf (fp, "\n\t%s", p);
7997 free (s);
7999 fputs (";\n", fp);
8001 fputs (RCSSYMBOLS, fp);
8002 /* If we haven't had to convert the symbols to a list yet, don't
8003 force a conversion now; just write out the string. */
8004 if (rcs->symbols == NULL && rcs->symbols_data != NULL)
8006 fputs ("\n\t", fp);
8007 fputs (rcs->symbols_data, fp);
8009 else
8010 walklist (RCS_symbols (rcs), putsymbol_proc, fp);
8011 fputs (";\n", fp);
8013 fputs ("locks", fp);
8014 if (rcs->locks_data)
8015 fprintf (fp, "\t%s", rcs->locks_data);
8016 else if (rcs->locks)
8017 walklist (rcs->locks, putlock_proc, fp);
8018 if (rcs->strict_locks)
8019 fprintf (fp, "; strict");
8020 fputs (";\n", fp);
8022 if (rcs->comment)
8024 fprintf (fp, "comment\t@");
8025 expand_at_signs (rcs->comment, (off_t) strlen (rcs->comment), fp);
8026 fputs ("@;\n", fp);
8028 if (rcs->expand && ! STREQ (rcs->expand, "kv"))
8029 fprintf (fp, "%s\t@%s@;\n", RCSEXPAND, rcs->expand);
8031 walklist (rcs->other, putrcsfield_proc, fp);
8033 putc ('\n', fp);
8038 static void
8039 putdelta (RCSVers *vers, FILE *fp)
8041 Node *bp, *start;
8043 /* Skip if no revision was supplied, or if it is outdated (cvs admin -o) */
8044 if (vers == NULL || vers->outdated)
8045 return;
8047 fprintf (fp, "\n%s\n%s\t%s;\t%s %s;\t%s %s;\nbranches",
8048 vers->version,
8049 RCSDATE, vers->date,
8050 "author", vers->author,
8051 "state", vers->state ? vers->state : "");
8053 if (vers->branches != NULL)
8055 start = vers->branches->list;
8056 for (bp = start->next; bp != start; bp = bp->next)
8057 fprintf (fp, "\n\t%s", bp->key);
8060 fprintf (fp, ";\nnext\t%s;", vers->next ? vers->next : "");
8062 walklist (vers->other_delta, putrcsfield_proc, fp);
8064 #ifdef PRESERVE_PERMISSIONS_SUPPORT
8065 if (vers->hardlinks)
8067 fprintf (fp, "\nhardlinks");
8068 walklist (vers->hardlinks, puthardlink_proc, fp);
8069 putc (';', fp);
8071 #endif
8072 putc ('\n', fp);
8077 static void
8078 RCS_putdtree (RCSNode *rcs, char *rev, FILE *fp)
8080 RCSVers *versp;
8081 Node *p, *branch;
8083 /* Previously, this function used a recursive implementation, but
8084 if the trunk has a huge number of revisions and the program
8085 stack is not big, a stack overflow could occur, so this
8086 nonrecursive version was developed to be more safe. */
8087 Node *branchlist, *onebranch;
8088 List *branches;
8089 List *onebranchlist;
8091 if (rev == NULL)
8092 return;
8094 branches = getlist();
8096 for (; rev != NULL;)
8098 /* Find the delta node for this revision. */
8099 p = findnode (rcs->versions, rev);
8100 if (p == NULL)
8102 error (1, 0,
8103 "error parsing repository file %s, file may be corrupt.",
8104 rcs->path);
8107 versp = p->data;
8109 /* Print the delta node and go for its `next' node. This
8110 prints the trunk. If there are any branches printed on this
8111 revision, mark we have some. */
8112 putdelta (versp, fp);
8113 /* Store branch information into branch list so to write its
8114 trunk afterwards */
8115 if (versp->branches != NULL)
8117 branch = getnode();
8118 branch->data = versp->branches;
8120 addnode(branches, branch);
8123 rev = versp->next;
8126 /* If there are any branches printed on this revision,
8127 print those trunks as well. */
8128 branchlist = branches->list;
8129 for (branch = branchlist->next;
8130 branch != branchlist;
8131 branch = branch->next)
8133 onebranchlist = (List *)(branch->data);
8134 onebranch = onebranchlist->list;
8135 for (p = onebranch->next; p != onebranch; p = p->next)
8136 RCS_putdtree (rcs, p->key, fp);
8138 branch->data = NULL; /* so to prevent its freeing on dellist */
8141 dellist(&branches);
8146 static void
8147 RCS_putdesc (RCSNode *rcs, FILE *fp)
8149 fprintf (fp, "\n\n%s\n@", RCSDESC);
8150 if (rcs->desc != NULL)
8152 off_t len = (off_t) strlen (rcs->desc);
8153 if (len > 0)
8155 expand_at_signs (rcs->desc, len, fp);
8156 if (rcs->desc[len-1] != '\n')
8157 putc ('\n', fp);
8160 fputs ("@\n", fp);
8165 static void
8166 putdeltatext (FILE *fp, Deltatext *d)
8168 fprintf (fp, "\n\n%s\nlog\n@", d->version);
8169 if (d->log != NULL)
8171 int loglen = strlen (d->log);
8172 expand_at_signs (d->log, (off_t) loglen, fp);
8173 if (d->log[loglen-1] != '\n')
8174 putc ('\n', fp);
8176 putc ('@', fp);
8178 walklist (d->other, putrcsfield_proc, fp);
8180 fputs ("\ntext\n@", fp);
8181 if (d->text != NULL)
8182 expand_at_signs (d->text, (off_t) d->len, fp);
8183 fputs ("@\n", fp);
8188 /* TODO: the whole mechanism for updating deltas is kludgey... more
8189 sensible would be to supply all the necessary info in a `newdeltatext'
8190 field for RCSVers nodes. -twp */
8192 /* Copy delta text nodes from FIN to FOUT. If NEWDTEXT is non-NULL, it
8193 is a new delta text node, and should be added to the tree at the
8194 node whose revision number is INSERTPT. (Note that trunk nodes are
8195 written in decreasing order, and branch nodes are written in
8196 increasing order.) */
8197 static void
8198 RCS_copydeltas (RCSNode *rcs, FILE *fin, struct rcsbuffer *rcsbufin,
8199 FILE *fout, Deltatext *newdtext, char *insertpt)
8201 int actions;
8202 RCSVers *dadmin;
8203 Node *np;
8204 int insertbefore, found;
8205 char *bufrest;
8206 int nls;
8207 size_t buflen;
8208 #ifndef HAVE_MMAP
8209 char buf[8192];
8210 int got;
8211 #endif
8213 /* Count the number of versions for which we have to do some
8214 special operation. */
8215 actions = walklist (rcs->versions, count_delta_actions, NULL);
8217 /* Make a note of whether NEWDTEXT should be inserted
8218 before or after its INSERTPT. */
8219 insertbefore = (newdtext != NULL && numdots (newdtext->version) == 1);
8221 while (actions != 0 || newdtext != NULL)
8223 Deltatext *dtext;
8225 dtext = RCS_getdeltatext (rcs, fin, rcsbufin);
8227 /* We shouldn't hit EOF here, because that would imply that
8228 some action was not taken, or that we could not insert
8229 NEWDTEXT. */
8230 if (dtext == NULL)
8231 error (1, 0, "internal error: EOF too early in RCS_copydeltas");
8233 found = (insertpt != NULL && STREQ (dtext->version, insertpt));
8234 if (found && insertbefore)
8236 putdeltatext (fout, newdtext);
8237 newdtext = NULL;
8238 insertpt = NULL;
8241 np = findnode (rcs->versions, dtext->version);
8242 dadmin = np->data;
8244 /* If this revision has been outdated, just skip it. */
8245 if (dadmin->outdated)
8247 freedeltatext (dtext);
8248 --actions;
8249 continue;
8252 /* Update the change text for this delta. New change text
8253 data may come from cvs admin -m, cvs admin -o, or cvs ci. */
8254 if (dadmin->text != NULL)
8256 if (dadmin->text->log != NULL || dadmin->text->text != NULL)
8257 --actions;
8258 if (dadmin->text->log != NULL)
8260 free (dtext->log);
8261 dtext->log = dadmin->text->log;
8262 dadmin->text->log = NULL;
8264 if (dadmin->text->text != NULL)
8266 free (dtext->text);
8267 dtext->text = dadmin->text->text;
8268 dtext->len = dadmin->text->len;
8269 dadmin->text->text = NULL;
8272 putdeltatext (fout, dtext);
8273 freedeltatext (dtext);
8275 if (found && !insertbefore)
8277 putdeltatext (fout, newdtext);
8278 newdtext = NULL;
8279 insertpt = NULL;
8283 /* Copy the rest of the file directly, without bothering to
8284 interpret it. The caller will handle error checking by calling
8285 ferror.
8287 We just wrote a newline to the file, either in putdeltatext or
8288 in the caller. However, we may not have read the corresponding
8289 newline from the file, because rcsbuf_getkey returns as soon as
8290 it finds the end of the '@' string for the desc or text key.
8291 Therefore, we may read three newlines when we should really
8292 only write two, and we check for that case here. This is not
8293 an semantically important issue; we only do it to make our RCS
8294 files look traditional. */
8296 nls = 3;
8298 rcsbuf_get_buffered (rcsbufin, &bufrest, &buflen);
8299 if (buflen > 0)
8301 if (bufrest[0] != '\n'
8302 || strncmp (bufrest, "\n\n\n", buflen < 3 ? buflen : 3) != 0)
8304 nls = 0;
8306 else
8308 if (buflen < 3)
8309 nls -= buflen;
8310 else
8312 ++bufrest;
8313 --buflen;
8314 nls = 0;
8318 fwrite (bufrest, 1, buflen, fout);
8320 #ifndef HAVE_MMAP
8321 /* This bit isn't necessary when using mmap since the entire file
8322 * will already be available via the RCS buffer. Besides, the
8323 * mmap code doesn't always keep the file pointer up to date, so
8324 * this adds some data twice.
8326 while ((got = fread (buf, 1, sizeof buf, fin)) != 0)
8328 if (nls > 0
8329 && got >= nls
8330 && buf[0] == '\n'
8331 && strncmp (buf, "\n\n\n", nls) == 0)
8333 fwrite (buf + 1, 1, got - 1, fout);
8335 else
8337 fwrite (buf, 1, got, fout);
8340 nls = 0;
8342 #endif /* HAVE_MMAP */
8347 /* A helper procedure for RCS_copydeltas. This is called via walklist
8348 to count the number of RCS revisions for which some special action
8349 is required. */
8350 static int
8351 count_delta_actions (Node *np, void *ignore)
8353 RCSVers *dadmin = np->data;
8355 if (dadmin->outdated)
8356 return 1;
8358 if (dadmin->text != NULL
8359 && (dadmin->text->log != NULL || dadmin->text->text != NULL))
8361 return 1;
8364 return 0;
8370 * Clean up temporary files.
8372 * NOTES
8373 * This function needs to be reentrant since a call to exit() can cause a
8374 * call to this function, which can then be interrupted by a signal, which
8375 * can cause a second call to this function.
8377 * RETURNS
8378 * Nothing.
8380 static void
8381 rcs_cleanup (void)
8383 static int reenter = 0;
8385 if (reenter++)
8386 _exit(1);
8388 TRACE (TRACE_FUNCTION, "rcs_cleanup()");
8390 /* FIXME: Do not perform buffered I/O from an interrupt handler like
8391 * this (via error). However, I'm leaving the error-calling code there
8392 * in the hope that on the rare occasion the error call is actually made
8393 * (e.g., a fluky I/O error or permissions problem prevents the deletion
8394 * of a just-created file) reentrancy won't be an issue.
8397 /* We don't want to be interrupted during calls which set globals to NULL,
8398 * but we know that by the time we reach this function, interrupts have
8399 * already been blocked.
8401 if (rcs_lockfile != NULL)
8403 /* Use a tmp var since any of these functions could call exit, causing
8404 * us to be called a second time.
8406 char *tmp = rcs_lockfile;
8407 rcs_lockfile = NULL;
8408 if (rcs_lockfd >= 0)
8410 if (close (rcs_lockfd) != 0)
8411 error (0, errno, "error closing lock file %s", tmp);
8412 rcs_lockfd = -1;
8415 /* Note that the checks for existence_error are because we can be
8416 * called from a signal handler, so we don't know whether the
8417 * files got created.
8419 if (unlink_file (tmp) < 0
8420 && !existence_error (errno))
8421 error (0, errno, "cannot remove %s", tmp);
8427 /* RCS_internal_lockfile and RCS_internal_unlockfile perform RCS-style
8428 locking on the specified RCSFILE: for a file called `foo,v', open
8429 for writing a file called `,foo,'.
8431 Note that we what do here is quite different from what RCS does.
8432 RCS creates the ,foo, file before it reads the RCS file (if it
8433 knows that it will be writing later), so that it actually serves as
8434 a lock. We don't; instead we rely on CVS writelocks. This means
8435 that if someone is running RCS on the file at the same time they
8436 are running CVS on it, they might lose (we read the file,
8437 then RCS writes it, then we write it, clobbering the
8438 changes made by RCS). I believe the current sentiment about this
8439 is "well, don't do that".
8441 A concern has been expressed about whether adopting the RCS
8442 strategy would slow us down. I don't think so, since we need to
8443 write the ,foo, file anyway (unless perhaps if O_EXCL is slower or
8444 something).
8446 These do not perform quite the same function as the RCS -l option
8447 for locking files: they are intended to prevent competing RCS
8448 processes from stomping all over each other's laundry. Hence,
8449 they are `internal' locking functions.
8451 If there is an error, give a fatal error; if we return we always
8452 return a non-NULL value. */
8453 static FILE *
8454 rcs_internal_lockfile (char *rcsfile)
8456 struct stat rstat;
8457 FILE *fp;
8458 static int first_call = 1;
8460 if (first_call)
8462 first_call = 0;
8463 /* Clean up if we get a signal or exit. */
8464 cleanup_register (rcs_cleanup);
8467 /* Get the lock file name: `,file,' for RCS file `file,v'. */
8468 assert (rcs_lockfile == NULL);
8469 assert (rcs_lockfd < 0);
8470 rcs_lockfile = rcs_lockfilename (rcsfile);
8472 /* Use the existing RCS file mode, or read-only if this is a new
8473 file. (Really, this is a lie -- if this is a new file,
8474 RCS_checkin uses the permissions from the working copy. For
8475 actually creating the file, we use 0444 as a safe default mode.) */
8476 if (stat (rcsfile, &rstat) < 0)
8478 if (existence_error (errno))
8479 rstat.st_mode = S_IRUSR | S_IRGRP | S_IROTH;
8480 else
8481 error (1, errno, "cannot stat %s", rcsfile);
8484 /* Try to open exclusively. POSIX.1 guarantees that O_EXCL|O_CREAT
8485 guarantees an exclusive open. According to the RCS source, with
8486 NFS v2 we must also throw in O_TRUNC and use an open mask that makes
8487 the file unwriteable. For extensive justification, see the comments for
8488 rcswriteopen() in rcsedit.c, in RCS 5.7. This is kind of pointless
8489 in the CVS case; see comment at the start of this file concerning
8490 general ,foo, file strategy.
8492 There is some sentiment that with NFSv3 and such, that one can
8493 rely on O_EXCL these days. This might be true for unix (I
8494 don't really know), but I am still pretty skeptical in the case
8495 of the non-unix systems. */
8496 rcs_lockfd = open (rcs_lockfile,
8497 OPEN_BINARY | O_WRONLY | O_CREAT | O_EXCL | O_TRUNC,
8498 S_IRUSR | S_IRGRP | S_IROTH);
8500 if (rcs_lockfd < 0)
8502 error (1, errno, "could not open lock file `%s'", rcs_lockfile);
8505 /* Force the file permissions, and return a stream object. */
8506 /* Because we change the modes later, we don't worry about
8507 this in the non-HAVE_FCHMOD case. */
8508 #ifdef HAVE_FCHMOD
8509 if (fchmod (rcs_lockfd, rstat.st_mode) < 0)
8510 error (1, errno, "cannot change mode for %s", rcs_lockfile);
8511 #endif
8512 fp = fdopen (rcs_lockfd, FOPEN_BINARY_WRITE);
8513 if (fp == NULL)
8514 error (1, errno, "cannot fdopen %s", rcs_lockfile);
8516 return fp;
8521 static void
8522 rcs_internal_unlockfile (FILE *fp, char *rcsfile)
8524 assert (rcs_lockfile != NULL);
8525 assert (rcs_lockfd >= 0);
8527 /* Abort if we could not write everything successfully to LOCKFILE.
8528 This is not a great error-handling mechanism, but should prevent
8529 corrupting the repository. */
8531 if (ferror (fp))
8532 /* Using errno here may well be misleanding since the most recent
8533 call that set errno may not have anything whatsoever to do with
8534 the error that set the flag, but it's better than nothing. The
8535 real solution is to check each call to fprintf rather than waiting
8536 until the end like this. */
8537 error (1, errno, "error writing to lock file %s", rcs_lockfile);
8539 /* Flush and sync the file, or the user may be told the commit completed,
8540 * while a server crash/power failure could still cause the data to be
8541 * lost.
8543 * Invoking rename(",<file>," , "<file>,v") on Linux and almost all UNIXs
8544 * only flushes the inode for the target file to disk, it does not
8545 * guarantee flush of the kernel buffers allocated for the ,<file>,.
8546 * Depending upon the load on the machine, the Linux kernel's flush daemon
8547 * process may not flush for a while. In the meantime the CVS transaction
8548 * could have been declared committed to the end CVS user (CVS process has
8549 * returned the final "OK"). If the machine crashes prior to syncing the
8550 * changes to disk, the committed transaction can be lost.
8552 if (fflush (fp) != 0)
8553 error (1, errno, "error flushing file `%s' to kernel buffers",
8554 rcs_lockfile);
8555 #ifdef HAVE_FSYNC
8556 if (fsync (rcs_lockfd) < 0)
8557 error (1, errno, "error fsyncing file `%s'", rcs_lockfile);
8558 #endif
8560 if (fclose (fp) == EOF)
8561 error (1, errno, "error closing lock file %s", rcs_lockfile);
8562 rcs_lockfd = -1;
8564 rename_file (rcs_lockfile, rcsfile);
8567 /* Use a temporary to make sure there's no interval
8568 (after rcs_lockfile has been freed but before it's set to NULL)
8569 during which the signal handler's use of rcs_lockfile would
8570 reference freed memory. */
8571 char *tmp = rcs_lockfile;
8572 rcs_lockfile = NULL;
8573 free (tmp);
8579 static char *
8580 rcs_lockfilename (const char *rcsfile)
8582 char *lockfile, *lockp;
8583 const char *rcsbase, *rcsp, *rcsend;
8584 int rcslen;
8586 /* Create the lockfile name. */
8587 rcslen = strlen (rcsfile);
8588 lockfile = xmalloc (rcslen + 10);
8589 rcsbase = last_component (rcsfile);
8590 rcsend = rcsfile + rcslen - sizeof(RCSEXT);
8591 for (lockp = lockfile, rcsp = rcsfile; rcsp < rcsbase; ++rcsp)
8592 *lockp++ = *rcsp;
8593 *lockp++ = ',';
8594 while (rcsp <= rcsend)
8595 *lockp++ = *rcsp++;
8596 *lockp++ = ',';
8597 *lockp = '\0';
8599 return lockfile;
8604 /* Rewrite an RCS file. The basic idea here is that the caller should
8605 first call RCS_reparsercsfile, then munge the data structures as
8606 desired (via RCS_delete_revs, RCS_settag, &c), then call RCS_rewrite. */
8607 void
8608 RCS_rewrite (RCSNode *rcs, Deltatext *newdtext, char *insertpt)
8610 FILE *fin, *fout;
8611 struct rcsbuffer rcsbufin;
8613 if (noexec)
8614 return;
8616 /* Make sure we're operating on an actual file and not a symlink. */
8617 resolve_symlink (&(rcs->path));
8619 fout = rcs_internal_lockfile (rcs->path);
8621 RCS_putadmin (rcs, fout);
8622 RCS_putdtree (rcs, rcs->head, fout);
8623 RCS_putdesc (rcs, fout);
8625 /* Open the original RCS file and seek to the first delta text. */
8626 rcsbuf_cache_open (rcs, rcs->delta_pos, &fin, &rcsbufin);
8628 /* Update delta_pos to the current position in the output file.
8629 Do NOT move these statements: they must be done after fin has
8630 been positioned at the old delta_pos, but before any delta
8631 texts have been written to fout.
8633 rcs->delta_pos = ftello (fout);
8634 if (rcs->delta_pos == -1)
8635 error (1, errno, "cannot ftello in RCS file %s", rcs->path);
8637 RCS_copydeltas (rcs, fin, &rcsbufin, fout, newdtext, insertpt);
8639 /* We don't want to call rcsbuf_cache here, since we're about to
8640 delete the file. */
8641 rcsbuf_close (&rcsbufin);
8642 if (ferror (fin))
8643 /* The only case in which using errno here would be meaningful
8644 is if we happen to have left errno unmolested since the call
8645 which produced the error (e.g. fread). That is pretty
8646 fragile even if it happens to sometimes be true. The real
8647 solution is to make sure that all the code which reads
8648 from fin checks for errors itself (some does, some doesn't). */
8649 error (0, 0, "warning: ferror set while rewriting RCS file `%s'", rcs->path);
8650 if (fclose (fin) < 0)
8651 error (0, errno, "warning: closing RCS file `%s'", rcs->path);
8653 rcs_internal_unlockfile (fout, rcs->path);
8658 /* Abandon changes to an RCS file. */
8659 void
8660 RCS_abandon (RCSNode *rcs)
8662 free_rcsnode_contents (rcs);
8663 rcs->symbols_data = NULL;
8664 rcs->expand = NULL;
8665 rcs->access = NULL;
8666 rcs->locks_data = NULL;
8667 rcs->comment = NULL;
8668 rcs->desc = NULL;
8669 rcs->flags |= PARTIAL;
8675 * For a given file with full pathname PATH and revision number REV,
8676 * produce a file label suitable for passing to diff. The default
8677 * file label as used by RCS 5.7 looks like this:
8679 * FILENAME <tab> YYYY/MM/DD <sp> HH:MM:SS <tab> REVNUM
8681 * The date and time used are the revision's last checkin date and time.
8682 * If REV is NULL, use the working copy's mtime instead.
8684 * /dev/null is not statted but assumed to have been created on the Epoch.
8685 * At least using the POSIX.2 definition of patch, this should cause creation
8686 * of files on platforms such as Windoze where the null IO device isn't named
8687 * /dev/null to be parsed by patch properly.
8689 char *
8690 make_file_label (const char *path, const char *rev, RCSNode *rcs)
8692 char datebuf[MAXDATELEN + 1];
8693 char *label;
8695 if (rev)
8697 char date[MAXDATELEN + 1];
8698 /* revs cannot be attached to /dev/null ... duh. */
8699 assert (strcmp(DEVNULL, path));
8700 RCS_getrevtime (rcs, rev, datebuf, 0);
8701 (void) date_to_internet (date, datebuf);
8702 label = Xasprintf ("-L%s\t%s\t%s", path, date, rev);
8704 else
8706 struct stat sb;
8707 struct tm *wm;
8709 if (strcmp(DEVNULL, path))
8711 const char *file = last_component (path);
8712 if (stat (file, &sb) < 0)
8713 /* Assume that if the stat fails,then the later read for the
8714 * diff will too.
8716 error (1, errno, "could not get info for `%s'", path);
8717 wm = gmtime (&sb.st_mtime);
8719 else
8721 time_t t = 0;
8722 wm = gmtime(&t);
8725 (void) tm_to_internet (datebuf, wm);
8726 label = Xasprintf ("-L%s\t%s", path, datebuf);
8728 return label;
8734 * Set up a local/custom RCS keyword for expansion.
8736 * INPUTS
8737 * infopath Path to file being parsed, for error messages.
8738 * ln Line number of INFOPATH being processed, for error
8739 * messages.
8740 * keywords_in
8741 * arg
8743 * OUTPUTS
8744 * keywords_in
8746 void
8747 RCS_setlocalid (const char *infopath, unsigned int ln,
8748 void **keywords_in, const char *arg)
8750 char *copy, *next, *key, *s;
8751 struct rcs_keyword *keywords;
8752 enum keyword save_expandto;
8754 if (!*keywords_in)
8755 *keywords_in = new_keywords ();
8756 keywords = *keywords_in;
8758 copy = xstrdup (arg);
8759 next = copy;
8760 key = strtok (next, "=");
8763 * Validate key
8765 for (s = key; *s != '\0'; s++)
8767 if (! isalpha ((unsigned char) *s))
8769 if (!parse_error (infopath, ln))
8770 error (0, 0,
8771 "%s [%u]: LocalKeyword ignored: Bad character `%c' in key `%s'",
8772 primary_root_inverse_translate (infopath),
8773 ln, *s, key);
8774 free (copy);
8775 return;
8779 save_expandto = keywords[KEYWORD_LOCALID].expandto;
8781 /* options? */
8782 while ((key = strtok (NULL, ",")) != NULL) {
8783 if (!strcmp(key, keywords[KEYWORD_ID].string))
8784 keywords[KEYWORD_LOCALID].expandto = KEYWORD_ID;
8785 else if (!strcmp(key, keywords[KEYWORD_HEADER].string))
8786 keywords[KEYWORD_LOCALID].expandto = KEYWORD_HEADER;
8787 else if (!strcmp(key, keywords[KEYWORD_CVSHEADER].string))
8788 keywords[KEYWORD_LOCALID].expandto = KEYWORD_CVSHEADER;
8789 else
8791 keywords[KEYWORD_LOCALID].expandto = save_expandto;
8792 if (!parse_error (infopath, ln))
8793 error (0, 0,
8794 "%s [%u]: LocalKeyword ignored: Unknown LocalId mode: `%s'",
8795 primary_root_inverse_translate (infopath),
8796 ln, key);
8797 free (copy);
8798 return;
8802 keywords[KEYWORD_LOCALID].string = xstrdup (next);
8803 keywords[KEYWORD_LOCALID].len = strlen (next);
8804 keywords[KEYWORD_LOCALID].expandit = 1;
8806 free (copy);
8811 void
8812 RCS_setincexc (void **keywords_in, const char *arg)
8814 char *key;
8815 char *copy, *next;
8816 bool include = false;
8817 struct rcs_keyword *keyword;
8818 struct rcs_keyword *keywords;
8820 if (!*keywords_in)
8821 *keywords_in = new_keywords ();
8822 keywords = *keywords_in;
8824 copy = xstrdup(arg);
8825 next = copy;
8826 switch (*next++) {
8827 case 'e':
8828 include = false;
8829 break;
8830 case 'i':
8831 include = true;
8832 break;
8833 default:
8834 free(copy);
8835 return;
8838 if (include)
8839 for (keyword = keywords; keyword->string != NULL; keyword++)
8841 keyword->expandit = false;
8844 key = strtok(next, ",");
8845 while (key) {
8846 for (keyword = keywords; keyword->string != NULL; keyword++) {
8847 if (strcmp (keyword->string, key) == 0)
8848 keyword->expandit = include;
8850 key = strtok(NULL, ",");
8852 free(copy);
8853 return;
8858 #define ATTIC "/" CVSATTIC
8859 static char *
8860 getfullCVSname(char *CVSname, char **pathstore)
8862 if (current_parsed_root->directory) {
8863 int rootlen;
8864 char *c = NULL;
8865 int alen = sizeof(ATTIC) - 1;
8867 *pathstore = xstrdup(CVSname);
8868 if ((c = strrchr(*pathstore, '/')) != NULL) {
8869 if (c - *pathstore >= alen) {
8870 if (!strncmp(c - alen, ATTIC, alen)) {
8871 while (*c != '\0') {
8872 *(c - alen) = *c;
8873 c++;
8875 *(c - alen) = '\0';
8880 rootlen = strlen(current_parsed_root->directory);
8881 if (!strncmp(*pathstore, current_parsed_root->directory, rootlen) &&
8882 (*pathstore)[rootlen] == '/')
8883 CVSname = (*pathstore + rootlen + 1);
8884 else
8885 CVSname = (*pathstore);
8887 return CVSname;