Deleted Added
sdiff udiff text old ( 109660 ) new ( 128269 )
full compact
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 * $FreeBSD: head/contrib/cvs/src/rcs.c 109660 2003-01-21 22:01:38Z peter $
11 */
12
13#include <assert.h>
14#include "cvs.h"
15#include "edit.h"
16#include "hardlink.h"
17
18/* These need to be source after cvs.h or HAVE_MMAP won't be set... */

--- 40 unchanged lines hidden (view full) ---

59 int at_string;
60 /* The number of embedded '@' characters in an '@' string. If
61 this is non-zero, we must search the string for pairs of '@'
62 and convert them to a single '@'. */
63 int embedded_at;
64};
65
66static RCSNode *RCS_parsercsfile_i PROTO((FILE * fp, const char *rcsfile));
67static char *RCS_getdatebranch PROTO((RCSNode * rcs, char *date, char *branch));
68static void rcsbuf_open PROTO ((struct rcsbuffer *, FILE *fp,
69 const char *filename, unsigned long pos));
70static void rcsbuf_close PROTO ((struct rcsbuffer *));
71static int rcsbuf_getkey PROTO ((struct rcsbuffer *, char **keyp,
72 char **valp));
73static int rcsbuf_getrevnum PROTO ((struct rcsbuffer *, char **revp));
74#ifndef HAVE_MMAP
75static char *rcsbuf_fill PROTO ((struct rcsbuffer *, char *ptr, char **keyp,

--- 46 unchanged lines hidden (view full) ---

122static int putsymbol_proc PROTO ((Node *, void *));
123static void RCS_copydeltas PROTO ((RCSNode *, FILE *, struct rcsbuffer *,
124 FILE *, Deltatext *, char *));
125static int count_delta_actions PROTO ((Node *, void *));
126static void putdeltatext PROTO ((FILE *, Deltatext *));
127
128static FILE *rcs_internal_lockfile PROTO ((char *));
129static void rcs_internal_unlockfile PROTO ((FILE *, char *));
130static char *rcs_lockfilename PROTO ((char *));
131
132/* The RCS file reading functions are called a lot, and they do some
133 string comparisons. This macro speeds things up a bit by skipping
134 the function call when the first characters are different. It
135 evaluates its arguments multiple times. */
136#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp ((a), (b)) == 0)
137
138static char * getfullCVSname PROTO ((char *, char **));
139
140/*
141 * We don't want to use isspace() from the C library because:
142 *
143 * 1. The definition of "whitespace" in RCS files includes ASCII
144 * backspace, but the C locale doesn't.

--- 19 unchanged lines hidden (view full) ---

164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* 0xf0 - 0xff */
165};
166
167#define whitespace(c) (spacetab[(unsigned char)c] != 0)
168
169static char *rcs_lockfile;
170static int rcs_lockfd = -1;
171
172/* A few generic thoughts on error handling, in particular the
173 printing of unexpected characters that we find in the RCS file
174 (that is, why we use '\x%x' rather than %c or some such).
175
176 * Avoiding %c means we don't have to worry about what is printable
177 and other such stuff. In error handling, often better to keep it
178 simple.
179

--- 14 unchanged lines hidden (view full) ---

194 the current behavior). */
195RCSNode *
196RCS_parse (file, repos)
197 const char *file;
198 const char *repos;
199{
200 RCSNode *rcs;
201 FILE *fp;
202 RCSNode *retval;
203 char *rcsfile;
204
205 /* We're creating a new RCSNode, so there is no hope of finding it
206 in the cache. */
207 rcsbuf_cache_close ();
208
209 rcsfile = xmalloc (strlen (repos) + strlen (file)
210 + sizeof (RCSEXT) + sizeof (CVSATTIC) + 10);
211 (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
212 if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL)
213 {
214 rcs = RCS_parsercsfile_i(fp, rcsfile);
215 if (rcs != NULL)
216 rcs->flags |= VALID;
217
218 retval = rcs;
219 goto out;
220 }
221 else if (! existence_error (errno))
222 {
223 error (0, errno, "cannot open %s", rcsfile);
224 retval = NULL;
225 goto out;
226 }
227
228 (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT);
229 if ((fp = CVS_FOPEN (rcsfile, FOPEN_BINARY_READ)) != NULL)
230 {
231 rcs = RCS_parsercsfile_i(fp, rcsfile);
232 if (rcs != NULL)
233 {
234 rcs->flags |= INATTIC;
235 rcs->flags |= VALID;
236 }
237
238 retval = rcs;
239 goto out;
240 }
241 else if (! existence_error (errno))
242 {
243 error (0, errno, "cannot open %s", rcsfile);
244 retval = NULL;
245 goto out;
246 }
247#if defined (SERVER_SUPPORT) && !defined (FILENAMES_CASE_INSENSITIVE)
248 else if (ign_case)
249 {
250 int status;
251 char *found_path;
252
253 /* The client might be asking for a file which we do have
254 (which the client doesn't know about), but for which the
255 filename case differs. We only consider this case if the
256 regular CVS_FOPENs fail, because fopen_case is such an
257 expensive call. */
258 (void) sprintf (rcsfile, "%s/%s%s", repos, file, RCSEXT);
259 status = fopen_case (rcsfile, "rb", &fp, &found_path);
260 if (status == 0)
261 {
262 rcs = RCS_parsercsfile_i (fp, rcsfile);
263 if (rcs != NULL)
264 rcs->flags |= VALID;
265
266 free (rcs->path);
267 rcs->path = found_path;
268 retval = rcs;
269 goto out;
270 }
271 else if (! existence_error (status))
272 {
273 error (0, status, "cannot open %s", rcsfile);
274 retval = NULL;
275 goto out;
276 }
277
278 (void) sprintf (rcsfile, "%s/%s/%s%s", repos, CVSATTIC, file, RCSEXT);
279 status = fopen_case (rcsfile, "rb", &fp, &found_path);
280 if (status == 0)
281 {
282 rcs = RCS_parsercsfile_i (fp, rcsfile);
283 if (rcs != NULL)
284 {
285 rcs->flags |= INATTIC;
286 rcs->flags |= VALID;
287 }
288
289 free (rcs->path);
290 rcs->path = found_path;
291 retval = rcs;
292 goto out;
293 }
294 else if (! existence_error (status))
295 {
296 error (0, status, "cannot open %s", rcsfile);
297 retval = NULL;
298 goto out;
299 }
300 }
301#endif
302 retval = NULL;
303
304 out:
305 free (rcsfile);
306
307 return retval;
308}
309
310/*
311 * Parse a specific rcsfile.
312 */
313RCSNode *
314RCS_parsercsfile (rcsfile)
315 char *rcsfile;
316{
317 FILE *fp;
318 RCSNode *rcs;
319
320 /* We're creating a new RCSNode, so there is no hope of finding it
321 in the cache. */
322 rcsbuf_cache_close ();
323

--- 5 unchanged lines hidden (view full) ---

329 }
330
331 rcs = RCS_parsercsfile_i (fp, rcsfile);
332
333 return (rcs);
334}
335
336
337/*
338 */
339static RCSNode *
340RCS_parsercsfile_i (fp, rcsfile)
341 FILE *fp;
342 const char *rcsfile;
343{
344 RCSNode *rdata;
345 struct rcsbuffer rcsbuf;
346 char *key, *value;
347
348 /* make a node */
349 rdata = (RCSNode *) xmalloc (sizeof (RCSNode));
350 memset ((char *) rdata, 0, sizeof (RCSNode));
351 rdata->refcount = 1;
352 rdata->path = xstrdup (rcsfile);
353
354 /* Process HEAD, BRANCH, and EXPAND keywords from the RCS header.
355
356 Most cvs operations on the main branch don't need any more
357 information. Those that do call RCS_reparsercsfile to parse
358 the rest of the header and the deltas. */
359
360 rcsbuf_open (&rcsbuf, fp, rcsfile, 0);
361
362 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
363 goto l_error;
364 if (STREQ (key, RCSDESC))
365 goto l_error;
366
367 if (STREQ (RCSHEAD, key) && value != NULL)
368 rdata->head = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL);
369
370 if (! rcsbuf_getkey (&rcsbuf, &key, &value))
371 goto l_error;
372 if (STREQ (key, RCSDESC))
373 goto l_error;
374
375 if (STREQ (RCSBRANCH, key) && value != NULL)
376 {
377 char *cp;
378
379 rdata->branch = rcsbuf_valcopy (&rcsbuf, value, 0, (size_t *) NULL);
380 if ((numdots (rdata->branch) & 1) != 0)
381 {
382 /* turn it into a branch if it's a revision */
383 cp = strrchr (rdata->branch, '.');
384 *cp = '\0';
385 }
386 }
387
388 /* Look ahead for expand, stopping when we see desc or a revision
389 number. */
390 while (1)
391 {
392 char *cp;
393
394 if (STREQ (RCSEXPAND, key))
395 {
396 rdata->expand = rcsbuf_valcopy (&rcsbuf, value, 0,
397 (size_t *) NULL);
398 break;
399 }
400
401 for (cp = key;
402 (isdigit ((unsigned char) *cp) || *cp == '.') && *cp != '\0';
403 cp++)
404 /* do nothing */ ;
405 if (*cp == '\0')
406 break;
407
408 if (STREQ (RCSDESC, key))
409 break;
410

--- 8 unchanged lines hidden (view full) ---

419 return rdata;
420
421l_error:
422 error (0, 0, "`%s' does not appear to be a valid rcs file",
423 rcsfile);
424 rcsbuf_close (&rcsbuf);
425 freercsnode (&rdata);
426 fclose (fp);
427 return (NULL);
428}
429
430
431/* Do the real work of parsing an RCS file.
432
433 On error, die with a fatal error; if it returns at all it was successful.
434
435 If PFP is NULL, close the file when done. Otherwise, leave it open
436 and store the FILE * in *PFP. */
437void
438RCS_reparsercsfile (rdata, pfp, rcsbufp)

