1184054Slulf/*- 2186743Slulf * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> 3184054Slulf * All rights reserved. 4184054Slulf * 5184054Slulf * Redistribution and use in source and binary forms, with or without 6184054Slulf * modification, are permitted provided that the following conditions 7184054Slulf * are met: 8184054Slulf * 1. Redistributions of source code must retain the above copyright 9184054Slulf * notice, this list of conditions and the following disclaimer. 10184054Slulf * 2. Redistributions in binary form must reproduce the above copyright 11184054Slulf * notice, this list of conditions and the following disclaimer in the 12184054Slulf * documentation and/or other materials provided with the distribution. 13184054Slulf * 14184054Slulf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184054Slulf * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184054Slulf * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184054Slulf * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184054Slulf * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184054Slulf * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184054Slulf * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184054Slulf * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184054Slulf * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184054Slulf * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184054Slulf * SUCH DAMAGE. 25184054Slulf * 26184054Slulf * $FreeBSD$ 27184054Slulf */ 28184054Slulf 29184054Slulf#include <assert.h> 30185134Slulf#include <err.h> 31185134Slulf#include <errno.h> 32184054Slulf#include <stdio.h> 33184054Slulf#include <stdlib.h> 34184054Slulf#include <string.h> 35185134Slulf 36184054Slulf#include "diff.h" 37185134Slulf#include "keyword.h" 38184054Slulf#include "misc.h" 39185134Slulf#include "proto.h" 40185134Slulf#include "queue.h" 41184054Slulf#include "rcsfile.h" 42184054Slulf#include "rcsparse.h" 43184054Slulf#include "stream.h" 44184054Slulf 45185134Slulf#define BUF_SIZE_DEFAULT 128 46185134Slulf 47184054Slulf/* 48184054Slulf * RCS parser library. This is the part of the library that handles the 49184054Slulf * importing, editing and exporting of RCS files. It currently supports only the 50184054Slulf * part of the RCS file specification that is needed for csup (for instance, 51184054Slulf * newphrases are not supported), and assumes that you can store the whole RCS 52184054Slulf * file in memory. 53184054Slulf */ 54184054Slulf 55184054Slulf/* 56184054Slulf * Linked list for string tokens. 57184054Slulf */ 58184054Slulfstruct string { 59184054Slulf char *str; 60184054Slulf STAILQ_ENTRY(string) string_next; 61184054Slulf}; 62184054Slulf 63184054Slulf/* 64184054Slulf * Linked list of tags and revision numbers, in the RCS file header. 65184054Slulf */ 66184054Slulfstruct tag { 67184054Slulf char *tag; 68184054Slulf char *revnum; 69184054Slulf STAILQ_ENTRY(tag) tag_next; 70184054Slulf}; 71184054Slulf 72184054Slulf/* 73184054Slulf * A RCS delta. The delta is identified by a revision number, and contains the 74184054Slulf * most important RCS attributes that is needed by csup. It also contains 75184054Slulf * pointers to other nodes in the RCS file delta structure. 76184054Slulf */ 77184054Slulfstruct delta { 78184054Slulf char *revdate; 79184054Slulf char *revnum; 80184054Slulf char *author; 81184054Slulf char *state; 82184054Slulf struct buf *log; 83184054Slulf struct buf *text; 84184054Slulf int placeholder; 85184054Slulf struct delta *diffbase; 86184054Slulf struct delta *prev; 87184054Slulf 88184054Slulf LIST_ENTRY(delta) delta_next; 89184054Slulf STAILQ_ENTRY(delta) delta_prev; 90184054Slulf LIST_ENTRY(delta) table_next; 91184054Slulf STAILQ_ENTRY(delta) stack_next; 92186727Slulf LIST_HEAD(, branch) branchlist; 93184054Slulf LIST_ENTRY(delta) branch_next_date; 94184054Slulf}; 95184054Slulf 96184054Slulf/* 97184054Slulf * A branch data structure containing information about deltas in the branch as 98184054Slulf * well as a base revision number. 99184054Slulf */ 100184054Slulfstruct branch { 101184054Slulf char *revnum; 102184054Slulf LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ 103186727Slulf LIST_ENTRY(branch) branch_next; 104184054Slulf}; 105184054Slulf 106184054Slulf/* 107184054Slulf * The rcsfile structure is the "main" structure of the RCS parser library. It 108184054Slulf * contains administrative data as well as pointers to the deltas within the 109184054Slulf * file. 110184054Slulf */ 111184054Slulfstruct rcsfile { 112184054Slulf char *name; 113184054Slulf char *head; 114184054Slulf char *branch; /* Default branch. */ 115184054Slulf char *cvsroot; 116184054Slulf char *colltag; 117184054Slulf STAILQ_HEAD(, string) accesslist; 118184054Slulf STAILQ_HEAD(, tag) taglist; 119184054Slulf int strictlock; 120184054Slulf char *comment; 121184054Slulf int expand; 122186700Slulf int ro; 123184054Slulf struct branch *trunk; /* The tip delta. */ 124184054Slulf 125184054Slulf LIST_HEAD(, delta) deltatable; 126184054Slulf 127184054Slulf char *desc; 128184054Slulf}; 129184054Slulf 130184054Slulfstatic void rcsfile_freedelta(struct delta *); 131184054Slulfstatic void rcsfile_insertdelta(struct branch *, struct delta *, 132184054Slulf int); 133184054Slulfstatic struct delta *rcsfile_createdelta(char *); 134184054Slulfstatic int rcsfile_write_deltatext(struct rcsfile *, 135184054Slulf struct stream *); 136184054Slulfstatic int rcsfile_puttext(struct rcsfile *, struct stream *, 137184054Slulf struct delta *, struct delta *); 138184054Slulfstatic struct branch *rcsfile_getbranch(struct rcsfile *, char *); 139184054Slulfstatic void rcsfile_insertsorteddelta(struct rcsfile *, 140184054Slulf struct delta *); 141184054Slulfstatic struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *, 142184054Slulf struct buf **); 143190422Slulfstatic int rcsdelta_writestring(char *, size_t, struct stream *); 144186727Slulfstatic void rcsdelta_insertbranch(struct delta *, struct branch *); 145184054Slulf 146184054Slulf/* Space formatting of RCS file. */ 147184054Slulfconst char *head_space = "\t"; 148184054Slulfconst char *branch_space = "\t"; 149184054Slulfconst char *tag_space = "\t"; 150184054Slulfconst char *date_space = "\t"; 151184054Slulfconst char *auth_space = "\t"; 152184054Slulfconst char *state_space = "\t"; 153184054Slulfconst char *next_space = "\t"; 154184054Slulfconst char *branches_space = "\t"; 155184054Slulfconst char *comment_space ="\t"; 156186700Slulfconst char *expand_space = "\t"; 157184054Slulf 158184054Slulfvoid print_stream(struct stream *); 159184054Slulf 160184054Slulf/* Print the contents of a stream, for debugging. */ 161184054Slulfvoid 162184054Slulfprint_stream(struct stream *s) 163184054Slulf{ 164184054Slulf char *line; 165184054Slulf 166184054Slulf line = stream_getln(s, NULL); 167184054Slulf while (line != NULL) { 168185134Slulf lprintf(-1, "%s\n", line); 169184054Slulf line = stream_getln(s, NULL); 170184054Slulf } 171185134Slulf lprintf(-1, "\n"); 172184054Slulf} 173184054Slulf 174184054Slulf/* 175184054Slulf * Parse rcsfile from path and return a pointer to it. 176184054Slulf */ 177184054Slulfstruct rcsfile * 178216542Slulfrcsfile_frompath(const char *path, const char *name, const char *cvsroot, 179216542Slulf const char *colltag, int ro) 180184054Slulf{ 181185134Slulf struct rcsfile *rf; 182184054Slulf FILE *infp; 183184054Slulf int error; 184184054Slulf 185184054Slulf if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL) 186184054Slulf return (NULL); 187184054Slulf 188184054Slulf rf = xmalloc(sizeof(struct rcsfile)); 189184054Slulf rf->name = xstrdup(name); 190184054Slulf rf->cvsroot = xstrdup(cvsroot); 191184054Slulf rf->colltag = xstrdup(colltag); 192184054Slulf 193184054Slulf /* Initialize head branch. */ 194184054Slulf rf->trunk = xmalloc(sizeof(struct branch)); 195185134Slulf rf->trunk->revnum = xstrdup("1"); 196184054Slulf LIST_INIT(&rf->trunk->deltalist); 197184054Slulf /* Initialize delta list. */ 198184054Slulf LIST_INIT(&rf->deltatable); 199184054Slulf /* Initialize tag list. */ 200184054Slulf STAILQ_INIT(&rf->taglist); 201184054Slulf /* Initialize accesslist. */ 202184054Slulf STAILQ_INIT(&rf->accesslist); 203184054Slulf 204184054Slulf /* Initialize all fields. */ 205184054Slulf rf->head = NULL; 206184054Slulf rf->branch = NULL; 207184054Slulf rf->strictlock = 0; 208184054Slulf rf->comment = NULL; 209185592Slulf rf->expand = EXPAND_DEFAULT; 210184054Slulf rf->desc = NULL; 211186700Slulf rf->ro = ro; 212184054Slulf 213184054Slulf infp = fopen(path, "r"); 214184054Slulf if (infp == NULL) { 215184054Slulf lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno)); 216184054Slulf rcsfile_free(rf); 217184054Slulf return (NULL); 218184054Slulf } 219186700Slulf error = rcsparse_run(rf, infp, ro); 220184054Slulf fclose(infp); 221184054Slulf if (error) { 222184054Slulf lprintf(-1, "Error parsing \"%s\"\n", name); 223184054Slulf rcsfile_free(rf); 224184054Slulf return (NULL); 225184054Slulf } 226184054Slulf return (rf); 227184054Slulf} 228184054Slulf 229184054Slulf/* 230184054Slulf * Write content of rcsfile to server. Assumes we have a complete RCS file 231184054Slulf * loaded. 232184054Slulf */ 233184054Slulfint 234184054Slulfrcsfile_send_details(struct rcsfile *rf, struct stream *wr) 235184054Slulf{ 236184054Slulf struct delta *d; 237184054Slulf struct tag *t; 238185811Slulf const char *keyword; 239184054Slulf int error; 240184054Slulf 241184054Slulf assert(rf != NULL); 242184054Slulf 243184054Slulf error = proto_printf(wr, "V %s\n", rf->name); 244184054Slulf if (error) 245184054Slulf return(error); 246184054Slulf 247184054Slulf /* Write default branch. */ 248184054Slulf if (rf->branch == NULL) 249184054Slulf error = proto_printf(wr, "b\n"); 250184054Slulf else 251184054Slulf error = proto_printf(wr, "B %s\n", rf->branch); 252184054Slulf if (error) 253184054Slulf return(error); 254184054Slulf 255184054Slulf /* Write deltas to server. */ 256184054Slulf error = proto_printf(wr, "D\n"); 257184054Slulf if (error) 258184054Slulf return(error); 259184054Slulf 260184054Slulf LIST_FOREACH(d, &rf->deltatable, table_next) { 261184054Slulf error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate); 262184054Slulf if (error) 263184054Slulf return(error); 264184054Slulf } 265184054Slulf error = proto_printf(wr, ".\n"); 266184054Slulf 267184054Slulf if (error) 268184054Slulf return(error); 269184054Slulf /* Write expand. */ 270185592Slulf if (rf->expand != EXPAND_DEFAULT) { 271185811Slulf keyword = keyword_encode_expand(rf->expand); 272185811Slulf if (keyword != NULL) { 273185811Slulf error = proto_printf(wr, "E %s\n", 274185811Slulf keyword_encode_expand(rf->expand)); 275185811Slulf if (error) 276185811Slulf return(error); 277185811Slulf } 278184054Slulf } 279184054Slulf 280184054Slulf /* Write tags to server. */ 281184054Slulf error = proto_printf(wr, "T\n"); 282184054Slulf if (error) 283184054Slulf return(error); 284184054Slulf STAILQ_FOREACH(t, &rf->taglist, tag_next) { 285184054Slulf error = proto_printf(wr, "%s %s\n", t->tag, t->revnum); 286184054Slulf if (error) 287184054Slulf return(error); 288184054Slulf } 289184054Slulf error = proto_printf(wr, ".\n"); 290184054Slulf if (error) 291184054Slulf return(error); 292184054Slulf error = proto_printf(wr, ".\n"); 293184054Slulf return (error); 294184054Slulf} 295184054Slulf 296184054Slulf/* 297184054Slulf * Write a RCS file to disk represented by the destination stream. Keep track of 298184054Slulf * deltas with a stack and an inverted stack. 299184054Slulf */ 300184054Slulfint 301184054Slulfrcsfile_write(struct rcsfile *rf, struct stream *dest) 302184054Slulf{ 303184054Slulf STAILQ_HEAD(, delta) deltastack; 304184054Slulf STAILQ_HEAD(, delta) deltalist_inverted; 305184054Slulf struct tag *t; 306184054Slulf struct branch *b; 307184054Slulf struct delta *d, *d_tmp, *d_next; 308184054Slulf int error; 309184054Slulf 310184054Slulf /* First write head. */ 311184054Slulf d = LIST_FIRST(&rf->trunk->deltalist); 312190422Slulf if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0) 313190422Slulf return (-1); 314184054Slulf 315184054Slulf /* Write branch, if we have. */ 316190422Slulf if (rf->branch != NULL) { 317190422Slulf if (stream_printf(dest, "branch%s%s;\n", branch_space, 318190422Slulf rf->branch) < 0) 319190422Slulf return (-1); 320190422Slulf } 321184054Slulf 322184054Slulf /* Write access. */ 323190422Slulf if (stream_printf(dest, "access") < 0) 324190422Slulf return (-1); 325184054Slulf#if 0 326184054Slulf if (!STAILQ_EMPTY(&rf->accesslist)) { 327184054Slulf /* 328184054Slulf * XXX: Write out access. This doesn't seem to be necessary for 329184054Slulf * the time being. 330184054Slulf */ 331184054Slulf } 332184054Slulf#endif 333190422Slulf if (stream_printf(dest, ";\n") < 0) 334190422Slulf return (-1); 335184054Slulf 336184054Slulf /* Write out taglist. */ 337190422Slulf if (stream_printf(dest, "symbols") < 0) 338190422Slulf return (-1); 339184054Slulf if (!STAILQ_EMPTY(&rf->taglist)) { 340184054Slulf STAILQ_FOREACH(t, &rf->taglist, tag_next) { 341190422Slulf if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag, 342190422Slulf t->revnum) < 0) 343190422Slulf return (-1); 344184054Slulf } 345184054Slulf } 346184054Slulf 347184054Slulf /* Write out locks and strict. */ 348190422Slulf if (stream_printf(dest, ";\nlocks;") < 0) 349190422Slulf return (-1); 350190422Slulf if (rf->strictlock) { 351190422Slulf if (stream_printf(dest, " strict;") < 0) 352190422Slulf return (-1); 353190422Slulf } 354190422Slulf if (stream_printf(dest, "\n") < 0) 355190422Slulf return (-1); 356184054Slulf 357184054Slulf /* Write out the comment. */ 358190422Slulf if (rf->comment != NULL) { 359190422Slulf if (stream_printf(dest, "comment%s%s;\n", comment_space, 360190422Slulf rf->comment) < 0) 361190422Slulf return (-1); 362190422Slulf } 363190422Slulf if (rf->expand != EXPAND_DEFAULT) { 364190422Slulf if (stream_printf(dest, "expand%s@%s@;\n", expand_space, 365190422Slulf keyword_encode_expand(rf->expand)) < 0) 366190422Slulf return (-1); 367190422Slulf } 368184054Slulf 369190422Slulf if (stream_printf(dest, "\n\n") < 0) 370190422Slulf return (-1); 371184054Slulf 372185134Slulf /* 373184054Slulf * Write out deltas. We use a stack where we push the appropriate deltas 374184054Slulf * that is to be written out during the loop. 375184054Slulf */ 376184054Slulf STAILQ_INIT(&deltastack); 377184054Slulf d = LIST_FIRST(&rf->trunk->deltalist); 378184054Slulf STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 379184054Slulf while (!STAILQ_EMPTY(&deltastack)) { 380184054Slulf d = STAILQ_FIRST(&deltastack); 381184054Slulf STAILQ_REMOVE_HEAD(&deltastack, stack_next); 382184054Slulf /* Do not write out placeholders just to be safe. */ 383184054Slulf if (d->placeholder) 384184054Slulf continue; 385190422Slulf if (stream_printf(dest, "%s\n", d->revnum) < 0) 386190422Slulf return (-1); 387190422Slulf if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate", 388184054Slulf date_space, d->revdate, auth_space, d->author, 389190422Slulf state_space) < 0) 390190422Slulf return (-1); 391190422Slulf if (d->state != NULL) { 392190422Slulf if (stream_printf(dest, " %s", d->state) < 0) 393190422Slulf return (-1); 394190422Slulf } 395190422Slulf if (stream_printf(dest, ";\nbranches") < 0) 396190422Slulf return (-1); 397184054Slulf /* 398184054Slulf * Write out our branches. Add them to a reversed list for use 399184054Slulf * later when we write out the text. 400184054Slulf */ 401184054Slulf STAILQ_INIT(&deltalist_inverted); 402186727Slulf LIST_FOREACH(b, &d->branchlist, branch_next) { 403184054Slulf d_tmp = LIST_FIRST(&b->deltalist); 404184054Slulf STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev); 405184054Slulf STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 406184054Slulf } 407184054Slulf 408184054Slulf /* Push branch heads on stack. */ 409184054Slulf STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { 410190422Slulf if (d_tmp == NULL) { 411190422Slulf lprintf(2, "Empty branch!\n"); 412190422Slulf return (-1); 413190422Slulf } 414190422Slulf if (stream_printf(dest, "\n%s%s", branches_space, 415190422Slulf d_tmp->revnum) < 0) 416190422Slulf return (-1); 417184054Slulf } 418184054Slulf 419190422Slulf if (stream_printf(dest, ";\nnext%s", next_space) < 0) 420190422Slulf return (-1); 421184054Slulf /* Push next delta on stack. */ 422184054Slulf d_next = LIST_NEXT(d, delta_next); 423184054Slulf if (d_next != NULL) { 424190422Slulf if (stream_printf(dest, "%s", d_next->revnum) < 0) 425190422Slulf return (-1); 426184054Slulf STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next); 427184054Slulf } 428190422Slulf if (stream_printf(dest, ";\n\n") < 0) 429190422Slulf return (-1); 430184054Slulf } 431184054Slulf /* Write out desc. */ 432190422Slulf if (stream_printf(dest, "\ndesc\n@@") < 0) 433190422Slulf return (-1); 434184054Slulf d = LIST_FIRST(&rf->trunk->deltalist); 435184054Slulf 436184054Slulf /* Write out deltatexts. */ 437184054Slulf error = rcsfile_write_deltatext(rf, dest); 438190422Slulf if (stream_printf(dest, "\n") < 0) 439190422Slulf return (-1); 440184054Slulf return (error); 441184054Slulf} 442184054Slulf 443184054Slulf/* 444184054Slulf * Write out deltatexts of a delta and it's subbranches recursively. 445184054Slulf */ 446184054Slulfint 447184054Slulfrcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest) 448184054Slulf{ 449184054Slulf STAILQ_HEAD(, delta) deltastack; 450184054Slulf LIST_HEAD(, delta) branchlist_datesorted; 451185134Slulf struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; 452184054Slulf struct stream *in; 453184054Slulf struct branch *b; 454185134Slulf size_t size; 455184054Slulf char *line; 456184054Slulf int error; 457184054Slulf 458184054Slulf error = 0; 459184054Slulf STAILQ_INIT(&deltastack); 460184054Slulf d = LIST_FIRST(&rf->trunk->deltalist); 461184054Slulf d->prev = NULL; 462184054Slulf STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 463184054Slulf while (!STAILQ_EMPTY(&deltastack)) { 464184054Slulf d = STAILQ_FIRST(&deltastack); 465184054Slulf STAILQ_REMOVE_HEAD(&deltastack, stack_next); 466184054Slulf /* Do not write out placeholders just to be safe. */ 467184054Slulf if (d->placeholder) 468184054Slulf return (0); 469190422Slulf if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0) 470190422Slulf return (-1); 471190422Slulf if (stream_printf(dest, "log\n@") < 0) 472190422Slulf return (-1); 473184054Slulf in = stream_open_buf(d->log); 474184054Slulf line = stream_getln(in, &size); 475184054Slulf while (line != NULL) { 476190422Slulf if (stream_write(dest, line, size) == -1) 477190422Slulf return (-1); 478184054Slulf line = stream_getln(in, &size); 479184054Slulf } 480184054Slulf stream_close(in); 481190422Slulf if (stream_printf(dest, "@\ntext\n@") < 0) 482190422Slulf return (-1); 483184054Slulf error = rcsfile_puttext(rf, dest, d, d->prev); 484184054Slulf if (error) 485184054Slulf return (error); 486190422Slulf if (stream_printf(dest, "@") < 0) 487190422Slulf return (-1); 488184054Slulf 489184054Slulf LIST_INIT(&branchlist_datesorted); 490184054Slulf d_next = LIST_NEXT(d, delta_next); 491184054Slulf if (d_next != NULL) { 492184054Slulf d_next->prev = d; 493184054Slulf /* 494184054Slulf * If it's trunk, treat it like the oldest, if not treat 495184054Slulf * it like a child. 496184054Slulf */ 497184054Slulf if (rcsrev_istrunk(d_next->revnum)) 498185134Slulf STAILQ_INSERT_HEAD(&deltastack, d_next, 499185134Slulf stack_next); 500184054Slulf else 501184054Slulf LIST_INSERT_HEAD(&branchlist_datesorted, d_next, 502184054Slulf branch_next_date); 503184054Slulf } 504184054Slulf 505184054Slulf /* 506184054Slulf * First, we need to sort our branches based on their date to 507184054Slulf * take into account some self-hacked RCS files. 508184054Slulf */ 509186727Slulf LIST_FOREACH(b, &d->branchlist, branch_next) { 510184054Slulf d_tmp = LIST_FIRST(&b->deltalist); 511184054Slulf if (LIST_EMPTY(&branchlist_datesorted)) { 512184054Slulf LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp, 513184054Slulf branch_next_date); 514184054Slulf continue; 515184054Slulf } 516184054Slulf 517184054Slulf d_tmp2 = LIST_FIRST(&branchlist_datesorted); 518186724Slulf if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) { 519185134Slulf LIST_INSERT_BEFORE(d_tmp2, d_tmp, 520185134Slulf branch_next_date); 521184054Slulf continue; 522184054Slulf } 523184054Slulf while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date)) 524184054Slulf != NULL) { 525184054Slulf if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate) 526186724Slulf <= 0) 527184054Slulf break; 528184054Slulf d_tmp2 = d_tmp3; 529184054Slulf } 530184054Slulf LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date); 531184054Slulf } 532185134Slulf /* 533184054Slulf * Invert the deltalist of a branch, since we're writing them 534184054Slulf * the opposite way. 535184054Slulf */ 536184054Slulf LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { 537184054Slulf d_tmp->prev = d; 538184054Slulf STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 539184054Slulf } 540184054Slulf } 541184054Slulf return (0); 542184054Slulf} 543184054Slulf 544184054Slulf/* 545184054Slulf * Generates text given a delta and a diffbase. 546184054Slulf */ 547184054Slulfstatic int 548184054Slulfrcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d, 549184054Slulf struct delta *diffbase) 550184054Slulf{ 551184054Slulf struct stream *in, *rd, *orig; 552184054Slulf struct keyword *k; 553184054Slulf struct diffinfo dibuf, *di; 554184054Slulf struct buf *b; 555185134Slulf size_t size; 556184054Slulf char *line; 557184054Slulf int error; 558184054Slulf 559184054Slulf di = &dibuf; 560184054Slulf b = NULL; 561184054Slulf error = 0; 562184054Slulf 563184054Slulf /* Write if the diffbase is the previous */ 564184054Slulf if (d->diffbase == diffbase) { 565184054Slulf 566184054Slulf /* Write out the text. */ 567184054Slulf in = stream_open_buf(d->text); 568184054Slulf line = stream_getln(in, &size); 569184054Slulf while (line != NULL) { 570190422Slulf if (stream_write(dest, line, size) == -1) { 571190422Slulf error = -1; 572190422Slulf goto cleanup; 573190422Slulf } 574184054Slulf line = stream_getln(in, &size); 575184054Slulf } 576184054Slulf stream_close(in); 577184054Slulf /* We need to apply diff to produce text, this is probably HEAD. */ 578184054Slulf } else if (diffbase == NULL) { 579184054Slulf /* Apply diff. */ 580184054Slulf orig = rcsfile_getdeltatext(rf, d, &b); 581184054Slulf if (orig == NULL) { 582184054Slulf error = -1; 583184054Slulf goto cleanup; 584184054Slulf } 585184054Slulf line = stream_getln(orig, &size); 586184054Slulf while (line != NULL) { 587190422Slulf if (stream_write(dest, line, size) == -1) { 588190422Slulf error = -1; 589190422Slulf goto cleanup; 590190422Slulf } 591184054Slulf line = stream_getln(orig, &size); 592184054Slulf } 593184054Slulf stream_close(orig); 594185134Slulf /* 595184054Slulf * A new head was probably added, and now the previous HEAD must be 596184054Slulf * changed to include the diff instead. 597184054Slulf */ 598184054Slulf } else if (diffbase->diffbase == d) { 599184054Slulf /* Get reverse diff. */ 600184054Slulf orig = rcsfile_getdeltatext(rf, d, &b); 601184054Slulf if (orig == NULL) { 602184054Slulf error = -1; 603184054Slulf goto cleanup; 604184054Slulf } 605184054Slulf di->di_rcsfile = rf->name; 606184054Slulf di->di_cvsroot = rf->cvsroot; 607184054Slulf di->di_revnum = d->revnum; 608184054Slulf di->di_revdate = d->revdate; 609184054Slulf di->di_author = d->author; 610184054Slulf di->di_tag = rf->colltag; 611184054Slulf di->di_state = d->state; 612185592Slulf di->di_expand = EXPAND_OLD; 613184054Slulf k = keyword_new(); 614185592Slulf 615184054Slulf rd = stream_open_buf(diffbase->text); 616184054Slulf error = diff_reverse(rd, orig, dest, k, di); 617184054Slulf if (error) { 618185134Slulf lprintf(-1, "Error applying reverse diff: %d\n", error); 619184054Slulf goto cleanup; 620184054Slulf } 621184054Slulf keyword_free(k); 622184054Slulf stream_close(rd); 623184054Slulf stream_close(orig); 624184054Slulf } 625184054Slulfcleanup: 626184054Slulf if (b != NULL) 627184054Slulf buf_free(b); 628184054Slulf return (error); 629184054Slulf} 630184054Slulf 631184054Slulf/* 632184054Slulf * Return a stream with an applied diff of a delta. 633184054Slulf * XXX: extra overhead on the last apply. Could write directly to file, but 634184054Slulf * makes things complicated though. 635184054Slulf */ 636184054Slulfstatic struct stream * 637184054Slulfrcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest) 638184054Slulf{ 639184054Slulf struct diffinfo dibuf, *di; 640184054Slulf struct stream *orig, *dest, *rd; 641184054Slulf struct buf *buf_orig; 642184054Slulf struct keyword *k; 643184054Slulf int error; 644184054Slulf 645184054Slulf buf_orig = NULL; 646184054Slulf error = 0; 647184054Slulf 648185134Slulf /* 649185134Slulf * If diffbase is NULL or we are head (the old head), we have a normal 650185134Slulf * complete deltatext. 651185134Slulf */ 652184054Slulf if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) { 653184054Slulf orig = stream_open_buf(d->text); 654184054Slulf return (orig); 655184054Slulf } 656184054Slulf 657184054Slulf di = &dibuf; 658184054Slulf /* If not, we need to apply our diff to that of our diffbase. */ 659184054Slulf orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig); 660184054Slulf if (orig == NULL) 661184054Slulf return (NULL); 662184054Slulf 663184054Slulf /* 664184054Slulf * Now that we are sure we have a complete deltatext in ret, let's apply 665184054Slulf * our diff to it. 666184054Slulf */ 667185134Slulf *buf_dest = buf_new(BUF_SIZE_DEFAULT); 668184054Slulf dest = stream_open_buf(*buf_dest); 669184054Slulf 670184054Slulf di->di_rcsfile = rf->name; 671184054Slulf di->di_cvsroot = rf->cvsroot; 672184054Slulf di->di_revnum = d->revnum; 673184054Slulf di->di_revdate = d->revdate; 674184054Slulf di->di_author = d->author; 675184054Slulf di->di_tag = rf->colltag; 676184054Slulf di->di_state = d->state; 677185592Slulf di->di_expand = EXPAND_OLD; 678184054Slulf rd = stream_open_buf(d->text); 679184054Slulf k = keyword_new(); 680184054Slulf error = diff_apply(rd, orig, dest, k, di, 0); 681184054Slulf stream_flush(dest); 682184054Slulf stream_close(rd); 683184054Slulf stream_close(orig); 684184054Slulf stream_close(dest); 685184054Slulf keyword_free(k); 686184054Slulf if (buf_orig != NULL) 687184054Slulf buf_free(buf_orig); 688184054Slulf if (error) { 689184054Slulf lprintf(-1, "Error applying diff: %d\n", error); 690184054Slulf return (NULL); 691184054Slulf } 692184054Slulf 693184054Slulf /* Now reopen the stream for the reading. */ 694184054Slulf dest = stream_open_buf(*buf_dest); 695184054Slulf return (dest); 696184054Slulf} 697184054Slulf 698184054Slulf/* Print content of rcsfile. Useful for debugging. */ 699184054Slulfvoid 700184054Slulfrcsfile_print(struct rcsfile *rf) 701184054Slulf{ 702184054Slulf struct delta *d; 703184054Slulf struct tag *t; 704184054Slulf struct string *s; 705185134Slulf struct stream *in; 706184054Slulf char *line; 707184054Slulf 708185134Slulf lprintf(1, "\n"); 709184054Slulf if (rf->name != NULL) 710185134Slulf lprintf(1, "name: '%s'\n", rf->name); 711184054Slulf if (rf->head != NULL) 712185134Slulf lprintf(1, "head: '%s'\n", rf->head); 713184054Slulf if (rf->branch != NULL) 714185134Slulf lprintf(1, "branch: '%s'\n", rf->branch); 715185134Slulf lprintf(1, "Access: "); 716185134Slulf STAILQ_FOREACH(s, &rf->accesslist, string_next) 717185134Slulf lprintf(1, "'%s' ", s->str); 718185134Slulf lprintf(1, "\n"); 719184054Slulf 720184054Slulf /* Print all tags. */ 721184054Slulf STAILQ_FOREACH(t, &rf->taglist, tag_next) { 722185134Slulf lprintf(1, "Tag: "); 723184054Slulf if (t->tag != NULL) 724185134Slulf lprintf(1, "name: %s ", t->tag); 725184054Slulf if (t->revnum != NULL) 726185134Slulf lprintf(1, "rev: %s", t->revnum); 727185134Slulf lprintf(1, "\n"); 728184054Slulf } 729184054Slulf 730184054Slulf if (rf->strictlock) 731185134Slulf lprintf(1, "Strict!\n"); 732184054Slulf if (rf->comment != NULL) 733185134Slulf lprintf(1, "comment: '%s'\n", rf->comment); 734194070Slulf if (rf->expand != EXPAND_DEFAULT) 735185134Slulf lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand)); 736184054Slulf 737184054Slulf /* Print all deltas. */ 738184054Slulf LIST_FOREACH(d, &rf->deltatable, table_next) { 739185134Slulf lprintf(1, "Delta: "); 740184054Slulf if (d->revdate != NULL) 741185134Slulf lprintf(1, "date: %s ", d->revdate); 742184054Slulf if (d->revnum != NULL) 743185134Slulf lprintf(1, "rev: %s", d->revnum); 744184054Slulf if (d->author != NULL) 745185134Slulf lprintf(1, "author: %s", d->author); 746184054Slulf if (d->state != NULL) 747185134Slulf lprintf(1, "state: %s", d->state); 748184054Slulf 749185134Slulf lprintf(1, "Text:\n"); 750184054Slulf in = stream_open_buf(d->text); 751184054Slulf line = stream_getln(in, NULL); 752184054Slulf while (line != NULL) { 753184054Slulf lprintf(1, "TEXT: %s\n", line); 754184054Slulf line = stream_getln(in, NULL); 755184054Slulf } 756184054Slulf stream_close(in); 757185134Slulf lprintf(1, "\n"); 758184054Slulf } 759184054Slulf 760184054Slulf if (rf->desc != NULL) 761185134Slulf lprintf(1, "desc: '%s'\n", rf->desc); 762184054Slulf} 763184054Slulf 764184054Slulf/* Free all memory associated with a struct rcsfile. */ 765184054Slulfvoid 766184054Slulfrcsfile_free(struct rcsfile *rf) 767184054Slulf{ 768184054Slulf struct delta *d; 769184054Slulf struct tag *t; 770184054Slulf struct string *s; 771184054Slulf 772184054Slulf if (rf->name != NULL) 773184054Slulf free(rf->name); 774184054Slulf if (rf->head != NULL) 775184054Slulf free(rf->head); 776184054Slulf if (rf->branch != NULL) 777184054Slulf free(rf->branch); 778184054Slulf if (rf->cvsroot != NULL) 779184054Slulf free(rf->cvsroot); 780184054Slulf if (rf->colltag != NULL) 781184054Slulf free(rf->colltag); 782184054Slulf 783184054Slulf /* Free all access ids. */ 784184054Slulf while (!STAILQ_EMPTY(&rf->accesslist)) { 785184054Slulf s = STAILQ_FIRST(&rf->accesslist); 786184054Slulf STAILQ_REMOVE_HEAD(&rf->accesslist, string_next); 787184054Slulf if (s->str != NULL) 788184054Slulf free(s->str); 789184054Slulf free(s); 790184054Slulf } 791184054Slulf 792184054Slulf /* Free all tags. */ 793184054Slulf while (!STAILQ_EMPTY(&rf->taglist)) { 794184054Slulf t = STAILQ_FIRST(&rf->taglist); 795184054Slulf STAILQ_REMOVE_HEAD(&rf->taglist, tag_next); 796184054Slulf if (t->tag != NULL) 797184054Slulf free(t->tag); 798184054Slulf if (t->revnum != NULL) 799184054Slulf free(t->revnum); 800184054Slulf free(t); 801184054Slulf } 802184054Slulf 803184054Slulf if (rf->comment != NULL) 804184054Slulf free(rf->comment); 805184054Slulf 806184054Slulf /* Free all deltas in global list */ 807184054Slulf while (!LIST_EMPTY(&rf->deltatable)) { 808184054Slulf d = LIST_FIRST(&rf->deltatable); 809186700Slulf if (!rf->ro) 810186700Slulf LIST_REMOVE(d, delta_next); 811184054Slulf LIST_REMOVE(d, table_next); 812184054Slulf rcsfile_freedelta(d); 813184054Slulf } 814184054Slulf 815184054Slulf /* Free global branch. */ 816184054Slulf if (rf->trunk->revnum != NULL) 817184054Slulf free(rf->trunk->revnum); 818184054Slulf free(rf->trunk); 819184054Slulf 820184054Slulf if (rf->desc != NULL) 821184054Slulf free(rf->desc); 822184054Slulf 823184054Slulf free(rf); 824184054Slulf} 825184054Slulf 826184054Slulf/* 827184054Slulf * Free a RCS delta. 828184054Slulf */ 829184054Slulfstatic void 830184054Slulfrcsfile_freedelta(struct delta *d) 831184054Slulf{ 832184054Slulf struct branch *b; 833184054Slulf 834184054Slulf if (d->revdate != NULL) 835184054Slulf free(d->revdate); 836184054Slulf if (d->revnum != NULL) 837184054Slulf free(d->revnum); 838184054Slulf if (d->author != NULL) 839184054Slulf free(d->author); 840184054Slulf if (d->state != NULL) 841184054Slulf free(d->state); 842184054Slulf if (d->log != NULL) 843184054Slulf buf_free(d->log); 844184054Slulf if (d->text != NULL) 845184054Slulf buf_free(d->text); 846184054Slulf 847184054Slulf /* Free all subbranches of a delta. */ 848186727Slulf while (!LIST_EMPTY(&d->branchlist)) { 849186727Slulf b = LIST_FIRST(&d->branchlist); 850186727Slulf LIST_REMOVE(b, branch_next); 851184054Slulf free(b->revnum); 852184054Slulf free(b); 853184054Slulf } 854184054Slulf free(d); 855184054Slulf} 856184054Slulf 857184054Slulf/* 858184054Slulf * Functions for editing RCS deltas. 859184054Slulf */ 860184054Slulf 861184054Slulf/* Add a new entry to the access list. */ 862184054Slulfvoid 863184054Slulfrcsfile_addaccess(struct rcsfile *rf, char *id) 864184054Slulf{ 865184054Slulf struct string *s; 866184054Slulf 867184054Slulf s = xmalloc(sizeof(struct string)); 868184054Slulf s->str = xstrdup(id); 869184054Slulf STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next); 870184054Slulf} 871184054Slulf 872184054Slulf/* Add a tag to a RCS file. */ 873184054Slulfvoid 874184054Slulfrcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum) 875184054Slulf{ 876184054Slulf struct tag *t; 877184054Slulf 878184054Slulf t = xmalloc(sizeof(struct tag)); 879184054Slulf t->tag = xstrdup(tag); 880184054Slulf t->revnum = xstrdup(revnum); 881184054Slulf 882184054Slulf STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next); 883184054Slulf} 884184054Slulf 885184054Slulf/* Import a tag to a RCS file. */ 886184054Slulfvoid 887184054Slulfrcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum) 888184054Slulf{ 889184054Slulf struct tag *t; 890184054Slulf 891184054Slulf t = xmalloc(sizeof(struct tag)); 892184054Slulf t->tag = xstrdup(tag); 893184054Slulf t->revnum = xstrdup(revnum); 894184054Slulf 895184054Slulf STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next); 896184054Slulf} 897184054Slulf 898184054Slulf/* 899184054Slulf * Delete a revision from the global delta list and the branch it is in. Csup 900184054Slulf * will tell us to delete the tags involved. 901184054Slulf */ 902184054Slulfvoid 903184054Slulfrcsfile_deleterev(struct rcsfile *rf, char *revname) 904184054Slulf{ 905184054Slulf struct delta *d; 906184054Slulf 907184054Slulf d = rcsfile_getdelta(rf, revname); 908186700Slulf if (!rf->ro) 909186700Slulf LIST_REMOVE(d, delta_next); 910184054Slulf LIST_REMOVE(d, table_next); 911184054Slulf rcsfile_freedelta(d); 912184054Slulf} 913184054Slulf 914184054Slulf/* Delete a tag from the tag list. */ 915184054Slulfvoid 916184054Slulfrcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum) 917184054Slulf{ 918184054Slulf struct tag *t; 919184054Slulf 920184054Slulf STAILQ_FOREACH(t, &rf->taglist, tag_next) { 921184054Slulf if ((strcmp(tag, t->tag) == 0) && 922184054Slulf (strcmp(revnum, t->revnum) == 0)) { 923184054Slulf STAILQ_REMOVE(&rf->taglist, t, tag, tag_next); 924184054Slulf free(t->tag); 925184054Slulf free(t->revnum); 926184054Slulf free(t); 927184054Slulf return; 928184054Slulf } 929184054Slulf } 930184054Slulf} 931184054Slulf 932186741Slulf/* 933184054Slulf * Searches the global deltalist for a delta. 934184054Slulf */ 935184054Slulfstruct delta * 936184054Slulfrcsfile_getdelta(struct rcsfile *rf, char *revnum) 937184054Slulf{ 938184054Slulf struct delta *d; 939184054Slulf 940184054Slulf LIST_FOREACH(d, &rf->deltatable, table_next) { 941184054Slulf if (strcmp(revnum, d->revnum) == 0) 942184054Slulf return (d); 943184054Slulf } 944184054Slulf return (NULL); 945184054Slulf} 946184054Slulf 947184054Slulf/* Set rcsfile head. */ 948184054Slulfvoid 949184054Slulfrcsfile_setval(struct rcsfile *rf, int field, char *val) 950184054Slulf{ 951186718Slulf size_t len; 952184054Slulf 953184054Slulf switch (field) { 954186741Slulf case RCSFILE_HEAD: 955186741Slulf if (rf->head != NULL) 956186741Slulf free(rf->head); 957186741Slulf rf->head = xstrdup(val); 958184054Slulf break; 959186741Slulf case RCSFILE_BRANCH: 960186741Slulf if (rf->branch != NULL) 961186741Slulf free(rf->branch); 962186741Slulf rf->branch = (val == NULL) ? NULL : xstrdup(val); 963184054Slulf break; 964186741Slulf case RCSFILE_STRICT: 965186741Slulf if (val != NULL) 966186741Slulf rf->strictlock = 1; 967184054Slulf break; 968186741Slulf case RCSFILE_COMMENT: 969186741Slulf if (rf->comment != NULL) 970186741Slulf free(rf->comment); 971186741Slulf rf->comment = xstrdup(val); 972184054Slulf break; 973186741Slulf case RCSFILE_EXPAND: 974186741Slulf len = strlen(val) - 1; 975186741Slulf val++; 976186741Slulf val[len - 1] = '\0'; 977186741Slulf rf->expand = keyword_decode_expand(val); 978184054Slulf break; 979186741Slulf case RCSFILE_DESC: 980186741Slulf if (rf->desc != NULL) 981186741Slulf free(rf->desc); 982186741Slulf rf->desc = xstrdup(val); 983184054Slulf break; 984186741Slulf default: 985186741Slulf lprintf(-1, "Setting invalid RCSfile value.\n"); 986184054Slulf break; 987184054Slulf } 988184054Slulf} 989184054Slulf 990184054Slulf/* Create and initialize a delta. */ 991184054Slulfstatic struct delta * 992184054Slulfrcsfile_createdelta(char *revnum) 993184054Slulf{ 994184054Slulf struct delta *d; 995184054Slulf 996184054Slulf d = xmalloc(sizeof(struct delta)); 997184054Slulf d->revnum = xstrdup(revnum); 998184054Slulf d->revdate = NULL; 999184054Slulf d->state = NULL; 1000184054Slulf d->author = NULL; 1001185134Slulf d->log = buf_new(BUF_SIZE_DEFAULT); 1002185134Slulf d->text = buf_new(BUF_SIZE_DEFAULT); 1003184054Slulf d->diffbase = NULL; 1004184054Slulf 1005186727Slulf LIST_INIT(&d->branchlist); 1006184054Slulf return (d); 1007184054Slulf} 1008184054Slulf 1009184054Slulf/* Add a delta to a imported delta tree. Used by the updater. */ 1010184054Slulfstruct delta * 1011184054Slulfrcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1012184054Slulf char *diffbase) 1013184054Slulf{ 1014184054Slulf struct branch *b; 1015184054Slulf struct delta *d, *d_bp, *d_next; 1016184054Slulf char *brev, *bprev; 1017184054Slulf int trunk; 1018184054Slulf 1019184054Slulf d_next = NULL; 1020184054Slulf d = rcsfile_getdelta(rf, revnum); 1021184054Slulf if (d != NULL) { 1022184054Slulf lprintf(-1, "Delta %s already exists!\n", revnum); 1023184054Slulf return (NULL); 1024184054Slulf } 1025184054Slulf d = rcsfile_createdelta(revnum); 1026184054Slulf d->placeholder = 0; 1027184054Slulf d->revdate = xstrdup(revdate); 1028184054Slulf d->author = xstrdup(author); 1029184054Slulf d->diffbase = rcsfile_getdelta(rf, diffbase); 1030184054Slulf 1031184054Slulf /* If it's trunk, insert it in the head branch list. */ 1032185134Slulf b = rcsrev_istrunk(d->revnum) ? rf->trunk : 1033185134Slulf rcsfile_getbranch(rf, d->revnum); 1034184054Slulf 1035184054Slulf /* 1036184054Slulf * We didn't find a branch, check if we can find a branchpoint and 1037184054Slulf * create a branch there. 1038184054Slulf */ 1039184054Slulf if (b == NULL) { 1040184054Slulf brev = rcsrev_prefix(d->revnum); 1041184054Slulf bprev = rcsrev_prefix(brev); 1042184054Slulf 1043184054Slulf d_bp = rcsfile_getdelta(rf, bprev); 1044184054Slulf free(bprev); 1045184054Slulf if (d_bp == NULL) { 1046184054Slulf lprintf(-1, "No branch point for adding delta %s\n", 1047184054Slulf d->revnum); 1048184054Slulf return (NULL); 1049184054Slulf } 1050184054Slulf 1051184054Slulf /* Create the branch and insert in delta. */ 1052184054Slulf b = xmalloc(sizeof(struct branch)); 1053184054Slulf b->revnum = brev; 1054184054Slulf LIST_INIT(&b->deltalist); 1055186727Slulf rcsdelta_insertbranch(d_bp, b); 1056184054Slulf } 1057184054Slulf 1058184054Slulf /* Insert both into the tree, and into the lookup list. */ 1059184054Slulf trunk = rcsrev_istrunk(d->revnum); 1060184054Slulf rcsfile_insertdelta(b, d, trunk); 1061184054Slulf rcsfile_insertsorteddelta(rf, d); 1062184054Slulf return (d); 1063184054Slulf} 1064184054Slulf 1065184054Slulf/* Adds a delta to a rcsfile struct. Used by the parser. */ 1066184054Slulfvoid 1067184054Slulfrcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1068184054Slulf char *state, char *next) 1069184054Slulf{ 1070184054Slulf struct branch *b; 1071184054Slulf struct delta *d, *d_bp, *d_next; 1072184054Slulf char *brev, *bprev; 1073184054Slulf int trunk; 1074184054Slulf 1075184054Slulf d_next = NULL; 1076184054Slulf d = rcsfile_getdelta(rf, revnum); 1077184054Slulf 1078184054Slulf if (d == NULL) { 1079184054Slulf /* If not, we'll just create a new entry. */ 1080184054Slulf d = rcsfile_createdelta(revnum); 1081184054Slulf d->placeholder = 0; 1082184054Slulf } else { 1083184054Slulf if (d->placeholder == 0) { 1084184054Slulf lprintf(-1, "Trying to import already existing delta\n"); 1085184054Slulf return; 1086184054Slulf } 1087184054Slulf } 1088184054Slulf /* 1089184054Slulf * If already exists, assume that only revnum is filled out, and set the 1090184054Slulf * rest of the fields. This should be an OK assumption given that we can 1091184054Slulf * be sure internally that the structure is sufficiently initialized so 1092184054Slulf * we won't have any unfreed memory. 1093184054Slulf */ 1094184054Slulf d->revdate = xstrdup(revdate); 1095184054Slulf d->author = xstrdup(author); 1096184054Slulf if (state != NULL) 1097184054Slulf d->state = xstrdup(state); 1098184054Slulf 1099184054Slulf /* If we have a next, create a placeholder for it. */ 1100184054Slulf if (next != NULL) { 1101184054Slulf d_next = rcsfile_createdelta(next); 1102184054Slulf d_next->placeholder = 1; 1103184054Slulf /* Diffbase should be the previous. */ 1104184054Slulf d_next->diffbase = d; 1105184054Slulf } 1106184054Slulf 1107186700Slulf /* If we're opening read-only, do minimal work. */ 1108186700Slulf if (rf->ro) { 1109186700Slulf if (!d->placeholder) 1110186700Slulf rcsfile_insertsorteddelta(rf, d); 1111186700Slulf else 1112186700Slulf d->placeholder = 0; 1113186700Slulf if (d_next != NULL) 1114186700Slulf rcsfile_insertsorteddelta(rf, d_next); 1115186700Slulf return; 1116186700Slulf } 1117186700Slulf 1118184054Slulf /* If it's trunk, insert it in the head branch list. */ 1119184054Slulf b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf, 1120184054Slulf d->revnum); 1121184054Slulf 1122184054Slulf /* 1123184054Slulf * We didn't find a branch, check if we can find a branchpoint and 1124184054Slulf * create a branch there. 1125184054Slulf */ 1126184054Slulf if (b == NULL) { 1127184054Slulf brev = rcsrev_prefix(d->revnum); 1128184054Slulf bprev = rcsrev_prefix(brev); 1129184054Slulf 1130184054Slulf d_bp = rcsfile_getdelta(rf, bprev); 1131184054Slulf free(bprev); 1132184054Slulf if (d_bp == NULL) { 1133184054Slulf lprintf(-1, "No branch point for adding delta %s\n", 1134184054Slulf d->revnum); 1135184054Slulf return; 1136184054Slulf } 1137184054Slulf 1138184054Slulf /* Create the branch and insert in delta. */ 1139184054Slulf b = xmalloc(sizeof(struct branch)); 1140184054Slulf b->revnum = brev; 1141184054Slulf LIST_INIT(&b->deltalist); 1142186727Slulf rcsdelta_insertbranch(d_bp, b); 1143184054Slulf } 1144184054Slulf 1145184054Slulf /* Insert if not a placeholder. */ 1146184054Slulf if (!d->placeholder) { 1147184054Slulf /* Insert both into the tree, and into the lookup list. */ 1148184054Slulf if (rcsrev_istrunk(d->revnum)) 1149184054Slulf rcsfile_insertdelta(b, d, 1); 1150184054Slulf else { 1151184054Slulf rcsfile_insertdelta(b, d, 0); 1152186741Slulf /* 1153184054Slulf * On import we need to set the diffbase to our 1154184054Slulf * branchpoint for writing out later. 1155184054Slulf */ 1156184054Slulf if (LIST_FIRST(&b->deltalist) == d) { 1157184054Slulf brev = rcsrev_prefix(d->revnum); 1158184054Slulf bprev = rcsrev_prefix(brev); 1159184054Slulf d_bp = rcsfile_getdelta(rf, bprev); 1160184054Slulf /* This should really not happen. */ 1161184054Slulf assert(d_bp != NULL); 1162184054Slulf d->diffbase = d_bp; 1163184054Slulf free(brev); 1164184054Slulf free(bprev); 1165184054Slulf } 1166184054Slulf } 1167184054Slulf rcsfile_insertsorteddelta(rf, d); 1168184054Slulf } else /* Not a placeholder anymore. */ { 1169184054Slulf d->placeholder = 0; 1170184054Slulf /* Put it into the tree. */ 1171184054Slulf trunk = rcsrev_istrunk(d->revnum); 1172184054Slulf rcsfile_insertdelta(b, d, trunk); 1173184054Slulf } 1174184054Slulf 1175184054Slulf /* If we have a next, insert the placeholder into the lookup list. */ 1176184054Slulf if (d_next != NULL) 1177184054Slulf rcsfile_insertsorteddelta(rf, d_next); 1178184054Slulf} 1179184054Slulf 1180184054Slulf/* 1181184054Slulf * Find the branch of a revision number. 1182184054Slulf */ 1183184054Slulfstatic struct branch * 1184184054Slulfrcsfile_getbranch(struct rcsfile *rf, char *revnum) 1185184054Slulf{ 1186184054Slulf struct branch *b; 1187184054Slulf struct delta *d; 1188185134Slulf char *branchrev, *bprev; 1189184054Slulf 1190184054Slulf branchrev = rcsrev_prefix(revnum); 1191184054Slulf bprev = rcsrev_prefix(branchrev); 1192184054Slulf d = rcsfile_getdelta(rf, bprev); 1193184054Slulf free(bprev); 1194186727Slulf LIST_FOREACH(b, &d->branchlist, branch_next) { 1195184054Slulf if(rcsnum_cmp(b->revnum, branchrev) == 0) { 1196184054Slulf free(branchrev); 1197184054Slulf return (b); 1198184054Slulf } 1199184054Slulf } 1200184054Slulf free(branchrev); 1201184054Slulf return (NULL); 1202184054Slulf} 1203184054Slulf 1204186727Slulf/* Insert a branch into a delta, sorted by branch revision date. */ 1205186727Slulfstatic void 1206186727Slulfrcsdelta_insertbranch(struct delta *d, struct branch *b) 1207186727Slulf{ 1208186727Slulf struct branch *b_iter; 1209186727Slulf 1210186727Slulf /* If it's empty, insert into head. */ 1211186727Slulf if (LIST_EMPTY(&d->branchlist)) { 1212186727Slulf LIST_INSERT_HEAD(&d->branchlist, b, branch_next); 1213186727Slulf return; 1214186727Slulf } 1215186727Slulf 1216186727Slulf /* Just put it in before the revdate that is lower. */ 1217186727Slulf LIST_FOREACH(b_iter, &d->branchlist, branch_next) { 1218186727Slulf if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) { 1219186727Slulf LIST_INSERT_BEFORE(b_iter, b, branch_next); 1220186727Slulf return; 1221186727Slulf } 1222186727Slulf if (LIST_NEXT(b_iter, branch_next) == NULL) 1223186727Slulf break; 1224186727Slulf } 1225186727Slulf /* Insert after last element. */ 1226186727Slulf LIST_INSERT_AFTER(b_iter, b, branch_next); 1227186727Slulf} 1228186727Slulf 1229186700Slulf/* Insert a delta into the correct place in the table of the rcsfile. */ 1230184054Slulfstatic void 1231184054Slulfrcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d) 1232184054Slulf{ 1233184054Slulf struct delta *d2; 1234184054Slulf 1235184054Slulf /* If it's empty, insert into head. */ 1236184054Slulf if (LIST_EMPTY(&rf->deltatable)) { 1237184054Slulf LIST_INSERT_HEAD(&rf->deltatable, d, table_next); 1238184054Slulf return; 1239184054Slulf } 1240184054Slulf 1241184054Slulf /* Just put it in before the revdate that is lower. */ 1242184054Slulf LIST_FOREACH(d2, &rf->deltatable, table_next) { 1243184054Slulf if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) { 1244184054Slulf LIST_INSERT_BEFORE(d2, d, table_next); 1245184054Slulf return; 1246184054Slulf } 1247184054Slulf if (LIST_NEXT(d2, table_next) == NULL) 1248184054Slulf break; 1249184054Slulf } 1250184054Slulf /* Insert after last element. */ 1251184054Slulf LIST_INSERT_AFTER(d2, d, table_next); 1252184054Slulf} 1253184054Slulf 1254184054Slulf/* 1255184054Slulf * Insert a delta into the correct place in branch. A trunk branch will have 1256184054Slulf * different ordering scheme and be sorted by revision number, but a normal 1257228992Suqs * branch will be sorted by date to maintain compatibility with branches that 1258228992Suqs * is "hand-hacked". 1259184054Slulf */ 1260184054Slulfstatic void 1261184054Slulfrcsfile_insertdelta(struct branch *b, struct delta *d, int trunk) 1262184054Slulf{ 1263184054Slulf struct delta *d2; 1264184054Slulf 1265184054Slulf /* If it's empty, insert into head. */ 1266184054Slulf if (LIST_EMPTY(&b->deltalist)) { 1267184054Slulf LIST_INSERT_HEAD(&b->deltalist, d, delta_next); 1268184054Slulf return; 1269184054Slulf } 1270184054Slulf 1271184054Slulf /* 1272184054Slulf * Just put it in before the revnum that is lower. Sort trunk branch by 1273184054Slulf * branchnum but the subbranches after deltadate. 1274184054Slulf */ 1275184054Slulf LIST_FOREACH(d2, &b->deltalist, delta_next) { 1276184054Slulf if (trunk) { 1277184054Slulf if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) { 1278184054Slulf LIST_INSERT_BEFORE(d2, d, delta_next); 1279184054Slulf return; 1280184054Slulf } 1281184054Slulf } else { 1282184054Slulf /* XXX: here we depend on the date being set, but it 1283184054Slulf * should be before this is called anyway. */ 1284186744Slulf if (rcsnum_cmp(d->revnum, d2->revnum) < 0) { 1285184054Slulf LIST_INSERT_BEFORE(d2, d, delta_next); 1286184054Slulf return; 1287184054Slulf } 1288184054Slulf } 1289184054Slulf if (LIST_NEXT(d2, delta_next) == NULL) 1290184054Slulf break; 1291184054Slulf } 1292184054Slulf /* Insert after last element. */ 1293184054Slulf LIST_INSERT_AFTER(d2, d, delta_next); 1294184054Slulf} 1295184054Slulf 1296184054Slulf 1297184054Slulf/* Add logtext to a delta. Assume the delta already exists. */ 1298184054Slulfint 1299185592Slulfrcsdelta_addlog(struct delta *d, char *log, int len) 1300184054Slulf{ 1301184054Slulf struct stream *dest; 1302190422Slulf int nbytes; 1303184054Slulf 1304184054Slulf assert(d != NULL); 1305185592Slulf /* Strip away '@' at beginning and end. */ 1306184054Slulf log++; 1307185592Slulf len--; 1308185592Slulf log[len - 1] = '\0'; 1309184054Slulf dest = stream_open_buf(d->log); 1310190422Slulf nbytes = stream_write(dest, log, len - 1); 1311184054Slulf stream_close(dest); 1312190422Slulf return ((nbytes == -1) ? -1 : 0); 1313184054Slulf} 1314184054Slulf 1315184054Slulf/* Add deltatext to a delta. Assume the delta already exists. */ 1316184054Slulfint 1317185592Slulfrcsdelta_addtext(struct delta *d, char *text, int len) 1318184054Slulf{ 1319184054Slulf struct stream *dest; 1320190422Slulf int nbytes; 1321184054Slulf 1322184054Slulf assert(d != NULL); 1323185592Slulf /* Strip away '@' at beginning and end. */ 1324184054Slulf text++; 1325185592Slulf len--; 1326185592Slulf text[len - 1] = '\0'; 1327184054Slulf 1328184054Slulf dest = stream_open_buf(d->text); 1329190422Slulf nbytes = stream_write(dest, text, len - 1); 1330184054Slulf stream_close(dest); 1331190422Slulf return ((nbytes == -1) ? -1 : 0); 1332184054Slulf} 1333184054Slulf 1334184054Slulf/* Add a deltatext logline to a delta. */ 1335190422Slulfint 1336184054Slulfrcsdelta_appendlog(struct delta *d, char *logline, size_t size) 1337184054Slulf{ 1338184054Slulf struct stream *dest; 1339190422Slulf int error; 1340184054Slulf 1341184054Slulf assert(d != NULL); 1342184054Slulf dest = stream_open_buf(d->log); 1343190422Slulf error = rcsdelta_writestring(logline, size, dest); 1344184054Slulf stream_close(dest); 1345190422Slulf return (error); 1346184054Slulf} 1347184054Slulf 1348184054Slulf/* Add a deltatext textline to a delta. */ 1349190422Slulfint 1350184054Slulfrcsdelta_appendtext(struct delta *d, char *textline, size_t size) 1351184054Slulf{ 1352184054Slulf struct stream *dest; 1353190422Slulf int error; 1354185134Slulf 1355185134Slulf assert(d != NULL); 1356185134Slulf dest = stream_open_buf(d->text); 1357190422Slulf error = rcsdelta_writestring(textline, size, dest); 1358185134Slulf stream_close(dest); 1359190422Slulf return (error); 1360185134Slulf} 1361185134Slulf 1362190422Slulfstatic int 1363185134Slulfrcsdelta_writestring(char *textline, size_t size, struct stream *dest) 1364185134Slulf{ 1365184054Slulf char buf[3]; 1366184054Slulf size_t i; 1367184054Slulf int count; 1368184054Slulf 1369184054Slulf for (i = 0; i < size; i++) { 1370184054Slulf buf[0] = textline[i]; 1371184054Slulf buf[1] = '\0'; 1372184054Slulf count = 1; 1373184054Slulf /* Expand @'s */ 1374184054Slulf if (buf[0] == '@') { 1375184054Slulf buf[1] = '@'; 1376184054Slulf buf[2] = '\0'; 1377184054Slulf count = 2; 1378184054Slulf } 1379190422Slulf if (stream_write(dest, buf, count) == -1) 1380190422Slulf return (-1); 1381184054Slulf } 1382190422Slulf return (0); 1383184054Slulf} 1384184054Slulf 1385184054Slulf/* Set delta state. */ 1386184054Slulfvoid 1387184054Slulfrcsdelta_setstate(struct delta *d, char *state) 1388184054Slulf{ 1389184054Slulf 1390184054Slulf if (d->state != NULL) 1391184054Slulf free(state); 1392184054Slulf if (state != NULL) { 1393184054Slulf d->state = xstrdup(state); 1394184054Slulf return; 1395184054Slulf } 1396184054Slulf d->state = NULL; 1397184054Slulf} 1398184054Slulf 1399184054Slulf/* Truncate the deltalog with a certain offset. */ 1400184054Slulfvoid 1401184054Slulfrcsdelta_truncatelog(struct delta *d, off_t offset) 1402184054Slulf{ 1403184054Slulf 1404184054Slulf stream_truncate_buf(d->log, offset); 1405184054Slulf} 1406184054Slulf 1407184054Slulf/* Truncate the deltatext with a certain offset. */ 1408184054Slulfvoid 1409184054Slulfrcsdelta_truncatetext(struct delta *d, off_t offset) 1410184054Slulf{ 1411184054Slulf 1412184054Slulf stream_truncate_buf(d->text, offset); 1413184054Slulf} 1414