1*65402675Sjsg /* $OpenBSD: rcs.c,v 1.322 2024/05/30 10:25:58 jsg Exp $ */
208f90673Sjfb /*
308f90673Sjfb * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
408f90673Sjfb * All rights reserved.
508f90673Sjfb *
608f90673Sjfb * Redistribution and use in source and binary forms, with or without
708f90673Sjfb * modification, are permitted provided that the following conditions
808f90673Sjfb * are met:
908f90673Sjfb *
1008f90673Sjfb * 1. Redistributions of source code must retain the above copyright
1108f90673Sjfb * notice, this list of conditions and the following disclaimer.
1208f90673Sjfb * 2. The name of the author may not be used to endorse or promote products
1308f90673Sjfb * derived from this software without specific prior written permission.
1408f90673Sjfb *
1508f90673Sjfb * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
1608f90673Sjfb * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
1708f90673Sjfb * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
1808f90673Sjfb * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1908f90673Sjfb * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
2008f90673Sjfb * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
2108f90673Sjfb * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
2208f90673Sjfb * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
2308f90673Sjfb * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
2408f90673Sjfb * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2508f90673Sjfb */
2608f90673Sjfb
271f8531bdSotto #include <sys/stat.h>
2808f90673Sjfb
291f8531bdSotto #include <ctype.h>
301f8531bdSotto #include <errno.h>
311f8531bdSotto #include <libgen.h>
321f8531bdSotto #include <pwd.h>
331f8531bdSotto #include <stdlib.h>
341f8531bdSotto #include <string.h>
351f8531bdSotto #include <unistd.h>
361f8531bdSotto
37bb59aeccStobias #include "atomicio.h"
3801af718aSjoris #include "cvs.h"
393ad3fb45Sjoris #include "diff.h"
409225b0caSxsa #include "rcs.h"
41fead43dfStobias #include "rcsparse.h"
4208f90673Sjfb
43b9fc9a72Sderaadt #define MINIMUM(a, b) (((a) < (b)) ? (a) : (b))
44b9fc9a72Sderaadt
45b71c7dfeSniallo #define RCS_KWEXP_SIZE 1024
4608f90673Sjfb
475e4c4390Stobias #define ANNOTATE_NEVER 0
485e4c4390Stobias #define ANNOTATE_NOW 1
495e4c4390Stobias #define ANNOTATE_LATER 2
505e4c4390Stobias
51a8b5986dSjfb /* invalid characters in RCS symbol names */
525be71afaSjfb static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
53a8b5986dSjfb
54ff88b297Sjfb /* comment leaders, depending on the file's suffix */
55ff88b297Sjfb static const struct rcs_comment {
56ff88b297Sjfb const char *rc_suffix;
57ff88b297Sjfb const char *rc_cstr;
58ff88b297Sjfb } rcs_comments[] = {
59ff88b297Sjfb { "1", ".\\\" " },
60ff88b297Sjfb { "2", ".\\\" " },
61ff88b297Sjfb { "3", ".\\\" " },
62ff88b297Sjfb { "4", ".\\\" " },
63ff88b297Sjfb { "5", ".\\\" " },
64ff88b297Sjfb { "6", ".\\\" " },
65ff88b297Sjfb { "7", ".\\\" " },
66ff88b297Sjfb { "8", ".\\\" " },
67ff88b297Sjfb { "9", ".\\\" " },
68ff88b297Sjfb { "a", "-- " }, /* Ada */
69ff88b297Sjfb { "ada", "-- " },
70ff88b297Sjfb { "adb", "-- " },
71ff88b297Sjfb { "asm", ";; " }, /* assembler (MS-DOS) */
72ff88b297Sjfb { "ads", "-- " }, /* Ada */
73ff88b297Sjfb { "bat", ":: " }, /* batch (MS-DOS) */
74ff88b297Sjfb { "body", "-- " }, /* Ada */
75ff88b297Sjfb { "c", " * " }, /* C */
76ff88b297Sjfb { "c++", "// " }, /* C++ */
77ff88b297Sjfb { "cc", "// " },
78ff88b297Sjfb { "cpp", "// " },
79ff88b297Sjfb { "cxx", "// " },
80ff88b297Sjfb { "m", "// " }, /* Objective-C */
81ff88b297Sjfb { "cl", ";;; " }, /* Common Lisp */
82ff88b297Sjfb { "cmd", ":: " }, /* command (OS/2) */
83ff88b297Sjfb { "cmf", "c " }, /* CM Fortran */
84ff88b297Sjfb { "csh", "# " }, /* shell */
85ff88b297Sjfb { "e", "# " }, /* efl */
86ff88b297Sjfb { "epsf", "% " }, /* encapsulated postscript */
87ff88b297Sjfb { "epsi", "% " }, /* encapsulated postscript */
88ff88b297Sjfb { "el", "; " }, /* Emacs Lisp */
89ff88b297Sjfb { "f", "c " }, /* Fortran */
90ff88b297Sjfb { "for", "c " },
91ff88b297Sjfb { "h", " * " }, /* C-header */
92ff88b297Sjfb { "hh", "// " }, /* C++ header */
93ff88b297Sjfb { "hpp", "// " },
94ff88b297Sjfb { "hxx", "// " },
95ff88b297Sjfb { "in", "# " }, /* for Makefile.in */
96ff88b297Sjfb { "l", " * " }, /* lex */
97ff88b297Sjfb { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
98ff88b297Sjfb { "mak", "# " }, /* makefile, e.g. Visual C++ */
99ff88b297Sjfb { "me", ".\\\" " }, /* me-macros t/nroff */
100ff88b297Sjfb { "ml", "; " }, /* mocklisp */
101ff88b297Sjfb { "mm", ".\\\" " }, /* mm-macros t/nroff */
102ff88b297Sjfb { "ms", ".\\\" " }, /* ms-macros t/nroff */
103ff88b297Sjfb { "man", ".\\\" " }, /* man-macros t/nroff */
104ff88b297Sjfb { "p", " * " }, /* pascal */
105ff88b297Sjfb { "pas", " * " },
10656bc38daSjfb { "pl", "# " }, /* Perl (conflict with Prolog) */
10756bc38daSjfb { "pm", "# " }, /* Perl module */
108ff88b297Sjfb { "ps", "% " }, /* postscript */
109ff88b297Sjfb { "psw", "% " }, /* postscript wrap */
110ff88b297Sjfb { "pswm", "% " }, /* postscript wrap */
111ff88b297Sjfb { "r", "# " }, /* ratfor */
112ff88b297Sjfb { "rc", " * " }, /* Microsoft Windows resource file */
113ff88b297Sjfb { "red", "% " }, /* psl/rlisp */
114ff88b297Sjfb { "sh", "# " }, /* shell */
115ff88b297Sjfb { "sl", "% " }, /* psl */
116ff88b297Sjfb { "spec", "-- " }, /* Ada */
117ff88b297Sjfb { "tex", "% " }, /* tex */
118ff88b297Sjfb { "y", " * " }, /* yacc */
119ff88b297Sjfb { "ye", " * " }, /* yacc-efl */
120ff88b297Sjfb { "yr", " * " }, /* yacc-ratfor */
121ff88b297Sjfb };
122ff88b297Sjfb
123a718728aSniallo struct rcs_kw rcs_expkw[] = {
124a718728aSniallo { "Author", RCS_KW_AUTHOR },
125a718728aSniallo { "Date", RCS_KW_DATE },
126a718728aSniallo { "Header", RCS_KW_HEADER },
127a718728aSniallo { "Id", RCS_KW_ID },
1285c14cef8Stobias { "Locker", RCS_KW_LOCKER },
129a718728aSniallo { "Log", RCS_KW_LOG },
130a718728aSniallo { "Name", RCS_KW_NAME },
131a718728aSniallo { "RCSfile", RCS_KW_RCSFILE },
132a718728aSniallo { "Revision", RCS_KW_REVISION },
133a718728aSniallo { "Source", RCS_KW_SOURCE },
134a718728aSniallo { "State", RCS_KW_STATE },
135f4827e12Sniallo { "Mdocdate", RCS_KW_MDOCDATE },
136a718728aSniallo };
137a718728aSniallo
138ff88b297Sjfb #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
139ff88b297Sjfb
140113cb29fStobias static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
1417bb3ddb0Sray int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *,
1427bb3ddb0Sray struct rcs_line **, struct rcs_delta *);
1431b6534b8Sjfb static void rcs_freedelta(struct rcs_delta *);
144f240ac99Sray static void rcs_strprint(const u_char *, size_t, FILE *);
1451b6534b8Sjfb
1467bb3ddb0Sray static void rcs_kwexp_line(char *, struct rcs_delta *, struct rcs_lines *,
1477bb3ddb0Sray struct rcs_line *, int mode);
1481b6534b8Sjfb
149cc89a134Snicm /*
150cc89a134Snicm * Prepare RCSFILE for parsing. The given file descriptor (if any) must be
151cc89a134Snicm * read-only and is closed on rcs_close().
152cc89a134Snicm */
15308f90673Sjfb RCSFILE *
rcs_open(const char * path,int fd,int flags,...)1543ad3fb45Sjoris rcs_open(const char *path, int fd, int flags, ...)
15508f90673Sjfb {
1563ad3fb45Sjoris int mode;
15788b7ad1cStobias mode_t fmode;
15808f90673Sjfb RCSFILE *rfp;
1591b6534b8Sjfb va_list vap;
1600d18a67fSjoris struct stat st;
161bbfcb536Sjoris struct rcs_delta *rdp;
162bbfcb536Sjoris struct rcs_lock *lkr;
16308f90673Sjfb
164c51cb395Sniallo fmode = S_IRUSR|S_IRGRP|S_IROTH;
1651b6534b8Sjfb flags &= 0xffff; /* ditch any internal flags */
1661b6534b8Sjfb
1671b6534b8Sjfb if (flags & RCS_CREATE) {
1681b6534b8Sjfb va_start(vap, flags);
169ee96bdbeSjoris mode = va_arg(vap, int);
1701b6534b8Sjfb va_end(vap);
171ee96bdbeSjoris fmode = (mode_t)mode;
1720d18a67fSjoris } else {
1730d18a67fSjoris if (fstat(fd, &st) == -1)
1740d18a67fSjoris fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
1750d18a67fSjoris fmode = st.st_mode;
17608f90673Sjfb }
17708f90673Sjfb
17888b7ad1cStobias fmode &= ~cvs_umask;
17922ab9685Stobias
1803b4c5c25Sray rfp = xcalloc(1, sizeof(*rfp));
18108f90673Sjfb
1820450b43bSjoris rfp->rf_path = xstrdup(path);
1833c066d8cSniallo rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
1841b6534b8Sjfb rfp->rf_mode = fmode;
185394437e6Stobias if (fd == -1)
186394437e6Stobias rfp->rf_file = NULL;
187394437e6Stobias else if ((rfp->rf_file = fdopen(fd, "r")) == NULL)
188394437e6Stobias fatal("rcs_open: %s: fdopen: %s", path, strerror(errno));
1893258e4a0Sjoris rfp->rf_dead = 0;
19008f90673Sjfb
19108f90673Sjfb TAILQ_INIT(&(rfp->rf_delta));
192d510a4e8Sjfb TAILQ_INIT(&(rfp->rf_access));
19308f90673Sjfb TAILQ_INIT(&(rfp->rf_symbols));
19408f90673Sjfb TAILQ_INIT(&(rfp->rf_locks));
19508f90673Sjfb
196fead43dfStobias if (!(rfp->rf_flags & RCS_CREATE)) {
197fead43dfStobias if (rcsparse_init(rfp))
198fead43dfStobias fatal("could not parse admin data");
199fead43dfStobias }
20008f90673Sjfb
201bbfcb536Sjoris /* fill in rd_locker */
202bbfcb536Sjoris TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
203bbfcb536Sjoris if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
204bbfcb536Sjoris rcs_close(rfp);
205bbfcb536Sjoris return (NULL);
206bbfcb536Sjoris }
207bbfcb536Sjoris
2080450b43bSjoris rdp->rd_locker = xstrdup(lkr->rl_name);
209bbfcb536Sjoris }
210bbfcb536Sjoris
21108f90673Sjfb return (rfp);
21208f90673Sjfb }
21308f90673Sjfb
21408f90673Sjfb /*
21508f90673Sjfb * rcs_close()
21608f90673Sjfb *
21708f90673Sjfb * Close an RCS file handle.
21808f90673Sjfb */
21908f90673Sjfb void
rcs_close(RCSFILE * rfp)22008f90673Sjfb rcs_close(RCSFILE *rfp)
22108f90673Sjfb {
22208f90673Sjfb struct rcs_delta *rdp;
22313e4ff88Smoritz struct rcs_access *rap;
2244232e9ddSjfb struct rcs_lock *rlp;
2254232e9ddSjfb struct rcs_sym *rsp;
22608f90673Sjfb
2271b6534b8Sjfb if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
2281b6534b8Sjfb rcs_write(rfp);
2291b6534b8Sjfb
23008f90673Sjfb while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
23108f90673Sjfb rdp = TAILQ_FIRST(&(rfp->rf_delta));
23208f90673Sjfb TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
23308f90673Sjfb rcs_freedelta(rdp);
23408f90673Sjfb }
23508f90673Sjfb
23613e4ff88Smoritz while (!TAILQ_EMPTY(&(rfp->rf_access))) {
23713e4ff88Smoritz rap = TAILQ_FIRST(&(rfp->rf_access));
23813e4ff88Smoritz TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
239397ddb8aSnicm free(rap->ra_name);
240397ddb8aSnicm free(rap);
24113e4ff88Smoritz }
24213e4ff88Smoritz
2434232e9ddSjfb while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
2444232e9ddSjfb rsp = TAILQ_FIRST(&(rfp->rf_symbols));
2454232e9ddSjfb TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
24653ce2177Sfcambus free(rsp->rs_num);
247397ddb8aSnicm free(rsp->rs_name);
248397ddb8aSnicm free(rsp);
2494232e9ddSjfb }
2504232e9ddSjfb
2514232e9ddSjfb while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
2524232e9ddSjfb rlp = TAILQ_FIRST(&(rfp->rf_locks));
2534232e9ddSjfb TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
25453ce2177Sfcambus free(rlp->rl_num);
255397ddb8aSnicm free(rlp->rl_name);
256397ddb8aSnicm free(rlp);
2574232e9ddSjfb }
2584232e9ddSjfb
25953ce2177Sfcambus free(rfp->rf_head);
26053ce2177Sfcambus free(rfp->rf_branch);
26108f90673Sjfb
262394437e6Stobias if (rfp->rf_file != NULL)
263394437e6Stobias fclose(rfp->rf_file);
264397ddb8aSnicm free(rfp->rf_path);
265397ddb8aSnicm free(rfp->rf_comment);
266397ddb8aSnicm free(rfp->rf_expand);
267397ddb8aSnicm free(rfp->rf_desc);
26824afc1baSjoris if (rfp->rf_pdata != NULL)
269fead43dfStobias rcsparse_free(rfp);
270397ddb8aSnicm free(rfp);
27108f90673Sjfb }
27208f90673Sjfb
27308f90673Sjfb /*
27408f90673Sjfb * rcs_write()
27508f90673Sjfb *
27608f90673Sjfb * Write the contents of the RCS file handle <rfp> to disk in the file whose
27708f90673Sjfb * path is in <rf_path>.
27808f90673Sjfb */
2793ad3fb45Sjoris void
rcs_write(RCSFILE * rfp)28008f90673Sjfb rcs_write(RCSFILE *rfp)
28108f90673Sjfb {
28208f90673Sjfb FILE *fp;
283b9fc9a72Sderaadt char numbuf[CVS_REV_BUFSZ], *fn, tmpdir[PATH_MAX];
284d510a4e8Sjfb struct rcs_access *ap;
28508f90673Sjfb struct rcs_sym *symp;
28652fd60b9Sjfb struct rcs_branch *brp;
28708f90673Sjfb struct rcs_delta *rdp;
2886bf66b8aSjoris struct rcs_lock *lkp;
28963b70358Sxsa size_t len;
2903e028447Sotto int fd, saved_errno;
2916bf66b8aSjoris
2923e028447Sotto fd = -1;
29308f90673Sjfb
294343bce0eSjfb if (rfp->rf_flags & RCS_SYNCED)
2953ad3fb45Sjoris return;
29608f90673Sjfb
2977938e528Sjoris if (cvs_noexec == 1)
2987938e528Sjoris return;
2997938e528Sjoris
30012b92713Sjoris /* Write operations need the whole file parsed */
301fead43dfStobias if (rcsparse_deltatexts(rfp, NULL))
302fead43dfStobias fatal("rcs_write: rcsparse_deltatexts");
30312b92713Sjoris
3043e028447Sotto if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
3053e028447Sotto fatal("rcs_write: truncation");
3063e028447Sotto (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
3073ad3fb45Sjoris
308296ae42bSxsa if ((fd = mkstemp(fn)) == -1)
3093ad3fb45Sjoris fatal("%s", fn);
310296ae42bSxsa
3113e028447Sotto if ((fp = fdopen(fd, "w")) == NULL) {
3123ad3fb45Sjoris saved_errno = errno;
3133ad3fb45Sjoris (void)unlink(fn);
3143e028447Sotto fatal("fdopen %s: %s", fn, strerror(saved_errno));
31508f90673Sjfb }
31608f90673Sjfb
3177a9e6d11Sray worklist_add(fn, &temp_files);
3183627c1f6Sjoris
319343bce0eSjfb if (rfp->rf_head != NULL)
32008f90673Sjfb rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
321343bce0eSjfb else
322343bce0eSjfb numbuf[0] = '\0';
323343bce0eSjfb
32408f90673Sjfb fprintf(fp, "head\t%s;\n", numbuf);
32595553451Sjfb
32695553451Sjfb if (rfp->rf_branch != NULL) {
32795553451Sjfb rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
32895553451Sjfb fprintf(fp, "branch\t%s;\n", numbuf);
32995553451Sjfb }
33095553451Sjfb
331d510a4e8Sjfb fputs("access", fp);
332d510a4e8Sjfb TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
333d510a4e8Sjfb fprintf(fp, "\n\t%s", ap->ra_name);
334d510a4e8Sjfb }
335d510a4e8Sjfb fputs(";\n", fp);
33608f90673Sjfb
337ca193349Sjoris fprintf(fp, "symbols");
33808f90673Sjfb TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
3390f301d36Stobias if (RCSNUM_ISBRANCH(symp->rs_num))
3400f301d36Stobias rcsnum_addmagic(symp->rs_num);
34108f90673Sjfb rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
342ad782d8bSzinovik fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
34308f90673Sjfb }
34408f90673Sjfb fprintf(fp, ";\n");
34508f90673Sjfb
3466bf66b8aSjoris fprintf(fp, "locks");
3476bf66b8aSjoris TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
3486bf66b8aSjoris rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
3496bf66b8aSjoris fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
3506bf66b8aSjoris }
3516bf66b8aSjoris
3526bf66b8aSjoris fprintf(fp, ";");
35308f90673Sjfb
3541b6534b8Sjfb if (rfp->rf_flags & RCS_SLOCK)
35508f90673Sjfb fprintf(fp, " strict;");
35608f90673Sjfb fputc('\n', fp);
35708f90673Sjfb
358163f330fSjfb fputs("comment\t@", fp);
359dedf3eaaSxsa if (rfp->rf_comment != NULL) {
360bc37d6f8Sxsa rcs_strprint((const u_char *)rfp->rf_comment,
361bc37d6f8Sxsa strlen(rfp->rf_comment), fp);
362163f330fSjfb fputs("@;\n", fp);
363dedf3eaaSxsa } else
364dedf3eaaSxsa fputs("# @;\n", fp);
36508f90673Sjfb
366163f330fSjfb if (rfp->rf_expand != NULL) {
367163f330fSjfb fputs("expand @", fp);
368bc37d6f8Sxsa rcs_strprint((const u_char *)rfp->rf_expand,
369bc37d6f8Sxsa strlen(rfp->rf_expand), fp);
370163f330fSjfb fputs("@;\n", fp);
371163f330fSjfb }
37208f90673Sjfb
37352fd60b9Sjfb fputs("\n\n", fp);
37408f90673Sjfb
37508f90673Sjfb TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
37608f90673Sjfb fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
37708f90673Sjfb sizeof(numbuf)));
37808f90673Sjfb fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
379b100d76eSjfb rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
38008f90673Sjfb rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
38108f90673Sjfb rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
38208f90673Sjfb fprintf(fp, "\tauthor %s;\tstate %s;\n",
38308f90673Sjfb rdp->rd_author, rdp->rd_state);
38452fd60b9Sjfb fputs("branches", fp);
38552fd60b9Sjfb TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
3863081741dStobias fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
38752fd60b9Sjfb sizeof(numbuf)));
38852fd60b9Sjfb }
38952fd60b9Sjfb fputs(";\n", fp);
39008f90673Sjfb fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
39108f90673Sjfb numbuf, sizeof(numbuf)));
39208f90673Sjfb }
39308f90673Sjfb
394163f330fSjfb fputs("\ndesc\n@", fp);
3957b7572f3Sray if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
39663b70358Sxsa rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
39763b70358Sxsa if (rfp->rf_desc[len-1] != '\n')
39863b70358Sxsa fputc('\n', fp);
39963b70358Sxsa }
40063b70358Sxsa fputs("@\n", fp);
40108f90673Sjfb
40208f90673Sjfb /* deltatexts */
40308f90673Sjfb TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
40463b70358Sxsa fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
40508f90673Sjfb sizeof(numbuf)));
406163f330fSjfb fputs("log\n@", fp);
40763b70358Sxsa if (rdp->rd_log != NULL) {
40863b70358Sxsa len = strlen(rdp->rd_log);
40963b70358Sxsa rcs_strprint((const u_char *)rdp->rd_log, len, fp);
4101d911502Snicm if (len == 0 || rdp->rd_log[len-1] != '\n')
41163b70358Sxsa fputc('\n', fp);
41263b70358Sxsa }
413163f330fSjfb fputs("@\ntext\n@", fp);
4147a9e6d11Sray if (rdp->rd_text != NULL)
415163f330fSjfb rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
41663b70358Sxsa fputs("@\n", fp);
41708f90673Sjfb }
4180d18a67fSjoris
4193e028447Sotto if (fchmod(fd, rfp->rf_mode) == -1) {
4203e028447Sotto saved_errno = errno;
4213e028447Sotto (void)unlink(fn);
4223e028447Sotto fatal("fchmod %s: %s", fn, strerror(saved_errno));
4233e028447Sotto }
4243e028447Sotto
4253ad3fb45Sjoris (void)fclose(fp);
4267bc8b3c9Sjoris
4273e028447Sotto if (rename(fn, rfp->rf_path) == -1) {
4283e028447Sotto saved_errno = errno;
4293ad3fb45Sjoris (void)unlink(fn);
4303e028447Sotto fatal("rename(%s, %s): %s", fn, rfp->rf_path,
4313e028447Sotto strerror(saved_errno));
432e8d7114cSniallo }
4337bc8b3c9Sjoris
4341b6534b8Sjfb rfp->rf_flags |= RCS_SYNCED;
435397ddb8aSnicm free(fn);
4363ad3fb45Sjoris }
4373ad3fb45Sjoris
4383ad3fb45Sjoris /*
43900f66f0dSjfb * rcs_head_get()
44000f66f0dSjfb *
44100f66f0dSjfb * Retrieve the revision number of the head revision for the RCS file <file>.
44200f66f0dSjfb */
44308458e59Sjoris RCSNUM *
rcs_head_get(RCSFILE * file)44400f66f0dSjfb rcs_head_get(RCSFILE *file)
44500f66f0dSjfb {
446ca2dc546Sniallo struct rcs_branch *brp;
447ca2dc546Sniallo struct rcs_delta *rdp;
448ca2dc546Sniallo RCSNUM *rev, *rootrev;
44908458e59Sjoris
4509af0ab72Stobias if (file->rf_head == NULL)
4519af0ab72Stobias return NULL;
4529af0ab72Stobias
453570941ffSjoris rev = rcsnum_alloc();
454ca2dc546Sniallo if (file->rf_branch != NULL) {
455ca2dc546Sniallo /* we have a default branch, use that to calculate the
456ca2dc546Sniallo * real HEAD*/
457ca2dc546Sniallo rootrev = rcsnum_alloc();
45818258a85Stobias rcsnum_cpy(file->rf_branch, rootrev,
45918258a85Stobias file->rf_branch->rn_len - 1);
460ca2dc546Sniallo if ((rdp = rcs_findrev(file, rootrev)) == NULL)
461ca2dc546Sniallo fatal("rcs_head_get: could not find root revision");
462ca2dc546Sniallo
463ca2dc546Sniallo /* HEAD should be the last revision on the default branch */
464ca2dc546Sniallo TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
46518258a85Stobias if (rcsnum_cmp(brp->rb_num, file->rf_branch,
466e60fc6c5Schl file->rf_branch->rn_len) == 0)
467ca2dc546Sniallo break;
468ca2dc546Sniallo }
46953ce2177Sfcambus free(rootrev);
4706daf1a4bStobias
47118258a85Stobias if (brp == NULL)
47218258a85Stobias fatal("rcs_head_get: could not find first default "
47318258a85Stobias "branch revision");
47418258a85Stobias
4756daf1a4bStobias if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
4766daf1a4bStobias fatal("rcs_head_get: could not find branch revision");
4776daf1a4bStobias while (rdp->rd_next->rn_len != 0)
4786daf1a4bStobias if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
4796daf1a4bStobias fatal("rcs_head_get: could not find "
4806daf1a4bStobias "next branch revision");
4816daf1a4bStobias
4826daf1a4bStobias rcsnum_cpy(rdp->rd_num, rev, 0);
483ca2dc546Sniallo } else {
484570941ffSjoris rcsnum_cpy(file->rf_head, rev, 0);
48508458e59Sjoris }
48608458e59Sjoris
487570941ffSjoris return (rev);
48800f66f0dSjfb }
48900f66f0dSjfb
49000f66f0dSjfb /*
49100f66f0dSjfb * rcs_head_set()
49200f66f0dSjfb *
49300f66f0dSjfb * Set the revision number of the head revision for the RCS file <file> to
49400f66f0dSjfb * <rev>, which must reference a valid revision within the file.
49500f66f0dSjfb */
49600f66f0dSjfb int
rcs_head_set(RCSFILE * file,RCSNUM * rev)497b71c7dfeSniallo rcs_head_set(RCSFILE *file, RCSNUM *rev)
49800f66f0dSjfb {
499eb421b0cSray if (rcs_findrev(file, rev) == NULL)
50000f66f0dSjfb return (-1);
50100f66f0dSjfb
5023422a189Sjoris if (file->rf_head == NULL)
5033422a189Sjoris file->rf_head = rcsnum_alloc();
50456bc38daSjfb
5053422a189Sjoris rcsnum_cpy(rev, file->rf_head, 0);
50608946b33Smoritz file->rf_flags &= ~RCS_SYNCED;
50700f66f0dSjfb return (0);
50800f66f0dSjfb }
50900f66f0dSjfb
51045b98c07Stobias /*
51145b98c07Stobias * rcs_branch_new()
51245b98c07Stobias *
51345b98c07Stobias * Create a new branch out of supplied revision for the RCS file <file>.
51445b98c07Stobias */
51545b98c07Stobias RCSNUM *
rcs_branch_new(RCSFILE * file,RCSNUM * rev)51645b98c07Stobias rcs_branch_new(RCSFILE *file, RCSNUM *rev)
51745b98c07Stobias {
51845b98c07Stobias RCSNUM *brev;
51945b98c07Stobias struct rcs_sym *sym;
52045b98c07Stobias
52145b98c07Stobias if ((brev = rcsnum_new_branch(rev)) == NULL)
52245b98c07Stobias return (NULL);
52345b98c07Stobias
52445b98c07Stobias for (;;) {
52545b98c07Stobias TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
52645b98c07Stobias if (!rcsnum_cmp(sym->rs_num, brev, 0))
52745b98c07Stobias break;
52845b98c07Stobias
5290e6e2e1bStobias if (sym == NULL)
53045b98c07Stobias break;
5310e6e2e1bStobias
5320e6e2e1bStobias if (rcsnum_inc(brev) == NULL ||
5330e6e2e1bStobias rcsnum_inc(brev) == NULL) {
53453ce2177Sfcambus free(brev);
5350e6e2e1bStobias return (NULL);
5360e6e2e1bStobias }
53745b98c07Stobias }
53845b98c07Stobias
53945b98c07Stobias return (brev);
54045b98c07Stobias }
54100f66f0dSjfb
54200f66f0dSjfb /*
54395553451Sjfb * rcs_branch_get()
54495553451Sjfb *
54595553451Sjfb * Retrieve the default branch number for the RCS file <file>.
54695553451Sjfb * Returns the number on success. If NULL is returned, then there is no
54795553451Sjfb * default branch for this file.
54895553451Sjfb */
54995553451Sjfb const RCSNUM *
rcs_branch_get(RCSFILE * file)55095553451Sjfb rcs_branch_get(RCSFILE *file)
55195553451Sjfb {
55295553451Sjfb return (file->rf_branch);
55395553451Sjfb }
55495553451Sjfb
55595553451Sjfb /*
55695553451Sjfb * rcs_branch_set()
55795553451Sjfb *
55895553451Sjfb * Set the default branch for the RCS file <file> to <bnum>.
55995553451Sjfb * Returns 0 on success, -1 on failure.
56095553451Sjfb */
56195553451Sjfb int
rcs_branch_set(RCSFILE * file,const RCSNUM * bnum)56295553451Sjfb rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
56395553451Sjfb {
5643422a189Sjoris if (file->rf_branch == NULL)
5653422a189Sjoris file->rf_branch = rcsnum_alloc();
56695553451Sjfb
5673422a189Sjoris rcsnum_cpy(bnum, file->rf_branch, 0);
56808946b33Smoritz file->rf_flags &= ~RCS_SYNCED;
56995553451Sjfb return (0);
57095553451Sjfb }
57195553451Sjfb
57295553451Sjfb /*
573d510a4e8Sjfb * rcs_access_add()
574d510a4e8Sjfb *
575d510a4e8Sjfb * Add the login name <login> to the access list for the RCS file <file>.
576d510a4e8Sjfb * Returns 0 on success, or -1 on failure.
577d510a4e8Sjfb */
578d510a4e8Sjfb int
rcs_access_add(RCSFILE * file,const char * login)579d510a4e8Sjfb rcs_access_add(RCSFILE *file, const char *login)
580d510a4e8Sjfb {
581d510a4e8Sjfb struct rcs_access *ap;
582d510a4e8Sjfb
583d510a4e8Sjfb /* first look for duplication */
584d510a4e8Sjfb TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
5855b95d21fSjoris if (strcmp(ap->ra_name, login) == 0)
586d510a4e8Sjfb return (-1);
587d510a4e8Sjfb }
588d510a4e8Sjfb
5893b4c5c25Sray ap = xmalloc(sizeof(*ap));
5900450b43bSjoris ap->ra_name = xstrdup(login);
591d510a4e8Sjfb TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
592d510a4e8Sjfb
593d510a4e8Sjfb /* not synced anymore */
594d510a4e8Sjfb file->rf_flags &= ~RCS_SYNCED;
595d510a4e8Sjfb return (0);
596d510a4e8Sjfb }
597d510a4e8Sjfb
598d510a4e8Sjfb /*
599d510a4e8Sjfb * rcs_access_remove()
600d510a4e8Sjfb *
601d510a4e8Sjfb * Remove an entry with login name <login> from the access list of the RCS
602d510a4e8Sjfb * file <file>.
603d510a4e8Sjfb * Returns 0 on success, or -1 on failure.
604d510a4e8Sjfb */
605d510a4e8Sjfb int
rcs_access_remove(RCSFILE * file,const char * login)606d510a4e8Sjfb rcs_access_remove(RCSFILE *file, const char *login)
607d510a4e8Sjfb {
608d510a4e8Sjfb struct rcs_access *ap;
609d510a4e8Sjfb
610d510a4e8Sjfb TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
611d510a4e8Sjfb if (strcmp(ap->ra_name, login) == 0)
612d510a4e8Sjfb break;
613d510a4e8Sjfb
6145b95d21fSjoris if (ap == NULL)
615d510a4e8Sjfb return (-1);
616d510a4e8Sjfb
617d510a4e8Sjfb TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
618397ddb8aSnicm free(ap->ra_name);
619397ddb8aSnicm free(ap);
620d510a4e8Sjfb
621d510a4e8Sjfb /* not synced anymore */
622d510a4e8Sjfb file->rf_flags &= ~RCS_SYNCED;
623d510a4e8Sjfb return (0);
624d510a4e8Sjfb }
625d510a4e8Sjfb
626d510a4e8Sjfb /*
6271b6534b8Sjfb * rcs_sym_add()
62808f90673Sjfb *
62908f90673Sjfb * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
63008f90673Sjfb * is named <sym> and is bound to the RCS revision <snum>.
63108f90673Sjfb */
63208f90673Sjfb int
rcs_sym_add(RCSFILE * rfp,const char * sym,RCSNUM * snum)6331b6534b8Sjfb rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
63408f90673Sjfb {
63508f90673Sjfb struct rcs_sym *symp;
63608f90673Sjfb
6375b95d21fSjoris if (!rcs_sym_check(sym))
63871ca9d3cSmoritz return (-1);
639a8b5986dSjfb
64008f90673Sjfb /* first look for duplication */
64108f90673Sjfb TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
6425b95d21fSjoris if (strcmp(symp->rs_name, sym) == 0)
6435b95d21fSjoris return (1);
64408f90673Sjfb }
64508f90673Sjfb
6463b4c5c25Sray symp = xmalloc(sizeof(*symp));
6470450b43bSjoris symp->rs_name = xstrdup(sym);
6483422a189Sjoris symp->rs_num = rcsnum_alloc();
64908f90673Sjfb rcsnum_cpy(snum, symp->rs_num, 0);
65008f90673Sjfb
65108f90673Sjfb TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
65208f90673Sjfb
65308f90673Sjfb /* not synced anymore */
6541b6534b8Sjfb rfp->rf_flags &= ~RCS_SYNCED;
65508f90673Sjfb return (0);
65608f90673Sjfb }
65708f90673Sjfb
65808f90673Sjfb /*
659101fc366Sjfb * rcs_sym_remove()
660101fc366Sjfb *
661101fc366Sjfb * Remove the symbol with name <sym> from the symbol list for the RCS file
662101fc366Sjfb * <file>. If no such symbol is found, the call fails and returns with an
663101fc366Sjfb * error.
664101fc366Sjfb * Returns 0 on success, or -1 on failure.
665101fc366Sjfb */
666101fc366Sjfb int
rcs_sym_remove(RCSFILE * file,const char * sym)667101fc366Sjfb rcs_sym_remove(RCSFILE *file, const char *sym)
668101fc366Sjfb {
669101fc366Sjfb struct rcs_sym *symp;
670101fc366Sjfb
6715b95d21fSjoris if (!rcs_sym_check(sym))
67271ca9d3cSmoritz return (-1);
673a8b5986dSjfb
674101fc366Sjfb TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
675101fc366Sjfb if (strcmp(symp->rs_name, sym) == 0)
676101fc366Sjfb break;
677101fc366Sjfb
6785b95d21fSjoris if (symp == NULL)
679101fc366Sjfb return (-1);
680101fc366Sjfb
681101fc366Sjfb TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
682397ddb8aSnicm free(symp->rs_name);
68353ce2177Sfcambus free(symp->rs_num);
684397ddb8aSnicm free(symp);
685101fc366Sjfb
686101fc366Sjfb /* not synced anymore */
687101fc366Sjfb file->rf_flags &= ~RCS_SYNCED;
688101fc366Sjfb return (0);
689101fc366Sjfb }
690101fc366Sjfb
691101fc366Sjfb /*
692a0d919f1Sxsa * rcs_sym_get()
693a0d919f1Sxsa *
694a0d919f1Sxsa * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
695a0d919f1Sxsa *
696a0d919f1Sxsa * Returns a pointer to the symbol on success, or NULL on failure.
697a0d919f1Sxsa */
698a0d919f1Sxsa struct rcs_sym *
rcs_sym_get(RCSFILE * file,const char * sym)699a0d919f1Sxsa rcs_sym_get(RCSFILE *file, const char *sym)
700a0d919f1Sxsa {
701a0d919f1Sxsa struct rcs_sym *symp;
702a0d919f1Sxsa
703a0d919f1Sxsa TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
704a0d919f1Sxsa if (strcmp(symp->rs_name, sym) == 0)
705a0d919f1Sxsa return (symp);
706a0d919f1Sxsa
707a0d919f1Sxsa return (NULL);
708a0d919f1Sxsa }
709a0d919f1Sxsa
710a0d919f1Sxsa /*
711101fc366Sjfb * rcs_sym_getrev()
712101fc366Sjfb *
713101fc366Sjfb * Retrieve the RCS revision number associated with the symbol <sym> for the
714101fc366Sjfb * RCS file <file>. The returned value is a dynamically-allocated copy and
715101fc366Sjfb * should be freed by the caller once they are done with it.
716101fc366Sjfb * Returns the RCSNUM on success, or NULL on failure.
717101fc366Sjfb */
718101fc366Sjfb RCSNUM *
rcs_sym_getrev(RCSFILE * file,const char * sym)719101fc366Sjfb rcs_sym_getrev(RCSFILE *file, const char *sym)
720101fc366Sjfb {
721101fc366Sjfb RCSNUM *num;
722101fc366Sjfb struct rcs_sym *symp;
723101fc366Sjfb
724e28eda4eStobias if (!rcs_sym_check(sym) || file->rf_head == NULL)
725a8b5986dSjfb return (NULL);
726a8b5986dSjfb
72745d60d54Sjoris if (!strcmp(sym, RCS_HEAD_BRANCH)) {
72845d60d54Sjoris num = rcsnum_alloc();
72945d60d54Sjoris rcsnum_cpy(file->rf_head, num, 0);
73045d60d54Sjoris return (num);
73145d60d54Sjoris }
73245d60d54Sjoris
733101fc366Sjfb num = NULL;
734101fc366Sjfb TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
735101fc366Sjfb if (strcmp(symp->rs_name, sym) == 0)
736101fc366Sjfb break;
737101fc366Sjfb
7385b95d21fSjoris if (symp != NULL) {
7393422a189Sjoris num = rcsnum_alloc();
7403422a189Sjoris rcsnum_cpy(symp->rs_num, num, 0);
741101fc366Sjfb }
742101fc366Sjfb
743101fc366Sjfb return (num);
744101fc366Sjfb }
745101fc366Sjfb
746101fc366Sjfb /*
747a8b5986dSjfb * rcs_sym_check()
748a8b5986dSjfb *
749a8b5986dSjfb * Check the RCS symbol name <sym> for any unsupported characters.
750a8b5986dSjfb * Returns 1 if the tag is correct, 0 if it isn't valid.
751a8b5986dSjfb */
752a8b5986dSjfb int
rcs_sym_check(const char * sym)753a8b5986dSjfb rcs_sym_check(const char *sym)
754a8b5986dSjfb {
755a8b5986dSjfb int ret;
756f6ac027fSokan const unsigned char *cp;
757a8b5986dSjfb
758a8b5986dSjfb ret = 1;
759a8b5986dSjfb cp = sym;
760a8b5986dSjfb if (!isalpha(*cp++))
761a8b5986dSjfb return (0);
762a8b5986dSjfb
763a8b5986dSjfb for (; *cp != '\0'; cp++)
764a8b5986dSjfb if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
765a8b5986dSjfb ret = 0;
766a8b5986dSjfb break;
767a8b5986dSjfb }
768a8b5986dSjfb
769a8b5986dSjfb return (ret);
770a8b5986dSjfb }
771a8b5986dSjfb
772a8b5986dSjfb /*
773a021dec5Sjfb * rcs_lock_getmode()
774a021dec5Sjfb *
775a021dec5Sjfb * Retrieve the locking mode of the RCS file <file>.
776a021dec5Sjfb */
777a021dec5Sjfb int
rcs_lock_getmode(RCSFILE * file)778a021dec5Sjfb rcs_lock_getmode(RCSFILE *file)
779a021dec5Sjfb {
780a021dec5Sjfb return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
781a021dec5Sjfb }
782a021dec5Sjfb
783a021dec5Sjfb /*
784a021dec5Sjfb * rcs_lock_setmode()
785a021dec5Sjfb *
786a021dec5Sjfb * Set the locking mode of the RCS file <file> to <mode>, which must either
787a021dec5Sjfb * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
788a021dec5Sjfb * Returns the previous mode on success, or -1 on failure.
789a021dec5Sjfb */
790a021dec5Sjfb int
rcs_lock_setmode(RCSFILE * file,int mode)791a021dec5Sjfb rcs_lock_setmode(RCSFILE *file, int mode)
792a021dec5Sjfb {
793a021dec5Sjfb int pmode;
794a021dec5Sjfb pmode = rcs_lock_getmode(file);
795a021dec5Sjfb
796a021dec5Sjfb if (mode == RCS_LOCK_STRICT)
797a021dec5Sjfb file->rf_flags |= RCS_SLOCK;
798a021dec5Sjfb else if (mode == RCS_LOCK_LOOSE)
799a021dec5Sjfb file->rf_flags &= ~RCS_SLOCK;
800291869e5Sxsa else
801291869e5Sxsa fatal("rcs_lock_setmode: invalid mode `%d'", mode);
802a021dec5Sjfb
80308946b33Smoritz file->rf_flags &= ~RCS_SYNCED;
804a021dec5Sjfb return (pmode);
805a021dec5Sjfb }
806a021dec5Sjfb
807a021dec5Sjfb /*
808a33e30caSjfb * rcs_lock_add()
809a33e30caSjfb *
810a33e30caSjfb * Add an RCS lock for the user <user> on revision <rev>.
811a33e30caSjfb * Returns 0 on success, or -1 on failure.
812a33e30caSjfb */
813a33e30caSjfb int
rcs_lock_add(RCSFILE * file,const char * user,RCSNUM * rev)814a33e30caSjfb rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
815a33e30caSjfb {
816a33e30caSjfb struct rcs_lock *lkp;
817a33e30caSjfb
818a33e30caSjfb /* first look for duplication */
819a33e30caSjfb TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
820d593696fSderaadt if (strcmp(lkp->rl_name, user) == 0 &&
8215b95d21fSjoris rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
822a33e30caSjfb return (-1);
823a33e30caSjfb }
824a33e30caSjfb
8253b4c5c25Sray lkp = xmalloc(sizeof(*lkp));
8260450b43bSjoris lkp->rl_name = xstrdup(user);
8273422a189Sjoris lkp->rl_num = rcsnum_alloc();
82846ced471Smoritz rcsnum_cpy(rev, lkp->rl_num, 0);
82946ced471Smoritz
830a33e30caSjfb TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
831a33e30caSjfb
832a33e30caSjfb /* not synced anymore */
833a33e30caSjfb file->rf_flags &= ~RCS_SYNCED;
834a33e30caSjfb return (0);
835a33e30caSjfb }
836a33e30caSjfb
837a33e30caSjfb
838a33e30caSjfb /*
839a33e30caSjfb * rcs_lock_remove()
840a33e30caSjfb *
841a33e30caSjfb * Remove the RCS lock on revision <rev>.
842a33e30caSjfb * Returns 0 on success, or -1 on failure.
843a33e30caSjfb */
844a33e30caSjfb int
rcs_lock_remove(RCSFILE * file,const char * user,RCSNUM * rev)845bbfcb536Sjoris rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
846a33e30caSjfb {
847a33e30caSjfb struct rcs_lock *lkp;
848a33e30caSjfb
849bbfcb536Sjoris TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
850d593696fSderaadt if (strcmp(lkp->rl_name, user) == 0 &&
851d593696fSderaadt rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
852a33e30caSjfb break;
853bbfcb536Sjoris }
854a33e30caSjfb
8555b95d21fSjoris if (lkp == NULL)
856a33e30caSjfb return (-1);
857a33e30caSjfb
858a33e30caSjfb TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
85953ce2177Sfcambus free(lkp->rl_num);
860397ddb8aSnicm free(lkp->rl_name);
861397ddb8aSnicm free(lkp);
862a33e30caSjfb
863a33e30caSjfb /* not synced anymore */
864a33e30caSjfb file->rf_flags &= ~RCS_SYNCED;
865a33e30caSjfb return (0);
866a33e30caSjfb }
867a33e30caSjfb
868a33e30caSjfb /*
869101fc366Sjfb * rcs_desc_get()
870101fc366Sjfb *
871101fc366Sjfb * Retrieve the description for the RCS file <file>.
872101fc366Sjfb */
873101fc366Sjfb const char *
rcs_desc_get(RCSFILE * file)874101fc366Sjfb rcs_desc_get(RCSFILE *file)
875101fc366Sjfb {
876101fc366Sjfb return (file->rf_desc);
877101fc366Sjfb }
878101fc366Sjfb
879101fc366Sjfb /*
880101fc366Sjfb * rcs_desc_set()
881101fc366Sjfb *
882101fc366Sjfb * Set the description for the RCS file <file>.
883101fc366Sjfb */
884f92ad66fSxsa void
rcs_desc_set(RCSFILE * file,const char * desc)885101fc366Sjfb rcs_desc_set(RCSFILE *file, const char *desc)
886101fc366Sjfb {
887101fc366Sjfb char *tmp;
888101fc366Sjfb
8890450b43bSjoris tmp = xstrdup(desc);
890397ddb8aSnicm free(file->rf_desc);
891101fc366Sjfb file->rf_desc = tmp;
892101fc366Sjfb file->rf_flags &= ~RCS_SYNCED;
893101fc366Sjfb }
894101fc366Sjfb
89570e8f52aSjfb /*
896ff88b297Sjfb * rcs_comment_lookup()
897ff88b297Sjfb *
898ff88b297Sjfb * Lookup the assumed comment leader based on a file's suffix.
899ff88b297Sjfb * Returns a pointer to the string on success, or NULL on failure.
900ff88b297Sjfb */
901ff88b297Sjfb const char *
rcs_comment_lookup(const char * filename)902ff88b297Sjfb rcs_comment_lookup(const char *filename)
903ff88b297Sjfb {
904ff88b297Sjfb int i;
905ff88b297Sjfb const char *sp;
906ff88b297Sjfb
9075b95d21fSjoris if ((sp = strrchr(filename, '.')) == NULL)
908ff88b297Sjfb return (NULL);
909ff88b297Sjfb sp++;
910ff88b297Sjfb
911ff88b297Sjfb for (i = 0; i < (int)NB_COMTYPES; i++)
912ff88b297Sjfb if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
913ff88b297Sjfb return (rcs_comments[i].rc_cstr);
914ff88b297Sjfb return (NULL);
915ff88b297Sjfb }
916ff88b297Sjfb
917ff88b297Sjfb /*
91870e8f52aSjfb * rcs_comment_get()
91970e8f52aSjfb *
92070e8f52aSjfb * Retrieve the comment leader for the RCS file <file>.
92170e8f52aSjfb */
92270e8f52aSjfb const char *
rcs_comment_get(RCSFILE * file)92370e8f52aSjfb rcs_comment_get(RCSFILE *file)
92470e8f52aSjfb {
92570e8f52aSjfb return (file->rf_comment);
92670e8f52aSjfb }
92770e8f52aSjfb
92870e8f52aSjfb /*
92970e8f52aSjfb * rcs_comment_set()
93070e8f52aSjfb *
93170e8f52aSjfb * Set the comment leader for the RCS file <file>.
93270e8f52aSjfb */
9331d612bafSxsa void
rcs_comment_set(RCSFILE * file,const char * comment)93470e8f52aSjfb rcs_comment_set(RCSFILE *file, const char *comment)
93570e8f52aSjfb {
93670e8f52aSjfb char *tmp;
93770e8f52aSjfb
9380450b43bSjoris tmp = xstrdup(comment);
939397ddb8aSnicm free(file->rf_comment);
94070e8f52aSjfb file->rf_comment = tmp;
94170e8f52aSjfb file->rf_flags &= ~RCS_SYNCED;
94270e8f52aSjfb }
943101fc366Sjfb
94401af718aSjoris int
rcs_patch_lines(struct rcs_lines * dlines,struct rcs_lines * plines,struct rcs_line ** alines,struct rcs_delta * rdp)9457bb3ddb0Sray rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines,
9467bb3ddb0Sray struct rcs_line **alines, struct rcs_delta *rdp)
9478df73530Svincent {
94890fdc30cSotto u_char op;
94990fdc30cSotto char *ep;
9507bb3ddb0Sray struct rcs_line *lp, *dlp, *ndlp;
9518df73530Svincent int i, lineno, nbln;
95231cfd684Sniallo u_char tmp;
95308f90673Sjfb
95401af718aSjoris dlp = TAILQ_FIRST(&(dlines->l_lines));
95501af718aSjoris lp = TAILQ_FIRST(&(plines->l_lines));
95608f90673Sjfb
95708f90673Sjfb /* skip first bogus line */
95801af718aSjoris for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
95901af718aSjoris lp = TAILQ_NEXT(lp, l_list)) {
96031cfd684Sniallo if (lp->l_len < 2)
96131cfd684Sniallo fatal("line too short, RCS patch seems broken");
96201af718aSjoris op = *(lp->l_line);
96331cfd684Sniallo /* NUL-terminate line buffer for strtol() safety. */
96431cfd684Sniallo tmp = lp->l_line[lp->l_len - 1];
96531cfd684Sniallo lp->l_line[lp->l_len - 1] = '\0';
96690fdc30cSotto lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
96731cfd684Sniallo if (lineno - 1 > dlines->l_nblines || lineno < 0) {
96831cfd684Sniallo fatal("invalid line specification in RCS patch");
96931cfd684Sniallo }
97008f90673Sjfb ep++;
97108f90673Sjfb nbln = (int)strtol(ep, &ep, 10);
97231cfd684Sniallo /* Restore the last byte of the buffer */
97331cfd684Sniallo lp->l_line[lp->l_len - 1] = tmp;
97431cfd684Sniallo if (nbln < 0)
975e643a8ebSniallo fatal("invalid line number specification in RCS patch");
97608f90673Sjfb
97708f90673Sjfb /* find the appropriate line */
97808f90673Sjfb for (;;) {
97908f90673Sjfb if (dlp == NULL)
98008f90673Sjfb break;
98101af718aSjoris if (dlp->l_lineno == lineno)
98208f90673Sjfb break;
98301af718aSjoris if (dlp->l_lineno > lineno) {
9847a9e6d11Sray dlp = TAILQ_PREV(dlp, tqh, l_list);
98501af718aSjoris } else if (dlp->l_lineno < lineno) {
986228c451cSniallo if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
987d593696fSderaadt ndlp->l_lineno > lineno)
98808f90673Sjfb break;
98908f90673Sjfb dlp = ndlp;
99008f90673Sjfb }
99108f90673Sjfb }
992e643a8ebSniallo if (dlp == NULL)
993e643a8ebSniallo fatal("can't find referenced line in RCS patch");
99408f90673Sjfb
99508f90673Sjfb if (op == 'd') {
99608f90673Sjfb for (i = 0; (i < nbln) && (dlp != NULL); i++) {
99701af718aSjoris ndlp = TAILQ_NEXT(dlp, l_list);
99801af718aSjoris TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
9995e4c4390Stobias if (alines != NULL && dlp->l_line != NULL) {
10005e4c4390Stobias dlp->l_delta = rdp;
10015e4c4390Stobias alines[dlp->l_lineno_orig - 1] =
10025e4c4390Stobias dlp;
10035e4c4390Stobias } else
1004397ddb8aSnicm free(dlp);
100508f90673Sjfb dlp = ndlp;
1006228c451cSniallo /* last line is gone - reset dlp */
1007228c451cSniallo if (dlp == NULL) {
1008228c451cSniallo ndlp = TAILQ_LAST(&(dlines->l_lines),
10097a9e6d11Sray tqh);
1010228c451cSniallo dlp = ndlp;
1011228c451cSniallo }
101208f90673Sjfb }
10133917c9bfSderaadt } else if (op == 'a') {
101408f90673Sjfb for (i = 0; i < nbln; i++) {
101508f90673Sjfb ndlp = lp;
101601af718aSjoris lp = TAILQ_NEXT(lp, l_list);
1017e643a8ebSniallo if (lp == NULL)
1018e643a8ebSniallo fatal("truncated RCS patch");
101901af718aSjoris TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
10205e4c4390Stobias if (alines != NULL) {
10215e4c4390Stobias if (lp->l_needsfree == 1)
1022397ddb8aSnicm free(lp->l_line);
10235e4c4390Stobias lp->l_line = NULL;
10245e4c4390Stobias lp->l_needsfree = 0;
10255e4c4390Stobias }
102637cbc181Stobias lp->l_delta = rdp;
102701af718aSjoris TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
102801af718aSjoris lp, l_list);
102908f90673Sjfb dlp = lp;
103008f90673Sjfb
103108f90673Sjfb /* we don't want lookup to block on those */
103201af718aSjoris lp->l_lineno = lineno;
103308f90673Sjfb
103408f90673Sjfb lp = ndlp;
103508f90673Sjfb }
1036e643a8ebSniallo } else
1037e643a8ebSniallo fatal("unknown RCS patch operation `%c'", op);
103808f90673Sjfb
103908f90673Sjfb /* last line of the patch, done */
104001af718aSjoris if (lp->l_lineno == plines->l_nblines)
104108f90673Sjfb break;
104208f90673Sjfb }
104308f90673Sjfb
104408f90673Sjfb /* once we're done patching, rebuild the line numbers */
1045384f8873Svincent lineno = 0;
104601af718aSjoris TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
104701af718aSjoris lp->l_lineno = lineno++;
104801af718aSjoris dlines->l_nblines = lineno - 1;
104908f90673Sjfb
10508df73530Svincent return (0);
105108f90673Sjfb }
105208f90673Sjfb
10539f5450fbSjoris void
rcs_delta_stats(struct rcs_delta * rdp,int * ladded,int * lremoved)10549f5450fbSjoris rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
10559f5450fbSjoris {
10567bb3ddb0Sray struct rcs_lines *plines;
10577bb3ddb0Sray struct rcs_line *lp;
10584fc7a9b5Snicm int added, i, nbln, removed;
10599f5450fbSjoris char op, *ep;
10609f5450fbSjoris u_char tmp;
10619f5450fbSjoris
10629f5450fbSjoris added = removed = 0;
10639f5450fbSjoris
10649f5450fbSjoris plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
10659f5450fbSjoris lp = TAILQ_FIRST(&(plines->l_lines));
10669f5450fbSjoris
10679f5450fbSjoris /* skip first bogus line */
10689f5450fbSjoris for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
10699f5450fbSjoris lp = TAILQ_NEXT(lp, l_list)) {
10709f5450fbSjoris if (lp->l_len < 2)
10719f5450fbSjoris fatal("line too short, RCS patch seems broken");
10729f5450fbSjoris op = *(lp->l_line);
10739f5450fbSjoris /* NUL-terminate line buffer for strtol() safety. */
10749f5450fbSjoris tmp = lp->l_line[lp->l_len - 1];
10759f5450fbSjoris lp->l_line[lp->l_len - 1] = '\0';
10764fc7a9b5Snicm (void)strtol((lp->l_line + 1), &ep, 10);
10779f5450fbSjoris ep++;
10789f5450fbSjoris nbln = (int)strtol(ep, &ep, 10);
10799f5450fbSjoris /* Restore the last byte of the buffer */
10809f5450fbSjoris lp->l_line[lp->l_len - 1] = tmp;
10819f5450fbSjoris if (nbln < 0)
10829f5450fbSjoris fatal("invalid line number specification in RCS patch");
10839f5450fbSjoris
10849f5450fbSjoris if (op == 'a') {
10859f5450fbSjoris added += nbln;
10869f5450fbSjoris for (i = 0; i < nbln; i++) {
10879f5450fbSjoris lp = TAILQ_NEXT(lp, l_list);
10889f5450fbSjoris if (lp == NULL)
10899f5450fbSjoris fatal("truncated RCS patch");
10909f5450fbSjoris }
10919f5450fbSjoris }
10929f5450fbSjoris else if (op == 'd')
10939f5450fbSjoris removed += nbln;
10949f5450fbSjoris else
10959f5450fbSjoris fatal("unknown RCS patch operation '%c'", op);
10969f5450fbSjoris }
10979f5450fbSjoris
1098e0641964Stobias cvs_freelines(plines);
1099e0641964Stobias
11009f5450fbSjoris *ladded = added;
11019f5450fbSjoris *lremoved = removed;
11029f5450fbSjoris }
11039f5450fbSjoris
110408f90673Sjfb /*
110556bc38daSjfb * rcs_rev_add()
110656bc38daSjfb *
11077873cb13Sjfb * Add a revision to the RCS file <rf>. The new revision's number can be
11087873cb13Sjfb * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
11097873cb13Sjfb * new revision will have a number equal to the previous head revision plus
11107873cb13Sjfb * one). The <msg> argument specifies the log message for that revision, and
11117873cb13Sjfb * <date> specifies the revision's date (a value of -1 is
11127873cb13Sjfb * equivalent to using the current time).
11139d97bd6aSray * If <author> is NULL, set the author for this revision to the current user.
111456bc38daSjfb * Returns 0 on success, or -1 on failure.
111556bc38daSjfb */
111656bc38daSjfb int
rcs_rev_add(RCSFILE * rf,RCSNUM * rev,const char * msg,time_t date,const char * author)111744b1b892Sniallo rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
11189d97bd6aSray const char *author)
111956bc38daSjfb {
112056bc38daSjfb time_t now;
1121e1936db1Stobias RCSNUM *root = NULL;
112256bc38daSjfb struct passwd *pw;
1123e1936db1Stobias struct rcs_branch *brp, *obrp;
1124181bf63dSjoris struct rcs_delta *ordp, *rdp;
1125181bf63dSjoris
112656bc38daSjfb if (rev == RCS_HEAD_REV) {
112795498594Sniallo if (rf->rf_flags & RCS_CREATE) {
112895498594Sniallo if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
112995498594Sniallo return (-1);
1130397ddb8aSnicm free(rf->rf_head);
1131509a3673Stobias rf->rf_head = rev;
1132e28eda4eStobias } else if (rf->rf_head == NULL) {
1133e28eda4eStobias return (-1);
113495498594Sniallo } else {
1135f9b67873Sniallo rev = rcsnum_inc(rf->rf_head);
113695498594Sniallo }
1137181bf63dSjoris } else {
11385b95d21fSjoris if ((rdp = rcs_findrev(rf, rev)) != NULL)
113956bc38daSjfb return (-1);
114056bc38daSjfb }
1141181bf63dSjoris
11423b4c5c25Sray rdp = xcalloc(1, sizeof(*rdp));
114356bc38daSjfb
114456bc38daSjfb TAILQ_INIT(&(rdp->rd_branches));
114556bc38daSjfb
11463422a189Sjoris rdp->rd_num = rcsnum_alloc();
114756bc38daSjfb rcsnum_cpy(rev, rdp->rd_num, 0);
114856bc38daSjfb
11493422a189Sjoris rdp->rd_next = rcsnum_alloc();
115053b9345bSjoris
11519d97bd6aSray if (!author && !(author = getlogin())) {
11529d97bd6aSray if (!(pw = getpwuid(getuid())))
11539d97bd6aSray fatal("getpwuid failed");
11549d97bd6aSray author = pw->pw_name;
11559d97bd6aSray }
11569d97bd6aSray rdp->rd_author = xstrdup(author);
11570450b43bSjoris rdp->rd_state = xstrdup(RCS_STATE_EXP);
11580450b43bSjoris rdp->rd_log = xstrdup(msg);
115956bc38daSjfb
11607873cb13Sjfb if (date != (time_t)(-1))
11617873cb13Sjfb now = date;
11627873cb13Sjfb else
116356bc38daSjfb time(&now);
116456bc38daSjfb gmtime_r(&now, &(rdp->rd_date));
116556bc38daSjfb
116608458e59Sjoris if (RCSNUM_ISBRANCHREV(rev))
116708458e59Sjoris TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
116808458e59Sjoris else
116956bc38daSjfb TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
117056bc38daSjfb rf->rf_ndelta++;
1171f9b67873Sniallo
117208458e59Sjoris if (!(rf->rf_flags & RCS_CREATE)) {
117308458e59Sjoris if (RCSNUM_ISBRANCHREV(rev)) {
1174e1936db1Stobias if (rev->rn_id[rev->rn_len - 1] == 1) {
1175e1936db1Stobias /* a new branch */
117618cf7829Sjoris root = rcsnum_branch_root(rev);
117708458e59Sjoris brp = xmalloc(sizeof(*brp));
117808458e59Sjoris brp->rb_num = rcsnum_alloc();
117908458e59Sjoris rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1180e1936db1Stobias
118118cf7829Sjoris if ((ordp = rcs_findrev(rf, root)) == NULL)
118218cf7829Sjoris fatal("root node not found");
1183e1936db1Stobias
1184e1936db1Stobias TAILQ_FOREACH(obrp, &(ordp->rd_branches),
1185e1936db1Stobias rb_list) {
1186e1936db1Stobias if (!rcsnum_cmp(obrp->rb_num,
1187e1936db1Stobias brp->rb_num,
1188e1936db1Stobias brp->rb_num->rn_len - 1))
1189e1936db1Stobias break;
1190e1936db1Stobias }
1191e1936db1Stobias
1192e1936db1Stobias if (obrp == NULL) {
119318cf7829Sjoris TAILQ_INSERT_TAIL(&(ordp->rd_branches),
119418cf7829Sjoris brp, rb_list);
119518cf7829Sjoris }
1196e1936db1Stobias } else {
1197e1936db1Stobias root = rcsnum_alloc();
1198e1936db1Stobias rcsnum_cpy(rev, root, 0);
1199e1936db1Stobias rcsnum_dec(root);
1200e1936db1Stobias if ((ordp = rcs_findrev(rf, root)) == NULL)
1201e1936db1Stobias fatal("previous revision not found");
120208458e59Sjoris rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1203e1936db1Stobias }
120408458e59Sjoris } else {
120508458e59Sjoris ordp = TAILQ_NEXT(rdp, rd_list);
120608458e59Sjoris rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
120708458e59Sjoris }
120808458e59Sjoris }
120908458e59Sjoris
121053ce2177Sfcambus free(root);
1211e1936db1Stobias
1212dddcab29Sniallo /* not synced anymore */
1213dddcab29Sniallo rf->rf_flags &= ~RCS_SYNCED;
121456bc38daSjfb
121556bc38daSjfb return (0);
121656bc38daSjfb }
121756bc38daSjfb
121856bc38daSjfb /*
121956bc38daSjfb * rcs_rev_remove()
122056bc38daSjfb *
122156bc38daSjfb * Remove the revision whose number is <rev> from the RCS file <rf>.
122256bc38daSjfb */
122356bc38daSjfb int
rcs_rev_remove(RCSFILE * rf,RCSNUM * rev)122456bc38daSjfb rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
122556bc38daSjfb {
12262e0d696aSjoris int fd1, fd2;
12274d33f394Sjoris char *path_tmp1, *path_tmp2;
1228ea1ee718Sjoris struct rcs_delta *rdp, *prevrdp, *nextrdp;
122990fdc30cSotto BUF *prevbuf, *newdiff, *newdeltatext;
123056bc38daSjfb
123156bc38daSjfb if (rev == RCS_HEAD_REV)
123256bc38daSjfb rev = rf->rf_head;
123356bc38daSjfb
1234e28eda4eStobias if (rev == NULL)
1235e28eda4eStobias return (-1);
1236e28eda4eStobias
123756bc38daSjfb /* do we actually have that revision? */
12385b95d21fSjoris if ((rdp = rcs_findrev(rf, rev)) == NULL)
1239ea1ee718Sjoris return (-1);
124056bc38daSjfb
1241ea1ee718Sjoris /*
1242ea1ee718Sjoris * This is confusing, the previous delta is next in the TAILQ list.
1243ea1ee718Sjoris * the next delta is the previous one in the TAILQ list.
1244ea1ee718Sjoris *
1245ea1ee718Sjoris * When the HEAD revision got specified, nextrdp will be NULL.
1246ea1ee718Sjoris * When the first revision got specified, prevrdp will be NULL.
1247ea1ee718Sjoris */
1248ea1ee718Sjoris prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
12497a9e6d11Sray nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list);
125056bc38daSjfb
1251ea1ee718Sjoris newdeltatext = NULL;
125290fdc30cSotto prevbuf = NULL;
125344b1b14fSjoris path_tmp1 = path_tmp2 = NULL;
1254ea1ee718Sjoris
1255d593696fSderaadt if (prevrdp != NULL && nextrdp != NULL) {
12567bb3ddb0Sray newdiff = buf_alloc(64);
1257ea1ee718Sjoris
1258ea1ee718Sjoris /* calculate new diff */
12593ad3fb45Sjoris (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
12602e0d696aSjoris fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1261ea1ee718Sjoris
12623ad3fb45Sjoris (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
12632e0d696aSjoris fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1264ea1ee718Sjoris
1265ea1ee718Sjoris diff_format = D_RCSDIFF;
126657003866Sray if (diffreg(path_tmp1, path_tmp2,
1267219c50abSray fd1, fd2, newdiff, D_FORCEASCII) == D_ERROR)
12683ad3fb45Sjoris fatal("rcs_diffreg failed");
1269ea1ee718Sjoris
12702e0d696aSjoris close(fd1);
12712e0d696aSjoris close(fd2);
12722e0d696aSjoris
12734d33f394Sjoris newdeltatext = newdiff;
1274d593696fSderaadt } else if (nextrdp == NULL && prevrdp != NULL) {
12754d33f394Sjoris newdeltatext = prevbuf;
1276ea1ee718Sjoris }
1277ea1ee718Sjoris
1278ea1ee718Sjoris if (newdeltatext != NULL) {
1279ea1ee718Sjoris if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1280ea1ee718Sjoris fatal("error setting new deltatext");
1281ea1ee718Sjoris }
1282ea1ee718Sjoris
1283ea1ee718Sjoris TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1284ea1ee718Sjoris
1285ea1ee718Sjoris /* update pointers */
1286d593696fSderaadt if (prevrdp != NULL && nextrdp != NULL) {
1287ea1ee718Sjoris rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1288ea1ee718Sjoris } else if (prevrdp != NULL) {
12891db03c24Sxsa if (rcs_head_set(rf, prevrdp->rd_num) < 0)
12901db03c24Sxsa fatal("rcs_head_set failed");
1291ea1ee718Sjoris } else if (nextrdp != NULL) {
129253ce2177Sfcambus free(nextrdp->rd_next);
1293ea1ee718Sjoris nextrdp->rd_next = rcsnum_alloc();
1294ea1ee718Sjoris } else {
129553ce2177Sfcambus free(rf->rf_head);
1296ea1ee718Sjoris rf->rf_head = NULL;
1297ea1ee718Sjoris }
1298ea1ee718Sjoris
1299ea1ee718Sjoris rf->rf_ndelta--;
1300ea1ee718Sjoris rf->rf_flags &= ~RCS_SYNCED;
1301ea1ee718Sjoris
1302ea1ee718Sjoris rcs_freedelta(rdp);
1303397ddb8aSnicm free(newdeltatext);
1304397ddb8aSnicm free(path_tmp1);
1305397ddb8aSnicm free(path_tmp2);
13063ad3fb45Sjoris
1307ea1ee718Sjoris return (0);
130856bc38daSjfb }
130956bc38daSjfb
131056bc38daSjfb /*
131108f90673Sjfb * rcs_findrev()
131208f90673Sjfb *
131308f90673Sjfb * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
131408f90673Sjfb * The revision number is given in <rev>.
1315b1ce7b24Sjoris *
131608f90673Sjfb * Returns a pointer to the delta on success, or NULL on failure.
131708f90673Sjfb */
1318fccdc061Sxsa struct rcs_delta *
rcs_findrev(RCSFILE * rfp,RCSNUM * rev)1319b71c7dfeSniallo rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
132008f90673Sjfb {
132140e206ceSjoris int isbrev;
1322b1ce7b24Sjoris struct rcs_delta *rdp;
132340e206ceSjoris
1324cd71f6bbStobias if (rev == NULL)
1325cd71f6bbStobias return NULL;
1326cd71f6bbStobias
132740e206ceSjoris isbrev = RCSNUM_ISBRANCHREV(rev);
132808f90673Sjfb
1329b71c7dfeSniallo /*
1330b71c7dfeSniallo * We need to do more parsing if the last revision in the linked list
1331b71c7dfeSniallo * is greater than the requested revision.
1332b71c7dfeSniallo */
1333b1ce7b24Sjoris rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1334d593696fSderaadt if (rdp == NULL ||
133540e206ceSjoris (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
133640e206ceSjoris ((isbrev && rdp->rd_num->rn_len < 4) ||
133740e206ceSjoris (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1338fead43dfStobias if (rcsparse_deltas(rfp, rev))
1339fead43dfStobias fatal("error parsing deltas");
1340b71c7dfeSniallo }
1341b71c7dfeSniallo
1342b1ce7b24Sjoris TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
134340e206ceSjoris if (rcsnum_differ(rdp->rd_num, rev))
134440e206ceSjoris continue;
134540e206ceSjoris else
1346b1ce7b24Sjoris return (rdp);
1347b1ce7b24Sjoris }
134808f90673Sjfb
134908f90673Sjfb return (NULL);
135008f90673Sjfb }
135108f90673Sjfb
135208f90673Sjfb /*
13531b6534b8Sjfb * rcs_kwexp_set()
13541b6534b8Sjfb *
13551b6534b8Sjfb * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
13561b6534b8Sjfb */
135786ca4362Sxsa void
rcs_kwexp_set(RCSFILE * file,int mode)13581b6534b8Sjfb rcs_kwexp_set(RCSFILE *file, int mode)
13591b6534b8Sjfb {
13601b6534b8Sjfb int i;
13611b6534b8Sjfb char *tmp, buf[8] = "";
13621b6534b8Sjfb
13631b6534b8Sjfb if (RCS_KWEXP_INVAL(mode))
136486ca4362Sxsa return;
13651b6534b8Sjfb
13661b6534b8Sjfb i = 0;
13671b6534b8Sjfb if (mode == RCS_KWEXP_NONE)
13681b6534b8Sjfb buf[0] = 'b';
13691b6534b8Sjfb else if (mode == RCS_KWEXP_OLD)
13701b6534b8Sjfb buf[0] = 'o';
13711b6534b8Sjfb else {
13721b6534b8Sjfb if (mode & RCS_KWEXP_NAME)
13731b6534b8Sjfb buf[i++] = 'k';
13741b6534b8Sjfb if (mode & RCS_KWEXP_VAL)
13751b6534b8Sjfb buf[i++] = 'v';
13761b6534b8Sjfb if (mode & RCS_KWEXP_LKR)
13771b6534b8Sjfb buf[i++] = 'l';
13781b6534b8Sjfb }
13791b6534b8Sjfb
13800450b43bSjoris tmp = xstrdup(buf);
1381397ddb8aSnicm free(file->rf_expand);
13821b6534b8Sjfb file->rf_expand = tmp;
1383dddcab29Sniallo /* not synced anymore */
1384dddcab29Sniallo file->rf_flags &= ~RCS_SYNCED;
13851b6534b8Sjfb }
13861b6534b8Sjfb
13871b6534b8Sjfb /*
13881b6534b8Sjfb * rcs_kwexp_get()
13891b6534b8Sjfb *
13901b6534b8Sjfb * Retrieve the keyword expansion mode to be used for the RCS file <file>.
13911b6534b8Sjfb */
13921b6534b8Sjfb int
rcs_kwexp_get(RCSFILE * file)13931b6534b8Sjfb rcs_kwexp_get(RCSFILE *file)
13941b6534b8Sjfb {
1395f2a06cf4Stobias if (file->rf_expand == NULL)
1396f2a06cf4Stobias return (RCS_KWEXP_DEFAULT);
1397f2a06cf4Stobias
1398f2a06cf4Stobias return (rcs_kflag_get(file->rf_expand));
13991b6534b8Sjfb }
14001b6534b8Sjfb
14011b6534b8Sjfb /*
1402b856966bSjfb * rcs_kflag_get()
1403b856966bSjfb *
1404b856966bSjfb * Get the keyword expansion mode from a set of character flags given in
1405b856966bSjfb * <flags> and return the appropriate flag mask. In case of an error, the
1406b856966bSjfb * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1407b856966bSjfb */
1408b856966bSjfb int
rcs_kflag_get(const char * flags)1409b856966bSjfb rcs_kflag_get(const char *flags)
1410b856966bSjfb {
1411b856966bSjfb int fl;
1412b856966bSjfb size_t len;
1413b856966bSjfb const char *fp;
1414b856966bSjfb
1415f2a06cf4Stobias if (flags == NULL || !(len = strlen(flags)))
1416f2a06cf4Stobias return (RCS_KWEXP_ERR);
14176a64c583Stobias
1418b856966bSjfb fl = 0;
1419b856966bSjfb for (fp = flags; *fp != '\0'; fp++) {
1420b856966bSjfb if (*fp == 'k')
1421b856966bSjfb fl |= RCS_KWEXP_NAME;
1422b856966bSjfb else if (*fp == 'v')
1423b856966bSjfb fl |= RCS_KWEXP_VAL;
1424b856966bSjfb else if (*fp == 'l')
1425b856966bSjfb fl |= RCS_KWEXP_LKR;
1426b856966bSjfb else if (*fp == 'o') {
1427b856966bSjfb if (len != 1)
1428b856966bSjfb fl |= RCS_KWEXP_ERR;
1429b856966bSjfb fl |= RCS_KWEXP_OLD;
1430b856966bSjfb } else if (*fp == 'b') {
1431b856966bSjfb if (len != 1)
1432b856966bSjfb fl |= RCS_KWEXP_ERR;
143356f996a2Sxsa fl |= RCS_KWEXP_NONE;
1434b856966bSjfb } else /* unknown letter */
1435b856966bSjfb fl |= RCS_KWEXP_ERR;
1436b856966bSjfb }
1437b856966bSjfb
1438b856966bSjfb return (fl);
1439b856966bSjfb }
1440b856966bSjfb
144108f90673Sjfb /*
144208f90673Sjfb * rcs_freedelta()
144308f90673Sjfb *
144408f90673Sjfb * Free the contents of a delta structure.
144508f90673Sjfb */
14467fe21e34Sjfb static void
rcs_freedelta(struct rcs_delta * rdp)144708f90673Sjfb rcs_freedelta(struct rcs_delta *rdp)
144808f90673Sjfb {
1449e0305c2eSjfb struct rcs_branch *rb;
145008f90673Sjfb
145153ce2177Sfcambus free(rdp->rd_num);
145253ce2177Sfcambus free(rdp->rd_next);
1453397ddb8aSnicm free(rdp->rd_author);
1454397ddb8aSnicm free(rdp->rd_locker);
1455397ddb8aSnicm free(rdp->rd_state);
1456397ddb8aSnicm free(rdp->rd_log);
1457397ddb8aSnicm free(rdp->rd_text);
145808f90673Sjfb
1459e0305c2eSjfb while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1460e0305c2eSjfb TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
146153ce2177Sfcambus free(rb->rb_num);
1462397ddb8aSnicm free(rb);
1463e0305c2eSjfb }
1464e0305c2eSjfb
1465397ddb8aSnicm free(rdp);
146608f90673Sjfb }
146708f90673Sjfb
146808f90673Sjfb /*
1469163f330fSjfb * rcs_strprint()
1470163f330fSjfb *
1471163f330fSjfb * Output an RCS string <str> of size <slen> to the stream <stream>. Any
1472163f330fSjfb * '@' characters are escaped. Otherwise, the string can contain arbitrary
1473163f330fSjfb * binary data.
1474163f330fSjfb */
1475f240ac99Sray static void
rcs_strprint(const u_char * str,size_t slen,FILE * stream)1476163f330fSjfb rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1477163f330fSjfb {
1478163f330fSjfb const u_char *ap, *ep, *sp;
1479163f330fSjfb
148056bc38daSjfb if (slen == 0)
1481f240ac99Sray return;
148256bc38daSjfb
1483163f330fSjfb ep = str + slen - 1;
1484163f330fSjfb
1485163f330fSjfb for (sp = str; sp <= ep;) {
1486163f330fSjfb ap = memchr(sp, '@', ep - sp);
1487163f330fSjfb if (ap == NULL)
1488163f330fSjfb ap = ep;
1489f240ac99Sray (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1490163f330fSjfb
1491163f330fSjfb if (*ap == '@')
1492163f330fSjfb putc('@', stream);
1493163f330fSjfb sp = ap + 1;
1494163f330fSjfb }
1495163f330fSjfb }
14963b63875eSjoris
14973b63875eSjoris /*
1498f9b67873Sniallo * rcs_deltatext_set()
1499f9b67873Sniallo *
1500f9b67873Sniallo * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1501f9b67873Sniallo * Returns -1 on error, 0 on success.
1502f9b67873Sniallo */
1503f9b67873Sniallo int
rcs_deltatext_set(RCSFILE * rfp,RCSNUM * rev,BUF * bp)15044d33f394Sjoris rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1505f9b67873Sniallo {
1506f9b67873Sniallo size_t len;
15074d33f394Sjoris u_char *dtext;
1508f9b67873Sniallo struct rcs_delta *rdp;
1509f9b67873Sniallo
1510b71c7dfeSniallo /* Write operations require full parsing */
1511fead43dfStobias if (rcsparse_deltatexts(rfp, NULL))
1512fead43dfStobias return (-1);
1513b71c7dfeSniallo
1514f9b67873Sniallo if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1515f9b67873Sniallo return (-1);
1516f9b67873Sniallo
1517397ddb8aSnicm free(rdp->rd_text);
1518f9b67873Sniallo
15197bb3ddb0Sray len = buf_len(bp);
15207bb3ddb0Sray dtext = buf_release(bp);
15214d33f394Sjoris bp = NULL;
15224d33f394Sjoris
152377cdcb05Sjoris if (len != 0) {
15244d33f394Sjoris rdp->rd_text = xmalloc(len);
1525f240ac99Sray rdp->rd_tlen = len;
15264d33f394Sjoris (void)memcpy(rdp->rd_text, dtext, len);
152777cdcb05Sjoris } else {
152877cdcb05Sjoris rdp->rd_text = NULL;
152977cdcb05Sjoris rdp->rd_tlen = 0;
153077cdcb05Sjoris }
1531f9b67873Sniallo
1532397ddb8aSnicm free(dtext);
1533f9b67873Sniallo return (0);
1534f9b67873Sniallo }
1535e2cf1205Sjoris
1536e2cf1205Sjoris /*
1537e2cf1205Sjoris * rcs_rev_setlog()
1538e2cf1205Sjoris *
15397a347c71Stobias * Sets the log message of revision <rev> to <logtext>.
1540e2cf1205Sjoris */
1541e2cf1205Sjoris int
rcs_rev_setlog(RCSFILE * rfp,RCSNUM * rev,const char * logtext)1542e2cf1205Sjoris rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
1543e2cf1205Sjoris {
1544e2cf1205Sjoris struct rcs_delta *rdp;
1545e2cf1205Sjoris
1546e2cf1205Sjoris if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1547e2cf1205Sjoris return (-1);
1548e2cf1205Sjoris
1549397ddb8aSnicm free(rdp->rd_log);
1550e2cf1205Sjoris
15510450b43bSjoris rdp->rd_log = xstrdup(logtext);
1552e2cf1205Sjoris rfp->rf_flags &= ~RCS_SYNCED;
1553e2cf1205Sjoris return (0);
1554e2cf1205Sjoris }
1555c4521683Sniallo /*
1556c4521683Sniallo * rcs_rev_getdate()
1557c4521683Sniallo *
1558c4521683Sniallo * Get the date corresponding to a given revision.
1559c4521683Sniallo * Returns the date on success, -1 on failure.
1560c4521683Sniallo */
1561c4521683Sniallo time_t
rcs_rev_getdate(RCSFILE * rfp,RCSNUM * rev)1562c4521683Sniallo rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1563c4521683Sniallo {
1564c4521683Sniallo struct rcs_delta *rdp;
1565c4521683Sniallo
1566c4521683Sniallo if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1567c4521683Sniallo return (-1);
1568c4521683Sniallo
156947e5fe63Sjoris return (timegm(&rdp->rd_date));
1570c4521683Sniallo }
15719aae8b12Sniallo
15729aae8b12Sniallo /*
15739aae8b12Sniallo * rcs_state_set()
15749aae8b12Sniallo *
15759aae8b12Sniallo * Sets the state of revision <rev> to <state>
15769aae8b12Sniallo * NOTE: default state is 'Exp'. States may not contain spaces.
15779aae8b12Sniallo *
15789aae8b12Sniallo * Returns -1 on failure, 0 on success.
15799aae8b12Sniallo */
15809aae8b12Sniallo int
rcs_state_set(RCSFILE * rfp,RCSNUM * rev,const char * state)15819aae8b12Sniallo rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
15829aae8b12Sniallo {
15839aae8b12Sniallo struct rcs_delta *rdp;
15849aae8b12Sniallo
15859aae8b12Sniallo if ((rdp = rcs_findrev(rfp, rev)) == NULL)
15869aae8b12Sniallo return (-1);
15879aae8b12Sniallo
1588397ddb8aSnicm free(rdp->rd_state);
15899aae8b12Sniallo
15900450b43bSjoris rdp->rd_state = xstrdup(state);
15919aae8b12Sniallo
15929aae8b12Sniallo rfp->rf_flags &= ~RCS_SYNCED;
15939aae8b12Sniallo
15949aae8b12Sniallo return (0);
15959aae8b12Sniallo }
15969aae8b12Sniallo
15979aae8b12Sniallo /*
15989aae8b12Sniallo * rcs_state_check()
15999aae8b12Sniallo *
16009aae8b12Sniallo * Check if string <state> is valid.
16019aae8b12Sniallo *
16029aae8b12Sniallo * Returns 0 if the string is valid, -1 otherwise.
16039aae8b12Sniallo */
16049aae8b12Sniallo int
rcs_state_check(const char * state)16059aae8b12Sniallo rcs_state_check(const char *state)
16069aae8b12Sniallo {
1607feca7f66Stobias if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
16089aae8b12Sniallo return (-1);
16099aae8b12Sniallo
16109aae8b12Sniallo return (0);
16119aae8b12Sniallo }
1612c4521683Sniallo
1613c4521683Sniallo /*
1614c4521683Sniallo * rcs_state_get()
1615c4521683Sniallo *
1616c4521683Sniallo * Get the state for a given revision of a specified RCSFILE.
1617c4521683Sniallo *
1618c4521683Sniallo * Returns NULL on failure.
1619c4521683Sniallo */
1620c4521683Sniallo const char *
rcs_state_get(RCSFILE * rfp,RCSNUM * rev)1621c4521683Sniallo rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
1622c4521683Sniallo {
1623c4521683Sniallo struct rcs_delta *rdp;
1624c4521683Sniallo
1625c4521683Sniallo if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1626c4521683Sniallo return (NULL);
1627c4521683Sniallo
1628c4521683Sniallo return (rdp->rd_state);
1629c4521683Sniallo }
1630c4521683Sniallo
1631113cb29fStobias /* rcs_get_revision() */
1632113cb29fStobias static RCSNUM *
rcs_get_revision(const char * revstr,RCSFILE * rfp)1633113cb29fStobias rcs_get_revision(const char *revstr, RCSFILE *rfp)
163445d60d54Sjoris {
1635ca2dc546Sniallo RCSNUM *rev, *brev, *frev;
163640e206ceSjoris struct rcs_branch *brp;
1637aa3964e7Sjoris struct rcs_delta *rdp;
1638ca2dc546Sniallo size_t i;
1639aa3964e7Sjoris
1640aa3964e7Sjoris rdp = NULL;
164145d60d54Sjoris
16425dd120b0Sjoris if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
1643cd71f6bbStobias if (rfp->rf_head == NULL)
1644a37ec391Stobias return (NULL);
1645cd71f6bbStobias
16465dd120b0Sjoris frev = rcsnum_alloc();
16475dd120b0Sjoris rcsnum_cpy(rfp->rf_head, frev, 0);
16485dd120b0Sjoris return (frev);
16495dd120b0Sjoris }
16505dd120b0Sjoris
1651ca2dc546Sniallo /* Possibly we could be passed a version number */
1652b1989067Stobias if ((rev = rcsnum_parse(revstr)) != NULL) {
1653b1989067Stobias /* Do not return if it is not in RCS file */
1654a37ec391Stobias if ((rdp = rcs_findrev(rfp, rev)) != NULL)
1655a37ec391Stobias return (rev);
1656b1989067Stobias } else {
1657ca2dc546Sniallo /* More likely we will be passed a symbol */
165845d60d54Sjoris rev = rcs_sym_getrev(rfp, revstr);
1659b1989067Stobias }
1660aa3964e7Sjoris
1661ca2dc546Sniallo if (rev == NULL)
1662b03d8731Stobias return (NULL);
166308458e59Sjoris
1664f53e07caSjoris /*
1665f53e07caSjoris * If it was not a branch, thats ok the symbolic
1666d9a51c35Sjmc * name referred to a revision, so return the resolved
166791112048Sjoris * revision for the given name. */
1668dd2f49b9Stobias if (!RCSNUM_ISBRANCH(rev)) {
166991112048Sjoris /* Sanity check: The first two elements of any
167091112048Sjoris * revision (be it on a branch or on trunk) cannot
167191112048Sjoris * be greater than HEAD.
167291112048Sjoris *
167391112048Sjoris * XXX: To avoid comparing to uninitialized memory,
167491112048Sjoris * the minimum of both revision lengths is taken
167591112048Sjoris * instead of just 2.
167691112048Sjoris */
1677a37ec391Stobias if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
1678b9fc9a72Sderaadt MINIMUM(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
167953ce2177Sfcambus free(rev);
1680a37ec391Stobias return (NULL);
1681dd2f49b9Stobias }
1682f53e07caSjoris return (rev);
1683dd2f49b9Stobias }
1684ca2dc546Sniallo
1685ca2dc546Sniallo brev = rcsnum_alloc();
1686ca2dc546Sniallo rcsnum_cpy(rev, brev, rev->rn_len - 1);
1687ca2dc546Sniallo
1688ca2dc546Sniallo if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1689113cb29fStobias fatal("rcs_get_revision: tag `%s' does not exist", revstr);
169053ce2177Sfcambus free(brev);
1691ca2dc546Sniallo
1692ca2dc546Sniallo TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1693ca2dc546Sniallo for (i = 0; i < rev->rn_len; i++)
1694ca2dc546Sniallo if (brp->rb_num->rn_id[i] != rev->rn_id[i])
1695ca2dc546Sniallo break;
1696ca2dc546Sniallo if (i != rev->rn_len)
1697ca2dc546Sniallo continue;
1698ca2dc546Sniallo break;
1699ca2dc546Sniallo }
1700ca2dc546Sniallo
170153ce2177Sfcambus free(rev);
1702ca2dc546Sniallo frev = rcsnum_alloc();
1703ca2dc546Sniallo if (brp == NULL) {
1704ca2dc546Sniallo rcsnum_cpy(rdp->rd_num, frev, 0);
1705ca2dc546Sniallo return (frev);
1706ca2dc546Sniallo } else {
1707ca2dc546Sniallo /* Fetch the delta with the correct branch num */
170808458e59Sjoris if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1709113cb29fStobias fatal("rcs_get_revision: could not fetch branch "
1710113cb29fStobias "delta");
1711ca2dc546Sniallo rcsnum_cpy(rdp->rd_num, frev, 0);
1712ca2dc546Sniallo return (frev);
171340e206ceSjoris }
171445d60d54Sjoris }
17150dbb72f1Sniallo
17160dbb72f1Sniallo /*
17170dbb72f1Sniallo * rcs_rev_getlines()
17180dbb72f1Sniallo *
171917b2872aStobias * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
17207bb3ddb0Sray * return it as a pointer to a struct rcs_lines.
17210dbb72f1Sniallo */
17227bb3ddb0Sray struct rcs_lines *
rcs_rev_getlines(RCSFILE * rfp,RCSNUM * frev,struct rcs_line *** alines)17237bb3ddb0Sray rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
17240dbb72f1Sniallo {
172590fdc30cSotto size_t plen;
17265e4c4390Stobias int annotate, done, i, nextroot;
17270dbb72f1Sniallo RCSNUM *tnum, *bnum;
17280dbb72f1Sniallo struct rcs_branch *brp;
17295e4c4390Stobias struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
17300dbb72f1Sniallo u_char *patch;
17317bb3ddb0Sray struct rcs_line *line, *nline;
17327bb3ddb0Sray struct rcs_lines *dlines, *plines;
17330dbb72f1Sniallo
1734bc415189Snicm hrdp = prdp = rdp = trdp = NULL;
1735bc415189Snicm
1736e28eda4eStobias if (rfp->rf_head == NULL ||
1737e28eda4eStobias (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
17382700dfbcSjoris fatal("rcs_rev_getlines: no HEAD revision");
17390dbb72f1Sniallo
17400dbb72f1Sniallo tnum = frev;
1741fead43dfStobias if (rcsparse_deltatexts(rfp, hrdp->rd_num))
1742fead43dfStobias fatal("rcs_rev_getlines: rcsparse_deltatexts");
17430dbb72f1Sniallo
17440dbb72f1Sniallo /* revision on branch, get the branch root */
17450dbb72f1Sniallo nextroot = 2;
17460dbb72f1Sniallo bnum = rcsnum_alloc();
1747db6c4493Sjoris if (RCSNUM_ISBRANCHREV(tnum))
17480dbb72f1Sniallo rcsnum_cpy(tnum, bnum, nextroot);
1749db6c4493Sjoris else
1750db6c4493Sjoris rcsnum_cpy(tnum, bnum, tnum->rn_len);
17510dbb72f1Sniallo
17525e4c4390Stobias if (alines != NULL) {
17535e4c4390Stobias /* start with annotate first at requested revision */
17545e4c4390Stobias annotate = ANNOTATE_LATER;
17555e4c4390Stobias *alines = NULL;
17565e4c4390Stobias } else
17575e4c4390Stobias annotate = ANNOTATE_NEVER;
17585e4c4390Stobias
17590dbb72f1Sniallo dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
17600dbb72f1Sniallo
17610dbb72f1Sniallo done = 0;
17620dbb72f1Sniallo
17630dbb72f1Sniallo rdp = hrdp;
17645e4c4390Stobias if (!rcsnum_differ(rdp->rd_num, bnum)) {
17655e4c4390Stobias if (annotate == ANNOTATE_LATER) {
17665e4c4390Stobias /* found requested revision for annotate */
17675e4c4390Stobias i = 0;
17685e4c4390Stobias TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
17695e4c4390Stobias line->l_lineno_orig = line->l_lineno;
17705e4c4390Stobias i++;
17715e4c4390Stobias }
17720dbb72f1Sniallo
17737bb3ddb0Sray *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
17745e4c4390Stobias (*alines)[i] = NULL;
17755e4c4390Stobias annotate = ANNOTATE_NOW;
17765e4c4390Stobias
17775e4c4390Stobias /* annotate down to 1.1 from where we are */
177853ce2177Sfcambus free(bnum);
17795e4c4390Stobias bnum = rcsnum_parse("1.1");
17805e4c4390Stobias if (!rcsnum_differ(rdp->rd_num, bnum)) {
17815e4c4390Stobias goto next;
17825e4c4390Stobias }
17835e4c4390Stobias } else
17845e4c4390Stobias goto next;
17855e4c4390Stobias }
17865e4c4390Stobias
17875e4c4390Stobias prdp = hrdp;
17880dbb72f1Sniallo if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
17890dbb72f1Sniallo goto done;
17900dbb72f1Sniallo
17910dbb72f1Sniallo again:
179247cd82aeSmillert while (rdp != NULL) {
17930dbb72f1Sniallo if (rdp->rd_next->rn_len != 0) {
17940dbb72f1Sniallo trdp = rcs_findrev(rfp, rdp->rd_next);
17950dbb72f1Sniallo if (trdp == NULL)
17960dbb72f1Sniallo fatal("failed to grab next revision");
17970dbb72f1Sniallo }
17980dbb72f1Sniallo
17990dbb72f1Sniallo if (rdp->rd_tlen == 0) {
1800fead43dfStobias if (rcsparse_deltatexts(rfp, rdp->rd_num))
1801fead43dfStobias fatal("rcs_rev_getlines: rcsparse_deltatexts");
18020dbb72f1Sniallo if (rdp->rd_tlen == 0) {
18030dbb72f1Sniallo if (!rcsnum_differ(rdp->rd_num, bnum))
18040dbb72f1Sniallo break;
18050dbb72f1Sniallo rdp = trdp;
18060dbb72f1Sniallo continue;
18070dbb72f1Sniallo }
18080dbb72f1Sniallo }
18090dbb72f1Sniallo
18100dbb72f1Sniallo plen = rdp->rd_tlen;
18110dbb72f1Sniallo patch = rdp->rd_text;
18120dbb72f1Sniallo plines = cvs_splitlines(patch, plen);
18135e4c4390Stobias if (annotate == ANNOTATE_NOW)
18145e4c4390Stobias rcs_patch_lines(dlines, plines, *alines, prdp);
18155e4c4390Stobias else
18165e4c4390Stobias rcs_patch_lines(dlines, plines, NULL, NULL);
18170dbb72f1Sniallo cvs_freelines(plines);
18180dbb72f1Sniallo
18195e4c4390Stobias if (!rcsnum_differ(rdp->rd_num, bnum)) {
18205e4c4390Stobias if (annotate != ANNOTATE_LATER)
18215e4c4390Stobias break;
18225e4c4390Stobias
18235e4c4390Stobias /* found requested revision for annotate */
18245e4c4390Stobias i = 0;
18255e4c4390Stobias TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
18265e4c4390Stobias line->l_lineno_orig = line->l_lineno;
18275e4c4390Stobias i++;
18285e4c4390Stobias }
18295e4c4390Stobias
18307bb3ddb0Sray *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
18315e4c4390Stobias (*alines)[i] = NULL;
18325e4c4390Stobias annotate = ANNOTATE_NOW;
18335e4c4390Stobias
18345e4c4390Stobias /* annotate down to 1.1 from where we are */
183553ce2177Sfcambus free(bnum);
18365e4c4390Stobias bnum = rcsnum_parse("1.1");
18375e4c4390Stobias
18380dbb72f1Sniallo if (!rcsnum_differ(rdp->rd_num, bnum))
18390dbb72f1Sniallo break;
18405e4c4390Stobias }
18410dbb72f1Sniallo
18425e4c4390Stobias prdp = rdp;
18430dbb72f1Sniallo rdp = trdp;
18440dbb72f1Sniallo }
18450dbb72f1Sniallo
18460dbb72f1Sniallo next:
184747cd82aeSmillert if (rdp == NULL || !rcsnum_differ(rdp->rd_num, frev))
18480dbb72f1Sniallo done = 1;
18490dbb72f1Sniallo
18500dbb72f1Sniallo if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
18510dbb72f1Sniallo nextroot += 2;
18520dbb72f1Sniallo rcsnum_cpy(frev, bnum, nextroot);
18530dbb72f1Sniallo
18540dbb72f1Sniallo TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1855ca2dc546Sniallo for (i = 0; i < nextroot - 1; i++)
1856ca2dc546Sniallo if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
18570dbb72f1Sniallo break;
1858ca2dc546Sniallo if (i == nextroot - 1)
18590dbb72f1Sniallo break;
18600dbb72f1Sniallo }
18610dbb72f1Sniallo
18625e4c4390Stobias if (brp == NULL) {
18635e4c4390Stobias if (annotate != ANNOTATE_NEVER) {
1864397ddb8aSnicm free(*alines);
18655e4c4390Stobias *alines = NULL;
18665e4c4390Stobias cvs_freelines(dlines);
186753ce2177Sfcambus free(bnum);
18685e4c4390Stobias return (NULL);
18695e4c4390Stobias }
18700dbb72f1Sniallo fatal("expected branch not found on branch list");
18715e4c4390Stobias }
18720dbb72f1Sniallo
18730dbb72f1Sniallo if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
18742700dfbcSjoris fatal("rcs_rev_getlines: failed to get delta for target rev");
18750dbb72f1Sniallo
18760dbb72f1Sniallo goto again;
18770dbb72f1Sniallo }
18780dbb72f1Sniallo done:
187937cbc181Stobias /* put remaining lines into annotate buffer */
18805e4c4390Stobias if (annotate == ANNOTATE_NOW) {
18815e4c4390Stobias for (line = TAILQ_FIRST(&(dlines->l_lines));
18825e4c4390Stobias line != NULL; line = nline) {
18835e4c4390Stobias nline = TAILQ_NEXT(line, l_list);
18845e4c4390Stobias TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
18855e4c4390Stobias if (line->l_line == NULL) {
1886397ddb8aSnicm free(line);
18875e4c4390Stobias continue;
18885e4c4390Stobias }
18895e4c4390Stobias
18905e4c4390Stobias line->l_delta = rdp;
18915e4c4390Stobias (*alines)[line->l_lineno_orig - 1] = line;
18925e4c4390Stobias }
18935e4c4390Stobias
18945e4c4390Stobias cvs_freelines(dlines);
18955e4c4390Stobias dlines = NULL;
18965e4c4390Stobias }
18975e4c4390Stobias
18980dbb72f1Sniallo if (bnum != tnum)
189953ce2177Sfcambus free(bnum);
19000dbb72f1Sniallo
19010dbb72f1Sniallo return (dlines);
19020dbb72f1Sniallo }
19030dbb72f1Sniallo
190437cbc181Stobias void
rcs_annotate_getlines(RCSFILE * rfp,RCSNUM * frev,struct rcs_line *** alines)19057bb3ddb0Sray rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
190637cbc181Stobias {
190737cbc181Stobias size_t plen;
190837cbc181Stobias int i, nextroot;
190937cbc181Stobias RCSNUM *bnum;
191037cbc181Stobias struct rcs_branch *brp;
191137cbc181Stobias struct rcs_delta *rdp, *trdp;
191237cbc181Stobias u_char *patch;
19137bb3ddb0Sray struct rcs_line *line;
19147bb3ddb0Sray struct rcs_lines *dlines, *plines;
191537cbc181Stobias
1916bc415189Snicm rdp = trdp = NULL;
1917bc415189Snicm
191837cbc181Stobias if (!RCSNUM_ISBRANCHREV(frev))
191937cbc181Stobias fatal("rcs_annotate_getlines: branch revision expected");
192037cbc181Stobias
192137cbc181Stobias /* revision on branch, get the branch root */
192237cbc181Stobias nextroot = 2;
192337cbc181Stobias bnum = rcsnum_alloc();
192437cbc181Stobias rcsnum_cpy(frev, bnum, nextroot);
192537cbc181Stobias
192637cbc181Stobias /*
192737cbc181Stobias * Going from HEAD to 1.1 enables the use of an array, which is
192837cbc181Stobias * much faster. Unfortunately this is not possible with branch
192937cbc181Stobias * revisions, so copy over our alines (array) into dlines (tailq).
193037cbc181Stobias */
193137cbc181Stobias dlines = xcalloc(1, sizeof(*dlines));
193237cbc181Stobias TAILQ_INIT(&(dlines->l_lines));
193337cbc181Stobias line = xcalloc(1, sizeof(*line));
193437cbc181Stobias TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
193537cbc181Stobias
193637cbc181Stobias for (i = 0; (*alines)[i] != NULL; i++) {
193737cbc181Stobias line = (*alines)[i];
193837cbc181Stobias line->l_lineno = i + 1;
193937cbc181Stobias TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
194037cbc181Stobias }
194137cbc181Stobias
194237cbc181Stobias rdp = rcs_findrev(rfp, bnum);
194337cbc181Stobias if (rdp == NULL)
194437cbc181Stobias fatal("failed to grab branch root revision");
194537cbc181Stobias
194637cbc181Stobias do {
194737cbc181Stobias nextroot += 2;
194837cbc181Stobias rcsnum_cpy(frev, bnum, nextroot);
194937cbc181Stobias
195037cbc181Stobias TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
195137cbc181Stobias for (i = 0; i < nextroot - 1; i++)
195237cbc181Stobias if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
195337cbc181Stobias break;
195437cbc181Stobias if (i == nextroot - 1)
195537cbc181Stobias break;
195637cbc181Stobias }
195737cbc181Stobias
195837cbc181Stobias if (brp == NULL)
195937cbc181Stobias fatal("expected branch not found on branch list");
196037cbc181Stobias
196137cbc181Stobias if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
196237cbc181Stobias fatal("failed to get delta for target rev");
196337cbc181Stobias
196437cbc181Stobias for (;;) {
196537cbc181Stobias if (rdp->rd_next->rn_len != 0) {
196637cbc181Stobias trdp = rcs_findrev(rfp, rdp->rd_next);
196737cbc181Stobias if (trdp == NULL)
196837cbc181Stobias fatal("failed to grab next revision");
196937cbc181Stobias }
197037cbc181Stobias
197137cbc181Stobias if (rdp->rd_tlen == 0) {
1972fead43dfStobias if (rcsparse_deltatexts(rfp, rdp->rd_num))
1973fead43dfStobias fatal("rcs_annotate_getlines: "
1974fead43dfStobias "rcsparse_deltatexts");
197537cbc181Stobias if (rdp->rd_tlen == 0) {
197637cbc181Stobias if (!rcsnum_differ(rdp->rd_num, bnum))
197737cbc181Stobias break;
197837cbc181Stobias rdp = trdp;
197937cbc181Stobias continue;
198037cbc181Stobias }
198137cbc181Stobias }
198237cbc181Stobias
198337cbc181Stobias plen = rdp->rd_tlen;
198437cbc181Stobias patch = rdp->rd_text;
198537cbc181Stobias plines = cvs_splitlines(patch, plen);
198637cbc181Stobias rcs_patch_lines(dlines, plines, NULL, rdp);
198737cbc181Stobias cvs_freelines(plines);
198837cbc181Stobias
198937cbc181Stobias if (!rcsnum_differ(rdp->rd_num, bnum))
199037cbc181Stobias break;
199137cbc181Stobias
199237cbc181Stobias rdp = trdp;
199337cbc181Stobias }
199437cbc181Stobias } while (rcsnum_differ(rdp->rd_num, frev));
199537cbc181Stobias
199637cbc181Stobias if (bnum != frev)
199753ce2177Sfcambus free(bnum);
199837cbc181Stobias
199937cbc181Stobias /*
200037cbc181Stobias * All lines have been parsed, now they must be copied over
200137cbc181Stobias * into alines (array) again.
200237cbc181Stobias */
2003397ddb8aSnicm free(*alines);
200437cbc181Stobias
200537cbc181Stobias i = 0;
200637cbc181Stobias TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
200737cbc181Stobias if (line->l_line != NULL)
200837cbc181Stobias i++;
200937cbc181Stobias }
20107bb3ddb0Sray *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
201137cbc181Stobias (*alines)[i] = NULL;
201237cbc181Stobias
201337cbc181Stobias i = 0;
201437cbc181Stobias TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
201537cbc181Stobias if (line->l_line != NULL)
201637cbc181Stobias (*alines)[i++] = line;
201737cbc181Stobias }
201837cbc181Stobias }
201937cbc181Stobias
20200dbb72f1Sniallo /*
20210dbb72f1Sniallo * rcs_rev_getbuf()
20220dbb72f1Sniallo *
20230dbb72f1Sniallo * XXX: This is really really slow and should be avoided if at all possible!
20240dbb72f1Sniallo *
20250dbb72f1Sniallo * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
20260dbb72f1Sniallo * return it as a BUF pointer.
20270dbb72f1Sniallo */
20280dbb72f1Sniallo BUF *
rcs_rev_getbuf(RCSFILE * rfp,RCSNUM * rev,int mode)2029ff7b57e3Sjoris rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
20300dbb72f1Sniallo {
2031ff7b57e3Sjoris int expmode, expand;
2032ff7b57e3Sjoris struct rcs_delta *rdp;
20337bb3ddb0Sray struct rcs_lines *lines;
20347bb3ddb0Sray struct rcs_line *lp, *nlp;
20350dbb72f1Sniallo BUF *bp;
20360dbb72f1Sniallo
2037bc415189Snicm rdp = NULL;
2038bc415189Snicm expmode = RCS_KWEXP_NONE;
2039ff7b57e3Sjoris expand = 0;
20405e4c4390Stobias lines = rcs_rev_getlines(rfp, rev, NULL);
20417bb3ddb0Sray bp = buf_alloc(1024 * 16);
2042ff7b57e3Sjoris
2043ff7b57e3Sjoris if (!(mode & RCS_KWEXP_NONE)) {
2044ff7b57e3Sjoris expmode = rcs_kwexp_get(rfp);
2045ff7b57e3Sjoris
2046ff7b57e3Sjoris if (!(expmode & RCS_KWEXP_NONE)) {
204747cd82aeSmillert if ((rdp = rcs_findrev(rfp, rev)) == NULL) {
204847cd82aeSmillert char version[RCSNUM_MAXSTR];
204947cd82aeSmillert
205047cd82aeSmillert rcsnum_tostr(rev, version, sizeof(version));
205147cd82aeSmillert fatal("could not find desired version %s in %s",
205247cd82aeSmillert version, rfp->rf_path);
205347cd82aeSmillert }
205447cd82aeSmillert
2055ff7b57e3Sjoris expand = 1;
2056ff7b57e3Sjoris }
2057ff7b57e3Sjoris }
2058ff7b57e3Sjoris
2059364cb4c9Stobias for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2060364cb4c9Stobias nlp = TAILQ_NEXT(lp, l_list);
2061364cb4c9Stobias
2062364cb4c9Stobias if (lp->l_line == NULL) {
2063364cb4c9Stobias lp = nlp;
20640dbb72f1Sniallo continue;
2065364cb4c9Stobias }
2066ff7b57e3Sjoris
2067ff7b57e3Sjoris if (expand)
2068364cb4c9Stobias rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2069ff7b57e3Sjoris
2070364cb4c9Stobias do {
20717bb3ddb0Sray buf_append(bp, lp->l_line, lp->l_len);
2072364cb4c9Stobias } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
20730dbb72f1Sniallo }
20740dbb72f1Sniallo
20750dbb72f1Sniallo cvs_freelines(lines);
20760dbb72f1Sniallo
20770dbb72f1Sniallo return (bp);
20780dbb72f1Sniallo }
20790dbb72f1Sniallo
20800dbb72f1Sniallo /*
20810dbb72f1Sniallo * rcs_rev_write_fd()
20820dbb72f1Sniallo *
20830dbb72f1Sniallo * Write the entire contents of revision <frev> from the rcsfile <rfp> to
20840dbb72f1Sniallo * file descriptor <fd>.
20850dbb72f1Sniallo */
20860dbb72f1Sniallo void
rcs_rev_write_fd(RCSFILE * rfp,RCSNUM * rev,int _fd,int mode)20877c1a09a6Sjoris rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
20880dbb72f1Sniallo {
20897c1a09a6Sjoris int fd;
20907c1a09a6Sjoris FILE *fp;
20917c1a09a6Sjoris size_t ret;
2092ff7b57e3Sjoris int expmode, expand;
20930dbb72f1Sniallo struct rcs_delta *rdp;
20947bb3ddb0Sray struct rcs_lines *lines;
20957bb3ddb0Sray struct rcs_line *lp, *nlp;
20967ac78d1dSjoris extern int print_stdout;
20970dbb72f1Sniallo
2098bc415189Snicm rdp = NULL;
2099bc415189Snicm expmode = RCS_KWEXP_NONE;
2100ff7b57e3Sjoris expand = 0;
21015e4c4390Stobias lines = rcs_rev_getlines(rfp, rev, NULL);
2102ff7b57e3Sjoris
21030dbb72f1Sniallo if (!(mode & RCS_KWEXP_NONE)) {
21040dbb72f1Sniallo expmode = rcs_kwexp_get(rfp);
21050dbb72f1Sniallo
21060dbb72f1Sniallo if (!(expmode & RCS_KWEXP_NONE)) {
21070dbb72f1Sniallo if ((rdp = rcs_findrev(rfp, rev)) == NULL)
21080dbb72f1Sniallo fatal("could not fetch revision");
2109ff7b57e3Sjoris expand = 1;
21100dbb72f1Sniallo }
21110dbb72f1Sniallo }
2112ff7b57e3Sjoris
21137c1a09a6Sjoris fd = dup(_fd);
21147c1a09a6Sjoris if (fd == -1)
21157c1a09a6Sjoris fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
21167c1a09a6Sjoris
21177c1a09a6Sjoris if ((fp = fdopen(fd, "w")) == NULL)
21187c1a09a6Sjoris fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
21197c1a09a6Sjoris
2120364cb4c9Stobias for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
2121364cb4c9Stobias nlp = TAILQ_NEXT(lp, l_list);
2122364cb4c9Stobias
2123364cb4c9Stobias if (lp->l_line == NULL) {
2124364cb4c9Stobias lp = nlp;
21250dbb72f1Sniallo continue;
2126364cb4c9Stobias }
2127ff7b57e3Sjoris
2128ff7b57e3Sjoris if (expand)
2129364cb4c9Stobias rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
2130ff7b57e3Sjoris
2131364cb4c9Stobias do {
21327ac78d1dSjoris /*
21337ac78d1dSjoris * Solely for the checkout and update -p options.
21347ac78d1dSjoris */
21357ac78d1dSjoris if (cvs_server_active == 1 &&
21367ac78d1dSjoris (cvs_cmdop == CVS_OP_CHECKOUT ||
21377ac78d1dSjoris cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
21387c1a09a6Sjoris ret = fwrite("M ", 1, 2, fp);
21397c1a09a6Sjoris if (ret != 2)
2140364cb4c9Stobias fatal("rcs_rev_write_fd: %s",
2141364cb4c9Stobias strerror(errno));
21427ac78d1dSjoris }
21437ac78d1dSjoris
21447c1a09a6Sjoris ret = fwrite(lp->l_line, 1, lp->l_len, fp);
21457c1a09a6Sjoris if (ret != lp->l_len)
21460dbb72f1Sniallo fatal("rcs_rev_write_fd: %s", strerror(errno));
2147364cb4c9Stobias } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
21480dbb72f1Sniallo }
21490dbb72f1Sniallo
21500dbb72f1Sniallo cvs_freelines(lines);
21517c1a09a6Sjoris (void)fclose(fp);
21520dbb72f1Sniallo }
21530dbb72f1Sniallo
21540dbb72f1Sniallo /*
21550dbb72f1Sniallo * rcs_rev_write_stmp()
21560dbb72f1Sniallo *
21570dbb72f1Sniallo * Write the contents of the rev <rev> to a temporary file whose path is
21580dbb72f1Sniallo * specified using <template> (see mkstemp(3)). NB. This function will modify
21590dbb72f1Sniallo * <template>, as per mkstemp.
21600dbb72f1Sniallo */
21612e0d696aSjoris int
rcs_rev_write_stmp(RCSFILE * rfp,RCSNUM * rev,char * template,int mode)21620dbb72f1Sniallo rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
21630dbb72f1Sniallo {
21640dbb72f1Sniallo int fd;
21650dbb72f1Sniallo
21660dbb72f1Sniallo if ((fd = mkstemp(template)) == -1)
21670dbb72f1Sniallo fatal("mkstemp: `%s': %s", template, strerror(errno));
21680dbb72f1Sniallo
21697a9e6d11Sray worklist_add(template, &temp_files);
21700dbb72f1Sniallo rcs_rev_write_fd(rfp, rev, fd, mode);
21710dbb72f1Sniallo
21723aaa63ebSderaadt if (lseek(fd, 0, SEEK_SET) == -1)
21732e0d696aSjoris fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
21742e0d696aSjoris
21752e0d696aSjoris return (fd);
21760dbb72f1Sniallo }
21770dbb72f1Sniallo
21780dbb72f1Sniallo static void
rcs_kwexp_line(char * rcsfile,struct rcs_delta * rdp,struct rcs_lines * lines,struct rcs_line * line,int mode)21797bb3ddb0Sray rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct rcs_lines *lines,
21807bb3ddb0Sray struct rcs_line *line, int mode)
21810dbb72f1Sniallo {
2182d0e85bc8Stobias BUF *tmpbuf;
21830dbb72f1Sniallo int kwtype;
21840dbb72f1Sniallo u_int j, found;
218590fdc30cSotto const u_char *c, *start, *fin, *end;
2186431378d1Snaddy char *kwstr, *rcsfile_basename;
2187431378d1Snaddy char expbuf[256], buf[256], path[PATH_MAX];
2188d0e85bc8Stobias size_t clen, kwlen, len, tlen;
21890dbb72f1Sniallo
21900dbb72f1Sniallo kwtype = 0;
21910dbb72f1Sniallo kwstr = NULL;
21920dbb72f1Sniallo
219384355cd7Stobias if (mode & RCS_KWEXP_OLD)
219484355cd7Stobias return;
219584355cd7Stobias
21960dbb72f1Sniallo len = line->l_len;
21970dbb72f1Sniallo if (len == 0)
21980dbb72f1Sniallo return;
21990dbb72f1Sniallo
22000dbb72f1Sniallo c = line->l_line;
22010dbb72f1Sniallo found = 0;
22020dbb72f1Sniallo /* Final character in buffer. */
22030dbb72f1Sniallo fin = c + len - 1;
22040dbb72f1Sniallo
2205431378d1Snaddy if (strlcpy(path, rcsfile, sizeof(path)) >= sizeof(path))
2206431378d1Snaddy fatal("rcs_kwexp_line: truncation");
2207431378d1Snaddy rcsfile_basename = basename(path);
2208431378d1Snaddy
22090dbb72f1Sniallo /*
22100dbb72f1Sniallo * Keyword formats:
22110dbb72f1Sniallo * $Keyword$
22120dbb72f1Sniallo * $Keyword: value$
22130dbb72f1Sniallo */
22140dbb72f1Sniallo for (; c < fin; c++) {
2215d0e85bc8Stobias if (*c != '$')
2216d0e85bc8Stobias continue;
22170dbb72f1Sniallo
22180dbb72f1Sniallo /* remember start of this possible keyword */
22190dbb72f1Sniallo start = c;
22200dbb72f1Sniallo
22210dbb72f1Sniallo /* first following character has to be alphanumeric */
22220dbb72f1Sniallo c++;
22230dbb72f1Sniallo if (!isalpha(*c)) {
22240dbb72f1Sniallo c = start;
22250dbb72f1Sniallo continue;
22260dbb72f1Sniallo }
22270dbb72f1Sniallo
22280dbb72f1Sniallo /* Number of characters between c and fin, inclusive. */
22290dbb72f1Sniallo clen = fin - c + 1;
22300dbb72f1Sniallo
22310dbb72f1Sniallo /* look for any matching keywords */
22320dbb72f1Sniallo found = 0;
22330dbb72f1Sniallo for (j = 0; j < RCS_NKWORDS; j++) {
22340dbb72f1Sniallo kwlen = strlen(rcs_expkw[j].kw_str);
22350dbb72f1Sniallo /*
22360dbb72f1Sniallo * kwlen must be less than clen since clen
22370dbb72f1Sniallo * includes either a terminating `$' or a `:'.
22380dbb72f1Sniallo */
22390dbb72f1Sniallo if (kwlen < clen &&
22400dbb72f1Sniallo memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
22410dbb72f1Sniallo (c[kwlen] == '$' || c[kwlen] == ':')) {
22420dbb72f1Sniallo found = 1;
22430dbb72f1Sniallo kwstr = rcs_expkw[j].kw_str;
22440dbb72f1Sniallo kwtype = rcs_expkw[j].kw_type;
22450dbb72f1Sniallo c += kwlen;
22460dbb72f1Sniallo break;
22470dbb72f1Sniallo }
22480dbb72f1Sniallo }
22490dbb72f1Sniallo
22502700dfbcSjoris if (found == 0 && cvs_tagname != NULL) {
22512700dfbcSjoris kwlen = strlen(cvs_tagname);
22522700dfbcSjoris if (kwlen < clen &&
22532700dfbcSjoris memcmp(c, cvs_tagname, kwlen) == 0 &&
22542700dfbcSjoris (c[kwlen] == '$' || c[kwlen] == ':')) {
22552700dfbcSjoris found = 1;
22562700dfbcSjoris kwstr = cvs_tagname;
22572700dfbcSjoris kwtype = RCS_KW_ID;
22582700dfbcSjoris c += kwlen;
22592700dfbcSjoris }
22602700dfbcSjoris }
22612700dfbcSjoris
22620dbb72f1Sniallo /* unknown keyword, continue looking */
22630dbb72f1Sniallo if (found == 0) {
22640dbb72f1Sniallo c = start;
22650dbb72f1Sniallo continue;
22660dbb72f1Sniallo }
22670dbb72f1Sniallo
22680dbb72f1Sniallo /*
22690dbb72f1Sniallo * if the next character was ':' we need to look for
22700dbb72f1Sniallo * an '$' before the end of the line to be sure it is
22710dbb72f1Sniallo * in fact a keyword.
22720dbb72f1Sniallo */
22730dbb72f1Sniallo if (*c == ':') {
22740dbb72f1Sniallo for (; c <= fin; ++c) {
22750dbb72f1Sniallo if (*c == '$' || *c == '\n')
22760dbb72f1Sniallo break;
22770dbb72f1Sniallo }
22780dbb72f1Sniallo
22790dbb72f1Sniallo if (*c != '$') {
22800dbb72f1Sniallo c = start;
22810dbb72f1Sniallo continue;
22820dbb72f1Sniallo }
22830dbb72f1Sniallo }
22840dbb72f1Sniallo end = c + 1;
22850dbb72f1Sniallo
22860dbb72f1Sniallo /* start constructing the expansion */
22870dbb72f1Sniallo expbuf[0] = '\0';
22880dbb72f1Sniallo
22890dbb72f1Sniallo if (mode & RCS_KWEXP_NAME) {
2290d0e85bc8Stobias if (strlcat(expbuf, "$", sizeof(expbuf)) >=
2291d0e85bc8Stobias sizeof(expbuf) || strlcat(expbuf, kwstr,
2292d0e85bc8Stobias sizeof(expbuf)) >= sizeof(expbuf))
2293ff7b57e3Sjoris fatal("rcs_kwexp_line: truncated");
22940dbb72f1Sniallo if ((mode & RCS_KWEXP_VAL) &&
2295d0e85bc8Stobias strlcat(expbuf, ": ", sizeof(expbuf)) >=
2296d0e85bc8Stobias sizeof(expbuf))
2297ff7b57e3Sjoris fatal("rcs_kwexp_line: truncated");
22980dbb72f1Sniallo }
22990dbb72f1Sniallo
23000dbb72f1Sniallo /*
23010dbb72f1Sniallo * order matters because of RCS_KW_ID and
23020dbb72f1Sniallo * RCS_KW_HEADER here
23030dbb72f1Sniallo */
23040dbb72f1Sniallo if (mode & RCS_KWEXP_VAL) {
23050dbb72f1Sniallo if (kwtype & RCS_KW_RCSFILE) {
23060dbb72f1Sniallo if (!(kwtype & RCS_KW_FULLPATH))
2307431378d1Snaddy (void)strlcat(expbuf, rcsfile_basename,
2308d0e85bc8Stobias sizeof(expbuf));
23090dbb72f1Sniallo else
2310d0e85bc8Stobias (void)strlcat(expbuf, rcsfile,
2311d0e85bc8Stobias sizeof(expbuf));
2312d0e85bc8Stobias if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2313d0e85bc8Stobias sizeof(expbuf))
2314ff7b57e3Sjoris fatal("rcs_kwexp_line: truncated");
23150dbb72f1Sniallo }
23160dbb72f1Sniallo
23170dbb72f1Sniallo if (kwtype & RCS_KW_REVISION) {
23180dbb72f1Sniallo rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2319d0e85bc8Stobias if (strlcat(buf, " ", sizeof(buf)) >=
2320d0e85bc8Stobias sizeof(buf) || strlcat(expbuf, buf,
2321d0e85bc8Stobias sizeof(expbuf)) >= sizeof(buf))
2322ff7b57e3Sjoris fatal("rcs_kwexp_line: truncated");
23230dbb72f1Sniallo }
23240dbb72f1Sniallo
23250dbb72f1Sniallo if (kwtype & RCS_KW_DATE) {
232622872efdScanacar if (strftime(buf, sizeof(buf),
232722872efdScanacar "%Y/%m/%d %H:%M:%S ",
2328d0e85bc8Stobias &rdp->rd_date) == 0)
2329d0e85bc8Stobias fatal("rcs_kwexp_line: strftime "
2330d0e85bc8Stobias "failure");
2331d0e85bc8Stobias if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2332d0e85bc8Stobias sizeof(expbuf))
2333d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2334d0e85bc8Stobias "truncated");
2335f4827e12Sniallo }
2336f4827e12Sniallo
2337f4827e12Sniallo if (kwtype & RCS_KW_MDOCDATE) {
23382b9d4d98Stobias /*
23392b9d4d98Stobias * Do not prepend ' ' for a single
23402b9d4d98Stobias * digit, %e would do so and there is
23412b9d4d98Stobias * no better format for strftime().
23422b9d4d98Stobias */
234322872efdScanacar if (strftime(buf, sizeof(buf),
234422872efdScanacar (rdp->rd_date.tm_mday < 10) ?
234522872efdScanacar "%B%e %Y " : "%B %e %Y ",
2346d0e85bc8Stobias &rdp->rd_date) == 0)
2347d0e85bc8Stobias fatal("rcs_kwexp_line: strftime "
2348d0e85bc8Stobias "failure");
2349d0e85bc8Stobias if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2350d0e85bc8Stobias sizeof(expbuf))
2351d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2352d0e85bc8Stobias "truncated");
23530dbb72f1Sniallo }
23540dbb72f1Sniallo
23550dbb72f1Sniallo if (kwtype & RCS_KW_AUTHOR) {
2356d0e85bc8Stobias if (strlcat(expbuf, rdp->rd_author,
2357d0e85bc8Stobias sizeof(expbuf)) >= sizeof(expbuf) ||
2358d0e85bc8Stobias strlcat(expbuf, " ", sizeof(expbuf)) >=
2359d0e85bc8Stobias sizeof(expbuf))
2360d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2361d0e85bc8Stobias "truncated");
23620dbb72f1Sniallo }
23630dbb72f1Sniallo
23640dbb72f1Sniallo if (kwtype & RCS_KW_STATE) {
2365d0e85bc8Stobias if (strlcat(expbuf, rdp->rd_state,
2366d0e85bc8Stobias sizeof(expbuf)) >= sizeof(expbuf) ||
2367d0e85bc8Stobias strlcat(expbuf, " ", sizeof(expbuf)) >=
2368d0e85bc8Stobias sizeof(expbuf))
2369d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2370d0e85bc8Stobias "truncated");
23710dbb72f1Sniallo }
23720dbb72f1Sniallo
23730dbb72f1Sniallo /* order does not matter anymore below */
2374364cb4c9Stobias if (kwtype & RCS_KW_LOG) {
2375364cb4c9Stobias char linebuf[256];
23767bb3ddb0Sray struct rcs_line *cur, *lp;
2377364cb4c9Stobias char *logp, *l_line, *prefix, *q, *sprefix;
2378364cb4c9Stobias size_t i;
2379364cb4c9Stobias
2380a5f59dfcStobias /* Log line */
2381364cb4c9Stobias if (!(kwtype & RCS_KW_FULLPATH))
2382364cb4c9Stobias (void)strlcat(expbuf,
2383431378d1Snaddy rcsfile_basename, sizeof(expbuf));
2384364cb4c9Stobias else
2385364cb4c9Stobias (void)strlcat(expbuf, rcsfile,
2386364cb4c9Stobias sizeof(expbuf));
2387364cb4c9Stobias
2388d0e85bc8Stobias if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2389d0e85bc8Stobias sizeof(expbuf))
2390d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2391d0e85bc8Stobias "truncated");
23920dbb72f1Sniallo
2393364cb4c9Stobias cur = line;
2394364cb4c9Stobias
2395364cb4c9Stobias /* copy rdp->rd_log for strsep */
2396364cb4c9Stobias logp = xstrdup(rdp->rd_log);
2397364cb4c9Stobias
2398364cb4c9Stobias /* copy our prefix for later processing */
2399364cb4c9Stobias prefix = xmalloc(start - line->l_line + 1);
2400364cb4c9Stobias memcpy(prefix, line->l_line,
2401364cb4c9Stobias start - line->l_line);
2402364cb4c9Stobias prefix[start - line->l_line] = '\0';
2403364cb4c9Stobias
2404364cb4c9Stobias /* copy also prefix without trailing blanks. */
2405364cb4c9Stobias sprefix = xstrdup(prefix);
2406364cb4c9Stobias for (i = strlen(sprefix); i > 0 &&
2407364cb4c9Stobias sprefix[i - 1] == ' '; i--)
2408364cb4c9Stobias sprefix[i - 1] = '\0';
2409364cb4c9Stobias
2410364cb4c9Stobias /* new line: revision + date + author */
2411364cb4c9Stobias linebuf[0] = '\0';
2412364cb4c9Stobias if (strlcat(linebuf, "Revision ",
2413364cb4c9Stobias sizeof(linebuf)) >= sizeof(linebuf))
2414364cb4c9Stobias fatal("rcs_kwexp_line: truncated");
2415364cb4c9Stobias rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2416364cb4c9Stobias if (strlcat(linebuf, buf, sizeof(linebuf))
2417364cb4c9Stobias >= sizeof(buf))
2418364cb4c9Stobias fatal("rcs_kwexp_line: truncated");
241922872efdScanacar if (strftime(buf, sizeof(buf),
242022872efdScanacar " %Y/%m/%d %H:%M:%S ",
2421364cb4c9Stobias &rdp->rd_date) == 0)
2422364cb4c9Stobias fatal("rcs_kwexp_line: strftime "
2423364cb4c9Stobias "failure");
2424364cb4c9Stobias if (strlcat(linebuf, buf, sizeof(linebuf))
2425364cb4c9Stobias >= sizeof(linebuf))
2426364cb4c9Stobias fatal("rcs_kwexp_line: string "
2427364cb4c9Stobias "truncated");
2428364cb4c9Stobias if (strlcat(linebuf, rdp->rd_author,
2429364cb4c9Stobias sizeof(linebuf)) >= sizeof(linebuf))
2430364cb4c9Stobias fatal("rcs_kwexp_line: string "
2431364cb4c9Stobias "truncated");
2432364cb4c9Stobias
2433364cb4c9Stobias lp = xcalloc(1, sizeof(*lp));
2434ae886706Smillert xasprintf((char **)&(lp->l_line), "%s%s\n",
2435364cb4c9Stobias prefix, linebuf);
2436364cb4c9Stobias lp->l_len = strlen(lp->l_line);
2437364cb4c9Stobias TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2438364cb4c9Stobias l_list);
2439364cb4c9Stobias cur = lp;
2440364cb4c9Stobias
2441364cb4c9Stobias /* Log message */
2442364cb4c9Stobias q = logp;
2443364cb4c9Stobias while ((l_line = strsep(&q, "\n")) != NULL &&
2444364cb4c9Stobias q != NULL) {
2445364cb4c9Stobias lp = xcalloc(1, sizeof(*lp));
2446364cb4c9Stobias
2447364cb4c9Stobias if (l_line[0] == '\0') {
2448ae886706Smillert xasprintf((char **)&(lp->l_line),
2449ae886706Smillert "%s\n", sprefix);
2450364cb4c9Stobias } else {
2451ae886706Smillert xasprintf((char **)&(lp->l_line),
2452364cb4c9Stobias "%s%s\n", prefix, l_line);
2453364cb4c9Stobias }
2454364cb4c9Stobias
2455364cb4c9Stobias lp->l_len = strlen(lp->l_line);
2456364cb4c9Stobias TAILQ_INSERT_AFTER(&(lines->l_lines),
2457364cb4c9Stobias cur, lp, l_list);
2458364cb4c9Stobias cur = lp;
2459364cb4c9Stobias }
2460397ddb8aSnicm free(logp);
2461364cb4c9Stobias
2462364cb4c9Stobias /*
2463364cb4c9Stobias * This is just another hairy mess, but it must
2464e2d7d3ceStobias * be done: All characters behind Log will be
2465364cb4c9Stobias * written in a new line next to log messages.
2466364cb4c9Stobias * But that's not enough, we have to strip all
2467364cb4c9Stobias * trailing whitespaces of our prefix.
2468364cb4c9Stobias */
2469364cb4c9Stobias lp = xcalloc(1, sizeof(*lp));
2470c522126cSmillert xasprintf((char **)&lp->l_line, "%s%s",
2471c522126cSmillert sprefix, end);
2472364cb4c9Stobias lp->l_len = strlen(lp->l_line);
2473364cb4c9Stobias TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2474364cb4c9Stobias l_list);
2475364cb4c9Stobias cur = lp;
2476364cb4c9Stobias
2477364cb4c9Stobias end = line->l_line + line->l_len - 1;
2478364cb4c9Stobias
2479397ddb8aSnicm free(prefix);
2480397ddb8aSnicm free(sprefix);
2481364cb4c9Stobias
2482364cb4c9Stobias }
2483364cb4c9Stobias
24840dbb72f1Sniallo if (kwtype & RCS_KW_SOURCE) {
2485d0e85bc8Stobias if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
2486d0e85bc8Stobias sizeof(expbuf) || strlcat(expbuf, " ",
2487d0e85bc8Stobias sizeof(expbuf)) >= sizeof(expbuf))
2488d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2489d0e85bc8Stobias "truncated");
24900dbb72f1Sniallo }
24910dbb72f1Sniallo
24920dbb72f1Sniallo if (kwtype & RCS_KW_NAME)
2493d0e85bc8Stobias if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2494d0e85bc8Stobias sizeof(expbuf))
2495d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2496d0e85bc8Stobias "truncated");
24975c14cef8Stobias
24985c14cef8Stobias if (kwtype & RCS_KW_LOCKER)
2499d0e85bc8Stobias if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2500d0e85bc8Stobias sizeof(expbuf))
2501d0e85bc8Stobias fatal("rcs_kwexp_line: string "
2502d0e85bc8Stobias "truncated");
25030dbb72f1Sniallo }
25040dbb72f1Sniallo
25050dbb72f1Sniallo /* end the expansion */
25060dbb72f1Sniallo if (mode & RCS_KWEXP_NAME)
25070dbb72f1Sniallo if (strlcat(expbuf, "$",
25080dbb72f1Sniallo sizeof(expbuf)) >= sizeof(expbuf))
2509ff7b57e3Sjoris fatal("rcs_kwexp_line: truncated");
25100dbb72f1Sniallo
25110dbb72f1Sniallo /* Concatenate everything together. */
25127bb3ddb0Sray tmpbuf = buf_alloc(len + strlen(expbuf));
25130dbb72f1Sniallo /* Append everything before keyword. */
25147bb3ddb0Sray buf_append(tmpbuf, line->l_line,
251590fdc30cSotto start - line->l_line);
25160dbb72f1Sniallo /* Append keyword. */
25177bb3ddb0Sray buf_puts(tmpbuf, expbuf);
25180dbb72f1Sniallo /* Point c to end of keyword. */
25197bb3ddb0Sray tlen = buf_len(tmpbuf) - 1;
25200dbb72f1Sniallo /* Append everything after keyword. */
25217bb3ddb0Sray buf_append(tmpbuf, end,
252290fdc30cSotto line->l_line + line->l_len - end);
25237bb3ddb0Sray c = buf_get(tmpbuf) + tlen;
25240dbb72f1Sniallo /* Point fin to end of data. */
25257bb3ddb0Sray fin = buf_get(tmpbuf) + buf_len(tmpbuf) - 1;
25260dbb72f1Sniallo /* Recalculate new length. */
25277bb3ddb0Sray len = buf_len(tmpbuf);
25280dbb72f1Sniallo
25290dbb72f1Sniallo /* tmpbuf is now ready, convert to string */
25307650a186Sotto if (line->l_needsfree)
2531397ddb8aSnicm free(line->l_line);
25320dbb72f1Sniallo line->l_len = len;
25337bb3ddb0Sray line->l_line = buf_release(tmpbuf);
25340951efdeSniallo line->l_needsfree = 1;
25350dbb72f1Sniallo }
25360dbb72f1Sniallo }
2537113cb29fStobias
2538113cb29fStobias /* rcs_translate_tag() */
2539113cb29fStobias RCSNUM *
rcs_translate_tag(const char * revstr,RCSFILE * rfp)2540113cb29fStobias rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2541113cb29fStobias {
2542113cb29fStobias int follow;
254325d391d7Sjoris time_t deltatime;
254467a02cd8Sjoris char branch[CVS_REV_BUFSZ];
2545272fac53Sjoris RCSNUM *brev, *frev, *rev;
2546113cb29fStobias struct rcs_delta *rdp, *trdp;
2547b87788a5Stobias time_t cdate;
2548113cb29fStobias
2549272fac53Sjoris brev = frev = NULL;
2550113cb29fStobias
255167a02cd8Sjoris if (revstr == NULL) {
255267a02cd8Sjoris if (rfp->rf_branch != NULL) {
255367a02cd8Sjoris rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
255467a02cd8Sjoris revstr = branch;
255567a02cd8Sjoris } else {
2556113cb29fStobias revstr = RCS_HEAD_BRANCH;
255767a02cd8Sjoris }
255867a02cd8Sjoris }
2559113cb29fStobias
2560113cb29fStobias if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
256125d391d7Sjoris return (NULL);
256225d391d7Sjoris
256325d391d7Sjoris if ((rdp = rcs_findrev(rfp, rev)) == NULL)
25641db92473Stobias return (NULL);
256525d391d7Sjoris
2566e1936db1Stobias /* let's see if we must follow a branch */
2567e1936db1Stobias if (!strcmp(revstr, RCS_HEAD_BRANCH))
2568e1936db1Stobias follow = 1;
2569e1936db1Stobias else {
2570e1936db1Stobias frev = rcs_sym_getrev(rfp, revstr);
2571e1936db1Stobias if (frev == NULL)
2572272fac53Sjoris frev = rcsnum_parse(revstr);
2573e1936db1Stobias
2574e1936db1Stobias brev = rcsnum_alloc();
2575e1936db1Stobias rcsnum_cpy(rev, brev, rev->rn_len - 1);
2576e1936db1Stobias
2577e1936db1Stobias if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
2578e1936db1Stobias !rcsnum_cmp(frev, brev, 0)) {
2579e1936db1Stobias follow = 1;
2580e1936db1Stobias } else
2581e1936db1Stobias follow = 0;
2582e1936db1Stobias
258353ce2177Sfcambus free(brev);
2584*65402675Sjsg brev = NULL;
2585e1936db1Stobias }
2586e1936db1Stobias
2587b87788a5Stobias if (cvs_specified_date != -1)
2588b87788a5Stobias cdate = cvs_specified_date;
2589b87788a5Stobias else
2590b87788a5Stobias cdate = cvs_directory_date;
2591b87788a5Stobias
2592b87788a5Stobias if (cdate == -1) {
25934f5b80e5Sjoris free(frev);
25944f5b80e5Sjoris
259525d391d7Sjoris /* XXX */
2596e1936db1Stobias if (rev->rn_len < 4 || !follow) {
259725d391d7Sjoris return (rev);
259825d391d7Sjoris }
259925d391d7Sjoris
260025d391d7Sjoris /* Find the latest delta on that branch */
260153ce2177Sfcambus free(rev);
260225d391d7Sjoris for (;;) {
260325d391d7Sjoris if (rdp->rd_next->rn_len == 0)
260425d391d7Sjoris break;
260525d391d7Sjoris if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
26062c97fb1eStobias fatal("rcs_translate_tag: could not fetch "
260725d391d7Sjoris "branch delta");
260825d391d7Sjoris }
260925d391d7Sjoris
261025d391d7Sjoris rev = rcsnum_alloc();
261125d391d7Sjoris rcsnum_cpy(rdp->rd_num, rev, 0);
261225d391d7Sjoris return (rev);
261325d391d7Sjoris }
2614113cb29fStobias
2615113cb29fStobias if (frev != NULL) {
2616113cb29fStobias brev = rcsnum_revtobr(frev);
26177cee39ecSjoris brev->rn_len = rev->rn_len - 1;
261853ce2177Sfcambus free(frev);
2619113cb29fStobias }
2620113cb29fStobias
262153ce2177Sfcambus free(rev);
2622113cb29fStobias
2623113cb29fStobias do {
2624761daeb8Stobias deltatime = timegm(&(rdp->rd_date));
262525d391d7Sjoris
262625d391d7Sjoris if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
2627b87788a5Stobias if (deltatime > cdate) {
262825d391d7Sjoris trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
262925d391d7Sjoris if (trdp == NULL)
263025d391d7Sjoris trdp = rdp;
26317cee39ecSjoris
26327cee39ecSjoris if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
26337cee39ecSjoris return (NULL);
26347cee39ecSjoris
263525d391d7Sjoris rev = rcsnum_alloc();
263625d391d7Sjoris rcsnum_cpy(trdp->rd_num, rev, 0);
263725d391d7Sjoris return (rev);
263825d391d7Sjoris }
26397cee39ecSjoris
26407cee39ecSjoris if (rdp->rd_next->rn_len == 0) {
26417cee39ecSjoris rev = rcsnum_alloc();
26427cee39ecSjoris rcsnum_cpy(rdp->rd_num, rev, 0);
26437cee39ecSjoris return (rev);
26447cee39ecSjoris }
264525d391d7Sjoris } else {
2646b87788a5Stobias if (deltatime < cdate) {
2647113cb29fStobias rev = rcsnum_alloc();
2648113cb29fStobias rcsnum_cpy(rdp->rd_num, rev, 0);
264925d391d7Sjoris return (rev);
265025d391d7Sjoris }
2651113cb29fStobias }
2652113cb29fStobias
2653113cb29fStobias if (follow && rdp->rd_next->rn_len != 0) {
2654113cb29fStobias if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
2655113cb29fStobias break;
2656113cb29fStobias
2657113cb29fStobias trdp = rcs_findrev(rfp, rdp->rd_next);
2658113cb29fStobias if (trdp == NULL)
2659113cb29fStobias fatal("failed to grab next revision");
2660113cb29fStobias rdp = trdp;
2661113cb29fStobias } else
2662113cb29fStobias follow = 0;
2663113cb29fStobias } while (follow);
2664113cb29fStobias
266525d391d7Sjoris return (NULL);
2666113cb29fStobias }
2667