--- 136 unchanged lines hidden (view full) ---

575 and its value). This is what getdelta expects to receive. */
576
577 while ((vnode = getdelta (&rcsbuf, rcsfile, &key, &value)) != NULL)
578 {
579 /* get the node */
580 q = getnode ();
581 q->type = RCSVERS;
582 q->delproc = rcsvers_delproc;
583 q->data = (char *) vnode;
584 q->key = vnode->version;
585
586 /* add the nodes to the list */
587 if (addnode (rdata->versions, q) != 0)
588 {
589#if 0
590 purify_printf("WARNING: Adding duplicate version: %s (%s)\n",
591 q->key, rcsfile);

--- 35 unchanged lines hidden (view full) ---

627 atomic, or that kind of thing). If there is an error, print a message
628 and return 1. On success, return 0. */
629int
630RCS_setattic (rcs, toattic)
631 RCSNode *rcs;
632 int toattic;
633{
634 char *newpath;
635 char *p;
636 char *q;
637
638 /* Some systems aren't going to let us rename an open file. */
639 rcsbuf_cache_close ();
640
641 /* Could make the pathname computations in this file, and probably
642 in other parts of rcs.c too, easier if the REPOS and FILE
643 arguments to RCS_parse got stashed in the RCSNode. */

--- 92 unchanged lines hidden (view full) ---

736 while (1)
737 {
738 char *key, *value;
739 Node *vers;
740 RCSVers *vnode;
741
742 /* Rather than try to keep track of how much information we
743 have read, just read to the end of the file. */
744 if (! rcsbuf_getrevnum (&rcsbuf, &key))
745 break;
746
747 vers = findnode (rcs->versions, key);
748 if (vers == NULL)
749 error (1, 0,
750 "mismatch in rcs file %s between deltas and deltatexts (%s)",
751 rcs->path, key);
752
753 vnode = (RCSVers *) vers->data;
754
755 while (rcsbuf_getkey (&rcsbuf, &key, &value))
756 {
757 if (! STREQ (key, "text"))
758 {
759 Node *kv;
760
761 if (vnode->other == NULL)
762 vnode->other = getlist ();
763 kv = getnode ();
764 kv->type = rcsbuf_valcmp (&rcsbuf) ? RCSCMPFLD : RCSFIELD;
765 kv->key = xstrdup (key);
766 kv->data = rcsbuf_valcopy (&rcsbuf, value, kv->type == RCSFIELD,
767 (size_t *) NULL);
768 if (addnode (vnode->other, kv) != 0)
769 {
770 error (0, 0,
771 "\
772warning: duplicate key `%s' in version `%s' of RCS file `%s'",
773 key, vnode->version, rcs->path);
774 freenode (kv);
775 }
776
777 continue;
778 }
779
780 if (! STREQ (vnode->version, rcs->head))
781 {
782 unsigned long add, del;
783 char buf[50];
784 Node *kv;
785
786 /* This is a change text. Store the add and delete
787 counts. */
788 add = 0;

--- 82 unchanged lines hidden (view full) ---

871 next revision. */
872 break;
873 }
874 }
875
876 rcsbuf_cache (rcs, &rcsbuf);
877}
878
879/*
880 * freercsnode - free up the info for an RCSNode
881 */
882void
883freercsnode (rnodep)
884 RCSNode **rnodep;
885{
886 if (rnodep == NULL || *rnodep == NULL)

--- 77 unchanged lines hidden (view full) ---

964
965/*
966 * rcsvers_delproc - free up an RCSVers type node
967 */
968static void
969rcsvers_delproc (p)
970 Node *p;
971{
972 free_rcsvers_contents ((RCSVers *) p->data);
973}
974
975/* These functions retrieve keys and values from an RCS file using a
976 buffer. We use this somewhat complex approach because it turns out
977 that for many common operations, CVS spends most of its time
978 reading keys, so it's worth doing some fairly hairy optimization. */
979
980/* The number of bytes we try to read each time we need more data. */

--- 1201 unchanged lines hidden (view full) ---

2182 * Returns the requested version number of the RCS file, satisfying tags and/or
2183 * dates, and walking branches, if necessary.
2184 *
2185 * The result is returned; null-string if error.
2186 */
2187char *
2188RCS_getversion (rcs, tag, date, force_tag_match, simple_tag)
2189 RCSNode *rcs;
2190 char *tag;
2191 char *date;
2192 int force_tag_match;
2193 int *simple_tag;
2194{
2195 if (simple_tag != NULL)
2196 *simple_tag = 0;
2197
2198 /* make sure we have something to look at... */
2199 assert (rcs != NULL);

--- 16 unchanged lines hidden (view full) ---

2216 branch = xstrdup (tag);
2217
2218 /* Fetch the revision of branch as of date. */
2219 rev = RCS_getdatebranch (rcs, date, branch);
2220 free (branch);
2221 return (rev);
2222 }
2223 else if (tag)
2224 return (RCS_gettag (rcs, tag, force_tag_match, simple_tag));
2225 else if (date)
2226 return (RCS_getdate (rcs, date, force_tag_match));
2227 else
2228 return (RCS_head (rcs));
2229
2230}
2231
2232/*
2233 * Get existing revision number corresponding to tag or revision.
2234 * Similar to RCS_gettag but less interpretation imposed.
2235 * For example:
2236 * -- If tag designates a magic branch, RCS_tag2rev
2237 * returns the magic branch number.
2238 * -- If tag is a branch tag, returns the branch number, not
2239 * the revision of the head of the branch.

--- 89 unchanged lines hidden (view full) ---

2329 *
2330 * If the matched tag is a branch tag, find the head of the branch.
2331 *
2332 * Returns pointer to newly malloc'd string, or NULL.
2333 */
2334char *
2335RCS_gettag (rcs, symtag, force_tag_match, simple_tag)
2336 RCSNode *rcs;
2337 char *symtag;
2338 int force_tag_match;
2339 int *simple_tag;
2340{
2341 char *tag = symtag;
2342 int tag_allocated = 0;
2343
2344 if (simple_tag != NULL)
2345 *simple_tag = 0;
2346
2347 /* make sure we have something to look at... */
2348 assert (rcs != NULL);
2349
2350 /* XXX this is probably not necessary, --jtc */
2351 if (rcs->flags & PARTIAL)
2352 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
2353
2354 /* If tag is "HEAD", special case to get head RCS revision */
2355 if (tag && STREQ (tag, TAG_HEAD))
2356#if 0 /* This #if 0 is only in the Cygnus code. Why? Death support? */
2357 if (force_tag_match && (rcs->flags & VALID) && (rcs->flags & INATTIC))
2358 return ((char *) NULL); /* head request for removed file */
2359 else
2360#endif
2361 return (RCS_head (rcs));
2362
2363 if (!isdigit ((unsigned char) tag[0]))
2364 {
2365 char *version;
2366
2367 /* If we got a symbolic tag, resolve it to a numeric */
2368 version = translate_symtag (rcs, tag);
2369 if (version != NULL)
2370 {
2371 int dots;
2372 char *magic, *branch, *cp;
2373
2374 tag = version;
2375 tag_allocated = 1;
2376
2377 /*
2378 * If this is a magic revision, we turn it into either its
2379 * physical branch equivalent (if one exists) or into
2380 * its base revision, which we assume exists.
2381 */
2382 dots = numdots (tag);
2383 if (dots > 2 && (dots & 1) != 0)

--- 11 unchanged lines hidden (view full) ---

2395 /* it's magic. See if the branch exists */
2396 *cp = '\0'; /* turn it into a revision */
2397 (void) sprintf (magic, "%s.%s", tag, branch);
2398 branch = RCS_getbranch (rcs, magic, 1);
2399 free (magic);
2400 if (branch != NULL)
2401 {
2402 free (tag);
2403 return (branch);
2404 }
2405 return (tag);
2406 }
2407 free (magic);
2408 }
2409 }
2410 else
2411 {
2412 /* The tag wasn't there, so return the head or NULL */
2413 if (force_tag_match)
2414 return (NULL);
2415 else
2416 return (RCS_head (rcs));
2417 }
2418 }
2419
2420 /*
2421 * numeric tag processing:
2422 * 1) revision number - just return it
2423 * 2) branch number - find head of branch
2424 */
2425
2426 /* strip trailing dots */
2427 while (tag[strlen (tag) - 1] == '.')
2428 tag[strlen (tag) - 1] = '\0';
2429
2430 if ((numdots (tag) & 1) == 0)
2431 {
2432 char *branch;
2433
2434 /* we have a branch tag, so we need to walk the branch */
2435 branch = RCS_getbranch (rcs, tag, force_tag_match);
2436 if (tag_allocated)
2437 free (tag);
2438 return branch;
2439 }
2440 else
2441 {
2442 Node *p;
2443
2444 /* we have a revision tag, so make sure it exists */
2445 p = findnode (rcs->versions, tag);
2446 if (p != NULL)
2447 {
2448 /* We have found a numeric revision for the revision tag.
2449 To support expanding the RCS keyword Name, if
2450 SIMPLE_TAG is not NULL, tell the the caller that this
2451 is a simple tag which co will recognize. FIXME: Are
2452 there other cases in which we should set this? In
2453 particular, what if we expand RCS keywords internally
2454 without calling co? */
2455 if (simple_tag != NULL)
2456 *simple_tag = 1;
2457 if (! tag_allocated)
2458 tag = xstrdup (tag);
2459 return (tag);
2460 }
2461 else
2462 {
2463 /* The revision wasn't there, so return the head or NULL */
2464 if (tag_allocated)
2465 free (tag);
2466 if (force_tag_match)
2467 return (NULL);
2468 else
2469 return (RCS_head (rcs));
2470 }
2471 }
2472}
2473
2474/*
2475 * Return a "magic" revision as a virtual branch off of REV for the RCS file.
2476 * A "magic" revision is one which is unique in the RCS file. By unique, I
2477 * mean we return a revision which:

--- 208 unchanged lines hidden (view full) ---

2686/*
2687 * Get the head of the specified branch. If the branch does not exist,
2688 * return NULL or RCS_head depending on force_tag_match.
2689 * Returns NULL or a newly malloc'd string.
2690 */
2691char *
2692RCS_getbranch (rcs, tag, force_tag_match)
2693 RCSNode *rcs;
2694 char *tag;
2695 int force_tag_match;
2696{
2697 Node *p, *head;
2698 RCSVers *vn;
2699 char *xtag;
2700 char *nextvers;
2701 char *cp;
2702

--- 20 unchanged lines hidden (view full) ---

2723 if (p == NULL)
2724 {
2725 free (xtag);
2726 if (force_tag_match)
2727 return (NULL);
2728 else
2729 return (RCS_head (rcs));
2730 }
2731 vn = (RCSVers *) p->data;
2732 cp = vn->next;
2733 }
2734 free (xtag);
2735 if (cp == NULL)
2736 {
2737 if (force_tag_match)
2738 return (NULL);
2739 else

--- 16 unchanged lines hidden (view full) ---

2756 /* if the base revision didn't exist, return head or NULL */
2757 if (force_tag_match)
2758 return (NULL);
2759 else
2760 return (RCS_head (rcs));
2761 }
2762
2763 /* find the first element of the branch we are looking for */
2764 vn = (RCSVers *) p->data;
2765 if (vn->branches == NULL)
2766 return (NULL);
2767 xtag = xmalloc (strlen (tag) + 1 + 1); /* 1 for the extra '.' */
2768 (void) strcpy (xtag, tag);
2769 (void) strcat (xtag, ".");
2770 head = vn->branches->list;
2771 for (p = head->next; p != head; p = p->next)
2772 if (strncmp (p->key, xtag, strlen (xtag)) == 0)

