Annotation of src/usr.bin/cvs/rcs.c, Revision 1.305
1.305 ! tobias 1: /* $OpenBSD: rcs.c,v 1.304 2010/10/15 08:46:23 tobias Exp $ */
1.1 jfb 2: /*
3: * Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
1.15 tedu 4: * All rights reserved.
1.1 jfb 5: *
1.15 tedu 6: * Redistribution and use in source and binary forms, with or without
7: * modification, are permitted provided that the following conditions
8: * are met:
1.1 jfb 9: *
1.15 tedu 10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
1.1 jfb 12: * 2. The name of the author may not be used to endorse or promote products
1.15 tedu 13: * derived from this software without specific prior written permission.
1.1 jfb 14: *
15: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
16: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
17: * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
18: * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21: * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22: * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1.15 tedu 24: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1.1 jfb 25: */
26:
1.210 otto 27: #include <sys/stat.h>
28:
29: #include <ctype.h>
30: #include <errno.h>
31: #include <libgen.h>
32: #include <pwd.h>
33: #include <stdlib.h>
34: #include <string.h>
35: #include <unistd.h>
1.1 jfb 36:
1.219 tobias 37: #include "atomicio.h"
1.94 joris 38: #include "cvs.h"
1.172 joris 39: #include "diff.h"
1.1 jfb 40: #include "rcs.h"
1.303 tobias 41: #include "rcsparse.h"
1.1 jfb 42:
1.117 niallo 43: #define RCS_KWEXP_SIZE 1024
1.1 jfb 44:
1.218 tobias 45: #define ANNOTATE_NEVER 0
46: #define ANNOTATE_NOW 1
47: #define ANNOTATE_LATER 2
48:
1.47 jfb 49: /* invalid characters in RCS symbol names */
1.49 jfb 50: static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
1.47 jfb 51:
1.51 jfb 52: /* comment leaders, depending on the file's suffix */
53: static const struct rcs_comment {
1.57 xsa 54: const char *rc_suffix;
55: const char *rc_cstr;
1.51 jfb 56: } rcs_comments[] = {
57: { "1", ".\\\" " },
58: { "2", ".\\\" " },
59: { "3", ".\\\" " },
60: { "4", ".\\\" " },
61: { "5", ".\\\" " },
62: { "6", ".\\\" " },
63: { "7", ".\\\" " },
64: { "8", ".\\\" " },
65: { "9", ".\\\" " },
66: { "a", "-- " }, /* Ada */
67: { "ada", "-- " },
68: { "adb", "-- " },
69: { "asm", ";; " }, /* assembler (MS-DOS) */
70: { "ads", "-- " }, /* Ada */
71: { "bat", ":: " }, /* batch (MS-DOS) */
72: { "body", "-- " }, /* Ada */
73: { "c", " * " }, /* C */
74: { "c++", "// " }, /* C++ */
75: { "cc", "// " },
76: { "cpp", "// " },
77: { "cxx", "// " },
78: { "m", "// " }, /* Objective-C */
79: { "cl", ";;; " }, /* Common Lisp */
80: { "cmd", ":: " }, /* command (OS/2) */
81: { "cmf", "c " }, /* CM Fortran */
82: { "csh", "# " }, /* shell */
83: { "e", "# " }, /* efl */
84: { "epsf", "% " }, /* encapsulated postscript */
85: { "epsi", "% " }, /* encapsulated postscript */
86: { "el", "; " }, /* Emacs Lisp */
87: { "f", "c " }, /* Fortran */
88: { "for", "c " },
89: { "h", " * " }, /* C-header */
90: { "hh", "// " }, /* C++ header */
91: { "hpp", "// " },
92: { "hxx", "// " },
93: { "in", "# " }, /* for Makefile.in */
94: { "l", " * " }, /* lex */
95: { "mac", ";; " }, /* macro (DEC-10, MS-DOS, PDP-11, VMS, etc) */
96: { "mak", "# " }, /* makefile, e.g. Visual C++ */
97: { "me", ".\\\" " }, /* me-macros t/nroff */
98: { "ml", "; " }, /* mocklisp */
99: { "mm", ".\\\" " }, /* mm-macros t/nroff */
100: { "ms", ".\\\" " }, /* ms-macros t/nroff */
101: { "man", ".\\\" " }, /* man-macros t/nroff */
102: { "p", " * " }, /* pascal */
103: { "pas", " * " },
1.52 jfb 104: { "pl", "# " }, /* Perl (conflict with Prolog) */
105: { "pm", "# " }, /* Perl module */
1.51 jfb 106: { "ps", "% " }, /* postscript */
107: { "psw", "% " }, /* postscript wrap */
108: { "pswm", "% " }, /* postscript wrap */
109: { "r", "# " }, /* ratfor */
110: { "rc", " * " }, /* Microsoft Windows resource file */
111: { "red", "% " }, /* psl/rlisp */
112: { "sh", "# " }, /* shell */
113: { "sl", "% " }, /* psl */
114: { "spec", "-- " }, /* Ada */
115: { "tex", "% " }, /* tex */
116: { "y", " * " }, /* yacc */
117: { "ye", " * " }, /* yacc-efl */
118: { "yr", " * " }, /* yacc-ratfor */
119: };
120:
1.128 niallo 121: struct rcs_kw rcs_expkw[] = {
122: { "Author", RCS_KW_AUTHOR },
123: { "Date", RCS_KW_DATE },
124: { "Header", RCS_KW_HEADER },
125: { "Id", RCS_KW_ID },
1.229 tobias 126: { "Locker", RCS_KW_LOCKER },
1.128 niallo 127: { "Log", RCS_KW_LOG },
128: { "Name", RCS_KW_NAME },
129: { "RCSfile", RCS_KW_RCSFILE },
130: { "Revision", RCS_KW_REVISION },
131: { "Source", RCS_KW_SOURCE },
132: { "State", RCS_KW_STATE },
1.211 niallo 133: { "Mdocdate", RCS_KW_MDOCDATE },
1.128 niallo 134: };
135:
1.57 xsa 136: #define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
1.51 jfb 137:
1.246 tobias 138: static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
1.297 ray 139: int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *,
140: struct rcs_line **, struct rcs_delta *);
1.57 xsa 141: static void rcs_freedelta(struct rcs_delta *);
1.155 ray 142: static void rcs_strprint(const u_char *, size_t, FILE *);
1.57 xsa 143:
1.297 ray 144: static void rcs_kwexp_line(char *, struct rcs_delta *, struct rcs_lines *,
145: struct rcs_line *, int mode);
1.26 jfb 146:
1.60 xsa 147: RCSFILE *
1.172 joris 148: rcs_open(const char *path, int fd, int flags, ...)
1.1 jfb 149: {
1.172 joris 150: int mode;
1.259 tobias 151: mode_t fmode;
1.1 jfb 152: RCSFILE *rfp;
1.26 jfb 153: va_list vap;
1.269 joris 154: struct stat st;
1.109 joris 155: struct rcs_delta *rdp;
156: struct rcs_lock *lkr;
1.26 jfb 157:
1.152 niallo 158: fmode = S_IRUSR|S_IRGRP|S_IROTH;
1.26 jfb 159: flags &= 0xffff; /* ditch any internal flags */
1.1 jfb 160:
1.172 joris 161: if (flags & RCS_CREATE) {
162: va_start(vap, flags);
163: mode = va_arg(vap, int);
164: va_end(vap);
165: fmode = (mode_t)mode;
1.269 joris 166: } else {
167: if (fstat(fd, &st) == -1)
168: fatal("rcs_open: %s: fstat: %s", path, strerror(errno));
169: fmode = st.st_mode;
1.1 jfb 170: }
1.228 tobias 171:
1.259 tobias 172: fmode &= ~cvs_umask;
1.1 jfb 173:
1.161 ray 174: rfp = xcalloc(1, sizeof(*rfp));
1.1 jfb 175:
1.110 joris 176: rfp->rf_path = xstrdup(path);
1.142 niallo 177: rfp->rf_flags = flags | RCS_SLOCK | RCS_SYNCED;
1.26 jfb 178: rfp->rf_mode = fmode;
1.305 ! tobias 179: if (fd == -1)
! 180: rfp->rf_file = NULL;
! 181: else if ((rfp->rf_file = fdopen(fd, "r")) == NULL)
! 182: fatal("rcs_open: %s: fdopen: %s", path, strerror(errno));
1.175 joris 183: rfp->rf_dead = 0;
1.1 jfb 184:
185: TAILQ_INIT(&(rfp->rf_delta));
1.29 jfb 186: TAILQ_INIT(&(rfp->rf_access));
1.1 jfb 187: TAILQ_INIT(&(rfp->rf_symbols));
188: TAILQ_INIT(&(rfp->rf_locks));
189:
1.303 tobias 190: if (!(rfp->rf_flags & RCS_CREATE)) {
191: if (rcsparse_init(rfp))
192: fatal("could not parse admin data");
193: }
1.1 jfb 194:
1.109 joris 195: /* fill in rd_locker */
196: TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
197: if ((rdp = rcs_findrev(rfp, lkr->rl_num)) == NULL) {
198: rcs_close(rfp);
199: return (NULL);
200: }
201:
1.110 joris 202: rdp->rd_locker = xstrdup(lkr->rl_name);
1.109 joris 203: }
204:
1.1 jfb 205: return (rfp);
206: }
207:
208: /*
209: * rcs_close()
210: *
211: * Close an RCS file handle.
212: */
213: void
214: rcs_close(RCSFILE *rfp)
215: {
216: struct rcs_delta *rdp;
1.71 moritz 217: struct rcs_access *rap;
1.13 jfb 218: struct rcs_lock *rlp;
219: struct rcs_sym *rsp;
1.1 jfb 220:
1.26 jfb 221: if ((rfp->rf_flags & RCS_WRITE) && !(rfp->rf_flags & RCS_SYNCED))
222: rcs_write(rfp);
223:
1.1 jfb 224: while (!TAILQ_EMPTY(&(rfp->rf_delta))) {
225: rdp = TAILQ_FIRST(&(rfp->rf_delta));
226: TAILQ_REMOVE(&(rfp->rf_delta), rdp, rd_list);
227: rcs_freedelta(rdp);
1.71 moritz 228: }
229:
230: while (!TAILQ_EMPTY(&(rfp->rf_access))) {
231: rap = TAILQ_FIRST(&(rfp->rf_access));
232: TAILQ_REMOVE(&(rfp->rf_access), rap, ra_list);
1.110 joris 233: xfree(rap->ra_name);
234: xfree(rap);
1.1 jfb 235: }
236:
1.13 jfb 237: while (!TAILQ_EMPTY(&(rfp->rf_symbols))) {
238: rsp = TAILQ_FIRST(&(rfp->rf_symbols));
239: TAILQ_REMOVE(&(rfp->rf_symbols), rsp, rs_list);
240: rcsnum_free(rsp->rs_num);
1.110 joris 241: xfree(rsp->rs_name);
242: xfree(rsp);
1.13 jfb 243: }
244:
245: while (!TAILQ_EMPTY(&(rfp->rf_locks))) {
246: rlp = TAILQ_FIRST(&(rfp->rf_locks));
247: TAILQ_REMOVE(&(rfp->rf_locks), rlp, rl_list);
248: rcsnum_free(rlp->rl_num);
1.110 joris 249: xfree(rlp->rl_name);
250: xfree(rlp);
1.13 jfb 251: }
252:
1.1 jfb 253: if (rfp->rf_head != NULL)
254: rcsnum_free(rfp->rf_head);
1.11 joris 255: if (rfp->rf_branch != NULL)
256: rcsnum_free(rfp->rf_branch);
1.1 jfb 257:
1.305 ! tobias 258: if (rfp->rf_file != NULL)
! 259: fclose(rfp->rf_file);
1.1 jfb 260: if (rfp->rf_path != NULL)
1.110 joris 261: xfree(rfp->rf_path);
1.1 jfb 262: if (rfp->rf_comment != NULL)
1.110 joris 263: xfree(rfp->rf_comment);
1.1 jfb 264: if (rfp->rf_expand != NULL)
1.110 joris 265: xfree(rfp->rf_expand);
1.1 jfb 266: if (rfp->rf_desc != NULL)
1.110 joris 267: xfree(rfp->rf_desc);
1.118 joris 268: if (rfp->rf_pdata != NULL)
1.303 tobias 269: rcsparse_free(rfp);
1.110 joris 270: xfree(rfp);
1.1 jfb 271: }
272:
273: /*
274: * rcs_write()
275: *
276: * Write the contents of the RCS file handle <rfp> to disk in the file whose
277: * path is in <rf_path>.
278: */
1.172 joris 279: void
1.1 jfb 280: rcs_write(RCSFILE *rfp)
281: {
282: FILE *fp;
1.292 zinovik 283: char numbuf[CVS_REV_BUFSZ], *fn, tmpdir[MAXPATHLEN];
1.29 jfb 284: struct rcs_access *ap;
1.1 jfb 285: struct rcs_sym *symp;
1.46 jfb 286: struct rcs_branch *brp;
1.1 jfb 287: struct rcs_delta *rdp;
1.75 joris 288: struct rcs_lock *lkp;
1.100 xsa 289: size_t len;
1.207 otto 290: int fd, saved_errno;
1.75 joris 291:
1.207 otto 292: fd = -1;
1.1 jfb 293:
1.137 joris 294: if (rfp->rf_flags & RCS_SYNCED)
1.181 joris 295: return;
296:
297: if (cvs_noexec == 1)
1.172 joris 298: return;
1.137 joris 299:
1.117 niallo 300: /* Write operations need the whole file parsed */
1.303 tobias 301: if (rcsparse_deltatexts(rfp, NULL))
302: fatal("rcs_write: rcsparse_deltatexts");
1.1 jfb 303:
1.207 otto 304: if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
305: fatal("rcs_write: truncation");
306: (void)xasprintf(&fn, "%s/rcs.XXXXXXXXXX", dirname(tmpdir));
1.172 joris 307:
1.113 xsa 308: if ((fd = mkstemp(fn)) == -1)
1.172 joris 309: fatal("%s", fn);
1.113 xsa 310:
1.207 otto 311: if ((fp = fdopen(fd, "w")) == NULL) {
1.172 joris 312: saved_errno = errno;
313: (void)unlink(fn);
1.207 otto 314: fatal("fdopen %s: %s", fn, strerror(saved_errno));
1.1 jfb 315: }
1.284 joris 316:
1.296 ray 317: worklist_add(fn, &temp_files);
1.1 jfb 318:
1.28 jfb 319: if (rfp->rf_head != NULL)
320: rcsnum_tostr(rfp->rf_head, numbuf, sizeof(numbuf));
321: else
322: numbuf[0] = '\0';
323:
1.1 jfb 324: fprintf(fp, "head\t%s;\n", numbuf);
1.35 jfb 325:
326: if (rfp->rf_branch != NULL) {
327: rcsnum_tostr(rfp->rf_branch, numbuf, sizeof(numbuf));
328: fprintf(fp, "branch\t%s;\n", numbuf);
329: }
330:
1.29 jfb 331: fputs("access", fp);
332: TAILQ_FOREACH(ap, &(rfp->rf_access), ra_list) {
333: fprintf(fp, "\n\t%s", ap->ra_name);
334: }
335: fputs(";\n", fp);
1.1 jfb 336:
1.85 joris 337: fprintf(fp, "symbols");
1.1 jfb 338: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
1.243 tobias 339: if (RCSNUM_ISBRANCH(symp->rs_num))
340: rcsnum_addmagic(symp->rs_num);
1.1 jfb 341: rcsnum_tostr(symp->rs_num, numbuf, sizeof(numbuf));
1.292 zinovik 342: fprintf(fp, "\n\t%s:%s", symp->rs_name, numbuf);
1.1 jfb 343: }
344: fprintf(fp, ";\n");
345:
1.75 joris 346: fprintf(fp, "locks");
347: TAILQ_FOREACH(lkp, &(rfp->rf_locks), rl_list) {
348: rcsnum_tostr(lkp->rl_num, numbuf, sizeof(numbuf));
349: fprintf(fp, "\n\t%s:%s", lkp->rl_name, numbuf);
350: }
351:
352: fprintf(fp, ";");
1.1 jfb 353:
1.26 jfb 354: if (rfp->rf_flags & RCS_SLOCK)
1.1 jfb 355: fprintf(fp, " strict;");
356: fputc('\n', fp);
357:
1.129 xsa 358: fputs("comment\t@", fp);
1.42 jfb 359: if (rfp->rf_comment != NULL) {
1.58 xsa 360: rcs_strprint((const u_char *)rfp->rf_comment,
361: strlen(rfp->rf_comment), fp);
1.42 jfb 362: fputs("@;\n", fp);
1.129 xsa 363: } else
364: fputs("# @;\n", fp);
1.1 jfb 365:
1.42 jfb 366: if (rfp->rf_expand != NULL) {
367: fputs("expand @", fp);
1.58 xsa 368: rcs_strprint((const u_char *)rfp->rf_expand,
369: strlen(rfp->rf_expand), fp);
1.42 jfb 370: fputs("@;\n", fp);
371: }
1.1 jfb 372:
1.46 jfb 373: fputs("\n\n", fp);
1.1 jfb 374:
375: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
376: fprintf(fp, "%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
377: sizeof(numbuf)));
378: fprintf(fp, "date\t%d.%02d.%02d.%02d.%02d.%02d;",
1.44 jfb 379: rdp->rd_date.tm_year + 1900, rdp->rd_date.tm_mon + 1,
1.1 jfb 380: rdp->rd_date.tm_mday, rdp->rd_date.tm_hour,
381: rdp->rd_date.tm_min, rdp->rd_date.tm_sec);
382: fprintf(fp, "\tauthor %s;\tstate %s;\n",
383: rdp->rd_author, rdp->rd_state);
1.46 jfb 384: fputs("branches", fp);
385: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.232 tobias 386: fprintf(fp, "\n\t%s", rcsnum_tostr(brp->rb_num, numbuf,
1.46 jfb 387: sizeof(numbuf)));
388: }
389: fputs(";\n", fp);
1.1 jfb 390: fprintf(fp, "next\t%s;\n\n", rcsnum_tostr(rdp->rd_next,
391: numbuf, sizeof(numbuf)));
392: }
393:
1.42 jfb 394: fputs("\ndesc\n@", fp);
1.141 ray 395: if (rfp->rf_desc != NULL && (len = strlen(rfp->rf_desc)) > 0) {
1.100 xsa 396: rcs_strprint((const u_char *)rfp->rf_desc, len, fp);
397: if (rfp->rf_desc[len-1] != '\n')
398: fputc('\n', fp);
399: }
400: fputs("@\n", fp);
1.1 jfb 401:
402: /* deltatexts */
403: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.100 xsa 404: fprintf(fp, "\n\n%s\n", rcsnum_tostr(rdp->rd_num, numbuf,
1.1 jfb 405: sizeof(numbuf)));
1.42 jfb 406: fputs("log\n@", fp);
1.100 xsa 407: if (rdp->rd_log != NULL) {
408: len = strlen(rdp->rd_log);
409: rcs_strprint((const u_char *)rdp->rd_log, len, fp);
1.294 nicm 410: if (len == 0 || rdp->rd_log[len-1] != '\n')
1.100 xsa 411: fputc('\n', fp);
412: }
1.42 jfb 413: fputs("@\ntext\n@", fp);
1.296 ray 414: if (rdp->rd_text != NULL)
1.100 xsa 415: rcs_strprint(rdp->rd_text, rdp->rd_tlen, fp);
416: fputs("@\n", fp);
1.1 jfb 417: }
1.269 joris 418:
1.207 otto 419: if (fchmod(fd, rfp->rf_mode) == -1) {
420: saved_errno = errno;
421: (void)unlink(fn);
422: fatal("fchmod %s: %s", fn, strerror(saved_errno));
423: }
424:
1.172 joris 425: (void)fclose(fp);
1.73 joris 426:
1.207 otto 427: if (rename(fn, rfp->rf_path) == -1) {
428: saved_errno = errno;
1.172 joris 429: (void)unlink(fn);
1.207 otto 430: fatal("rename(%s, %s): %s", fn, rfp->rf_path,
431: strerror(saved_errno));
1.172 joris 432: }
1.73 joris 433:
1.172 joris 434: rfp->rf_flags |= RCS_SYNCED;
1.73 joris 435:
1.172 joris 436: if (fn != NULL)
437: xfree(fn);
1.1 jfb 438: }
439:
440: /*
1.43 jfb 441: * rcs_head_get()
442: *
443: * Retrieve the revision number of the head revision for the RCS file <file>.
444: */
1.180 joris 445: RCSNUM *
1.43 jfb 446: rcs_head_get(RCSFILE *file)
447: {
1.213 niallo 448: struct rcs_branch *brp;
449: struct rcs_delta *rdp;
450: RCSNUM *rev, *rootrev;
1.180 joris 451:
1.234 tobias 452: if (file->rf_head == NULL)
453: return NULL;
454:
1.213 niallo 455: rev = rcsnum_alloc();
1.180 joris 456: if (file->rf_branch != NULL) {
1.213 niallo 457: /* we have a default branch, use that to calculate the
458: * real HEAD*/
459: rootrev = rcsnum_alloc();
1.304 tobias 460: rcsnum_cpy(file->rf_branch, rootrev,
461: file->rf_branch->rn_len - 1);
1.213 niallo 462: if ((rdp = rcs_findrev(file, rootrev)) == NULL)
463: fatal("rcs_head_get: could not find root revision");
464:
465: /* HEAD should be the last revision on the default branch */
466: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.304 tobias 467: if (rcsnum_cmp(brp->rb_num, file->rf_branch,
468: file->rf_branch->rn_len) == NULL)
1.213 niallo 469: break;
470: }
471: rcsnum_free(rootrev);
1.304 tobias 472:
473: if (brp == NULL)
474: fatal("rcs_head_get: could not find first default "
475: "branch revision");
1.226 tobias 476:
477: if ((rdp = rcs_findrev(file, brp->rb_num)) == NULL)
478: fatal("rcs_head_get: could not find branch revision");
479: while (rdp->rd_next->rn_len != 0)
480: if ((rdp = rcs_findrev(file, rdp->rd_next)) == NULL)
481: fatal("rcs_head_get: could not find "
482: "next branch revision");
483:
484: rcsnum_cpy(rdp->rd_num, rev, 0);
1.195 joris 485: } else {
486: rcsnum_cpy(file->rf_head, rev, 0);
1.180 joris 487: }
488:
1.195 joris 489: return (rev);
1.43 jfb 490: }
491:
492: /*
493: * rcs_head_set()
494: *
495: * Set the revision number of the head revision for the RCS file <file> to
496: * <rev>, which must reference a valid revision within the file.
497: */
498: int
1.117 niallo 499: rcs_head_set(RCSFILE *file, RCSNUM *rev)
1.43 jfb 500: {
1.167 ray 501: if (rcs_findrev(file, rev) == NULL)
1.43 jfb 502: return (-1);
503:
1.111 joris 504: if (file->rf_head == NULL)
505: file->rf_head = rcsnum_alloc();
1.43 jfb 506:
1.111 joris 507: rcsnum_cpy(rev, file->rf_head, 0);
1.70 moritz 508: file->rf_flags &= ~RCS_SYNCED;
1.43 jfb 509: return (0);
510: }
511:
1.276 tobias 512: /*
513: * rcs_branch_new()
514: *
515: * Create a new branch out of supplied revision for the RCS file <file>.
516: */
517: RCSNUM *
518: rcs_branch_new(RCSFILE *file, RCSNUM *rev)
519: {
520: RCSNUM *brev;
521: struct rcs_sym *sym;
522:
523: if ((brev = rcsnum_new_branch(rev)) == NULL)
524: return (NULL);
525:
526: for (;;) {
527: TAILQ_FOREACH(sym, &(file->rf_symbols), rs_list)
528: if (!rcsnum_cmp(sym->rs_num, brev, 0))
529: break;
530:
1.287 tobias 531: if (sym == NULL)
1.276 tobias 532: break;
1.287 tobias 533:
534: if (rcsnum_inc(brev) == NULL ||
535: rcsnum_inc(brev) == NULL) {
536: rcsnum_free(brev);
537: return (NULL);
538: }
1.276 tobias 539: }
540:
541: return (brev);
542: }
1.43 jfb 543:
544: /*
1.35 jfb 545: * rcs_branch_get()
546: *
547: * Retrieve the default branch number for the RCS file <file>.
548: * Returns the number on success. If NULL is returned, then there is no
549: * default branch for this file.
550: */
1.60 xsa 551: const RCSNUM *
1.35 jfb 552: rcs_branch_get(RCSFILE *file)
553: {
554: return (file->rf_branch);
555: }
556:
557: /*
558: * rcs_branch_set()
559: *
560: * Set the default branch for the RCS file <file> to <bnum>.
561: * Returns 0 on success, -1 on failure.
562: */
563: int
564: rcs_branch_set(RCSFILE *file, const RCSNUM *bnum)
565: {
1.111 joris 566: if (file->rf_branch == NULL)
567: file->rf_branch = rcsnum_alloc();
1.35 jfb 568:
1.111 joris 569: rcsnum_cpy(bnum, file->rf_branch, 0);
1.70 moritz 570: file->rf_flags &= ~RCS_SYNCED;
1.35 jfb 571: return (0);
572: }
573:
574: /*
1.29 jfb 575: * rcs_access_add()
576: *
577: * Add the login name <login> to the access list for the RCS file <file>.
578: * Returns 0 on success, or -1 on failure.
579: */
580: int
581: rcs_access_add(RCSFILE *file, const char *login)
582: {
583: struct rcs_access *ap;
584:
585: /* first look for duplication */
586: TAILQ_FOREACH(ap, &(file->rf_access), ra_list) {
1.254 joris 587: if (strcmp(ap->ra_name, login) == 0)
1.29 jfb 588: return (-1);
589: }
590:
1.161 ray 591: ap = xmalloc(sizeof(*ap));
1.110 joris 592: ap->ra_name = xstrdup(login);
1.29 jfb 593: TAILQ_INSERT_TAIL(&(file->rf_access), ap, ra_list);
594:
595: /* not synced anymore */
596: file->rf_flags &= ~RCS_SYNCED;
597: return (0);
598: }
599:
600: /*
601: * rcs_access_remove()
602: *
603: * Remove an entry with login name <login> from the access list of the RCS
604: * file <file>.
605: * Returns 0 on success, or -1 on failure.
606: */
607: int
608: rcs_access_remove(RCSFILE *file, const char *login)
609: {
610: struct rcs_access *ap;
611:
612: TAILQ_FOREACH(ap, &(file->rf_access), ra_list)
613: if (strcmp(ap->ra_name, login) == 0)
614: break;
615:
1.254 joris 616: if (ap == NULL)
1.29 jfb 617: return (-1);
618:
619: TAILQ_REMOVE(&(file->rf_access), ap, ra_list);
1.110 joris 620: xfree(ap->ra_name);
621: xfree(ap);
1.29 jfb 622:
623: /* not synced anymore */
624: file->rf_flags &= ~RCS_SYNCED;
625: return (0);
626: }
627:
628: /*
1.26 jfb 629: * rcs_sym_add()
1.1 jfb 630: *
631: * Add a symbol to the list of symbols for the RCS file <rfp>. The new symbol
632: * is named <sym> and is bound to the RCS revision <snum>.
633: */
634: int
1.26 jfb 635: rcs_sym_add(RCSFILE *rfp, const char *sym, RCSNUM *snum)
1.1 jfb 636: {
637: struct rcs_sym *symp;
638:
1.254 joris 639: if (!rcs_sym_check(sym))
1.69 moritz 640: return (-1);
1.47 jfb 641:
1.1 jfb 642: /* first look for duplication */
643: TAILQ_FOREACH(symp, &(rfp->rf_symbols), rs_list) {
1.254 joris 644: if (strcmp(symp->rs_name, sym) == 0)
645: return (1);
1.1 jfb 646: }
647:
1.161 ray 648: symp = xmalloc(sizeof(*symp));
1.110 joris 649: symp->rs_name = xstrdup(sym);
1.111 joris 650: symp->rs_num = rcsnum_alloc();
1.1 jfb 651: rcsnum_cpy(snum, symp->rs_num, 0);
652:
653: TAILQ_INSERT_HEAD(&(rfp->rf_symbols), symp, rs_list);
654:
655: /* not synced anymore */
1.26 jfb 656: rfp->rf_flags &= ~RCS_SYNCED;
1.1 jfb 657: return (0);
658: }
659:
660: /*
1.27 jfb 661: * rcs_sym_remove()
662: *
663: * Remove the symbol with name <sym> from the symbol list for the RCS file
664: * <file>. If no such symbol is found, the call fails and returns with an
665: * error.
666: * Returns 0 on success, or -1 on failure.
667: */
668: int
669: rcs_sym_remove(RCSFILE *file, const char *sym)
670: {
671: struct rcs_sym *symp;
672:
1.254 joris 673: if (!rcs_sym_check(sym))
1.69 moritz 674: return (-1);
1.47 jfb 675:
1.27 jfb 676: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
677: if (strcmp(symp->rs_name, sym) == 0)
678: break;
679:
1.254 joris 680: if (symp == NULL)
1.27 jfb 681: return (-1);
682:
683: TAILQ_REMOVE(&(file->rf_symbols), symp, rs_list);
1.110 joris 684: xfree(symp->rs_name);
1.27 jfb 685: rcsnum_free(symp->rs_num);
1.110 joris 686: xfree(symp);
1.27 jfb 687:
688: /* not synced anymore */
689: file->rf_flags &= ~RCS_SYNCED;
690: return (0);
1.184 xsa 691: }
692:
693: /*
694: * rcs_sym_get()
695: *
696: * Find a specific symbol <sym> entry in the tree of the RCS file <file>.
697: *
698: * Returns a pointer to the symbol on success, or NULL on failure.
699: */
700: struct rcs_sym *
701: rcs_sym_get(RCSFILE *file, const char *sym)
702: {
703: struct rcs_sym *symp;
704:
705: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
706: if (strcmp(symp->rs_name, sym) == 0)
707: return (symp);
708:
709: return (NULL);
1.27 jfb 710: }
711:
712: /*
713: * rcs_sym_getrev()
714: *
715: * Retrieve the RCS revision number associated with the symbol <sym> for the
716: * RCS file <file>. The returned value is a dynamically-allocated copy and
717: * should be freed by the caller once they are done with it.
718: * Returns the RCSNUM on success, or NULL on failure.
719: */
1.60 xsa 720: RCSNUM *
1.27 jfb 721: rcs_sym_getrev(RCSFILE *file, const char *sym)
722: {
723: RCSNUM *num;
724: struct rcs_sym *symp;
725:
1.275 tobias 726: if (!rcs_sym_check(sym) || file->rf_head == NULL)
1.47 jfb 727: return (NULL);
728:
1.176 joris 729: if (!strcmp(sym, RCS_HEAD_BRANCH)) {
730: num = rcsnum_alloc();
731: rcsnum_cpy(file->rf_head, num, 0);
732: return (num);
733: }
734:
1.27 jfb 735: num = NULL;
736: TAILQ_FOREACH(symp, &(file->rf_symbols), rs_list)
737: if (strcmp(symp->rs_name, sym) == 0)
738: break;
739:
1.254 joris 740: if (symp != NULL) {
1.111 joris 741: num = rcsnum_alloc();
742: rcsnum_cpy(symp->rs_num, num, 0);
1.27 jfb 743: }
744:
745: return (num);
1.47 jfb 746: }
747:
748: /*
749: * rcs_sym_check()
750: *
751: * Check the RCS symbol name <sym> for any unsupported characters.
752: * Returns 1 if the tag is correct, 0 if it isn't valid.
753: */
754: int
755: rcs_sym_check(const char *sym)
756: {
757: int ret;
758: const char *cp;
759:
760: ret = 1;
761: cp = sym;
762: if (!isalpha(*cp++))
763: return (0);
764:
765: for (; *cp != '\0'; cp++)
766: if (!isgraph(*cp) || (strchr(rcs_sym_invch, *cp) != NULL)) {
767: ret = 0;
768: break;
769: }
770:
771: return (ret);
1.30 jfb 772: }
773:
774: /*
775: * rcs_lock_getmode()
776: *
777: * Retrieve the locking mode of the RCS file <file>.
778: */
779: int
780: rcs_lock_getmode(RCSFILE *file)
781: {
782: return (file->rf_flags & RCS_SLOCK) ? RCS_LOCK_STRICT : RCS_LOCK_LOOSE;
783: }
784:
785: /*
786: * rcs_lock_setmode()
787: *
788: * Set the locking mode of the RCS file <file> to <mode>, which must either
789: * be RCS_LOCK_LOOSE or RCS_LOCK_STRICT.
790: * Returns the previous mode on success, or -1 on failure.
791: */
792: int
793: rcs_lock_setmode(RCSFILE *file, int mode)
794: {
795: int pmode;
796: pmode = rcs_lock_getmode(file);
797:
798: if (mode == RCS_LOCK_STRICT)
799: file->rf_flags |= RCS_SLOCK;
800: else if (mode == RCS_LOCK_LOOSE)
801: file->rf_flags &= ~RCS_SLOCK;
1.145 xsa 802: else
803: fatal("rcs_lock_setmode: invalid mode `%d'", mode);
1.30 jfb 804:
1.70 moritz 805: file->rf_flags &= ~RCS_SYNCED;
1.30 jfb 806: return (pmode);
1.27 jfb 807: }
808:
809: /*
1.40 jfb 810: * rcs_lock_add()
811: *
812: * Add an RCS lock for the user <user> on revision <rev>.
813: * Returns 0 on success, or -1 on failure.
814: */
815: int
816: rcs_lock_add(RCSFILE *file, const char *user, RCSNUM *rev)
817: {
818: struct rcs_lock *lkp;
819:
820: /* first look for duplication */
821: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 822: if (strcmp(lkp->rl_name, user) == 0 &&
1.254 joris 823: rcsnum_cmp(rev, lkp->rl_num, 0) == 0)
1.40 jfb 824: return (-1);
825: }
826:
1.161 ray 827: lkp = xmalloc(sizeof(*lkp));
1.110 joris 828: lkp->rl_name = xstrdup(user);
1.111 joris 829: lkp->rl_num = rcsnum_alloc();
1.68 moritz 830: rcsnum_cpy(rev, lkp->rl_num, 0);
1.40 jfb 831:
832: TAILQ_INSERT_TAIL(&(file->rf_locks), lkp, rl_list);
833:
834: /* not synced anymore */
835: file->rf_flags &= ~RCS_SYNCED;
836: return (0);
837: }
838:
839:
840: /*
841: * rcs_lock_remove()
842: *
843: * Remove the RCS lock on revision <rev>.
844: * Returns 0 on success, or -1 on failure.
845: */
846: int
1.109 joris 847: rcs_lock_remove(RCSFILE *file, const char *user, RCSNUM *rev)
1.40 jfb 848: {
849: struct rcs_lock *lkp;
850:
1.109 joris 851: TAILQ_FOREACH(lkp, &(file->rf_locks), rl_list) {
1.168 deraadt 852: if (strcmp(lkp->rl_name, user) == 0 &&
853: rcsnum_cmp(lkp->rl_num, rev, 0) == 0)
1.40 jfb 854: break;
1.109 joris 855: }
1.40 jfb 856:
1.254 joris 857: if (lkp == NULL)
1.40 jfb 858: return (-1);
859:
860: TAILQ_REMOVE(&(file->rf_locks), lkp, rl_list);
861: rcsnum_free(lkp->rl_num);
1.110 joris 862: xfree(lkp->rl_name);
863: xfree(lkp);
1.40 jfb 864:
865: /* not synced anymore */
866: file->rf_flags &= ~RCS_SYNCED;
867: return (0);
868: }
869:
870: /*
1.27 jfb 871: * rcs_desc_get()
872: *
873: * Retrieve the description for the RCS file <file>.
874: */
1.57 xsa 875: const char *
1.27 jfb 876: rcs_desc_get(RCSFILE *file)
877: {
878: return (file->rf_desc);
879: }
880:
881: /*
882: * rcs_desc_set()
883: *
884: * Set the description for the RCS file <file>.
885: */
1.149 xsa 886: void
1.27 jfb 887: rcs_desc_set(RCSFILE *file, const char *desc)
888: {
889: char *tmp;
890:
1.110 joris 891: tmp = xstrdup(desc);
1.27 jfb 892: if (file->rf_desc != NULL)
1.110 joris 893: xfree(file->rf_desc);
1.27 jfb 894: file->rf_desc = tmp;
895: file->rf_flags &= ~RCS_SYNCED;
1.51 jfb 896: }
897:
898: /*
899: * rcs_comment_lookup()
900: *
901: * Lookup the assumed comment leader based on a file's suffix.
902: * Returns a pointer to the string on success, or NULL on failure.
903: */
1.57 xsa 904: const char *
1.51 jfb 905: rcs_comment_lookup(const char *filename)
906: {
907: int i;
908: const char *sp;
909:
1.254 joris 910: if ((sp = strrchr(filename, '.')) == NULL)
1.51 jfb 911: return (NULL);
912: sp++;
913:
914: for (i = 0; i < (int)NB_COMTYPES; i++)
915: if (strcmp(rcs_comments[i].rc_suffix, sp) == 0)
916: return (rcs_comments[i].rc_cstr);
917: return (NULL);
1.27 jfb 918: }
919:
1.33 jfb 920: /*
921: * rcs_comment_get()
922: *
923: * Retrieve the comment leader for the RCS file <file>.
924: */
1.57 xsa 925: const char *
1.33 jfb 926: rcs_comment_get(RCSFILE *file)
927: {
928: return (file->rf_comment);
929: }
930:
931: /*
932: * rcs_comment_set()
933: *
934: * Set the comment leader for the RCS file <file>.
935: */
1.150 xsa 936: void
1.33 jfb 937: rcs_comment_set(RCSFILE *file, const char *comment)
938: {
939: char *tmp;
940:
1.110 joris 941: tmp = xstrdup(comment);
1.33 jfb 942: if (file->rf_comment != NULL)
1.110 joris 943: xfree(file->rf_comment);
1.33 jfb 944: file->rf_comment = tmp;
945: file->rf_flags &= ~RCS_SYNCED;
1.40 jfb 946: }
947:
1.94 joris 948: int
1.297 ray 949: rcs_patch_lines(struct rcs_lines *dlines, struct rcs_lines *plines,
950: struct rcs_line **alines, struct rcs_delta *rdp)
1.5 vincent 951: {
1.209 otto 952: u_char op;
953: char *ep;
1.297 ray 954: struct rcs_line *lp, *dlp, *ndlp;
1.5 vincent 955: int i, lineno, nbln;
1.193 niallo 956: u_char tmp;
1.1 jfb 957:
1.94 joris 958: dlp = TAILQ_FIRST(&(dlines->l_lines));
959: lp = TAILQ_FIRST(&(plines->l_lines));
1.1 jfb 960:
961: /* skip first bogus line */
1.94 joris 962: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
963: lp = TAILQ_NEXT(lp, l_list)) {
1.193 niallo 964: if (lp->l_len < 2)
965: fatal("line too short, RCS patch seems broken");
1.94 joris 966: op = *(lp->l_line);
1.193 niallo 967: /* NUL-terminate line buffer for strtol() safety. */
968: tmp = lp->l_line[lp->l_len - 1];
969: lp->l_line[lp->l_len - 1] = '\0';
1.209 otto 970: lineno = (int)strtol((char*)(lp->l_line + 1), &ep, 10);
1.193 niallo 971: if (lineno - 1 > dlines->l_nblines || lineno < 0) {
972: fatal("invalid line specification in RCS patch");
973: }
1.1 jfb 974: ep++;
975: nbln = (int)strtol(ep, &ep, 10);
1.193 niallo 976: /* Restore the last byte of the buffer */
977: lp->l_line[lp->l_len - 1] = tmp;
978: if (nbln < 0)
1.132 niallo 979: fatal("invalid line number specification in RCS patch");
1.1 jfb 980:
981: /* find the appropriate line */
982: for (;;) {
983: if (dlp == NULL)
984: break;
1.94 joris 985: if (dlp->l_lineno == lineno)
1.1 jfb 986: break;
1.94 joris 987: if (dlp->l_lineno > lineno) {
1.296 ray 988: dlp = TAILQ_PREV(dlp, tqh, l_list);
1.94 joris 989: } else if (dlp->l_lineno < lineno) {
1.133 niallo 990: if (((ndlp = TAILQ_NEXT(dlp, l_list)) == NULL) ||
1.168 deraadt 991: ndlp->l_lineno > lineno)
1.1 jfb 992: break;
993: dlp = ndlp;
994: }
995: }
1.132 niallo 996: if (dlp == NULL)
997: fatal("can't find referenced line in RCS patch");
1.1 jfb 998:
999: if (op == 'd') {
1000: for (i = 0; (i < nbln) && (dlp != NULL); i++) {
1.94 joris 1001: ndlp = TAILQ_NEXT(dlp, l_list);
1002: TAILQ_REMOVE(&(dlines->l_lines), dlp, l_list);
1.218 tobias 1003: if (alines != NULL && dlp->l_line != NULL) {
1004: dlp->l_delta = rdp;
1005: alines[dlp->l_lineno_orig - 1] =
1006: dlp;
1007: } else
1008: xfree(dlp);
1.1 jfb 1009: dlp = ndlp;
1.133 niallo 1010: /* last line is gone - reset dlp */
1011: if (dlp == NULL) {
1012: ndlp = TAILQ_LAST(&(dlines->l_lines),
1.296 ray 1013: tqh);
1.133 niallo 1014: dlp = ndlp;
1015: }
1.1 jfb 1016: }
1.14 deraadt 1017: } else if (op == 'a') {
1.1 jfb 1018: for (i = 0; i < nbln; i++) {
1019: ndlp = lp;
1.94 joris 1020: lp = TAILQ_NEXT(lp, l_list);
1.132 niallo 1021: if (lp == NULL)
1022: fatal("truncated RCS patch");
1.94 joris 1023: TAILQ_REMOVE(&(plines->l_lines), lp, l_list);
1.218 tobias 1024: if (alines != NULL) {
1025: if (lp->l_needsfree == 1)
1026: xfree(lp->l_line);
1027: lp->l_line = NULL;
1028: lp->l_needsfree = 0;
1029: }
1.225 tobias 1030: lp->l_delta = rdp;
1.94 joris 1031: TAILQ_INSERT_AFTER(&(dlines->l_lines), dlp,
1032: lp, l_list);
1.1 jfb 1033: dlp = lp;
1034:
1035: /* we don't want lookup to block on those */
1.94 joris 1036: lp->l_lineno = lineno;
1.1 jfb 1037:
1038: lp = ndlp;
1039: }
1.132 niallo 1040: } else
1041: fatal("unknown RCS patch operation `%c'", op);
1.1 jfb 1042:
1043: /* last line of the patch, done */
1.94 joris 1044: if (lp->l_lineno == plines->l_nblines)
1.1 jfb 1045: break;
1046: }
1047:
1048: /* once we're done patching, rebuild the line numbers */
1.2 vincent 1049: lineno = 0;
1.94 joris 1050: TAILQ_FOREACH(lp, &(dlines->l_lines), l_list)
1051: lp->l_lineno = lineno++;
1052: dlines->l_nblines = lineno - 1;
1.1 jfb 1053:
1.5 vincent 1054: return (0);
1.241 joris 1055: }
1056:
1057: void
1058: rcs_delta_stats(struct rcs_delta *rdp, int *ladded, int *lremoved)
1059: {
1.297 ray 1060: struct rcs_lines *plines;
1061: struct rcs_line *lp;
1.241 joris 1062: int added, i, lineno, nbln, removed;
1063: char op, *ep;
1064: u_char tmp;
1065:
1066: added = removed = 0;
1067:
1068: plines = cvs_splitlines(rdp->rd_text, rdp->rd_tlen);
1069: lp = TAILQ_FIRST(&(plines->l_lines));
1070:
1071: /* skip first bogus line */
1072: for (lp = TAILQ_NEXT(lp, l_list); lp != NULL;
1073: lp = TAILQ_NEXT(lp, l_list)) {
1074: if (lp->l_len < 2)
1075: fatal("line too short, RCS patch seems broken");
1076: op = *(lp->l_line);
1077: /* NUL-terminate line buffer for strtol() safety. */
1078: tmp = lp->l_line[lp->l_len - 1];
1079: lp->l_line[lp->l_len - 1] = '\0';
1080: lineno = (int)strtol((lp->l_line + 1), &ep, 10);
1081: ep++;
1082: nbln = (int)strtol(ep, &ep, 10);
1083: /* Restore the last byte of the buffer */
1084: lp->l_line[lp->l_len - 1] = tmp;
1085: if (nbln < 0)
1086: fatal("invalid line number specification in RCS patch");
1087:
1088: if (op == 'a') {
1089: added += nbln;
1090: for (i = 0; i < nbln; i++) {
1091: lp = TAILQ_NEXT(lp, l_list);
1092: if (lp == NULL)
1093: fatal("truncated RCS patch");
1094: }
1095: }
1096: else if (op == 'd')
1097: removed += nbln;
1098: else
1099: fatal("unknown RCS patch operation '%c'", op);
1100: }
1.263 tobias 1101:
1102: cvs_freelines(plines);
1.241 joris 1103:
1104: *ladded = added;
1105: *lremoved = removed;
1.1 jfb 1106: }
1107:
1108: /*
1.52 jfb 1109: * rcs_rev_add()
1110: *
1.53 jfb 1111: * Add a revision to the RCS file <rf>. The new revision's number can be
1112: * specified in <rev> (which can also be RCS_HEAD_REV, in which case the
1113: * new revision will have a number equal to the previous head revision plus
1114: * one). The <msg> argument specifies the log message for that revision, and
1115: * <date> specifies the revision's date (a value of -1 is
1116: * equivalent to using the current time).
1.295 ray 1117: * If <author> is NULL, set the author for this revision to the current user.
1.52 jfb 1118: * Returns 0 on success, or -1 on failure.
1119: */
1120: int
1.90 niallo 1121: rcs_rev_add(RCSFILE *rf, RCSNUM *rev, const char *msg, time_t date,
1.295 ray 1122: const char *author)
1.52 jfb 1123: {
1124: time_t now;
1.264 tobias 1125: RCSNUM *root = NULL;
1.52 jfb 1126: struct passwd *pw;
1.264 tobias 1127: struct rcs_branch *brp, *obrp;
1.83 joris 1128: struct rcs_delta *ordp, *rdp;
1129:
1.52 jfb 1130: if (rev == RCS_HEAD_REV) {
1.101 niallo 1131: if (rf->rf_flags & RCS_CREATE) {
1132: if ((rev = rcsnum_parse(RCS_HEAD_INIT)) == NULL)
1133: return (-1);
1.275 tobias 1134: if (rf->rf_head != NULL)
1135: xfree(rf->rf_head);
1.299 tobias 1136: rf->rf_head = rev;
1.275 tobias 1137: } else if (rf->rf_head == NULL) {
1138: return (-1);
1.101 niallo 1139: } else {
1140: rev = rcsnum_inc(rf->rf_head);
1141: }
1.83 joris 1142: } else {
1.254 joris 1143: if ((rdp = rcs_findrev(rf, rev)) != NULL)
1.83 joris 1144: return (-1);
1.52 jfb 1145: }
1146:
1.161 ray 1147: rdp = xcalloc(1, sizeof(*rdp));
1.52 jfb 1148:
1149: TAILQ_INIT(&(rdp->rd_branches));
1150:
1.111 joris 1151: rdp->rd_num = rcsnum_alloc();
1.52 jfb 1152: rcsnum_cpy(rev, rdp->rd_num, 0);
1.83 joris 1153:
1.111 joris 1154: rdp->rd_next = rcsnum_alloc();
1.92 joris 1155:
1.295 ray 1156: if (!author && !(author = getlogin())) {
1157: if (!(pw = getpwuid(getuid())))
1158: fatal("getpwuid failed");
1159: author = pw->pw_name;
1160: }
1161: rdp->rd_author = xstrdup(author);
1.110 joris 1162: rdp->rd_state = xstrdup(RCS_STATE_EXP);
1163: rdp->rd_log = xstrdup(msg);
1.52 jfb 1164:
1.53 jfb 1165: if (date != (time_t)(-1))
1166: now = date;
1167: else
1168: time(&now);
1.52 jfb 1169: gmtime_r(&now, &(rdp->rd_date));
1170:
1.180 joris 1171: if (RCSNUM_ISBRANCHREV(rev))
1172: TAILQ_INSERT_TAIL(&(rf->rf_delta), rdp, rd_list);
1173: else
1174: TAILQ_INSERT_HEAD(&(rf->rf_delta), rdp, rd_list);
1.52 jfb 1175: rf->rf_ndelta++;
1.81 niallo 1176:
1.180 joris 1177: if (!(rf->rf_flags & RCS_CREATE)) {
1178: if (RCSNUM_ISBRANCHREV(rev)) {
1.264 tobias 1179: if (rev->rn_id[rev->rn_len - 1] == 1) {
1180: /* a new branch */
1181: root = rcsnum_branch_root(rev);
1182: brp = xmalloc(sizeof(*brp));
1183: brp->rb_num = rcsnum_alloc();
1184: rcsnum_cpy(rdp->rd_num, brp->rb_num, 0);
1185:
1186: if ((ordp = rcs_findrev(rf, root)) == NULL)
1187: fatal("root node not found");
1188:
1189: TAILQ_FOREACH(obrp, &(ordp->rd_branches),
1190: rb_list) {
1191: if (!rcsnum_cmp(obrp->rb_num,
1192: brp->rb_num,
1193: brp->rb_num->rn_len - 1))
1194: break;
1195: }
1.222 joris 1196:
1.264 tobias 1197: if (obrp == NULL) {
1198: TAILQ_INSERT_TAIL(&(ordp->rd_branches),
1199: brp, rb_list);
1200: }
1201: } else {
1202: root = rcsnum_alloc();
1203: rcsnum_cpy(rev, root, 0);
1204: rcsnum_dec(root);
1205: if ((ordp = rcs_findrev(rf, root)) == NULL)
1206: fatal("previous revision not found");
1.222 joris 1207: rcsnum_cpy(rdp->rd_num, ordp->rd_next, 0);
1.264 tobias 1208: }
1.180 joris 1209: } else {
1210: ordp = TAILQ_NEXT(rdp, rd_list);
1211: rcsnum_cpy(ordp->rd_num, rdp->rd_next, 0);
1212: }
1213: }
1214:
1.264 tobias 1215: if (root != NULL)
1216: rcsnum_free(root);
1217:
1.64 niallo 1218: /* not synced anymore */
1219: rf->rf_flags &= ~RCS_SYNCED;
1.52 jfb 1220:
1221: return (0);
1222: }
1223:
1224: /*
1225: * rcs_rev_remove()
1226: *
1227: * Remove the revision whose number is <rev> from the RCS file <rf>.
1228: */
1229: int
1230: rcs_rev_remove(RCSFILE *rf, RCSNUM *rev)
1231: {
1.251 joris 1232: int fd1, fd2;
1.194 joris 1233: char *path_tmp1, *path_tmp2;
1.166 joris 1234: struct rcs_delta *rdp, *prevrdp, *nextrdp;
1.209 otto 1235: BUF *prevbuf, *newdiff, *newdeltatext;
1.166 joris 1236:
1.52 jfb 1237: if (rev == RCS_HEAD_REV)
1238: rev = rf->rf_head;
1239:
1.275 tobias 1240: if (rev == NULL)
1241: return (-1);
1242:
1.52 jfb 1243: /* do we actually have that revision? */
1.254 joris 1244: if ((rdp = rcs_findrev(rf, rev)) == NULL)
1.166 joris 1245: return (-1);
1246:
1247: /*
1248: * This is confusing, the previous delta is next in the TAILQ list.
1249: * the next delta is the previous one in the TAILQ list.
1250: *
1251: * When the HEAD revision got specified, nextrdp will be NULL.
1252: * When the first revision got specified, prevrdp will be NULL.
1253: */
1254: prevrdp = (struct rcs_delta *)TAILQ_NEXT(rdp, rd_list);
1.296 ray 1255: nextrdp = (struct rcs_delta *)TAILQ_PREV(rdp, tqh, rd_list);
1.166 joris 1256:
1257: newdeltatext = NULL;
1.209 otto 1258: prevbuf = NULL;
1.262 joris 1259: path_tmp1 = path_tmp2 = NULL;
1.166 joris 1260:
1.168 deraadt 1261: if (prevrdp != NULL && nextrdp != NULL) {
1.297 ray 1262: newdiff = buf_alloc(64);
1.166 joris 1263:
1264: /* calculate new diff */
1.172 joris 1265: (void)xasprintf(&path_tmp1, "%s/diff1.XXXXXXXXXX", cvs_tmpdir);
1.251 joris 1266: fd1 = rcs_rev_write_stmp(rf, nextrdp->rd_num, path_tmp1, 0);
1.166 joris 1267:
1.172 joris 1268: (void)xasprintf(&path_tmp2, "%s/diff2.XXXXXXXXXX", cvs_tmpdir);
1.251 joris 1269: fd2 = rcs_rev_write_stmp(rf, prevrdp->rd_num, path_tmp2, 0);
1.166 joris 1270:
1271: diff_format = D_RCSDIFF;
1.291 ray 1272: if (diffreg(path_tmp1, path_tmp2,
1.290 ray 1273: fd1, fd2, newdiff, D_FORCEASCII) == D_ERROR)
1.172 joris 1274: fatal("rcs_diffreg failed");
1.166 joris 1275:
1.251 joris 1276: close(fd1);
1277: close(fd2);
1278:
1.194 joris 1279: newdeltatext = newdiff;
1.168 deraadt 1280: } else if (nextrdp == NULL && prevrdp != NULL) {
1.194 joris 1281: newdeltatext = prevbuf;
1.166 joris 1282: }
1283:
1284: if (newdeltatext != NULL) {
1285: if (rcs_deltatext_set(rf, prevrdp->rd_num, newdeltatext) < 0)
1286: fatal("error setting new deltatext");
1287: }
1288:
1289: TAILQ_REMOVE(&(rf->rf_delta), rdp, rd_list);
1290:
1291: /* update pointers */
1.168 deraadt 1292: if (prevrdp != NULL && nextrdp != NULL) {
1.166 joris 1293: rcsnum_cpy(prevrdp->rd_num, nextrdp->rd_next, 0);
1294: } else if (prevrdp != NULL) {
1.170 xsa 1295: if (rcs_head_set(rf, prevrdp->rd_num) < 0)
1296: fatal("rcs_head_set failed");
1.166 joris 1297: } else if (nextrdp != NULL) {
1298: rcsnum_free(nextrdp->rd_next);
1299: nextrdp->rd_next = rcsnum_alloc();
1.52 jfb 1300: } else {
1.166 joris 1301: rcsnum_free(rf->rf_head);
1302: rf->rf_head = NULL;
1.52 jfb 1303: }
1304:
1.166 joris 1305: rf->rf_ndelta--;
1306: rf->rf_flags &= ~RCS_SYNCED;
1307:
1308: rcs_freedelta(rdp);
1309:
1310: if (newdeltatext != NULL)
1311: xfree(newdeltatext);
1.52 jfb 1312:
1.172 joris 1313: if (path_tmp1 != NULL)
1314: xfree(path_tmp1);
1315: if (path_tmp2 != NULL)
1316: xfree(path_tmp2);
1317:
1.166 joris 1318: return (0);
1.52 jfb 1319: }
1320:
1321: /*
1.1 jfb 1322: * rcs_findrev()
1323: *
1324: * Find a specific revision's delta entry in the tree of the RCS file <rfp>.
1325: * The revision number is given in <rev>.
1.156 joris 1326: *
1.1 jfb 1327: * Returns a pointer to the delta on success, or NULL on failure.
1328: */
1.102 xsa 1329: struct rcs_delta *
1.117 niallo 1330: rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
1.1 jfb 1331: {
1.178 joris 1332: int isbrev;
1.156 joris 1333: struct rcs_delta *rdp;
1.178 joris 1334:
1.261 tobias 1335: if (rev == NULL)
1336: return NULL;
1337:
1.178 joris 1338: isbrev = RCSNUM_ISBRANCHREV(rev);
1.26 jfb 1339:
1.117 niallo 1340: /*
1341: * We need to do more parsing if the last revision in the linked list
1342: * is greater than the requested revision.
1343: */
1.156 joris 1344: rdp = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
1.168 deraadt 1345: if (rdp == NULL ||
1.178 joris 1346: (!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
1347: ((isbrev && rdp->rd_num->rn_len < 4) ||
1348: (isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
1.303 tobias 1349: if (rcsparse_deltas(rfp, rev))
1350: fatal("error parsing deltas");
1.117 niallo 1351: }
1352:
1.156 joris 1353: TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
1.178 joris 1354: if (rcsnum_differ(rdp->rd_num, rev))
1355: continue;
1356: else
1.156 joris 1357: return (rdp);
1358: }
1.1 jfb 1359:
1360: return (NULL);
1.20 jfb 1361: }
1362:
1363: /*
1.26 jfb 1364: * rcs_kwexp_set()
1365: *
1366: * Set the keyword expansion mode to use on the RCS file <file> to <mode>.
1367: */
1.162 xsa 1368: void
1.26 jfb 1369: rcs_kwexp_set(RCSFILE *file, int mode)
1370: {
1371: int i;
1372: char *tmp, buf[8] = "";
1373:
1374: if (RCS_KWEXP_INVAL(mode))
1.162 xsa 1375: return;
1.26 jfb 1376:
1377: i = 0;
1378: if (mode == RCS_KWEXP_NONE)
1379: buf[0] = 'b';
1380: else if (mode == RCS_KWEXP_OLD)
1381: buf[0] = 'o';
1382: else {
1383: if (mode & RCS_KWEXP_NAME)
1384: buf[i++] = 'k';
1385: if (mode & RCS_KWEXP_VAL)
1386: buf[i++] = 'v';
1387: if (mode & RCS_KWEXP_LKR)
1388: buf[i++] = 'l';
1389: }
1390:
1.110 joris 1391: tmp = xstrdup(buf);
1.27 jfb 1392: if (file->rf_expand != NULL)
1.147 ray 1393: xfree(file->rf_expand);
1.26 jfb 1394: file->rf_expand = tmp;
1.64 niallo 1395: /* not synced anymore */
1396: file->rf_flags &= ~RCS_SYNCED;
1.26 jfb 1397: }
1398:
1399: /*
1400: * rcs_kwexp_get()
1401: *
1402: * Retrieve the keyword expansion mode to be used for the RCS file <file>.
1403: */
1404: int
1405: rcs_kwexp_get(RCSFILE *file)
1406: {
1407: return rcs_kflag_get(file->rf_expand);
1408: }
1409:
1410: /*
1.20 jfb 1411: * rcs_kflag_get()
1412: *
1413: * Get the keyword expansion mode from a set of character flags given in
1414: * <flags> and return the appropriate flag mask. In case of an error, the
1415: * returned mask will have the RCS_KWEXP_ERR bit set to 1.
1416: */
1417: int
1418: rcs_kflag_get(const char *flags)
1419: {
1420: int fl;
1421: size_t len;
1422: const char *fp;
1.253 tobias 1423:
1424: if (flags == NULL)
1425: return 0;
1.20 jfb 1426:
1427: fl = 0;
1.235 tobias 1428: if (!(len = strlen(flags)))
1429: return RCS_KWEXP_ERR;
1.20 jfb 1430:
1431: for (fp = flags; *fp != '\0'; fp++) {
1432: if (*fp == 'k')
1433: fl |= RCS_KWEXP_NAME;
1434: else if (*fp == 'v')
1435: fl |= RCS_KWEXP_VAL;
1436: else if (*fp == 'l')
1437: fl |= RCS_KWEXP_LKR;
1438: else if (*fp == 'o') {
1439: if (len != 1)
1440: fl |= RCS_KWEXP_ERR;
1441: fl |= RCS_KWEXP_OLD;
1442: } else if (*fp == 'b') {
1443: if (len != 1)
1444: fl |= RCS_KWEXP_ERR;
1.205 xsa 1445: fl |= RCS_KWEXP_NONE;
1.20 jfb 1446: } else /* unknown letter */
1447: fl |= RCS_KWEXP_ERR;
1448: }
1449:
1450: return (fl);
1.1 jfb 1451: }
1452:
1453: /*
1454: * rcs_freedelta()
1455: *
1456: * Free the contents of a delta structure.
1457: */
1.18 jfb 1458: static void
1.1 jfb 1459: rcs_freedelta(struct rcs_delta *rdp)
1460: {
1.12 jfb 1461: struct rcs_branch *rb;
1.1 jfb 1462:
1.12 jfb 1463: if (rdp->rd_num != NULL)
1464: rcsnum_free(rdp->rd_num);
1465: if (rdp->rd_next != NULL)
1466: rcsnum_free(rdp->rd_next);
1467:
1.1 jfb 1468: if (rdp->rd_author != NULL)
1.110 joris 1469: xfree(rdp->rd_author);
1.109 joris 1470: if (rdp->rd_locker != NULL)
1.110 joris 1471: xfree(rdp->rd_locker);
1.1 jfb 1472: if (rdp->rd_state != NULL)
1.110 joris 1473: xfree(rdp->rd_state);
1.1 jfb 1474: if (rdp->rd_log != NULL)
1.110 joris 1475: xfree(rdp->rd_log);
1.1 jfb 1476: if (rdp->rd_text != NULL)
1.110 joris 1477: xfree(rdp->rd_text);
1.12 jfb 1478:
1479: while ((rb = TAILQ_FIRST(&(rdp->rd_branches))) != NULL) {
1480: TAILQ_REMOVE(&(rdp->rd_branches), rb, rb_list);
1481: rcsnum_free(rb->rb_num);
1.110 joris 1482: xfree(rb);
1.1 jfb 1483: }
1484:
1.110 joris 1485: xfree(rdp);
1.1 jfb 1486: }
1487:
1488: /*
1.42 jfb 1489: * rcs_strprint()
1490: *
1491: * Output an RCS string <str> of size <slen> to the stream <stream>. Any
1492: * '@' characters are escaped. Otherwise, the string can contain arbitrary
1493: * binary data.
1494: */
1.155 ray 1495: static void
1.42 jfb 1496: rcs_strprint(const u_char *str, size_t slen, FILE *stream)
1497: {
1498: const u_char *ap, *ep, *sp;
1.52 jfb 1499:
1500: if (slen == 0)
1.155 ray 1501: return;
1.42 jfb 1502:
1503: ep = str + slen - 1;
1504:
1505: for (sp = str; sp <= ep;) {
1506: ap = memchr(sp, '@', ep - sp);
1507: if (ap == NULL)
1508: ap = ep;
1.155 ray 1509: (void)fwrite(sp, sizeof(u_char), ap - sp + 1, stream);
1.42 jfb 1510:
1511: if (*ap == '@')
1512: putc('@', stream);
1513: sp = ap + 1;
1.63 joris 1514: }
1515: }
1516:
1517: /*
1.81 niallo 1518: * rcs_deltatext_set()
1519: *
1520: * Set deltatext for <rev> in RCS file <rfp> to <dtext>
1.96 xsa 1521: * Returns -1 on error, 0 on success.
1.81 niallo 1522: */
1523: int
1.194 joris 1524: rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
1.81 niallo 1525: {
1526: size_t len;
1.194 joris 1527: u_char *dtext;
1.81 niallo 1528: struct rcs_delta *rdp;
1.117 niallo 1529:
1530: /* Write operations require full parsing */
1.303 tobias 1531: if (rcsparse_deltatexts(rfp, NULL))
1532: return (-1);
1.81 niallo 1533:
1534: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1535: return (-1);
1536:
1537: if (rdp->rd_text != NULL)
1.110 joris 1538: xfree(rdp->rd_text);
1.81 niallo 1539:
1.297 ray 1540: len = buf_len(bp);
1541: dtext = buf_release(bp);
1.194 joris 1542: bp = NULL;
1543:
1.103 joris 1544: if (len != 0) {
1.194 joris 1545: rdp->rd_text = xmalloc(len);
1.155 ray 1546: rdp->rd_tlen = len;
1.194 joris 1547: (void)memcpy(rdp->rd_text, dtext, len);
1.103 joris 1548: } else {
1549: rdp->rd_text = NULL;
1550: rdp->rd_tlen = 0;
1551: }
1.194 joris 1552:
1553: if (dtext != NULL)
1554: xfree(dtext);
1.18 jfb 1555:
1.86 joris 1556: return (0);
1557: }
1558:
1559: /*
1560: * rcs_rev_setlog()
1561: *
1.282 tobias 1562: * Sets the log message of revision <rev> to <logtext>.
1.86 joris 1563: */
1564: int
1565: rcs_rev_setlog(RCSFILE *rfp, RCSNUM *rev, const char *logtext)
1566: {
1567: struct rcs_delta *rdp;
1568:
1569: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1570: return (-1);
1571:
1572: if (rdp->rd_log != NULL)
1.110 joris 1573: xfree(rdp->rd_log);
1.86 joris 1574:
1.110 joris 1575: rdp->rd_log = xstrdup(logtext);
1.86 joris 1576: rfp->rf_flags &= ~RCS_SYNCED;
1.95 niallo 1577: return (0);
1578: }
1.97 niallo 1579: /*
1580: * rcs_rev_getdate()
1581: *
1582: * Get the date corresponding to a given revision.
1583: * Returns the date on success, -1 on failure.
1584: */
1585: time_t
1586: rcs_rev_getdate(RCSFILE *rfp, RCSNUM *rev)
1587: {
1588: struct rcs_delta *rdp;
1589:
1590: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1591: return (-1);
1592:
1.204 joris 1593: return (timegm(&rdp->rd_date));
1.97 niallo 1594: }
1.95 niallo 1595:
1596: /*
1597: * rcs_state_set()
1598: *
1599: * Sets the state of revision <rev> to <state>
1600: * NOTE: default state is 'Exp'. States may not contain spaces.
1601: *
1602: * Returns -1 on failure, 0 on success.
1603: */
1604: int
1605: rcs_state_set(RCSFILE *rfp, RCSNUM *rev, const char *state)
1606: {
1607: struct rcs_delta *rdp;
1608:
1609: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1610: return (-1);
1611:
1612: if (rdp->rd_state != NULL)
1.110 joris 1613: xfree(rdp->rd_state);
1.95 niallo 1614:
1.110 joris 1615: rdp->rd_state = xstrdup(state);
1.95 niallo 1616:
1617: rfp->rf_flags &= ~RCS_SYNCED;
1618:
1619: return (0);
1620: }
1621:
1622: /*
1623: * rcs_state_check()
1624: *
1625: * Check if string <state> is valid.
1626: *
1.96 xsa 1627: * Returns 0 if the string is valid, -1 otherwise.
1.95 niallo 1628: */
1629: int
1630: rcs_state_check(const char *state)
1631: {
1.279 tobias 1632: if (strcmp(state, RCS_STATE_DEAD) && strcmp(state, RCS_STATE_EXP))
1.95 niallo 1633: return (-1);
1634:
1.18 jfb 1635: return (0);
1.1 jfb 1636: }
1.97 niallo 1637:
1638: /*
1639: * rcs_state_get()
1640: *
1641: * Get the state for a given revision of a specified RCSFILE.
1642: *
1643: * Returns NULL on failure.
1644: */
1645: const char *
1646: rcs_state_get(RCSFILE *rfp, RCSNUM *rev)
1647: {
1648: struct rcs_delta *rdp;
1649:
1650: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1651: return (NULL);
1652:
1653: return (rdp->rd_state);
1654: }
1655:
1.246 tobias 1656: /* rcs_get_revision() */
1657: static RCSNUM *
1658: rcs_get_revision(const char *revstr, RCSFILE *rfp)
1.176 joris 1659: {
1.213 niallo 1660: RCSNUM *rev, *brev, *frev;
1.178 joris 1661: struct rcs_branch *brp;
1.183 joris 1662: struct rcs_delta *rdp;
1.213 niallo 1663: size_t i;
1.183 joris 1664:
1665: rdp = NULL;
1.215 joris 1666:
1667: if (!strcmp(revstr, RCS_HEAD_BRANCH)) {
1.261 tobias 1668: if (rfp->rf_head == NULL)
1.280 tobias 1669: return (NULL);
1.261 tobias 1670:
1.215 joris 1671: frev = rcsnum_alloc();
1672: rcsnum_cpy(rfp->rf_head, frev, 0);
1673: return (frev);
1674: }
1.176 joris 1675:
1.213 niallo 1676: /* Possibly we could be passed a version number */
1.223 tobias 1677: if ((rev = rcsnum_parse(revstr)) != NULL) {
1678: /* Do not return if it is not in RCS file */
1.280 tobias 1679: if ((rdp = rcs_findrev(rfp, rev)) != NULL)
1680: return (rev);
1.223 tobias 1681: } else {
1682: /* More likely we will be passed a symbol */
1683: rev = rcs_sym_getrev(rfp, revstr);
1684: }
1.222 joris 1685:
1.213 niallo 1686: if (rev == NULL)
1.245 tobias 1687: return (NULL);
1.183 joris 1688:
1.220 joris 1689: /*
1690: * If it was not a branch, thats ok the symbolic
1691: * name refered to a revision, so return the resolved
1.265 joris 1692: * revision for the given name. */
1.224 tobias 1693: if (!RCSNUM_ISBRANCH(rev)) {
1.265 joris 1694: /* Sanity check: The first two elements of any
1695: * revision (be it on a branch or on trunk) cannot
1696: * be greater than HEAD.
1697: *
1698: * XXX: To avoid comparing to uninitialized memory,
1699: * the minimum of both revision lengths is taken
1700: * instead of just 2.
1701: */
1.280 tobias 1702: if (rfp->rf_head == NULL || rcsnum_cmp(rev, rfp->rf_head,
1.265 joris 1703: MIN(rfp->rf_head->rn_len, rev->rn_len)) < 0) {
1.224 tobias 1704: rcsnum_free(rev);
1.280 tobias 1705: return (NULL);
1.224 tobias 1706: }
1.220 joris 1707: return (rev);
1.224 tobias 1708: }
1.183 joris 1709:
1.213 niallo 1710: brev = rcsnum_alloc();
1711: rcsnum_cpy(rev, brev, rev->rn_len - 1);
1.178 joris 1712:
1.180 joris 1713: if ((rdp = rcs_findrev(rfp, brev)) == NULL)
1.246 tobias 1714: fatal("rcs_get_revision: tag `%s' does not exist", revstr);
1.213 niallo 1715: rcsnum_free(brev);
1.178 joris 1716:
1.213 niallo 1717: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1718: for (i = 0; i < rev->rn_len; i++)
1719: if (brp->rb_num->rn_id[i] != rev->rn_id[i])
1720: break;
1721: if (i != rev->rn_len)
1722: continue;
1723: break;
1724: }
1.178 joris 1725:
1.240 tobias 1726: rcsnum_free(rev);
1.213 niallo 1727: frev = rcsnum_alloc();
1728: if (brp == NULL) {
1729: rcsnum_cpy(rdp->rd_num, frev, 0);
1730: return (frev);
1731: } else {
1732: /* Fetch the delta with the correct branch num */
1.180 joris 1733: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.246 tobias 1734: fatal("rcs_get_revision: could not fetch branch "
1735: "delta");
1.213 niallo 1736: rcsnum_cpy(rdp->rd_num, frev, 0);
1737: return (frev);
1.176 joris 1738: }
1.196 niallo 1739: }
1740:
1741: /*
1742: * rcs_rev_getlines()
1743: *
1.238 tobias 1744: * Get the entire contents of revision <frev> from the RCSFILE <rfp> and
1.297 ray 1745: * return it as a pointer to a struct rcs_lines.
1.196 niallo 1746: */
1.297 ray 1747: struct rcs_lines *
1748: rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1.196 niallo 1749: {
1.209 otto 1750: size_t plen;
1.218 tobias 1751: int annotate, done, i, nextroot;
1.196 niallo 1752: RCSNUM *tnum, *bnum;
1753: struct rcs_branch *brp;
1.218 tobias 1754: struct rcs_delta *hrdp, *prdp, *rdp, *trdp;
1.196 niallo 1755: u_char *patch;
1.297 ray 1756: struct rcs_line *line, *nline;
1757: struct rcs_lines *dlines, *plines;
1.196 niallo 1758:
1.275 tobias 1759: if (rfp->rf_head == NULL ||
1760: (hrdp = rcs_findrev(rfp, rfp->rf_head)) == NULL)
1.197 joris 1761: fatal("rcs_rev_getlines: no HEAD revision");
1.196 niallo 1762:
1763: tnum = frev;
1.303 tobias 1764: if (rcsparse_deltatexts(rfp, hrdp->rd_num))
1765: fatal("rcs_rev_getlines: rcsparse_deltatexts");
1.196 niallo 1766:
1767: /* revision on branch, get the branch root */
1768: nextroot = 2;
1.278 joris 1769: bnum = rcsnum_alloc();
1770: if (RCSNUM_ISBRANCHREV(tnum))
1.196 niallo 1771: rcsnum_cpy(tnum, bnum, nextroot);
1.278 joris 1772: else
1773: rcsnum_cpy(tnum, bnum, tnum->rn_len);
1.196 niallo 1774:
1.218 tobias 1775: if (alines != NULL) {
1776: /* start with annotate first at requested revision */
1777: annotate = ANNOTATE_LATER;
1778: *alines = NULL;
1779: } else
1780: annotate = ANNOTATE_NEVER;
1781:
1.196 niallo 1782: dlines = cvs_splitlines(hrdp->rd_text, hrdp->rd_tlen);
1783:
1784: done = 0;
1785:
1786: rdp = hrdp;
1.218 tobias 1787: if (!rcsnum_differ(rdp->rd_num, bnum)) {
1788: if (annotate == ANNOTATE_LATER) {
1789: /* found requested revision for annotate */
1790: i = 0;
1791: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
1792: line->l_lineno_orig = line->l_lineno;
1793: i++;
1794: }
1795:
1.297 ray 1796: *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1.218 tobias 1797: (*alines)[i] = NULL;
1798: annotate = ANNOTATE_NOW;
1799:
1800: /* annotate down to 1.1 from where we are */
1.278 joris 1801: rcsnum_free(bnum);
1.218 tobias 1802: bnum = rcsnum_parse("1.1");
1803: if (!rcsnum_differ(rdp->rd_num, bnum)) {
1804: goto next;
1805: }
1806: } else
1807: goto next;
1808: }
1.196 niallo 1809:
1.218 tobias 1810: prdp = hrdp;
1.196 niallo 1811: if ((rdp = rcs_findrev(rfp, hrdp->rd_next)) == NULL)
1812: goto done;
1813:
1814: again:
1815: for (;;) {
1816: if (rdp->rd_next->rn_len != 0) {
1817: trdp = rcs_findrev(rfp, rdp->rd_next);
1818: if (trdp == NULL)
1819: fatal("failed to grab next revision");
1.298 nicm 1820: } else {
1821: /*
1822: * XXX Fail, although the caller does not always do the
1823: * right thing (eg cvs diff when the tree is ahead of
1824: * the repository).
1825: */
1826: break;
1.196 niallo 1827: }
1828:
1829: if (rdp->rd_tlen == 0) {
1.303 tobias 1830: if (rcsparse_deltatexts(rfp, rdp->rd_num))
1831: fatal("rcs_rev_getlines: rcsparse_deltatexts");
1.196 niallo 1832: if (rdp->rd_tlen == 0) {
1833: if (!rcsnum_differ(rdp->rd_num, bnum))
1834: break;
1835: rdp = trdp;
1836: continue;
1837: }
1838: }
1839:
1840: plen = rdp->rd_tlen;
1841: patch = rdp->rd_text;
1842: plines = cvs_splitlines(patch, plen);
1.218 tobias 1843: if (annotate == ANNOTATE_NOW)
1844: rcs_patch_lines(dlines, plines, *alines, prdp);
1845: else
1846: rcs_patch_lines(dlines, plines, NULL, NULL);
1.196 niallo 1847: cvs_freelines(plines);
1848:
1.218 tobias 1849: if (!rcsnum_differ(rdp->rd_num, bnum)) {
1850: if (annotate != ANNOTATE_LATER)
1851: break;
1852:
1853: /* found requested revision for annotate */
1854: i = 0;
1855: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
1856: line->l_lineno_orig = line->l_lineno;
1857: i++;
1858: }
1859:
1.297 ray 1860: *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1.218 tobias 1861: (*alines)[i] = NULL;
1862: annotate = ANNOTATE_NOW;
1863:
1864: /* annotate down to 1.1 from where we are */
1.278 joris 1865: rcsnum_free(bnum);
1.218 tobias 1866: bnum = rcsnum_parse("1.1");
1867:
1868: if (!rcsnum_differ(rdp->rd_num, bnum))
1869: break;
1870: }
1.196 niallo 1871:
1.218 tobias 1872: prdp = rdp;
1.196 niallo 1873: rdp = trdp;
1874: }
1875:
1876: next:
1877: if (!rcsnum_differ(rdp->rd_num, frev))
1878: done = 1;
1879:
1880: if (RCSNUM_ISBRANCHREV(frev) && done != 1) {
1881: nextroot += 2;
1882: rcsnum_cpy(frev, bnum, nextroot);
1883:
1884: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1.213 niallo 1885: for (i = 0; i < nextroot - 1; i++)
1886: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1.196 niallo 1887: break;
1.213 niallo 1888: if (i == nextroot - 1)
1889: break;
1.196 niallo 1890: }
1891:
1.218 tobias 1892: if (brp == NULL) {
1893: if (annotate != ANNOTATE_NEVER) {
1894: if (*alines != NULL)
1895: xfree(*alines);
1896: *alines = NULL;
1897: cvs_freelines(dlines);
1.278 joris 1898: rcsnum_free(bnum);
1.218 tobias 1899: return (NULL);
1900: }
1.196 niallo 1901: fatal("expected branch not found on branch list");
1.218 tobias 1902: }
1.196 niallo 1903:
1904: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1.197 joris 1905: fatal("rcs_rev_getlines: failed to get delta for target rev");
1.196 niallo 1906:
1907: goto again;
1908: }
1909: done:
1.225 tobias 1910: /* put remaining lines into annotate buffer */
1.218 tobias 1911: if (annotate == ANNOTATE_NOW) {
1912: for (line = TAILQ_FIRST(&(dlines->l_lines));
1913: line != NULL; line = nline) {
1914: nline = TAILQ_NEXT(line, l_list);
1915: TAILQ_REMOVE(&(dlines->l_lines), line, l_list);
1916: if (line->l_line == NULL) {
1917: xfree(line);
1918: continue;
1919: }
1920:
1921: line->l_delta = rdp;
1922: (*alines)[line->l_lineno_orig - 1] = line;
1923: }
1924:
1925: cvs_freelines(dlines);
1926: dlines = NULL;
1927: }
1928:
1.196 niallo 1929: if (bnum != tnum)
1930: rcsnum_free(bnum);
1931:
1932: return (dlines);
1.225 tobias 1933: }
1934:
1935: void
1.297 ray 1936: rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
1.225 tobias 1937: {
1938: size_t plen;
1939: int i, nextroot;
1940: RCSNUM *bnum;
1941: struct rcs_branch *brp;
1942: struct rcs_delta *rdp, *trdp;
1943: u_char *patch;
1.297 ray 1944: struct rcs_line *line;
1945: struct rcs_lines *dlines, *plines;
1.225 tobias 1946:
1947: if (!RCSNUM_ISBRANCHREV(frev))
1948: fatal("rcs_annotate_getlines: branch revision expected");
1949:
1950: /* revision on branch, get the branch root */
1951: nextroot = 2;
1952: bnum = rcsnum_alloc();
1953: rcsnum_cpy(frev, bnum, nextroot);
1954:
1955: /*
1956: * Going from HEAD to 1.1 enables the use of an array, which is
1957: * much faster. Unfortunately this is not possible with branch
1958: * revisions, so copy over our alines (array) into dlines (tailq).
1959: */
1960: dlines = xcalloc(1, sizeof(*dlines));
1961: TAILQ_INIT(&(dlines->l_lines));
1962: line = xcalloc(1, sizeof(*line));
1963: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
1964:
1965: for (i = 0; (*alines)[i] != NULL; i++) {
1966: line = (*alines)[i];
1967: line->l_lineno = i + 1;
1968: TAILQ_INSERT_TAIL(&(dlines->l_lines), line, l_list);
1969: }
1970:
1971: rdp = rcs_findrev(rfp, bnum);
1972: if (rdp == NULL)
1973: fatal("failed to grab branch root revision");
1974:
1975: do {
1976: nextroot += 2;
1977: rcsnum_cpy(frev, bnum, nextroot);
1978:
1979: TAILQ_FOREACH(brp, &(rdp->rd_branches), rb_list) {
1980: for (i = 0; i < nextroot - 1; i++)
1981: if (brp->rb_num->rn_id[i] != bnum->rn_id[i])
1982: break;
1983: if (i == nextroot - 1)
1984: break;
1985: }
1986:
1987: if (brp == NULL)
1988: fatal("expected branch not found on branch list");
1989:
1990: if ((rdp = rcs_findrev(rfp, brp->rb_num)) == NULL)
1991: fatal("failed to get delta for target rev");
1992:
1993: for (;;) {
1994: if (rdp->rd_next->rn_len != 0) {
1995: trdp = rcs_findrev(rfp, rdp->rd_next);
1996: if (trdp == NULL)
1997: fatal("failed to grab next revision");
1998: }
1999:
2000: if (rdp->rd_tlen == 0) {
1.303 tobias 2001: if (rcsparse_deltatexts(rfp, rdp->rd_num))
2002: fatal("rcs_annotate_getlines: "
2003: "rcsparse_deltatexts");
1.225 tobias 2004: if (rdp->rd_tlen == 0) {
2005: if (!rcsnum_differ(rdp->rd_num, bnum))
2006: break;
2007: rdp = trdp;
2008: continue;
2009: }
2010: }
2011:
2012: plen = rdp->rd_tlen;
2013: patch = rdp->rd_text;
2014: plines = cvs_splitlines(patch, plen);
2015: rcs_patch_lines(dlines, plines, NULL, rdp);
2016: cvs_freelines(plines);
2017:
2018: if (!rcsnum_differ(rdp->rd_num, bnum))
2019: break;
2020:
2021: rdp = trdp;
2022: }
2023: } while (rcsnum_differ(rdp->rd_num, frev));
2024:
2025: if (bnum != frev)
2026: rcsnum_free(bnum);
2027:
2028: /*
2029: * All lines have been parsed, now they must be copied over
2030: * into alines (array) again.
2031: */
2032: xfree(*alines);
2033:
2034: i = 0;
2035: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2036: if (line->l_line != NULL)
2037: i++;
2038: }
1.297 ray 2039: *alines = xcalloc(i + 1, sizeof(struct rcs_line *));
1.225 tobias 2040: (*alines)[i] = NULL;
2041:
2042: i = 0;
2043: TAILQ_FOREACH(line, &(dlines->l_lines), l_list) {
2044: if (line->l_line != NULL)
2045: (*alines)[i++] = line;
2046: }
1.196 niallo 2047: }
2048:
2049: /*
2050: * rcs_rev_getbuf()
2051: *
2052: * XXX: This is really really slow and should be avoided if at all possible!
2053: *
2054: * Get the entire contents of revision <rev> from the RCSFILE <rfp> and
2055: * return it as a BUF pointer.
2056: */
2057: BUF *
1.200 joris 2058: rcs_rev_getbuf(RCSFILE *rfp, RCSNUM *rev, int mode)
1.196 niallo 2059: {
1.200 joris 2060: int expmode, expand;
2061: struct rcs_delta *rdp;
1.297 ray 2062: struct rcs_lines *lines;
2063: struct rcs_line *lp, *nlp;
1.196 niallo 2064: BUF *bp;
2065:
1.200 joris 2066: expand = 0;
1.218 tobias 2067: lines = rcs_rev_getlines(rfp, rev, NULL);
1.297 ray 2068: bp = buf_alloc(1024 * 16);
1.200 joris 2069:
2070: if (!(mode & RCS_KWEXP_NONE)) {
2071: if (rfp->rf_expand != NULL)
2072: expmode = rcs_kwexp_get(rfp);
2073: else
2074: expmode = RCS_KWEXP_DEFAULT;
2075:
2076: if (!(expmode & RCS_KWEXP_NONE)) {
2077: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2078: fatal("could not fetch revision");
2079: expand = 1;
2080: }
2081: }
2082:
1.255 deraadt 2083: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 2084: nlp = TAILQ_NEXT(lp, l_list);
2085:
2086: if (lp->l_line == NULL) {
2087: lp = nlp;
1.196 niallo 2088: continue;
1.233 tobias 2089: }
1.200 joris 2090:
2091: if (expand)
1.233 tobias 2092: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.200 joris 2093:
1.233 tobias 2094: do {
1.297 ray 2095: buf_append(bp, lp->l_line, lp->l_len);
1.233 tobias 2096: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 2097: }
2098:
2099: cvs_freelines(lines);
2100:
2101: return (bp);
2102: }
2103:
2104: /*
2105: * rcs_rev_write_fd()
2106: *
2107: * Write the entire contents of revision <frev> from the rcsfile <rfp> to
2108: * file descriptor <fd>.
2109: */
2110: void
1.273 joris 2111: rcs_rev_write_fd(RCSFILE *rfp, RCSNUM *rev, int _fd, int mode)
1.196 niallo 2112: {
1.273 joris 2113: int fd;
2114: FILE *fp;
2115: size_t ret;
1.200 joris 2116: int expmode, expand;
1.196 niallo 2117: struct rcs_delta *rdp;
1.297 ray 2118: struct rcs_lines *lines;
2119: struct rcs_line *lp, *nlp;
1.217 joris 2120: extern int print_stdout;
1.196 niallo 2121:
1.200 joris 2122: expand = 0;
1.218 tobias 2123: lines = rcs_rev_getlines(rfp, rev, NULL);
1.200 joris 2124:
1.196 niallo 2125: if (!(mode & RCS_KWEXP_NONE)) {
2126: if (rfp->rf_expand != NULL)
2127: expmode = rcs_kwexp_get(rfp);
2128: else
2129: expmode = RCS_KWEXP_DEFAULT;
2130:
2131: if (!(expmode & RCS_KWEXP_NONE)) {
2132: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
2133: fatal("could not fetch revision");
1.200 joris 2134: expand = 1;
1.196 niallo 2135: }
2136: }
1.200 joris 2137:
1.273 joris 2138: fd = dup(_fd);
2139: if (fd == -1)
2140: fatal("rcs_rev_write_fd: dup: %s", strerror(errno));
2141:
2142: if ((fp = fdopen(fd, "w")) == NULL)
2143: fatal("rcs_rev_write_fd: fdopen: %s", strerror(errno));
2144:
1.255 deraadt 2145: for (lp = TAILQ_FIRST(&lines->l_lines); lp != NULL;) {
1.233 tobias 2146: nlp = TAILQ_NEXT(lp, l_list);
2147:
2148: if (lp->l_line == NULL) {
2149: lp = nlp;
1.196 niallo 2150: continue;
1.233 tobias 2151: }
1.200 joris 2152:
2153: if (expand)
1.233 tobias 2154: rcs_kwexp_line(rfp->rf_path, rdp, lines, lp, expmode);
1.217 joris 2155:
1.233 tobias 2156: do {
2157: /*
2158: * Solely for the checkout and update -p options.
2159: */
2160: if (cvs_server_active == 1 &&
2161: (cvs_cmdop == CVS_OP_CHECKOUT ||
2162: cvs_cmdop == CVS_OP_UPDATE) && print_stdout == 1) {
1.273 joris 2163: ret = fwrite("M ", 1, 2, fp);
2164: if (ret != 2)
1.233 tobias 2165: fatal("rcs_rev_write_fd: %s",
2166: strerror(errno));
2167: }
2168:
1.273 joris 2169: ret = fwrite(lp->l_line, 1, lp->l_len, fp);
2170: if (ret != lp->l_len)
1.217 joris 2171: fatal("rcs_rev_write_fd: %s", strerror(errno));
1.233 tobias 2172: } while ((lp = TAILQ_NEXT(lp, l_list)) != nlp);
1.196 niallo 2173: }
2174:
2175: cvs_freelines(lines);
1.273 joris 2176: (void)fclose(fp);
1.196 niallo 2177: }
2178:
2179: /*
2180: * rcs_rev_write_stmp()
2181: *
2182: * Write the contents of the rev <rev> to a temporary file whose path is
2183: * specified using <template> (see mkstemp(3)). NB. This function will modify
2184: * <template>, as per mkstemp.
2185: */
1.251 joris 2186: int
1.196 niallo 2187: rcs_rev_write_stmp(RCSFILE *rfp, RCSNUM *rev, char *template, int mode)
2188: {
2189: int fd;
2190:
2191: if ((fd = mkstemp(template)) == -1)
2192: fatal("mkstemp: `%s': %s", template, strerror(errno));
2193:
1.296 ray 2194: worklist_add(template, &temp_files);
1.196 niallo 2195: rcs_rev_write_fd(rfp, rev, fd, mode);
2196:
1.257 joris 2197: if (lseek(fd, 0, SEEK_SET) < 0)
1.251 joris 2198: fatal("rcs_rev_write_stmp: lseek: %s", strerror(errno));
2199:
2200: return (fd);
1.196 niallo 2201: }
2202:
2203: static void
1.297 ray 2204: rcs_kwexp_line(char *rcsfile, struct rcs_delta *rdp, struct rcs_lines *lines,
2205: struct rcs_line *line, int mode)
1.196 niallo 2206: {
1.231 tobias 2207: BUF *tmpbuf;
1.196 niallo 2208: int kwtype;
2209: u_int j, found;
1.209 otto 2210: const u_char *c, *start, *fin, *end;
2211: char *kwstr;
1.196 niallo 2212: char expbuf[256], buf[256];
1.231 tobias 2213: size_t clen, kwlen, len, tlen;
1.196 niallo 2214:
2215: kwtype = 0;
2216: kwstr = NULL;
1.230 tobias 2217:
2218: if (mode & RCS_KWEXP_OLD)
2219: return;
1.196 niallo 2220:
2221: len = line->l_len;
2222: if (len == 0)
2223: return;
2224:
2225: c = line->l_line;
2226: found = 0;
2227: /* Final character in buffer. */
2228: fin = c + len - 1;
2229:
2230: /*
2231: * Keyword formats:
2232: * $Keyword$
2233: * $Keyword: value$
2234: */
2235: for (; c < fin; c++) {
1.231 tobias 2236: if (*c != '$')
2237: continue;
1.196 niallo 2238:
1.231 tobias 2239: /* remember start of this possible keyword */
2240: start = c;
1.196 niallo 2241:
1.231 tobias 2242: /* first following character has to be alphanumeric */
2243: c++;
2244: if (!isalpha(*c)) {
2245: c = start;
2246: continue;
2247: }
1.196 niallo 2248:
1.231 tobias 2249: /* Number of characters between c and fin, inclusive. */
2250: clen = fin - c + 1;
1.196 niallo 2251:
1.231 tobias 2252: /* look for any matching keywords */
2253: found = 0;
2254: for (j = 0; j < RCS_NKWORDS; j++) {
2255: kwlen = strlen(rcs_expkw[j].kw_str);
1.196 niallo 2256: /*
1.231 tobias 2257: * kwlen must be less than clen since clen
2258: * includes either a terminating `$' or a `:'.
1.196 niallo 2259: */
1.231 tobias 2260: if (kwlen < clen &&
2261: memcmp(c, rcs_expkw[j].kw_str, kwlen) == 0 &&
2262: (c[kwlen] == '$' || c[kwlen] == ':')) {
2263: found = 1;
2264: kwstr = rcs_expkw[j].kw_str;
2265: kwtype = rcs_expkw[j].kw_type;
2266: c += kwlen;
2267: break;
2268: }
2269: }
1.196 niallo 2270:
1.231 tobias 2271: if (found == 0 && cvs_tagname != NULL) {
2272: kwlen = strlen(cvs_tagname);
2273: if (kwlen < clen &&
2274: memcmp(c, cvs_tagname, kwlen) == 0 &&
2275: (c[kwlen] == '$' || c[kwlen] == ':')) {
2276: found = 1;
2277: kwstr = cvs_tagname;
2278: kwtype = RCS_KW_ID;
2279: c += kwlen;
1.196 niallo 2280: }
1.231 tobias 2281: }
1.196 niallo 2282:
1.231 tobias 2283: /* unknown keyword, continue looking */
2284: if (found == 0) {
2285: c = start;
2286: continue;
2287: }
1.196 niallo 2288:
1.231 tobias 2289: /*
2290: * if the next character was ':' we need to look for
2291: * an '$' before the end of the line to be sure it is
2292: * in fact a keyword.
2293: */
2294: if (*c == ':') {
2295: for (; c <= fin; ++c) {
2296: if (*c == '$' || *c == '\n')
2297: break;
1.196 niallo 2298: }
2299:
1.231 tobias 2300: if (*c != '$') {
2301: c = start;
2302: continue;
2303: }
2304: }
2305: end = c + 1;
1.196 niallo 2306:
1.231 tobias 2307: /* start constructing the expansion */
2308: expbuf[0] = '\0';
1.196 niallo 2309:
1.231 tobias 2310: if (mode & RCS_KWEXP_NAME) {
2311: if (strlcat(expbuf, "$", sizeof(expbuf)) >=
2312: sizeof(expbuf) || strlcat(expbuf, kwstr,
2313: sizeof(expbuf)) >= sizeof(expbuf))
2314: fatal("rcs_kwexp_line: truncated");
2315: if ((mode & RCS_KWEXP_VAL) &&
2316: strlcat(expbuf, ": ", sizeof(expbuf)) >=
2317: sizeof(expbuf))
2318: fatal("rcs_kwexp_line: truncated");
2319: }
1.196 niallo 2320:
1.231 tobias 2321: /*
2322: * order matters because of RCS_KW_ID and
2323: * RCS_KW_HEADER here
2324: */
2325: if (mode & RCS_KWEXP_VAL) {
2326: if (kwtype & RCS_KW_RCSFILE) {
2327: if (!(kwtype & RCS_KW_FULLPATH))
2328: (void)strlcat(expbuf, basename(rcsfile),
2329: sizeof(expbuf));
2330: else
2331: (void)strlcat(expbuf, rcsfile,
2332: sizeof(expbuf));
2333: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2334: sizeof(expbuf))
2335: fatal("rcs_kwexp_line: truncated");
2336: }
1.211 niallo 2337:
1.231 tobias 2338: if (kwtype & RCS_KW_REVISION) {
2339: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2340: if (strlcat(buf, " ", sizeof(buf)) >=
2341: sizeof(buf) || strlcat(expbuf, buf,
2342: sizeof(expbuf)) >= sizeof(buf))
2343: fatal("rcs_kwexp_line: truncated");
2344: }
1.196 niallo 2345:
1.231 tobias 2346: if (kwtype & RCS_KW_DATE) {
1.283 canacar 2347: if (strftime(buf, sizeof(buf),
2348: "%Y/%m/%d %H:%M:%S ",
1.231 tobias 2349: &rdp->rd_date) == 0)
2350: fatal("rcs_kwexp_line: strftime "
2351: "failure");
2352: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2353: sizeof(expbuf))
2354: fatal("rcs_kwexp_line: string "
2355: "truncated");
2356: }
1.196 niallo 2357:
1.231 tobias 2358: if (kwtype & RCS_KW_MDOCDATE) {
2359: /*
2360: * Do not prepend ' ' for a single
2361: * digit, %e would do so and there is
2362: * no better format for strftime().
2363: */
1.283 canacar 2364: if (strftime(buf, sizeof(buf),
2365: (rdp->rd_date.tm_mday < 10) ?
1.285 joris 2366: "%B%e %Y " : "%B %e %Y ",
1.231 tobias 2367: &rdp->rd_date) == 0)
2368: fatal("rcs_kwexp_line: strftime "
2369: "failure");
2370: if (strlcat(expbuf, buf, sizeof(expbuf)) >=
2371: sizeof(expbuf))
2372: fatal("rcs_kwexp_line: string "
2373: "truncated");
2374: }
2375:
2376: if (kwtype & RCS_KW_AUTHOR) {
2377: if (strlcat(expbuf, rdp->rd_author,
2378: sizeof(expbuf)) >= sizeof(expbuf) ||
2379: strlcat(expbuf, " ", sizeof(expbuf)) >=
2380: sizeof(expbuf))
2381: fatal("rcs_kwexp_line: string "
2382: "truncated");
2383: }
2384:
2385: if (kwtype & RCS_KW_STATE) {
2386: if (strlcat(expbuf, rdp->rd_state,
2387: sizeof(expbuf)) >= sizeof(expbuf) ||
2388: strlcat(expbuf, " ", sizeof(expbuf)) >=
2389: sizeof(expbuf))
2390: fatal("rcs_kwexp_line: string "
2391: "truncated");
2392: }
2393:
2394: /* order does not matter anymore below */
1.233 tobias 2395: if (kwtype & RCS_KW_LOG) {
2396: char linebuf[256];
1.297 ray 2397: struct rcs_line *cur, *lp;
1.233 tobias 2398: char *logp, *l_line, *prefix, *q, *sprefix;
2399: size_t i;
2400:
1.236 tobias 2401: /* Log line */
1.233 tobias 2402: if (!(kwtype & RCS_KW_FULLPATH))
2403: (void)strlcat(expbuf,
2404: basename(rcsfile), sizeof(expbuf));
2405: else
2406: (void)strlcat(expbuf, rcsfile,
2407: sizeof(expbuf));
2408:
1.231 tobias 2409: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2410: sizeof(expbuf))
2411: fatal("rcs_kwexp_line: string "
2412: "truncated");
1.233 tobias 2413:
2414: cur = line;
2415:
2416: /* copy rdp->rd_log for strsep */
2417: logp = xstrdup(rdp->rd_log);
2418:
2419: /* copy our prefix for later processing */
2420: prefix = xmalloc(start - line->l_line + 1);
2421: memcpy(prefix, line->l_line,
2422: start - line->l_line);
2423: prefix[start - line->l_line] = '\0';
2424:
2425: /* copy also prefix without trailing blanks. */
2426: sprefix = xstrdup(prefix);
2427: for (i = strlen(sprefix); i > 0 &&
2428: sprefix[i - 1] == ' '; i--)
2429: sprefix[i - 1] = '\0';
2430:
2431: /* new line: revision + date + author */
2432: linebuf[0] = '\0';
2433: if (strlcat(linebuf, "Revision ",
2434: sizeof(linebuf)) >= sizeof(linebuf))
2435: fatal("rcs_kwexp_line: truncated");
2436: rcsnum_tostr(rdp->rd_num, buf, sizeof(buf));
2437: if (strlcat(linebuf, buf, sizeof(linebuf))
2438: >= sizeof(buf))
2439: fatal("rcs_kwexp_line: truncated");
1.283 canacar 2440: if (strftime(buf, sizeof(buf),
2441: " %Y/%m/%d %H:%M:%S ",
1.233 tobias 2442: &rdp->rd_date) == 0)
2443: fatal("rcs_kwexp_line: strftime "
2444: "failure");
2445: if (strlcat(linebuf, buf, sizeof(linebuf))
2446: >= sizeof(linebuf))
2447: fatal("rcs_kwexp_line: string "
2448: "truncated");
2449: if (strlcat(linebuf, rdp->rd_author,
2450: sizeof(linebuf)) >= sizeof(linebuf))
2451: fatal("rcs_kwexp_line: string "
2452: "truncated");
2453:
2454: lp = xcalloc(1, sizeof(*lp));
2455: xasprintf(&(lp->l_line), "%s%s\n",
2456: prefix, linebuf);
2457: lp->l_len = strlen(lp->l_line);
2458: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2459: l_list);
2460: cur = lp;
2461:
2462: /* Log message */
2463: q = logp;
2464: while ((l_line = strsep(&q, "\n")) != NULL &&
2465: q != NULL) {
2466: lp = xcalloc(1, sizeof(*lp));
2467:
2468: if (l_line[0] == '\0') {
2469: xasprintf(&(lp->l_line), "%s\n",
2470: sprefix);
2471: } else {
2472: xasprintf(&(lp->l_line),
2473: "%s%s\n", prefix, l_line);
2474: }
2475:
2476: lp->l_len = strlen(lp->l_line);
2477: TAILQ_INSERT_AFTER(&(lines->l_lines),
2478: cur, lp, l_list);
2479: cur = lp;
2480: }
2481: xfree(logp);
2482:
2483: /*
2484: * This is just another hairy mess, but it must
1.237 tobias 2485: * be done: All characters behind Log will be
1.233 tobias 2486: * written in a new line next to log messages.
2487: * But that's not enough, we have to strip all
2488: * trailing whitespaces of our prefix.
2489: */
2490: lp = xcalloc(1, sizeof(*lp));
2491: lp->l_line = xcalloc(strlen(sprefix) +
2492: line->l_line + line->l_len - end + 1, 1);
2493: strlcpy(lp->l_line, sprefix,
2494: strlen(sprefix) + 1);
2495: memcpy(lp->l_line + strlen(sprefix),
2496: end, line->l_line + line->l_len - end);
2497: lp->l_len = strlen(lp->l_line);
2498: TAILQ_INSERT_AFTER(&(lines->l_lines), cur, lp,
2499: l_list);
2500: cur = lp;
2501:
2502: end = line->l_line + line->l_len - 1;
2503:
2504: xfree(prefix);
2505: xfree(sprefix);
2506:
2507: }
1.231 tobias 2508:
2509: if (kwtype & RCS_KW_SOURCE) {
2510: if (strlcat(expbuf, rcsfile, sizeof(expbuf)) >=
2511: sizeof(expbuf) || strlcat(expbuf, " ",
2512: sizeof(expbuf)) >= sizeof(expbuf))
2513: fatal("rcs_kwexp_line: string "
2514: "truncated");
1.196 niallo 2515: }
2516:
1.231 tobias 2517: if (kwtype & RCS_KW_NAME)
2518: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2519: sizeof(expbuf))
2520: fatal("rcs_kwexp_line: string "
2521: "truncated");
1.196 niallo 2522:
1.231 tobias 2523: if (kwtype & RCS_KW_LOCKER)
2524: if (strlcat(expbuf, " ", sizeof(expbuf)) >=
2525: sizeof(expbuf))
2526: fatal("rcs_kwexp_line: string "
2527: "truncated");
1.196 niallo 2528: }
1.231 tobias 2529:
2530: /* end the expansion */
2531: if (mode & RCS_KWEXP_NAME)
2532: if (strlcat(expbuf, "$",
2533: sizeof(expbuf)) >= sizeof(expbuf))
2534: fatal("rcs_kwexp_line: truncated");
2535:
2536: /* Concatenate everything together. */
1.297 ray 2537: tmpbuf = buf_alloc(len + strlen(expbuf));
1.231 tobias 2538: /* Append everything before keyword. */
1.297 ray 2539: buf_append(tmpbuf, line->l_line,
1.231 tobias 2540: start - line->l_line);
2541: /* Append keyword. */
1.297 ray 2542: buf_puts(tmpbuf, expbuf);
1.231 tobias 2543: /* Point c to end of keyword. */
1.297 ray 2544: tlen = buf_len(tmpbuf) - 1;
1.231 tobias 2545: /* Append everything after keyword. */
1.297 ray 2546: buf_append(tmpbuf, end,
1.231 tobias 2547: line->l_line + line->l_len - end);
1.297 ray 2548: c = buf_get(tmpbuf) + tlen;
1.231 tobias 2549: /* Point fin to end of data. */
1.297 ray 2550: fin = buf_get(tmpbuf) + buf_len(tmpbuf) - 1;
1.231 tobias 2551: /* Recalculate new length. */
1.297 ray 2552: len = buf_len(tmpbuf);
1.231 tobias 2553:
2554: /* tmpbuf is now ready, convert to string */
2555: if (line->l_needsfree)
2556: xfree(line->l_line);
2557: line->l_len = len;
1.297 ray 2558: line->l_line = buf_release(tmpbuf);
1.231 tobias 2559: line->l_needsfree = 1;
1.196 niallo 2560: }
1.131 niallo 2561: }
1.246 tobias 2562:
2563: /* rcs_translate_tag() */
2564: RCSNUM *
2565: rcs_translate_tag(const char *revstr, RCSFILE *rfp)
2566: {
2567: int follow;
1.258 joris 2568: time_t deltatime;
1.250 joris 2569: char branch[CVS_REV_BUFSZ];
1.288 joris 2570: RCSNUM *brev, *frev, *rev;
1.246 tobias 2571: struct rcs_delta *rdp, *trdp;
1.274 tobias 2572: time_t cdate;
1.246 tobias 2573:
1.288 joris 2574: brev = frev = NULL;
1.246 tobias 2575:
1.250 joris 2576: if (revstr == NULL) {
2577: if (rfp->rf_branch != NULL) {
2578: rcsnum_tostr(rfp->rf_branch, branch, sizeof(branch));
2579: revstr = branch;
2580: } else {
2581: revstr = RCS_HEAD_BRANCH;
2582: }
2583: }
1.246 tobias 2584:
2585: if ((rev = rcs_get_revision(revstr, rfp)) == NULL)
1.258 joris 2586: return (NULL);
2587:
2588: if ((rdp = rcs_findrev(rfp, rev)) == NULL)
1.266 tobias 2589: return (NULL);
1.258 joris 2590:
1.264 tobias 2591: /* let's see if we must follow a branch */
2592: if (!strcmp(revstr, RCS_HEAD_BRANCH))
2593: follow = 1;
2594: else {
2595: frev = rcs_sym_getrev(rfp, revstr);
2596: if (frev == NULL)
1.288 joris 2597: frev = rcsnum_parse(revstr);
1.264 tobias 2598:
2599: brev = rcsnum_alloc();
2600: rcsnum_cpy(rev, brev, rev->rn_len - 1);
2601:
2602: if (frev != NULL && RCSNUM_ISBRANCH(frev) &&
2603: !rcsnum_cmp(frev, brev, 0)) {
2604: follow = 1;
2605: } else
2606: follow = 0;
2607:
2608: rcsnum_free(brev);
2609: }
2610:
1.274 tobias 2611: if (cvs_specified_date != -1)
2612: cdate = cvs_specified_date;
2613: else
2614: cdate = cvs_directory_date;
2615:
2616: if (cdate == -1) {
1.258 joris 2617: /* XXX */
1.264 tobias 2618: if (rev->rn_len < 4 || !follow) {
1.258 joris 2619: return (rev);
2620: }
2621:
2622: /* Find the latest delta on that branch */
2623: rcsnum_free(rev);
2624: for (;;) {
2625: if (rdp->rd_next->rn_len == 0)
2626: break;
2627: if ((rdp = rcs_findrev(rfp, rdp->rd_next)) == NULL)
1.272 tobias 2628: fatal("rcs_translate_tag: could not fetch "
1.258 joris 2629: "branch delta");
2630: }
2631:
2632: rev = rcsnum_alloc();
2633: rcsnum_cpy(rdp->rd_num, rev, 0);
2634: return (rev);
1.246 tobias 2635: }
2636:
2637: if (frev != NULL) {
2638: brev = rcsnum_revtobr(frev);
1.260 joris 2639: brev->rn_len = rev->rn_len - 1;
1.288 joris 2640: rcsnum_free(frev);
1.246 tobias 2641: }
2642:
2643: rcsnum_free(rev);
2644:
2645: do {
1.286 tobias 2646: deltatime = timegm(&(rdp->rd_date));
1.258 joris 2647:
2648: if (RCSNUM_ISBRANCHREV(rdp->rd_num)) {
1.274 tobias 2649: if (deltatime > cdate) {
1.258 joris 2650: trdp = TAILQ_PREV(rdp, rcs_dlist, rd_list);
2651: if (trdp == NULL)
2652: trdp = rdp;
1.260 joris 2653:
2654: if (trdp->rd_num->rn_len != rdp->rd_num->rn_len)
2655: return (NULL);
2656:
1.258 joris 2657: rev = rcsnum_alloc();
2658: rcsnum_cpy(trdp->rd_num, rev, 0);
1.260 joris 2659: return (rev);
2660: }
2661:
2662: if (rdp->rd_next->rn_len == 0) {
2663: rev = rcsnum_alloc();
2664: rcsnum_cpy(rdp->rd_num, rev, 0);
1.258 joris 2665: return (rev);
2666: }
2667: } else {
1.274 tobias 2668: if (deltatime < cdate) {
1.258 joris 2669: rev = rcsnum_alloc();
2670: rcsnum_cpy(rdp->rd_num, rev, 0);
2671: return (rev);
2672: }
1.246 tobias 2673: }
2674:
2675: if (follow && rdp->rd_next->rn_len != 0) {
2676: if (brev != NULL && !rcsnum_cmp(brev, rdp->rd_num, 0))
2677: break;
2678:
2679: trdp = rcs_findrev(rfp, rdp->rd_next);
2680: if (trdp == NULL)
2681: fatal("failed to grab next revision");
2682: rdp = trdp;
2683: } else
2684: follow = 0;
2685: } while (follow);
2686:
1.258 joris 2687: return (NULL);
1.246 tobias 2688: }