Bug Summary

File:src/gnu/usr.bin/cvs/src/rcs.c
Warning:line 5277, column 15
Although the value stored to 'buflen' is used in the enclosing expression, the value is never actually read from 'buflen'

Annotated Source Code

Press '?' to see keyboard shortcuts

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