--- 17 unchanged lines hidden (view full) ---

2790 if (p == NULL)
2791 {
2792 /* a link in the chain is missing - return head or NULL */
2793 if (force_tag_match)
2794 return (NULL);
2795 else
2796 return (RCS_head (rcs));
2797 }
2798 vn = (RCSVers *) p->data;
2799 nextvers = vn->next;
2800 } while (nextvers != NULL);
2801
2802 /* we have the version in our hand, so go for it */
2803 return (xstrdup (vn->version));
2804}
2805
2806/* Returns the head of the branch which REV is on. REV can be a

--- 74 unchanged lines hidden (view full) ---

2881 *bp = '\0';
2882
2883 vp = findnode (rcs->versions, branch);
2884 if (vp == NULL)
2885 {
2886 error (0, 0, "%s: can't find branch point %s", rcs->path, target);
2887 return NULL;
2888 }
2889 rev = (RCSVers *) vp->data;
2890
2891 *bp++ = '.';
2892 while (*bp && *bp != '.')
2893 ++bp;
2894 brlen = bp - branch;
2895
2896 vp = rev->branches->list->next;
2897 while (vp != rev->branches->list)

--- 42 unchanged lines hidden (view full) ---

2940
2941/*
2942 * Get the most recent revision, based on the supplied date, but use some
2943 * funky stuff and follow the vendor branch maybe
2944 */
2945char *
2946RCS_getdate (rcs, date, force_tag_match)
2947 RCSNode *rcs;
2948 char *date;
2949 int force_tag_match;
2950{
2951 char *cur_rev = NULL;
2952 char *retval = NULL;
2953 Node *p;
2954 RCSVers *vers = NULL;
2955
2956 /* make sure we have something to look at... */

--- 17 unchanged lines hidden (view full) ---

2974 if (p == NULL)
2975 {
2976 error (0, 0, "%s: head revision %s doesn't exist", rcs->path,
2977 rcs->head);
2978 }
2979 while (p != NULL)
2980 {
2981 /* if the date of this one is before date, take it */
2982 vers = (RCSVers *) p->data;
2983 if (RCS_datecmp (vers->date, date) <= 0)
2984 {
2985 cur_rev = vers->version;
2986 break;
2987 }
2988
2989 /* if there is a next version, find the node */
2990 if (vers->next != NULL)

--- 21 unchanged lines hidden (view full) ---

3012 1.1.1.1 version, then return 1.1. This happens when the first
3013 version of a file is created by a regular cvs add and commit,
3014 and there is a subsequent cvs import of the same file. */
3015 p = findnode (rcs->versions, "1.1.1.1");
3016 if (p)
3017 {
3018 char *date_1_1 = vers->date;
3019
3020 vers = (RCSVers *) p->data;
3021 if (RCS_datecmp (vers->date, date_1_1) != 0)
3022 return xstrdup ("1.1");
3023 }
3024 }
3025
3026 /* look on the vendor branch */
3027 retval = RCS_getdatebranch (rcs, date, CVSBRANCH);
3028
3029 /*
3030 * if we found a match, return it; otherwise, we return the first
3031 * revision on the trunk or NULL depending on force_tag_match and the
3032 * date of the first rev
3033 */
3034 if (retval != NULL)
3035 return (retval);
3036
3037 if (!force_tag_match ||
3038 (vers != NULL && RCS_datecmp (vers->date, date) <= 0))
3039 return (xstrdup (vers->version));
3040 else
3041 return (NULL);
3042}
3043
3044/*
3045 * Look up the last element on a branch that was put in before the specified
3046 * date (return the rev or NULL)
3047 */
3048static char *
3049RCS_getdatebranch (rcs, date, branch)
3050 RCSNode *rcs;
3051 char *date;
3052 char *branch;
3053{
3054 char *cur_rev = NULL;
3055 char *cp;
3056 char *xbranch, *xrev;
3057 Node *p;
3058 RCSVers *vers;
3059
3060 /* look up the first revision on the branch */

--- 10 unchanged lines hidden (view full) ---

3071
3072 if (rcs->flags & PARTIAL)
3073 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3074
3075 p = findnode (rcs->versions, xrev);
3076 free (xrev);
3077 if (p == NULL)
3078 return (NULL);
3079 vers = (RCSVers *) p->data;
3080
3081 /* Tentatively use this revision, if it is early enough. */
3082 if (RCS_datecmp (vers->date, date) <= 0)
3083 cur_rev = vers->version;
3084
3085 /* If no branches list, return now. This is what happens if the branch
3086 is a (magic) branch with no revisions yet. */
3087 if (vers->branches == NULL)

--- 16 unchanged lines hidden (view full) ---

3104 return xstrdup (cur_rev);
3105 }
3106
3107 p = findnode (rcs->versions, p->key);
3108
3109 /* walk the next pointers until you find the end, or the date is too late */
3110 while (p != NULL)
3111 {
3112 vers = (RCSVers *) p->data;
3113 if (RCS_datecmp (vers->date, date) <= 0)
3114 cur_rev = vers->version;
3115 else
3116 break;
3117
3118 /* if there is a next version, find the node */
3119 if (vers->next != NULL)
3120 p = findnode (rcs->versions, vers->next);
3121 else
3122 p = (Node *) NULL;
3123 }
3124
3125 /* Return whatever we found, which may be NULL. */
3126 return xstrdup (cur_rev);
3127}
3128
3129/*
3130 * Compare two dates in RCS format. Beware the change in format on January 1,
3131 * 2000, when years go from 2-digit to full format.
3132 */
3133int
3134RCS_datecmp (date1, date2)
3135 char *date1, *date2;
3136{
3137 int length_diff = strlen (date1) - strlen (date2);
3138
3139 return (length_diff ? length_diff : strcmp (date1, date2));
3140}
3141
3142/* Look up revision REV in RCS and return the date specified for the
3143 revision minus FUDGE seconds (FUDGE will generally be one, so that the
3144 logically previous revision will be found later, or zero, if we want
3145 the exact date).
3146
3147 The return value is the date being returned as a time_t, or (time_t)-1
3148 on error (previously was documented as zero on error; I haven't checked
3149 the callers to make sure that they really check for (time_t)-1, but
3150 the latter is what this function really returns). If DATE is non-NULL,
3151 then it must point to MAXDATELEN characters, and we store the same
3152 return value there in DATEFORM format. */
3153time_t
3154RCS_getrevtime (rcs, rev, date, fudge)
3155 RCSNode *rcs;
3156 char *rev;
3157 char *date;
3158 int fudge;
3159{
3160 char tdate[MAXDATELEN];
3161 struct tm xtm, *ftm;
3162 time_t revdate = 0;
3163 Node *p;
3164 RCSVers *vers;
3165
3166 /* make sure we have something to look at... */
3167 assert (rcs != NULL);
3168
3169 if (rcs->flags & PARTIAL)
3170 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3171
3172 /* look up the revision */
3173 p = findnode (rcs->versions, rev);
3174 if (p == NULL)
3175 return (-1);
3176 vers = (RCSVers *) p->data;
3177
3178 /* split up the date */
3179 ftm = &xtm;
3180 (void) sscanf (vers->date, SDATEFORM, &ftm->tm_year, &ftm->tm_mon,
3181 &ftm->tm_mday, &ftm->tm_hour, &ftm->tm_min,
3182 &ftm->tm_sec);
3183
3184 /* If the year is from 1900 to 1999, RCS files contain only two
3185 digits, and sscanf gives us a year from 0-99. If the year is
3186 2000+, RCS files contain all four digits and we subtract 1900,
3187 because the tm_year field should contain years since 1900. */
3188
3189 if (ftm->tm_year > 1900)
3190 ftm->tm_year -= 1900;
3191
3192 /* put the date in a form getdate can grok */
3193 (void) sprintf (tdate, "%d/%d/%d GMT %d:%d:%d", ftm->tm_mon,
3194 ftm->tm_mday, ftm->tm_year + 1900, ftm->tm_hour,
3195 ftm->tm_min, ftm->tm_sec);
3196
3197 /* turn it into seconds since the epoch */
3198 revdate = get_date (tdate, (struct timeb *) NULL);
3199 if (revdate != (time_t) -1)
3200 {
3201 revdate -= fudge; /* remove "fudge" seconds */
3202 if (date)
3203 {
3204 /* put an appropriate string into ``date'' if we were given one */
3205 ftm = gmtime (&revdate);
3206 (void) sprintf (date, DATEFORM,
3207 ftm->tm_year + (ftm->tm_year < 100 ? 0 : 1900),
3208 ftm->tm_mon + 1, ftm->tm_mday, ftm->tm_hour,
3209 ftm->tm_min, ftm->tm_sec);
3210 }
3211 }
3212 return (revdate);
3213}
3214
3215List *
3216RCS_getlocks (rcs)
3217 RCSNode *rcs;
3218{
3219 assert(rcs != NULL);
3220

--- 218 unchanged lines hidden (view full) ---

3439
3440 if (rcs->flags & PARTIAL)
3441 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
3442
3443 p = findnode (rcs->versions, tag);
3444 if (p == NULL)
3445 return (0);
3446
3447 version = (RCSVers *) p->data;
3448 return (version->dead);
3449}
3450
3451/* Return the RCS keyword expansion mode. For example "b" for binary.
3452 Returns a pointer into storage which is allocated and freed along with
3453 the rest of the RCS information; the caller should not modify this
3454 storage. Returns NULL if the RCS file does not specify a keyword
3455 expansion mode; for all other errors, die with a fatal error. */

