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