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