--- 6 unchanged lines hidden (view full) ---

3462 assert (rcs != NULL);
3463 return rcs->expand;
3464}
3465
3466/* Set keyword expansion mode to EXPAND. For example "b" for binary. */
3467void
3468RCS_setexpand (rcs, expand)
3469 RCSNode *rcs;
3470 char *expand;
3471{
3472 /* Since RCS_parsercsfile_i now reads expand, don't need to worry
3473 about RCS_reparsercsfile. */
3474 assert (rcs != NULL);
3475 if (rcs->expand != NULL)
3476 free (rcs->expand);
3477 rcs->expand = xstrdup (expand);
3478}

--- 265 unchanged lines hidden (view full) ---

3744 free_value = 1;
3745 break;
3746
3747 case KEYWORD_CVSHEADER:
3748 case KEYWORD_HEADER:
3749 case KEYWORD_ID:
3750 case KEYWORD_LOCALID:
3751 {
3752 char *path;
3753 int free_path;
3754 char *date;
3755 char *old_path;
3756
3757 old_path = NULL;
3758 if (kw == KEYWORD_HEADER ||
3759 (kw == KEYWORD_LOCALID &&
3760 keyword_local == KEYWORD_HEADER))

--- 15 unchanged lines hidden (view full) ---

3776 + 20);
3777
3778 sprintf (value, "%s %s %s %s %s%s%s",
3779 path, ver->version, date, ver->author,
3780 ver->state,
3781 locker != NULL ? " " : "",
3782 locker != NULL ? locker : "");
3783 if (free_path)
3784 free (path);
3785 if (old_path)
3786 free (old_path);
3787 free (date);
3788 free_value = 1;
3789 }
3790 break;
3791
3792 case KEYWORD_LOCKER:

--- 262 unchanged lines hidden (view full) ---

4055 free (ebufs->data);
4056 next = ebufs->next;
4057 free (ebufs);
4058 ebufs = next;
4059 }
4060 }
4061}
4062
4063/* Check out a revision from an RCS file.
4064
4065 If PFN is not NULL, then ignore WORKFILE and SOUT. Call PFN zero
4066 or more times with the contents of the file. CALLERDAT is passed,
4067 uninterpreted, to PFN. (The current code will always call PFN
4068 exactly once for a non empty file; however, the current code
4069 assumes that it can hold the entire file contents in memory, which
4070 is not a good assumption, and might change in the future).

--- 23 unchanged lines hidden (view full) ---

4094
4095/* This function mimics the behavior of `rcs co' almost exactly. The
4096 chief difference is in its support for preserving file ownership,
4097 permissions, and special files across checkin and checkout -- see
4098 comments in RCS_checkin for some issues about this. -twp */
4099
4100int
4101RCS_checkout (rcs, workfile, rev, nametag, options, sout, pfn, callerdat)
4102 RCSNode *rcs;
4103 char *workfile;
4104 char *rev;
4105 char *nametag;
4106 char *options;
4107 char *sout;
4108 RCSCHECKOUTPROC pfn;
4109 void *callerdat;
4110{
4111 int free_rev = 0;
4112 enum kflag expand;
4113 FILE *fp, *ofp;
4114 struct stat sb;
4115 struct rcsbuffer rcsbuf;
4116 char *key;
4117 char *value;

--- 10 unchanged lines hidden (view full) ---

4128 int change_rcs_mode = 0;
4129 int special_file = 0;
4130 unsigned long devnum_long;
4131 dev_t devnum = 0;
4132#endif
4133
4134 if (trace)
4135 {
4136 (void) fprintf (stderr, "%s-> checkout (%s, %s, %s, %s)\n",
4137#ifdef SERVER_SUPPORT
4138 server_active ? "S" : " ",
4139#else
4140 "",
4141#endif
4142 rcs->path,
4143 rev != NULL ? rev : "",
4144 options != NULL ? options : "",
4145 (pfn != NULL ? "(function)"
4146 : (workfile != NULL
4147 ? workfile
4148 : (sout != RUN_TTY ? sout : "(stdout)"))));
4149 }
4150
4151 assert (rev == NULL || isdigit ((unsigned char) *rev));

--- 38 unchanged lines hidden (view full) ---

4190 break;
4191 }
4192 }
4193
4194 if (! gothead)
4195 {
4196 error (0, 0, "internal error: cannot find head text");
4197 if (free_rev)
4198 free (rev);
4199 return 1;
4200 }
4201
4202 rcsbuf_valpolish (&rcsbuf, value, 0, &len);
4203
4204 if (fstat (fileno (fp), &sb) < 0)
4205 error (1, errno, "cannot fstat %s", rcs->path);
4206

--- 69 unchanged lines hidden (view full) ---

4276 {
4277 RCSVers *vers;
4278 Node *info;
4279
4280 vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4281 if (vp == NULL)
4282 error (1, 0, "internal error: no revision information for %s",
4283 rev == NULL ? rcs->head : rev);
4284 vers = (RCSVers *) vp->data;
4285
4286 /* First we look for symlinks, which are simplest to handle. */
4287 info = findnode (vers->other_delta, "symlink");
4288 if (info != NULL)
4289 {
4290 char *dest;
4291
4292 if (pfn != NULL || (workfile == NULL && sout == RUN_TTY))

--- 8 unchanged lines hidden (view full) ---

4301 since we just want the file not to be there. (TODO: decide
4302 whether it should be considered an error for `dest' to exist
4303 at this point. If so, the unlink call should be removed and
4304 `symlink' should signal the error. -twp) */
4305 if (CVS_UNLINK (dest) < 0 && !existence_error (errno))
4306 error (1, errno, "cannot remove %s", dest);
4307 if (symlink (info->data, dest) < 0)
4308 error (1, errno, "cannot create symbolic link from %s to %s",
4309 dest, info->data);
4310 if (free_value)
4311 free (value);
4312 if (free_rev)
4313 free (rev);
4314 return 0;
4315 }
4316
4317 /* Next, we look at this file's hardlinks field, and see whether
4318 it is linked to any other file that has been checked out.
4319 If so, we don't do anything else -- just link it to that file.
4320
4321 If we are checking out a file to a pipe or temporary storage,

--- 24 unchanged lines hidden (view full) ---

4346
4347 If one of these conditions is not met, then
4348 workfile is the first one in its hardlink group to
4349 be checked out, and we must continue with a full
4350 checkout. */
4351
4352 if (uptodate_link != NULL)
4353 {
4354 struct hardlink_info *hlinfo =
4355 (struct hardlink_info *) uptodate_link->data;
4356
4357 if (link (uptodate_link->key, workfile) < 0)
4358 error (1, errno, "cannot link %s to %s",
4359 workfile, uptodate_link->key);
4360 hlinfo->checked_out = 1; /* probably unnecessary */
4361 if (free_value)
4362 free (value);
4363 if (free_rev)
4364 free (rev);
4365 return 0;
4366 }
4367 }
4368 }
4369
4370 info = findnode (vers->other_delta, "owner");
4371 if (info != NULL)
4372 {

--- 16 unchanged lines hidden (view full) ---

4389 if (info != NULL)
4390 {
4391 /* If the size of `devtype' changes, fix the sscanf call also */
4392 char devtype[16];
4393
4394 if (sscanf (info->data, "%15s %lu",
4395 devtype, &devnum_long) < 2)
4396 error (1, 0, "%s:%s has bad `special' newphrase %s",
4397 workfile, vers->version, info->data);
4398 devnum = devnum_long;
4399 if (STREQ (devtype, "character"))
4400 special_file = S_IFCHR;
4401 else if (STREQ (devtype, "block"))
4402 special_file = S_IFBLK;
4403 else
4404 error (0, 0, "%s is a special file of unsupported type `%s'",
4405 workfile, info->data);
4406 }
4407 }
4408#endif
4409
4410 if (expand != KFLAG_O && expand != KFLAG_B)
4411 {
4412 char *newvalue;
4413
4414 /* Don't fetch the delta node again if we already have it. */
4415 if (vp == NULL)
4416 {
4417 vp = findnode (rcs->versions, rev == NULL ? rcs->head : rev);
4418 if (vp == NULL)
4419 error (1, 0, "internal error: no revision information for %s",
4420 rev == NULL ? rcs->head : rev);
4421 }
4422
4423 expand_keywords (rcs, (RCSVers *) vp->data, nametag, log, loglen,
4424 expand, value, len, &newvalue, &len);
4425
4426 if (newvalue != value)
4427 {
4428 if (free_value)
4429 free (value);
4430 value = newvalue;
4431 free_value = 1;
4432 }
4433 }
4434
4435 if (free_rev)
4436 free (rev);
4437
4438 if (log != NULL)
4439 {
4440 free (log);
4441 log = NULL;
4442 }
4443
4444 if (pfn != NULL)

--- 253 unchanged lines hidden (view full) ---

4698 p = findnode (rcs->versions, lock->key);
4699 if (p == NULL)
4700 {
4701 error (0, 0, "%s: can't unlock nonexistent revision %s",
4702 rcs->path,
4703 lock->key);
4704 return NULL;
4705 }
4706 return (RCSVers *) p->data;
4707 }
4708
4709 /* No existing lock. The RCS rule is that this is an error unless
4710 locking is nonstrict AND the file is owned by the current
4711 user. Trying to determine the latter is a portability nightmare
4712 in the face of NT, VMS, AFS, and other systems with non-unix-like
4713 ideas of users and owners. In the case of CVS, we should never get
4714 here (as long as the traditional behavior of making sure to call
4715 RCS_lock persists). Anyway, we skip the RCS error checks
4716 and just return the default branch or head. The reasoning is that
4717 those error checks are to make users lock before a checkin, and we do
4718 that in other ways if at all anyway (e.g. rcslock.pl). */
4719
4720 p = findnode (rcs->versions, RCS_getbranch (rcs, rcs->branch, 0));
4721 return (RCSVers *) p->data;
4722}
4723
4724/* Revision number string, R, must contain a `.'.
4725 Return a newly-malloc'd copy of the prefix of R up
4726 to but not including the final `.'. */
4727
4728static char *
4729truncate_revnum (r)

--- 114 unchanged lines hidden (view full) ---

4844 nodep = findnode (rcs->versions, branchpoint);
4845 if (nodep == NULL)
4846 {
4847 error (0, 0, "%s: can't find branch point %s", rcs->path, branchpoint);
4848 free (branchpoint);
4849 return NULL;
4850 }
4851 free (branchpoint);
4852 branchnode = (RCSVers *) nodep->data;
4853
4854 /* If BRANCH was a full branch number, make sure it is higher than MAX. */
4855 if ((numdots (branch) & 1) == 1)
4856 {
4857 if (branchnode->branches == NULL)
4858 {
4859 /* We have to create the first branch on this node, which means
4860 appending ".2" to the revision number. */

--- 83 unchanged lines hidden (view full) ---

4944 solution -- precisely because it diverges from RCS's behavior -- but
4945 it doesn't seem feasible to do this anywhere else in the code. [-twp]
4946
4947 Return value is -1 for error (and errno is set to indicate the
4948 error), positive for error (and an error message has been printed),
4949 or zero for success. */
4950
4951int
4952RCS_checkin (rcs, workfile, message, rev, flags)
4953 RCSNode *rcs;
4954 char *workfile;
4955 char *message;
4956 char *rev;
4957 int flags;
4958{
4959 RCSVers *delta, *commitpt;
4960 Deltatext *dtext;
4961 Node *nodep;
4962 char *tmpfile, *changefile, *chtext;
4963 char *diffopts;
4964 size_t bufsize;
4965 int buflen, chtextlen;
4966 int status, checkin_quiet, allocated_workfile;
4967 struct tm *ftm;
4968 time_t modtime;
4969 int adding_branch = 0;
4970#ifdef PRESERVE_PERMISSIONS_SUPPORT
4971 struct stat sb;
4972#endif
4973
4974 commitpt = NULL;
4975
4976 if (rcs->flags & PARTIAL)
4977 RCS_reparsercsfile (rcs, (FILE **) NULL, (struct rcsbuffer *) NULL);
4978
4979 /* Get basename of working file. Is there a library function to
4980 do this? I couldn't find one. -twp */
4981 allocated_workfile = 0;
4982 if (workfile == NULL)
4983 {
4984 char *p;
4985 int extlen = strlen (RCSEXT);
4986 workfile = xstrdup (last_component (rcs->path));
4987 p = workfile + (strlen (workfile) - extlen);
4988 assert (strncmp (p, RCSEXT, extlen) == 0);
4989 *p = '\0';
4990 allocated_workfile = 1;
4991 }
4992
4993 /* If the filename is a symbolic link, follow it and replace it
4994 with the destination of the link. We need to do this before
4995 calling rcs_internal_lockfile, or else we won't put the lock in
4996 the right place. */
4997 resolve_symlink (&(rcs->path));
4998

--- 140 unchanged lines hidden (view full) ---

5139
5140 /* Don't need to xstrdup NEWREV because it's already dynamic, and
5141 not used for anything else. (Don't need to free it, either.) */
5142 rcs->head = newrev;
5143 delta->version = xstrdup (newrev);
5144 nodep = getnode();
5145 nodep->type = RCSVERS;
5146 nodep->delproc = rcsvers_delproc;
5147 nodep->data = (char *) delta;
5148 nodep->key = delta->version;
5149 (void) addnode (rcs->versions, nodep);
5150
5151 dtext->version = xstrdup (newrev);
5152 bufsize = 0;
5153#ifdef PRESERVE_PERMISSIONS_SUPPORT
5154 if (preserve_perms && !S_ISREG (sb.st_mode))
5155 /* Pretend file is empty. */

--- 163 unchanged lines hidden (view full) ---

5319 delta->version = xstrdup (newrev);
5320 }
5321 else
5322 /* Just increment the tip number to get the new revision. */
5323 delta->version = increment_revnum (tip);
5324 }
5325
5326 nodep = findnode (rcs->versions, tip);
5327 commitpt = (RCSVers *) nodep->data;
5328
5329 free (branch);
5330 free (newrev);
5331 free (tip);
5332 }
5333
5334 assert (delta->version != NULL);
5335

--- 12 unchanged lines hidden (view full) ---

5348 the end of addbranch in ci.c in RCS 5.7, it calls
5349 removelock only if it is our own lock, not someone
5350 else's). */
5351
5352 if (!adding_branch)
5353 {
5354 error (0, 0, "%s: revision %s locked by %s",
5355 rcs->path,
5356 nodep->key, nodep->data);
5357 status = 1;
5358 goto checkin_done;
5359 }
5360 }
5361 else
5362 delnode (nodep);
5363 }
5364

--- 13 unchanged lines hidden (view full) ---

5378 : "-ko"),
5379 tmpfile,
5380 (RCSCHECKOUTPROC)0, NULL);
5381 if (status != 0)
5382 error (1, 0,
5383 "could not check out revision %s of `%s'",
5384 commitpt->version, rcs->path);
5385
5386 bufsize = buflen = 0;
5387 chtext = NULL;
5388 chtextlen = 0;
5389 changefile = cvs_temp_name();
5390
5391 /* Diff options should include --binary if the RCS file has -kb set
5392 in its `expand' field. */
5393 diffopts = (rcs->expand != NULL && STREQ (rcs->expand, "b")
5394 ? "-a -n --binary"
5395 : "-a -n");
5396

--- 117 unchanged lines hidden (view full) ---

5514 }
5515
5516 /* Add DELTA to RCS->VERSIONS. */
5517 if (rcs->versions == NULL)
5518 rcs->versions = getlist();
5519 nodep = getnode();
5520 nodep->type = RCSVERS;
5521 nodep->delproc = rcsvers_delproc;
5522 nodep->data = (char *) delta;
5523 nodep->key = delta->version;
5524 (void) addnode (rcs->versions, nodep);
5525
5526 /* Write the new RCS file, inserting the new delta at COMMITPT. */
5527 if (!checkin_quiet)
5528 {
5529 cvs_output ("new revision: ", 14);
5530 cvs_output (delta->version, 0);

--- 16 unchanged lines hidden (view full) ---

5547 if (unlink_file (changefile) < 0)
5548 error (0, errno, "cannot remove %s", changefile);
5549 free (changefile);
5550
5551 if (!checkin_quiet)
5552 cvs_output ("done\n", 5);
5553
5554 checkin_done:
5555 if (allocated_workfile)
5556 free (workfile);
5557
5558 if (commitpt != NULL && commitpt->text != NULL)
5559 {
5560 freedeltatext (commitpt->text);
5561 commitpt->text = NULL;
5562 }
5563
5564 freedeltatext (dtext);
5565 if (status != 0)
5566 free_rcsvers_contents (delta);
5567
5568 return status;
5569}
5570
5571/* This structure is passed between RCS_cmp_file and cmp_file_buffer. */
5572
5573struct cmp_file_data
5574{
5575 const char *filename;
5576 FILE *fp;
5577 int different;
5578};
5579
5580/* Compare the contents of revision REV of RCS file RCS with the
5581 contents of the file FILENAME. OPTIONS is a string for the keyword
5582 expansion options. Return 0 if the contents of the revision are
5583 the same as the contents of the file, 1 if they are different. */
5584
5585int
5586RCS_cmp_file (rcs, rev, options, filename)
5587 RCSNode *rcs;
5588 char *rev;
5589 char *options;
5590 const char *filename;
5591{
5592 int binary;
5593 FILE *fp;
5594 struct cmp_file_data data;
5595 int retcode;
5596
5597 if (options != NULL && options[0] != '\0')
5598 binary = STREQ (options, "-kb");
5599 else
5600 {
5601 char *expand;
5602
5603 expand = RCS_getexpand (rcs);

--- 11 unchanged lines hidden (view full) ---

5615 so calling it simplifies RCS_cmp_file. We *could* just yank
5616 the delta node out of the version tree and look for device
5617 numbers, but writing to disk and calling xcmp is a better
5618 abstraction (therefore probably more robust). -twp */
5619
5620 if (preserve_perms)
5621 {
5622 char *tmp;
5623
5624 tmp = cvs_temp_name();
5625 retcode = RCS_checkout(rcs, NULL, rev, NULL, options, tmp, NULL, NULL);
5626 if (retcode != 0)
5627 return 1;
5628
5629 retcode = xcmp (tmp, filename);
5630 if (CVS_UNLINK (tmp) < 0)
5631 error (0, errno, "cannot remove %s", tmp);
5632 free (tmp);
5633 return retcode;
5634 }
5635 else
5636#endif
5637 {
5638 fp = CVS_FOPEN (filename, binary ? FOPEN_BINARY_READ : "r");
5639 if (fp == NULL)
5640 /* FIXME-update-dir: should include update_dir in message. */
5641 error (1, errno, "cannot open file %s for comparing", filename);
5642
5643 data.filename = filename;
5644 data.fp = fp;
5645 data.different = 0;
5646
5647 retcode = RCS_checkout (rcs, (char *) NULL, rev, (char *) NULL,
5648 options, RUN_TTY, cmp_file_buffer,
5649 (void *) &data);
5650
5651 /* If we have not yet found a difference, make sure that we are at
5652 the end of the file. */
5653 if (! data.different)
5654 {
5655 if (getc (fp) != EOF)
5656 data.different = 1;
5657 }
5658
5659 fclose (fp);
5660
5661 if (retcode != 0)
5662 return 1;
5663
5664 return data.different;
5665 }
5666}
5667
5668/* This is a subroutine of RCS_cmp_file. It is passed to
5669 RCS_checkout. */
5670
5671#define CMP_BUF_SIZE (8 * 1024)
5672
5673static void
5674cmp_file_buffer (callerdat, buffer, len)
5675 void *callerdat;
5676 const char *buffer;
5677 size_t len;
5678{
5679 struct cmp_file_data *data = (struct cmp_file_data *) callerdat;
5680 char *filebuf;
5681
5682 /* If we've already found a difference, we don't need to check
5683 further. */
5684 if (data->different)
5685 return;
5686
5687 filebuf = xmalloc (len > CMP_BUF_SIZE ? CMP_BUF_SIZE : len);

--- 22 unchanged lines hidden (view full) ---

5710
5711 buffer += checklen;
5712 len -= checklen;
5713 }
5714
5715 free (filebuf);
5716}
5717
5718/* For RCS file RCS, make symbolic tag TAG point to revision REV.
5719 This validates that TAG is OK for a user to use. Return value is
5720 -1 for error (and errno is set to indicate the error), positive for
5721 error (and an error message has been printed), or zero for success. */
5722
5723int
5724RCS_settag (rcs, tag, rev)
5725 RCSNode *rcs;

--- 112 unchanged lines hidden (view full) ---

5838/* FIXME-twp: if a lock owned by someone else is broken, should this
5839 send mail to the lock owner? Prompt user? It seems like such an
5840 obscure situation for CVS as almost not worth worrying much
5841 about. */
5842
5843int
5844RCS_lock (rcs, rev, lock_quiet)
5845 RCSNode *rcs;
5846 char *rev;
5847 int lock_quiet;
5848{
5849 List *locks;
5850 Node *p;
5851 char *user;
5852 char *xrev = NULL;
5853
5854 if (rcs->flags & PARTIAL)

--- 47 unchanged lines hidden (view full) ---

5902 /* Break the lock. */
5903 if (!lock_quiet)
5904 {
5905 cvs_output (rev, 0);
5906 cvs_output (" unlocked\n", 0);
5907 }
5908 delnode (p);
5909#else
5910 error (1, 0, "Revision %s is already locked by %s", xrev, p->data);
5911#endif
5912 }
5913
5914 /* Create a new lock. */
5915 p = getnode();
5916 p->key = xrev; /* already xstrdupped */
5917 p->data = xstrdup (getcaller());
5918 (void) addnode_at_front (locks, p);

--- 99 unchanged lines hidden (view full) ---

6018 {
6019 /* If the revision is locked by someone else, notify
6020 them. Note that this shouldn't ever happen if RCS_unlock
6021 is called with a NULL revision, since that means "whatever
6022 revision is currently locked by the caller." */
6023 char *repos, *workfile;
6024 if (!unlock_quiet)
6025 error (0, 0, "\
6026%s: revision %s locked by %s; breaking lock", rcs->path, xrev, lock->data);
6027 repos = xstrdup (rcs->path);
6028 workfile = strrchr (repos, '/');
6029 *workfile++ = '\0';
6030 notify_do ('C', workfile, user, NULL, NULL, repos);
6031 free (repos);
6032 }
6033
6034 delnode (lock);

--- 288 unchanged lines hidden (view full) ---

6323 before = xstrdup (branchpoint);
6324 *bp = '.';
6325 }
6326 }
6327 else if (! STREQ (rev1, branchpoint))
6328 {
6329 /* Walk deltas from BRANCHPOINT on, looking for REV1. */
6330 nodep = findnode (rcs->versions, branchpoint);
6331 revp = (RCSVers *) nodep->data;
6332 while (revp->next != NULL && ! STREQ (revp->next, rev1))
6333 {
6334 revp = (RCSVers *) nodep->data;
6335 nodep = findnode (rcs->versions, revp->next);
6336 }
6337 if (revp->next == NULL)
6338 {
6339 error (0, 0, "%s: Revision %s doesn't exist.", rcs->path, rev1);
6340 goto delrev_done;
6341 }
6342 if (rev1_inclusive)

--- 27 unchanged lines hidden (view full) ---

6370 /* If any revision between REV1 and REV2 is locked or is a branch point,
6371 we can't delete that revision and must abort. */
6372 after = NULL;
6373 next = rev1;
6374 found = 0;
6375 while (!found && next != NULL)
6376 {
6377 nodep = findnode (rcs->versions, next);
6378 revp = (RCSVers *) nodep->data;
6379
6380 if (rev2 != NULL)
6381 found = STREQ (revp->version, rev2);
6382 next = revp->next;
6383
6384 if ((!found && next != NULL) || rev2_inclusive || rev2 == NULL)
6385 {
6386 if (findnode (RCS_getlocks (rcs), revp->version))

--- 87 unchanged lines hidden (view full) ---

6474 RCS_exec_rcsdiff with some changes, like being able
6475 to suppress diagnostic messages and to direct output. */
6476
6477 if (after != NULL)
6478 {
6479 char *diffbuf;
6480 size_t bufsize, len;
6481
6482#if defined (__CYGWIN32__) || defined (_WIN32)
6483 /* FIXME: This is an awful kludge, but at least until I have
6484 time to work on it a little more and test it, I'd rather
6485 give a fatal error than corrupt the file. I think that we
6486 need to use "-kb" and "--binary" and "rb" to get_file
6487 (probably can do it always, not just for binary files, if
6488 we are consistent between the RCS_checkout and the diff). */
6489 {
6490 char *expand = RCS_getexpand (rcs);
6491 if (expand != NULL && STREQ (expand, "b"))
6492 error (1, 0,
6493 "admin -o not implemented yet for binary on this system");
6494 }
6495#endif
6496
6497 afterfile = cvs_temp_name();
6498 status = RCS_checkout (rcs, NULL, after, NULL, "-ko", afterfile,
6499 (RCSCHECKOUTPROC)0, NULL);
6500 if (status > 0)
6501 goto delrev_done;
6502
6503 if (before == NULL)

--- 38 unchanged lines hidden (view full) ---

6542
6543 diffbuf = NULL;
6544 bufsize = 0;
6545 get_file (outfile, outfile, "r", &diffbuf, &bufsize, &len);
6546 }
6547
6548 /* Save the new change text in after's delta node. */
6549 nodep = findnode (rcs->versions, after);
6550 revp = (RCSVers *) nodep->data;
6551
6552 assert (revp->text == NULL);
6553
6554 revp->text = (Deltatext *) xmalloc (sizeof (Deltatext));
6555 memset ((Deltatext *) revp->text, 0, sizeof (Deltatext));
6556 revp->text->version = xstrdup (revp->version);
6557 revp->text->text = diffbuf;
6558 revp->text->len = len;

--- 11 unchanged lines hidden (view full) ---

6570 /* Walk through the revisions (again) to mark each one as
6571 outdated. (FIXME: would it be safe to use the `dead' field for
6572 this? Doubtful.) */
6573 for (next = rev1;
6574 next != NULL && (after == NULL || ! STREQ (next, after));
6575 next = revp->next)
6576 {
6577 nodep = findnode (rcs->versions, next);
6578 revp = (RCSVers *) nodep->data;
6579 revp->outdated = 1;
6580 }
6581
6582 /* Update delta links. If BEFORE == NULL, we're changing the
6583 head of the tree and don't need to update any `next' links. */
6584 if (before != NULL)
6585 {
6586 /* If REV1 is the first node on its branch, then BEFORE is its

--- 7 unchanged lines hidden (view full) ---

6594
6595 if (rev1 == NULL)
6596 /* beforep's ->next field already should be equal to after,
6597 which I think is always NULL in this case. */
6598 ;
6599 else if (STREQ (rev1, branchpoint))
6600 {
6601 nodep = findnode (rcs->versions, before);
6602 revp = (RCSVers *) nodep->data;
6603 nodep = revp->branches->list->next;
6604 while (nodep != revp->branches->list &&
6605 ! STREQ (nodep->key, rev1))
6606 nodep = nodep->next;
6607 assert (nodep != revp->branches->list);
6608 if (after == NULL)
6609 delnode (nodep);
6610 else
6611 {
6612 free (nodep->key);
6613 nodep->key = xstrdup (after);
6614 }
6615 }
6616 else
6617 {
6618 nodep = findnode (rcs->versions, before);
6619 beforep = (RCSVers *) nodep->data;
6620 free (beforep->next);
6621 beforep->next = xstrdup (after);
6622 }
6623 }
6624
6625 status = 0;
6626
6627 delrev_done:

--- 540 unchanged lines hidden (view full) ---

7168
7169 On error, give a fatal error. */
7170
7171void
7172RCS_deltas (rcs, fp, rcsbuf, version, op, text, len, log, loglen)
7173 RCSNode *rcs;
7174 FILE *fp;
7175 struct rcsbuffer *rcsbuf;
7176 char *version;
7177 enum rcs_delta_op op;
7178 char **text;
7179 size_t *len;
7180 char **log;
7181 size_t *loglen;
7182{
7183 struct rcsbuffer rcsbuf_local;
7184 char *branchversion;

--- 61 unchanged lines hidden (view full) ---

7246 if (node == NULL)
7247 error (1, 0,
7248 "mismatch in rcs file %s between deltas and deltatexts (%s)",
7249 rcs->path, key);
7250
7251 /* Stash the previous version. */
7252 prev_vers = vers;
7253
7254 vers = (RCSVers *) node->data;
7255 next = vers->next;
7256
7257 /* Compare key and trunkversion now, because key points to
7258 storage controlled by rcsbuf_getkey. */
7259 if (STREQ (branchversion, key))
7260 isversion = 1;
7261 else
7262 isversion = 0;

--- 552 unchanged lines hidden (view full) ---

7815
7816/* putlock_proc is like putsymbol_proc, but key and data are reversed. */
7817
7818static int
7819putlock_proc (symnode, fp)
7820 Node *symnode;
7821 void *fp;
7822{
7823 return fprintf ((FILE *) fp, "\n\t%s:%s", symnode->data, symnode->key);
7824}
7825
7826static int
7827putrcsfield_proc (node, vfp)
7828 Node *node;
7829 void *vfp;
7830{
7831 FILE *fp = (FILE *) vfp;

--- 179 unchanged lines hidden (view full) ---

8011 p = findnode (rcs->versions, rev);
8012 if (p == NULL)
8013 {
8014 error (1, 0,
8015 "error parsing repository file %s, file may be corrupt.",
8016 rcs->path);
8017 }
8018
8019 versp = (RCSVers *) p->data;
8020
8021 /* Print the delta node and recurse on its `next' node. This prints
8022 the trunk. If there are any branches printed on this revision,
8023 print those trunks as well. */
8024 putdelta (versp, fp);
8025 RCS_putdtree (rcs, versp->next, fp);
8026 if (versp->branches != NULL)
8027 {

--- 100 unchanged lines hidden (view full) ---

8128 if (found && insertbefore)
8129 {
8130 putdeltatext (fout, newdtext);
8131 newdtext = NULL;
8132 insertpt = NULL;
8133 }
8134
8135 np = findnode (rcs->versions, dtext->version);
8136 dadmin = (RCSVers *) np->data;
8137
8138 /* If this revision has been outdated, just skip it. */
8139 if (dadmin->outdated)
8140 {
8141 freedeltatext (dtext);
8142 --actions;
8143 continue;
8144 }

--- 95 unchanged lines hidden (view full) ---

8240 to count the number of RCS revisions for which some special action
8241 is required. */
8242
8243static int
8244count_delta_actions (np, ignore)
8245 Node *np;
8246 void *ignore;
8247{
8248 RCSVers *dadmin;
8249
8250 dadmin = (RCSVers *) np->data;
8251
8252 if (dadmin->outdated)
8253 return 1;
8254
8255 if (dadmin->text != NULL
8256 && (dadmin->text->log != NULL || dadmin->text->text != NULL))
8257 {
8258 return 1;
8259 }

--- 176 unchanged lines hidden (view full) ---

8436 char *tmp = rcs_lockfile;
8437 rcs_lockfile = NULL;
8438 free (tmp);
8439 }
8440}
8441
8442static char *
8443rcs_lockfilename (rcsfile)
8444 char *rcsfile;
8445{
8446 char *lockfile, *lockp;
8447 char *rcsbase, *rcsp, *rcsend;
8448 int rcslen;
8449
8450 /* Create the lockfile name. */
8451 rcslen = strlen (rcsfile);
8452 lockfile = (char *) xmalloc (rcslen + 10);
8453 rcsbase = last_component (rcsfile);
8454 rcsend = rcsfile + rcslen - sizeof(RCSEXT);
8455 for (lockp = lockfile, rcsp = rcsfile; rcsp < rcsbase; ++rcsp)

--- 91 unchanged lines hidden (view full) ---

8547 *
8548 * /dev/null is not statted but assumed to have been created on the Epoch.
8549 * At least using the POSIX.2 definition of patch, this should cause creation
8550 * of files on platforms such as Windoze where the null IO device isn't named
8551 * /dev/null to be parsed by patch properly.
8552 */
8553char *
8554make_file_label (path, rev, rcs)
8555 char *path;
8556 char *rev;
8557 RCSNode *rcs;
8558{
8559 char datebuf[MAXDATELEN + 1];
8560 char *label;
8561
8562 label = (char *) xmalloc (strlen (path)
8563 + (rev == NULL ? 0 : strlen (rev) + 1)
8564 + MAXDATELEN

--- 6 unchanged lines hidden (view full) ---

8571 assert (strcmp(DEVNULL, path));
8572 RCS_getrevtime (rcs, rev, datebuf, 0);
8573 (void) date_to_internet (date, datebuf);
8574 (void) sprintf (label, "-L%s\t%s\t%s", path, date, rev);
8575 }
8576 else
8577 {
8578 struct stat sb;
8579 struct tm *wm = NULL;
8580
8581 if (strcmp(DEVNULL, path))
8582 {
8583 char *file = last_component (path);
8584 if (CVS_STAT (file, &sb) < 0)
8585 error (0, 1, "could not get info for `%s'", path);
8586 else
8587 wm = gmtime (&sb.st_mtime);
8588 }
8589 else
8590 {
8591 time_t t = 0;
8592 wm = gmtime(&t);
8593 }
8594
8595 if (wm)
8596 {
8597 (void) tm_to_internet (datebuf, wm);
8598 (void) sprintf (label, "-L%s\t%s", path, datebuf);
8599 }
8600 }
8601 return label;
8602}
8603
8604void
8605RCS_setlocalid (arg)
8606 const char *arg;
8607{

--- 97 unchanged lines hidden ---