rcsfile.c revision 272461
1120945Snectar/*- 2120945Snectar * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> 3120945Snectar * All rights reserved. 4120945Snectar * 5120945Snectar * Redistribution and use in source and binary forms, with or without 6120945Snectar * modification, are permitted provided that the following conditions 7120945Snectar * are met: 8120945Snectar * 1. Redistributions of source code must retain the above copyright 9120945Snectar * notice, this list of conditions and the following disclaimer. 10120945Snectar * 2. Redistributions in binary form must reproduce the above copyright 11120945Snectar * notice, this list of conditions and the following disclaimer in the 12120945Snectar * documentation and/or other materials provided with the distribution. 13120945Snectar * 14120945Snectar * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15120945Snectar * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16120945Snectar * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17120945Snectar * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18120945Snectar * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19120945Snectar * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20120945Snectar * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21120945Snectar * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22120945Snectar * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23120945Snectar * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24120945Snectar * SUCH DAMAGE. 25120945Snectar * 26120945Snectar * $FreeBSD: releng/10.1/usr.bin/csup/rcsfile.c 228992 2011-12-30 11:02:40Z uqs $ 27120945Snectar */ 28120945Snectar 29120945Snectar#include <assert.h> 30120945Snectar#include <err.h> 31120945Snectar#include <errno.h> 32120945Snectar#include <stdio.h> 33120945Snectar#include <stdlib.h> 34120945Snectar#include <string.h> 35120945Snectar 36120945Snectar#include "diff.h" 37120945Snectar#include "keyword.h" 38120945Snectar#include "misc.h" 39120945Snectar#include "proto.h" 40120945Snectar#include "queue.h" 41120945Snectar#include "rcsfile.h" 42120945Snectar#include "rcsparse.h" 43120945Snectar#include "stream.h" 44120945Snectar 45120945Snectar#define BUF_SIZE_DEFAULT 128 46120945Snectar 47120945Snectar/* 48120945Snectar * RCS parser library. This is the part of the library that handles the 49120945Snectar * importing, editing and exporting of RCS files. It currently supports only the 50120945Snectar * part of the RCS file specification that is needed for csup (for instance, 51120945Snectar * newphrases are not supported), and assumes that you can store the whole RCS 52120945Snectar * file in memory. 53120945Snectar */ 54120945Snectar 55120945Snectar/* 56120945Snectar * Linked list for string tokens. 57120945Snectar */ 58120945Snectarstruct string { 59120945Snectar char *str; 60120945Snectar STAILQ_ENTRY(string) string_next; 61120945Snectar}; 62120945Snectar 63120945Snectar/* 64120945Snectar * Linked list of tags and revision numbers, in the RCS file header. 65120945Snectar */ 66120945Snectarstruct tag { 67120945Snectar char *tag; 68120945Snectar char *revnum; 69120945Snectar STAILQ_ENTRY(tag) tag_next; 70120945Snectar}; 71120945Snectar 72120945Snectar/* 73120945Snectar * A RCS delta. The delta is identified by a revision number, and contains the 74120945Snectar * most important RCS attributes that is needed by csup. It also contains 75120945Snectar * pointers to other nodes in the RCS file delta structure. 76120945Snectar */ 77120945Snectarstruct delta { 78120945Snectar char *revdate; 79120945Snectar char *revnum; 80120945Snectar char *author; 81120945Snectar char *state; 82120945Snectar struct buf *log; 83120945Snectar struct buf *text; 84120945Snectar int placeholder; 85120945Snectar struct delta *diffbase; 86120945Snectar struct delta *prev; 87120945Snectar 88120945Snectar LIST_ENTRY(delta) delta_next; 89120945Snectar STAILQ_ENTRY(delta) delta_prev; 90120945Snectar LIST_ENTRY(delta) table_next; 91120945Snectar STAILQ_ENTRY(delta) stack_next; 92120945Snectar LIST_HEAD(, branch) branchlist; 93120945Snectar LIST_ENTRY(delta) branch_next_date; 94120945Snectar}; 95120945Snectar 96120945Snectar/* 97120945Snectar * A branch data structure containing information about deltas in the branch as 98120945Snectar * well as a base revision number. 99120945Snectar */ 100120945Snectarstruct branch { 101120945Snectar char *revnum; 102120945Snectar LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ 103120945Snectar LIST_ENTRY(branch) branch_next; 104120945Snectar}; 105120945Snectar 106120945Snectar/* 107120945Snectar * The rcsfile structure is the "main" structure of the RCS parser library. It 108120945Snectar * contains administrative data as well as pointers to the deltas within the 109120945Snectar * file. 110120945Snectar */ 111120945Snectarstruct rcsfile { 112120945Snectar char *name; 113120945Snectar char *head; 114120945Snectar char *branch; /* Default branch. */ 115120945Snectar char *cvsroot; 116120945Snectar char *colltag; 117120945Snectar STAILQ_HEAD(, string) accesslist; 118120945Snectar STAILQ_HEAD(, tag) taglist; 119120945Snectar int strictlock; 120120945Snectar char *comment; 121120945Snectar int expand; 122120945Snectar int ro; 123120945Snectar struct branch *trunk; /* The tip delta. */ 124120945Snectar 125120945Snectar LIST_HEAD(, delta) deltatable; 126120945Snectar 127120945Snectar char *desc; 128120945Snectar}; 129120945Snectar 130120945Snectarstatic void rcsfile_freedelta(struct delta *); 131120945Snectarstatic void rcsfile_insertdelta(struct branch *, struct delta *, 132120945Snectar int); 133120945Snectarstatic struct delta *rcsfile_createdelta(char *); 134120945Snectarstatic int rcsfile_write_deltatext(struct rcsfile *, 135120945Snectar struct stream *); 136120945Snectarstatic int rcsfile_puttext(struct rcsfile *, struct stream *, 137120945Snectar struct delta *, struct delta *); 138120945Snectarstatic struct branch *rcsfile_getbranch(struct rcsfile *, char *); 139120945Snectarstatic void rcsfile_insertsorteddelta(struct rcsfile *, 140120945Snectar struct delta *); 141120945Snectarstatic struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *, 142120945Snectar struct buf **); 143120945Snectarstatic int rcsdelta_writestring(char *, size_t, struct stream *); 144120945Snectarstatic void rcsdelta_insertbranch(struct delta *, struct branch *); 145120945Snectar 146120945Snectar/* Space formatting of RCS file. */ 147120945Snectarconst char *head_space = "\t"; 148120945Snectarconst char *branch_space = "\t"; 149120945Snectarconst char *tag_space = "\t"; 150120945Snectarconst char *date_space = "\t"; 151120945Snectarconst char *auth_space = "\t"; 152120945Snectarconst char *state_space = "\t"; 153120945Snectarconst char *next_space = "\t"; 154120945Snectarconst char *branches_space = "\t"; 155120945Snectarconst char *comment_space ="\t"; 156120945Snectarconst char *expand_space = "\t"; 157120945Snectar 158120945Snectarvoid print_stream(struct stream *); 159120945Snectar 160120945Snectar/* Print the contents of a stream, for debugging. */ 161120945Snectarvoid 162120945Snectarprint_stream(struct stream *s) 163120945Snectar{ 164120945Snectar char *line; 165120945Snectar 166120945Snectar line = stream_getln(s, NULL); 167120945Snectar while (line != NULL) { 168120945Snectar lprintf(-1, "%s\n", line); 169120945Snectar line = stream_getln(s, NULL); 170120945Snectar } 171120945Snectar lprintf(-1, "\n"); 172120945Snectar} 173120945Snectar 174120945Snectar/* 175120945Snectar * Parse rcsfile from path and return a pointer to it. 176120945Snectar */ 177120945Snectarstruct rcsfile * 178120945Snectarrcsfile_frompath(const char *path, const char *name, const char *cvsroot, 179120945Snectar const char *colltag, int ro) 180120945Snectar{ 181120945Snectar struct rcsfile *rf; 182120945Snectar FILE *infp; 183120945Snectar int error; 184120945Snectar 185120945Snectar if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL) 186120945Snectar return (NULL); 187120945Snectar 188120945Snectar rf = xmalloc(sizeof(struct rcsfile)); 189120945Snectar rf->name = xstrdup(name); 190120945Snectar rf->cvsroot = xstrdup(cvsroot); 191120945Snectar rf->colltag = xstrdup(colltag); 192120945Snectar 193120945Snectar /* Initialize head branch. */ 194120945Snectar rf->trunk = xmalloc(sizeof(struct branch)); 195120945Snectar rf->trunk->revnum = xstrdup("1"); 196120945Snectar LIST_INIT(&rf->trunk->deltalist); 197120945Snectar /* Initialize delta list. */ 198120945Snectar LIST_INIT(&rf->deltatable); 199120945Snectar /* Initialize tag list. */ 200120945Snectar STAILQ_INIT(&rf->taglist); 201120945Snectar /* Initialize accesslist. */ 202120945Snectar STAILQ_INIT(&rf->accesslist); 203120945Snectar 204120945Snectar /* Initialize all fields. */ 205120945Snectar rf->head = NULL; 206120945Snectar rf->branch = NULL; 207120945Snectar rf->strictlock = 0; 208120945Snectar rf->comment = NULL; 209120945Snectar rf->expand = EXPAND_DEFAULT; 210120945Snectar rf->desc = NULL; 211120945Snectar rf->ro = ro; 212120945Snectar 213120945Snectar infp = fopen(path, "r"); 214120945Snectar if (infp == NULL) { 215120945Snectar lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno)); 216120945Snectar rcsfile_free(rf); 217120945Snectar return (NULL); 218120945Snectar } 219120945Snectar error = rcsparse_run(rf, infp, ro); 220120945Snectar fclose(infp); 221120945Snectar if (error) { 222120945Snectar lprintf(-1, "Error parsing \"%s\"\n", name); 223120945Snectar rcsfile_free(rf); 224120945Snectar return (NULL); 225120945Snectar } 226120945Snectar return (rf); 227120945Snectar} 228120945Snectar 229120945Snectar/* 230120945Snectar * Write content of rcsfile to server. Assumes we have a complete RCS file 231120945Snectar * loaded. 232120945Snectar */ 233120945Snectarint 234120945Snectarrcsfile_send_details(struct rcsfile *rf, struct stream *wr) 235120945Snectar{ 236120945Snectar struct delta *d; 237120945Snectar struct tag *t; 238120945Snectar const char *keyword; 239120945Snectar int error; 240120945Snectar 241120945Snectar assert(rf != NULL); 242120945Snectar 243120945Snectar error = proto_printf(wr, "V %s\n", rf->name); 244120945Snectar if (error) 245120945Snectar return(error); 246120945Snectar 247120945Snectar /* Write default branch. */ 248120945Snectar if (rf->branch == NULL) 249120945Snectar error = proto_printf(wr, "b\n"); 250120945Snectar else 251120945Snectar error = proto_printf(wr, "B %s\n", rf->branch); 252120945Snectar if (error) 253120945Snectar return(error); 254120945Snectar 255120945Snectar /* Write deltas to server. */ 256120945Snectar error = proto_printf(wr, "D\n"); 257120945Snectar if (error) 258120945Snectar return(error); 259120945Snectar 260120945Snectar LIST_FOREACH(d, &rf->deltatable, table_next) { 261120945Snectar error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate); 262120945Snectar if (error) 263120945Snectar return(error); 264120945Snectar } 265120945Snectar error = proto_printf(wr, ".\n"); 266120945Snectar 267120945Snectar if (error) 268120945Snectar return(error); 269120945Snectar /* Write expand. */ 270120945Snectar if (rf->expand != EXPAND_DEFAULT) { 271120945Snectar keyword = keyword_encode_expand(rf->expand); 272120945Snectar if (keyword != NULL) { 273120945Snectar error = proto_printf(wr, "E %s\n", 274120945Snectar keyword_encode_expand(rf->expand)); 275120945Snectar if (error) 276120945Snectar return(error); 277120945Snectar } 278120945Snectar } 279120945Snectar 280120945Snectar /* Write tags to server. */ 281120945Snectar error = proto_printf(wr, "T\n"); 282120945Snectar if (error) 283120945Snectar return(error); 284120945Snectar STAILQ_FOREACH(t, &rf->taglist, tag_next) { 285120945Snectar error = proto_printf(wr, "%s %s\n", t->tag, t->revnum); 286120945Snectar if (error) 287120945Snectar return(error); 288120945Snectar } 289120945Snectar error = proto_printf(wr, ".\n"); 290120945Snectar if (error) 291120945Snectar return(error); 292120945Snectar error = proto_printf(wr, ".\n"); 293120945Snectar return (error); 294120945Snectar} 295120945Snectar 296120945Snectar/* 297120945Snectar * Write a RCS file to disk represented by the destination stream. Keep track of 298120945Snectar * deltas with a stack and an inverted stack. 299120945Snectar */ 300120945Snectarint 301120945Snectarrcsfile_write(struct rcsfile *rf, struct stream *dest) 302120945Snectar{ 303120945Snectar STAILQ_HEAD(, delta) deltastack; 304120945Snectar STAILQ_HEAD(, delta) deltalist_inverted; 305120945Snectar struct tag *t; 306120945Snectar struct branch *b; 307120945Snectar struct delta *d, *d_tmp, *d_next; 308120945Snectar int error; 309120945Snectar 310120945Snectar /* First write head. */ 311120945Snectar d = LIST_FIRST(&rf->trunk->deltalist); 312120945Snectar if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0) 313120945Snectar return (-1); 314120945Snectar 315120945Snectar /* Write branch, if we have. */ 316120945Snectar if (rf->branch != NULL) { 317120945Snectar if (stream_printf(dest, "branch%s%s;\n", branch_space, 318120945Snectar rf->branch) < 0) 319120945Snectar return (-1); 320120945Snectar } 321120945Snectar 322120945Snectar /* Write access. */ 323120945Snectar if (stream_printf(dest, "access") < 0) 324120945Snectar return (-1); 325120945Snectar#if 0 326120945Snectar if (!STAILQ_EMPTY(&rf->accesslist)) { 327120945Snectar /* 328120945Snectar * XXX: Write out access. This doesn't seem to be necessary for 329120945Snectar * the time being. 330120945Snectar */ 331120945Snectar } 332120945Snectar#endif 333120945Snectar if (stream_printf(dest, ";\n") < 0) 334120945Snectar return (-1); 335120945Snectar 336120945Snectar /* Write out taglist. */ 337120945Snectar if (stream_printf(dest, "symbols") < 0) 338120945Snectar return (-1); 339120945Snectar if (!STAILQ_EMPTY(&rf->taglist)) { 340120945Snectar STAILQ_FOREACH(t, &rf->taglist, tag_next) { 341120945Snectar if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag, 342120945Snectar t->revnum) < 0) 343120945Snectar return (-1); 344120945Snectar } 345120945Snectar } 346120945Snectar 347120945Snectar /* Write out locks and strict. */ 348120945Snectar if (stream_printf(dest, ";\nlocks;") < 0) 349120945Snectar return (-1); 350120945Snectar if (rf->strictlock) { 351120945Snectar if (stream_printf(dest, " strict;") < 0) 352120945Snectar return (-1); 353120945Snectar } 354120945Snectar if (stream_printf(dest, "\n") < 0) 355120945Snectar return (-1); 356120945Snectar 357120945Snectar /* Write out the comment. */ 358120945Snectar if (rf->comment != NULL) { 359120945Snectar if (stream_printf(dest, "comment%s%s;\n", comment_space, 360120945Snectar rf->comment) < 0) 361120945Snectar return (-1); 362120945Snectar } 363120945Snectar if (rf->expand != EXPAND_DEFAULT) { 364120945Snectar if (stream_printf(dest, "expand%s@%s@;\n", expand_space, 365120945Snectar keyword_encode_expand(rf->expand)) < 0) 366120945Snectar return (-1); 367120945Snectar } 368120945Snectar 369120945Snectar if (stream_printf(dest, "\n\n") < 0) 370120945Snectar return (-1); 371120945Snectar 372120945Snectar /* 373120945Snectar * Write out deltas. We use a stack where we push the appropriate deltas 374120945Snectar * that is to be written out during the loop. 375120945Snectar */ 376120945Snectar STAILQ_INIT(&deltastack); 377120945Snectar d = LIST_FIRST(&rf->trunk->deltalist); 378120945Snectar STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 379120945Snectar while (!STAILQ_EMPTY(&deltastack)) { 380120945Snectar d = STAILQ_FIRST(&deltastack); 381120945Snectar STAILQ_REMOVE_HEAD(&deltastack, stack_next); 382120945Snectar /* Do not write out placeholders just to be safe. */ 383120945Snectar if (d->placeholder) 384120945Snectar continue; 385120945Snectar if (stream_printf(dest, "%s\n", d->revnum) < 0) 386120945Snectar return (-1); 387120945Snectar if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate", 388120945Snectar date_space, d->revdate, auth_space, d->author, 389120945Snectar state_space) < 0) 390120945Snectar return (-1); 391120945Snectar if (d->state != NULL) { 392120945Snectar if (stream_printf(dest, " %s", d->state) < 0) 393120945Snectar return (-1); 394120945Snectar } 395120945Snectar if (stream_printf(dest, ";\nbranches") < 0) 396120945Snectar return (-1); 397120945Snectar /* 398120945Snectar * Write out our branches. Add them to a reversed list for use 399120945Snectar * later when we write out the text. 400120945Snectar */ 401120945Snectar STAILQ_INIT(&deltalist_inverted); 402120945Snectar LIST_FOREACH(b, &d->branchlist, branch_next) { 403120945Snectar d_tmp = LIST_FIRST(&b->deltalist); 404120945Snectar STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev); 405120945Snectar STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 406120945Snectar } 407120945Snectar 408120945Snectar /* Push branch heads on stack. */ 409120945Snectar STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { 410120945Snectar if (d_tmp == NULL) { 411120945Snectar lprintf(2, "Empty branch!\n"); 412120945Snectar return (-1); 413120945Snectar } 414120945Snectar if (stream_printf(dest, "\n%s%s", branches_space, 415120945Snectar d_tmp->revnum) < 0) 416120945Snectar return (-1); 417120945Snectar } 418120945Snectar 419120945Snectar if (stream_printf(dest, ";\nnext%s", next_space) < 0) 420120945Snectar return (-1); 421120945Snectar /* Push next delta on stack. */ 422120945Snectar d_next = LIST_NEXT(d, delta_next); 423120945Snectar if (d_next != NULL) { 424120945Snectar if (stream_printf(dest, "%s", d_next->revnum) < 0) 425120945Snectar return (-1); 426120945Snectar STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next); 427120945Snectar } 428120945Snectar if (stream_printf(dest, ";\n\n") < 0) 429120945Snectar return (-1); 430120945Snectar } 431120945Snectar /* Write out desc. */ 432120945Snectar if (stream_printf(dest, "\ndesc\n@@") < 0) 433120945Snectar return (-1); 434120945Snectar d = LIST_FIRST(&rf->trunk->deltalist); 435120945Snectar 436120945Snectar /* Write out deltatexts. */ 437120945Snectar error = rcsfile_write_deltatext(rf, dest); 438120945Snectar if (stream_printf(dest, "\n") < 0) 439120945Snectar return (-1); 440120945Snectar return (error); 441120945Snectar} 442120945Snectar 443120945Snectar/* 444120945Snectar * Write out deltatexts of a delta and it's subbranches recursively. 445120945Snectar */ 446120945Snectarint 447120945Snectarrcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest) 448120945Snectar{ 449120945Snectar STAILQ_HEAD(, delta) deltastack; 450120945Snectar LIST_HEAD(, delta) branchlist_datesorted; 451120945Snectar struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; 452120945Snectar struct stream *in; 453120945Snectar struct branch *b; 454120945Snectar size_t size; 455120945Snectar char *line; 456120945Snectar int error; 457120945Snectar 458120945Snectar error = 0; 459120945Snectar STAILQ_INIT(&deltastack); 460120945Snectar d = LIST_FIRST(&rf->trunk->deltalist); 461120945Snectar d->prev = NULL; 462120945Snectar STAILQ_INSERT_HEAD(&deltastack, d, stack_next); 463120945Snectar while (!STAILQ_EMPTY(&deltastack)) { 464120945Snectar d = STAILQ_FIRST(&deltastack); 465120945Snectar STAILQ_REMOVE_HEAD(&deltastack, stack_next); 466120945Snectar /* Do not write out placeholders just to be safe. */ 467120945Snectar if (d->placeholder) 468120945Snectar return (0); 469120945Snectar if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0) 470120945Snectar return (-1); 471120945Snectar if (stream_printf(dest, "log\n@") < 0) 472120945Snectar return (-1); 473120945Snectar in = stream_open_buf(d->log); 474120945Snectar line = stream_getln(in, &size); 475120945Snectar while (line != NULL) { 476120945Snectar if (stream_write(dest, line, size) == -1) 477120945Snectar return (-1); 478120945Snectar line = stream_getln(in, &size); 479120945Snectar } 480120945Snectar stream_close(in); 481120945Snectar if (stream_printf(dest, "@\ntext\n@") < 0) 482120945Snectar return (-1); 483120945Snectar error = rcsfile_puttext(rf, dest, d, d->prev); 484120945Snectar if (error) 485120945Snectar return (error); 486120945Snectar if (stream_printf(dest, "@") < 0) 487120945Snectar return (-1); 488120945Snectar 489120945Snectar LIST_INIT(&branchlist_datesorted); 490120945Snectar d_next = LIST_NEXT(d, delta_next); 491120945Snectar if (d_next != NULL) { 492120945Snectar d_next->prev = d; 493120945Snectar /* 494120945Snectar * If it's trunk, treat it like the oldest, if not treat 495120945Snectar * it like a child. 496120945Snectar */ 497120945Snectar if (rcsrev_istrunk(d_next->revnum)) 498120945Snectar STAILQ_INSERT_HEAD(&deltastack, d_next, 499120945Snectar stack_next); 500120945Snectar else 501120945Snectar LIST_INSERT_HEAD(&branchlist_datesorted, d_next, 502120945Snectar branch_next_date); 503120945Snectar } 504120945Snectar 505120945Snectar /* 506120945Snectar * First, we need to sort our branches based on their date to 507120945Snectar * take into account some self-hacked RCS files. 508120945Snectar */ 509120945Snectar LIST_FOREACH(b, &d->branchlist, branch_next) { 510120945Snectar d_tmp = LIST_FIRST(&b->deltalist); 511120945Snectar if (LIST_EMPTY(&branchlist_datesorted)) { 512120945Snectar LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp, 513120945Snectar branch_next_date); 514120945Snectar continue; 515120945Snectar } 516120945Snectar 517120945Snectar d_tmp2 = LIST_FIRST(&branchlist_datesorted); 518120945Snectar if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) { 519120945Snectar LIST_INSERT_BEFORE(d_tmp2, d_tmp, 520120945Snectar branch_next_date); 521120945Snectar continue; 522120945Snectar } 523120945Snectar while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date)) 524120945Snectar != NULL) { 525120945Snectar if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate) 526120945Snectar <= 0) 527120945Snectar break; 528120945Snectar d_tmp2 = d_tmp3; 529120945Snectar } 530120945Snectar LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date); 531120945Snectar } 532120945Snectar /* 533120945Snectar * Invert the deltalist of a branch, since we're writing them 534120945Snectar * the opposite way. 535120945Snectar */ 536120945Snectar LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { 537120945Snectar d_tmp->prev = d; 538120945Snectar STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); 539120945Snectar } 540120945Snectar } 541120945Snectar return (0); 542120945Snectar} 543120945Snectar 544120945Snectar/* 545120945Snectar * Generates text given a delta and a diffbase. 546120945Snectar */ 547120945Snectarstatic int 548120945Snectarrcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d, 549120945Snectar struct delta *diffbase) 550120945Snectar{ 551120945Snectar struct stream *in, *rd, *orig; 552120945Snectar struct keyword *k; 553120945Snectar struct diffinfo dibuf, *di; 554120945Snectar struct buf *b; 555120945Snectar size_t size; 556120945Snectar char *line; 557120945Snectar int error; 558120945Snectar 559120945Snectar di = &dibuf; 560120945Snectar b = NULL; 561120945Snectar error = 0; 562120945Snectar 563120945Snectar /* Write if the diffbase is the previous */ 564120945Snectar if (d->diffbase == diffbase) { 565120945Snectar 566120945Snectar /* Write out the text. */ 567120945Snectar in = stream_open_buf(d->text); 568120945Snectar line = stream_getln(in, &size); 569120945Snectar while (line != NULL) { 570120945Snectar if (stream_write(dest, line, size) == -1) { 571120945Snectar error = -1; 572120945Snectar goto cleanup; 573120945Snectar } 574120945Snectar line = stream_getln(in, &size); 575120945Snectar } 576120945Snectar stream_close(in); 577120945Snectar /* We need to apply diff to produce text, this is probably HEAD. */ 578120945Snectar } else if (diffbase == NULL) { 579120945Snectar /* Apply diff. */ 580120945Snectar orig = rcsfile_getdeltatext(rf, d, &b); 581120945Snectar if (orig == NULL) { 582120945Snectar error = -1; 583120945Snectar goto cleanup; 584120945Snectar } 585120945Snectar line = stream_getln(orig, &size); 586120945Snectar while (line != NULL) { 587120945Snectar if (stream_write(dest, line, size) == -1) { 588120945Snectar error = -1; 589120945Snectar goto cleanup; 590120945Snectar } 591120945Snectar line = stream_getln(orig, &size); 592120945Snectar } 593120945Snectar stream_close(orig); 594120945Snectar /* 595120945Snectar * A new head was probably added, and now the previous HEAD must be 596120945Snectar * changed to include the diff instead. 597120945Snectar */ 598120945Snectar } else if (diffbase->diffbase == d) { 599120945Snectar /* Get reverse diff. */ 600120945Snectar orig = rcsfile_getdeltatext(rf, d, &b); 601120945Snectar if (orig == NULL) { 602120945Snectar error = -1; 603120945Snectar goto cleanup; 604120945Snectar } 605120945Snectar di->di_rcsfile = rf->name; 606120945Snectar di->di_cvsroot = rf->cvsroot; 607120945Snectar di->di_revnum = d->revnum; 608120945Snectar di->di_revdate = d->revdate; 609120945Snectar di->di_author = d->author; 610120945Snectar di->di_tag = rf->colltag; 611120945Snectar di->di_state = d->state; 612120945Snectar di->di_expand = EXPAND_OLD; 613120945Snectar k = keyword_new(); 614120945Snectar 615120945Snectar rd = stream_open_buf(diffbase->text); 616120945Snectar error = diff_reverse(rd, orig, dest, k, di); 617120945Snectar if (error) { 618120945Snectar lprintf(-1, "Error applying reverse diff: %d\n", error); 619120945Snectar goto cleanup; 620120945Snectar } 621120945Snectar keyword_free(k); 622120945Snectar stream_close(rd); 623120945Snectar stream_close(orig); 624120945Snectar } 625120945Snectarcleanup: 626120945Snectar if (b != NULL) 627120945Snectar buf_free(b); 628120945Snectar return (error); 629120945Snectar} 630120945Snectar 631120945Snectar/* 632120945Snectar * Return a stream with an applied diff of a delta. 633120945Snectar * XXX: extra overhead on the last apply. Could write directly to file, but 634120945Snectar * makes things complicated though. 635120945Snectar */ 636120945Snectarstatic struct stream * 637120945Snectarrcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest) 638120945Snectar{ 639120945Snectar struct diffinfo dibuf, *di; 640120945Snectar struct stream *orig, *dest, *rd; 641120945Snectar struct buf *buf_orig; 642120945Snectar struct keyword *k; 643120945Snectar int error; 644120945Snectar 645120945Snectar buf_orig = NULL; 646120945Snectar error = 0; 647120945Snectar 648120945Snectar /* 649120945Snectar * If diffbase is NULL or we are head (the old head), we have a normal 650120945Snectar * complete deltatext. 651120945Snectar */ 652120945Snectar if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) { 653120945Snectar orig = stream_open_buf(d->text); 654120945Snectar return (orig); 655120945Snectar } 656120945Snectar 657120945Snectar di = &dibuf; 658120945Snectar /* If not, we need to apply our diff to that of our diffbase. */ 659120945Snectar orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig); 660120945Snectar if (orig == NULL) 661120945Snectar return (NULL); 662120945Snectar 663120945Snectar /* 664120945Snectar * Now that we are sure we have a complete deltatext in ret, let's apply 665120945Snectar * our diff to it. 666233294Sstas */ 667120945Snectar *buf_dest = buf_new(BUF_SIZE_DEFAULT); 668120945Snectar dest = stream_open_buf(*buf_dest); 669120945Snectar 670120945Snectar di->di_rcsfile = rf->name; 671120945Snectar di->di_cvsroot = rf->cvsroot; 672120945Snectar di->di_revnum = d->revnum; 673120945Snectar di->di_revdate = d->revdate; 674120945Snectar di->di_author = d->author; 675120945Snectar di->di_tag = rf->colltag; 676120945Snectar di->di_state = d->state; 677120945Snectar di->di_expand = EXPAND_OLD; 678120945Snectar rd = stream_open_buf(d->text); 679120945Snectar k = keyword_new(); 680120945Snectar error = diff_apply(rd, orig, dest, k, di, 0); 681120945Snectar stream_flush(dest); 682120945Snectar stream_close(rd); 683120945Snectar stream_close(orig); 684120945Snectar stream_close(dest); 685120945Snectar keyword_free(k); 686120945Snectar if (buf_orig != NULL) 687120945Snectar buf_free(buf_orig); 688120945Snectar if (error) { 689120945Snectar lprintf(-1, "Error applying diff: %d\n", error); 690120945Snectar return (NULL); 691120945Snectar } 692120945Snectar 693120945Snectar /* Now reopen the stream for the reading. */ 694120945Snectar dest = stream_open_buf(*buf_dest); 695120945Snectar return (dest); 696120945Snectar} 697120945Snectar 698120945Snectar/* Print content of rcsfile. Useful for debugging. */ 699120945Snectarvoid 700120945Snectarrcsfile_print(struct rcsfile *rf) 701120945Snectar{ 702120945Snectar struct delta *d; 703120945Snectar struct tag *t; 704120945Snectar struct string *s; 705120945Snectar struct stream *in; 706120945Snectar char *line; 707120945Snectar 708120945Snectar lprintf(1, "\n"); 709120945Snectar if (rf->name != NULL) 710120945Snectar lprintf(1, "name: '%s'\n", rf->name); 711120945Snectar if (rf->head != NULL) 712120945Snectar lprintf(1, "head: '%s'\n", rf->head); 713120945Snectar if (rf->branch != NULL) 714120945Snectar lprintf(1, "branch: '%s'\n", rf->branch); 715120945Snectar lprintf(1, "Access: "); 716120945Snectar STAILQ_FOREACH(s, &rf->accesslist, string_next) 717120945Snectar lprintf(1, "'%s' ", s->str); 718120945Snectar lprintf(1, "\n"); 719120945Snectar 720120945Snectar /* Print all tags. */ 721120945Snectar STAILQ_FOREACH(t, &rf->taglist, tag_next) { 722120945Snectar lprintf(1, "Tag: "); 723120945Snectar if (t->tag != NULL) 724120945Snectar lprintf(1, "name: %s ", t->tag); 725120945Snectar if (t->revnum != NULL) 726120945Snectar lprintf(1, "rev: %s", t->revnum); 727 lprintf(1, "\n"); 728 } 729 730 if (rf->strictlock) 731 lprintf(1, "Strict!\n"); 732 if (rf->comment != NULL) 733 lprintf(1, "comment: '%s'\n", rf->comment); 734 if (rf->expand != EXPAND_DEFAULT) 735 lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand)); 736 737 /* Print all deltas. */ 738 LIST_FOREACH(d, &rf->deltatable, table_next) { 739 lprintf(1, "Delta: "); 740 if (d->revdate != NULL) 741 lprintf(1, "date: %s ", d->revdate); 742 if (d->revnum != NULL) 743 lprintf(1, "rev: %s", d->revnum); 744 if (d->author != NULL) 745 lprintf(1, "author: %s", d->author); 746 if (d->state != NULL) 747 lprintf(1, "state: %s", d->state); 748 749 lprintf(1, "Text:\n"); 750 in = stream_open_buf(d->text); 751 line = stream_getln(in, NULL); 752 while (line != NULL) { 753 lprintf(1, "TEXT: %s\n", line); 754 line = stream_getln(in, NULL); 755 } 756 stream_close(in); 757 lprintf(1, "\n"); 758 } 759 760 if (rf->desc != NULL) 761 lprintf(1, "desc: '%s'\n", rf->desc); 762} 763 764/* Free all memory associated with a struct rcsfile. */ 765void 766rcsfile_free(struct rcsfile *rf) 767{ 768 struct delta *d; 769 struct tag *t; 770 struct string *s; 771 772 if (rf->name != NULL) 773 free(rf->name); 774 if (rf->head != NULL) 775 free(rf->head); 776 if (rf->branch != NULL) 777 free(rf->branch); 778 if (rf->cvsroot != NULL) 779 free(rf->cvsroot); 780 if (rf->colltag != NULL) 781 free(rf->colltag); 782 783 /* Free all access ids. */ 784 while (!STAILQ_EMPTY(&rf->accesslist)) { 785 s = STAILQ_FIRST(&rf->accesslist); 786 STAILQ_REMOVE_HEAD(&rf->accesslist, string_next); 787 if (s->str != NULL) 788 free(s->str); 789 free(s); 790 } 791 792 /* Free all tags. */ 793 while (!STAILQ_EMPTY(&rf->taglist)) { 794 t = STAILQ_FIRST(&rf->taglist); 795 STAILQ_REMOVE_HEAD(&rf->taglist, tag_next); 796 if (t->tag != NULL) 797 free(t->tag); 798 if (t->revnum != NULL) 799 free(t->revnum); 800 free(t); 801 } 802 803 if (rf->comment != NULL) 804 free(rf->comment); 805 806 /* Free all deltas in global list */ 807 while (!LIST_EMPTY(&rf->deltatable)) { 808 d = LIST_FIRST(&rf->deltatable); 809 if (!rf->ro) 810 LIST_REMOVE(d, delta_next); 811 LIST_REMOVE(d, table_next); 812 rcsfile_freedelta(d); 813 } 814 815 /* Free global branch. */ 816 if (rf->trunk->revnum != NULL) 817 free(rf->trunk->revnum); 818 free(rf->trunk); 819 820 if (rf->desc != NULL) 821 free(rf->desc); 822 823 free(rf); 824} 825 826/* 827 * Free a RCS delta. 828 */ 829static void 830rcsfile_freedelta(struct delta *d) 831{ 832 struct branch *b; 833 834 if (d->revdate != NULL) 835 free(d->revdate); 836 if (d->revnum != NULL) 837 free(d->revnum); 838 if (d->author != NULL) 839 free(d->author); 840 if (d->state != NULL) 841 free(d->state); 842 if (d->log != NULL) 843 buf_free(d->log); 844 if (d->text != NULL) 845 buf_free(d->text); 846 847 /* Free all subbranches of a delta. */ 848 while (!LIST_EMPTY(&d->branchlist)) { 849 b = LIST_FIRST(&d->branchlist); 850 LIST_REMOVE(b, branch_next); 851 free(b->revnum); 852 free(b); 853 } 854 free(d); 855} 856 857/* 858 * Functions for editing RCS deltas. 859 */ 860 861/* Add a new entry to the access list. */ 862void 863rcsfile_addaccess(struct rcsfile *rf, char *id) 864{ 865 struct string *s; 866 867 s = xmalloc(sizeof(struct string)); 868 s->str = xstrdup(id); 869 STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next); 870} 871 872/* Add a tag to a RCS file. */ 873void 874rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum) 875{ 876 struct tag *t; 877 878 t = xmalloc(sizeof(struct tag)); 879 t->tag = xstrdup(tag); 880 t->revnum = xstrdup(revnum); 881 882 STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next); 883} 884 885/* Import a tag to a RCS file. */ 886void 887rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum) 888{ 889 struct tag *t; 890 891 t = xmalloc(sizeof(struct tag)); 892 t->tag = xstrdup(tag); 893 t->revnum = xstrdup(revnum); 894 895 STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next); 896} 897 898/* 899 * Delete a revision from the global delta list and the branch it is in. Csup 900 * will tell us to delete the tags involved. 901 */ 902void 903rcsfile_deleterev(struct rcsfile *rf, char *revname) 904{ 905 struct delta *d; 906 907 d = rcsfile_getdelta(rf, revname); 908 if (!rf->ro) 909 LIST_REMOVE(d, delta_next); 910 LIST_REMOVE(d, table_next); 911 rcsfile_freedelta(d); 912} 913 914/* Delete a tag from the tag list. */ 915void 916rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum) 917{ 918 struct tag *t; 919 920 STAILQ_FOREACH(t, &rf->taglist, tag_next) { 921 if ((strcmp(tag, t->tag) == 0) && 922 (strcmp(revnum, t->revnum) == 0)) { 923 STAILQ_REMOVE(&rf->taglist, t, tag, tag_next); 924 free(t->tag); 925 free(t->revnum); 926 free(t); 927 return; 928 } 929 } 930} 931 932/* 933 * Searches the global deltalist for a delta. 934 */ 935struct delta * 936rcsfile_getdelta(struct rcsfile *rf, char *revnum) 937{ 938 struct delta *d; 939 940 LIST_FOREACH(d, &rf->deltatable, table_next) { 941 if (strcmp(revnum, d->revnum) == 0) 942 return (d); 943 } 944 return (NULL); 945} 946 947/* Set rcsfile head. */ 948void 949rcsfile_setval(struct rcsfile *rf, int field, char *val) 950{ 951 size_t len; 952 953 switch (field) { 954 case RCSFILE_HEAD: 955 if (rf->head != NULL) 956 free(rf->head); 957 rf->head = xstrdup(val); 958 break; 959 case RCSFILE_BRANCH: 960 if (rf->branch != NULL) 961 free(rf->branch); 962 rf->branch = (val == NULL) ? NULL : xstrdup(val); 963 break; 964 case RCSFILE_STRICT: 965 if (val != NULL) 966 rf->strictlock = 1; 967 break; 968 case RCSFILE_COMMENT: 969 if (rf->comment != NULL) 970 free(rf->comment); 971 rf->comment = xstrdup(val); 972 break; 973 case RCSFILE_EXPAND: 974 len = strlen(val) - 1; 975 val++; 976 val[len - 1] = '\0'; 977 rf->expand = keyword_decode_expand(val); 978 break; 979 case RCSFILE_DESC: 980 if (rf->desc != NULL) 981 free(rf->desc); 982 rf->desc = xstrdup(val); 983 break; 984 default: 985 lprintf(-1, "Setting invalid RCSfile value.\n"); 986 break; 987 } 988} 989 990/* Create and initialize a delta. */ 991static struct delta * 992rcsfile_createdelta(char *revnum) 993{ 994 struct delta *d; 995 996 d = xmalloc(sizeof(struct delta)); 997 d->revnum = xstrdup(revnum); 998 d->revdate = NULL; 999 d->state = NULL; 1000 d->author = NULL; 1001 d->log = buf_new(BUF_SIZE_DEFAULT); 1002 d->text = buf_new(BUF_SIZE_DEFAULT); 1003 d->diffbase = NULL; 1004 1005 LIST_INIT(&d->branchlist); 1006 return (d); 1007} 1008 1009/* Add a delta to a imported delta tree. Used by the updater. */ 1010struct delta * 1011rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1012 char *diffbase) 1013{ 1014 struct branch *b; 1015 struct delta *d, *d_bp, *d_next; 1016 char *brev, *bprev; 1017 int trunk; 1018 1019 d_next = NULL; 1020 d = rcsfile_getdelta(rf, revnum); 1021 if (d != NULL) { 1022 lprintf(-1, "Delta %s already exists!\n", revnum); 1023 return (NULL); 1024 } 1025 d = rcsfile_createdelta(revnum); 1026 d->placeholder = 0; 1027 d->revdate = xstrdup(revdate); 1028 d->author = xstrdup(author); 1029 d->diffbase = rcsfile_getdelta(rf, diffbase); 1030 1031 /* If it's trunk, insert it in the head branch list. */ 1032 b = rcsrev_istrunk(d->revnum) ? rf->trunk : 1033 rcsfile_getbranch(rf, d->revnum); 1034 1035 /* 1036 * We didn't find a branch, check if we can find a branchpoint and 1037 * create a branch there. 1038 */ 1039 if (b == NULL) { 1040 brev = rcsrev_prefix(d->revnum); 1041 bprev = rcsrev_prefix(brev); 1042 1043 d_bp = rcsfile_getdelta(rf, bprev); 1044 free(bprev); 1045 if (d_bp == NULL) { 1046 lprintf(-1, "No branch point for adding delta %s\n", 1047 d->revnum); 1048 return (NULL); 1049 } 1050 1051 /* Create the branch and insert in delta. */ 1052 b = xmalloc(sizeof(struct branch)); 1053 b->revnum = brev; 1054 LIST_INIT(&b->deltalist); 1055 rcsdelta_insertbranch(d_bp, b); 1056 } 1057 1058 /* Insert both into the tree, and into the lookup list. */ 1059 trunk = rcsrev_istrunk(d->revnum); 1060 rcsfile_insertdelta(b, d, trunk); 1061 rcsfile_insertsorteddelta(rf, d); 1062 return (d); 1063} 1064 1065/* Adds a delta to a rcsfile struct. Used by the parser. */ 1066void 1067rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, 1068 char *state, char *next) 1069{ 1070 struct branch *b; 1071 struct delta *d, *d_bp, *d_next; 1072 char *brev, *bprev; 1073 int trunk; 1074 1075 d_next = NULL; 1076 d = rcsfile_getdelta(rf, revnum); 1077 1078 if (d == NULL) { 1079 /* If not, we'll just create a new entry. */ 1080 d = rcsfile_createdelta(revnum); 1081 d->placeholder = 0; 1082 } else { 1083 if (d->placeholder == 0) { 1084 lprintf(-1, "Trying to import already existing delta\n"); 1085 return; 1086 } 1087 } 1088 /* 1089 * If already exists, assume that only revnum is filled out, and set the 1090 * rest of the fields. This should be an OK assumption given that we can 1091 * be sure internally that the structure is sufficiently initialized so 1092 * we won't have any unfreed memory. 1093 */ 1094 d->revdate = xstrdup(revdate); 1095 d->author = xstrdup(author); 1096 if (state != NULL) 1097 d->state = xstrdup(state); 1098 1099 /* If we have a next, create a placeholder for it. */ 1100 if (next != NULL) { 1101 d_next = rcsfile_createdelta(next); 1102 d_next->placeholder = 1; 1103 /* Diffbase should be the previous. */ 1104 d_next->diffbase = d; 1105 } 1106 1107 /* If we're opening read-only, do minimal work. */ 1108 if (rf->ro) { 1109 if (!d->placeholder) 1110 rcsfile_insertsorteddelta(rf, d); 1111 else 1112 d->placeholder = 0; 1113 if (d_next != NULL) 1114 rcsfile_insertsorteddelta(rf, d_next); 1115 return; 1116 } 1117 1118 /* If it's trunk, insert it in the head branch list. */ 1119 b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf, 1120 d->revnum); 1121 1122 /* 1123 * We didn't find a branch, check if we can find a branchpoint and 1124 * create a branch there. 1125 */ 1126 if (b == NULL) { 1127 brev = rcsrev_prefix(d->revnum); 1128 bprev = rcsrev_prefix(brev); 1129 1130 d_bp = rcsfile_getdelta(rf, bprev); 1131 free(bprev); 1132 if (d_bp == NULL) { 1133 lprintf(-1, "No branch point for adding delta %s\n", 1134 d->revnum); 1135 return; 1136 } 1137 1138 /* Create the branch and insert in delta. */ 1139 b = xmalloc(sizeof(struct branch)); 1140 b->revnum = brev; 1141 LIST_INIT(&b->deltalist); 1142 rcsdelta_insertbranch(d_bp, b); 1143 } 1144 1145 /* Insert if not a placeholder. */ 1146 if (!d->placeholder) { 1147 /* Insert both into the tree, and into the lookup list. */ 1148 if (rcsrev_istrunk(d->revnum)) 1149 rcsfile_insertdelta(b, d, 1); 1150 else { 1151 rcsfile_insertdelta(b, d, 0); 1152 /* 1153 * On import we need to set the diffbase to our 1154 * branchpoint for writing out later. 1155 */ 1156 if (LIST_FIRST(&b->deltalist) == d) { 1157 brev = rcsrev_prefix(d->revnum); 1158 bprev = rcsrev_prefix(brev); 1159 d_bp = rcsfile_getdelta(rf, bprev); 1160 /* This should really not happen. */ 1161 assert(d_bp != NULL); 1162 d->diffbase = d_bp; 1163 free(brev); 1164 free(bprev); 1165 } 1166 } 1167 rcsfile_insertsorteddelta(rf, d); 1168 } else /* Not a placeholder anymore. */ { 1169 d->placeholder = 0; 1170 /* Put it into the tree. */ 1171 trunk = rcsrev_istrunk(d->revnum); 1172 rcsfile_insertdelta(b, d, trunk); 1173 } 1174 1175 /* If we have a next, insert the placeholder into the lookup list. */ 1176 if (d_next != NULL) 1177 rcsfile_insertsorteddelta(rf, d_next); 1178} 1179 1180/* 1181 * Find the branch of a revision number. 1182 */ 1183static struct branch * 1184rcsfile_getbranch(struct rcsfile *rf, char *revnum) 1185{ 1186 struct branch *b; 1187 struct delta *d; 1188 char *branchrev, *bprev; 1189 1190 branchrev = rcsrev_prefix(revnum); 1191 bprev = rcsrev_prefix(branchrev); 1192 d = rcsfile_getdelta(rf, bprev); 1193 free(bprev); 1194 LIST_FOREACH(b, &d->branchlist, branch_next) { 1195 if(rcsnum_cmp(b->revnum, branchrev) == 0) { 1196 free(branchrev); 1197 return (b); 1198 } 1199 } 1200 free(branchrev); 1201 return (NULL); 1202} 1203 1204/* Insert a branch into a delta, sorted by branch revision date. */ 1205static void 1206rcsdelta_insertbranch(struct delta *d, struct branch *b) 1207{ 1208 struct branch *b_iter; 1209 1210 /* If it's empty, insert into head. */ 1211 if (LIST_EMPTY(&d->branchlist)) { 1212 LIST_INSERT_HEAD(&d->branchlist, b, branch_next); 1213 return; 1214 } 1215 1216 /* Just put it in before the revdate that is lower. */ 1217 LIST_FOREACH(b_iter, &d->branchlist, branch_next) { 1218 if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) { 1219 LIST_INSERT_BEFORE(b_iter, b, branch_next); 1220 return; 1221 } 1222 if (LIST_NEXT(b_iter, branch_next) == NULL) 1223 break; 1224 } 1225 /* Insert after last element. */ 1226 LIST_INSERT_AFTER(b_iter, b, branch_next); 1227} 1228 1229/* Insert a delta into the correct place in the table of the rcsfile. */ 1230static void 1231rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d) 1232{ 1233 struct delta *d2; 1234 1235 /* If it's empty, insert into head. */ 1236 if (LIST_EMPTY(&rf->deltatable)) { 1237 LIST_INSERT_HEAD(&rf->deltatable, d, table_next); 1238 return; 1239 } 1240 1241 /* Just put it in before the revdate that is lower. */ 1242 LIST_FOREACH(d2, &rf->deltatable, table_next) { 1243 if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) { 1244 LIST_INSERT_BEFORE(d2, d, table_next); 1245 return; 1246 } 1247 if (LIST_NEXT(d2, table_next) == NULL) 1248 break; 1249 } 1250 /* Insert after last element. */ 1251 LIST_INSERT_AFTER(d2, d, table_next); 1252} 1253 1254/* 1255 * Insert a delta into the correct place in branch. A trunk branch will have 1256 * different ordering scheme and be sorted by revision number, but a normal 1257 * branch will be sorted by date to maintain compatibility with branches that 1258 * is "hand-hacked". 1259 */ 1260static void 1261rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk) 1262{ 1263 struct delta *d2; 1264 1265 /* If it's empty, insert into head. */ 1266 if (LIST_EMPTY(&b->deltalist)) { 1267 LIST_INSERT_HEAD(&b->deltalist, d, delta_next); 1268 return; 1269 } 1270 1271 /* 1272 * Just put it in before the revnum that is lower. Sort trunk branch by 1273 * branchnum but the subbranches after deltadate. 1274 */ 1275 LIST_FOREACH(d2, &b->deltalist, delta_next) { 1276 if (trunk) { 1277 if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) { 1278 LIST_INSERT_BEFORE(d2, d, delta_next); 1279 return; 1280 } 1281 } else { 1282 /* XXX: here we depend on the date being set, but it 1283 * should be before this is called anyway. */ 1284 if (rcsnum_cmp(d->revnum, d2->revnum) < 0) { 1285 LIST_INSERT_BEFORE(d2, d, delta_next); 1286 return; 1287 } 1288 } 1289 if (LIST_NEXT(d2, delta_next) == NULL) 1290 break; 1291 } 1292 /* Insert after last element. */ 1293 LIST_INSERT_AFTER(d2, d, delta_next); 1294} 1295 1296 1297/* Add logtext to a delta. Assume the delta already exists. */ 1298int 1299rcsdelta_addlog(struct delta *d, char *log, int len) 1300{ 1301 struct stream *dest; 1302 int nbytes; 1303 1304 assert(d != NULL); 1305 /* Strip away '@' at beginning and end. */ 1306 log++; 1307 len--; 1308 log[len - 1] = '\0'; 1309 dest = stream_open_buf(d->log); 1310 nbytes = stream_write(dest, log, len - 1); 1311 stream_close(dest); 1312 return ((nbytes == -1) ? -1 : 0); 1313} 1314 1315/* Add deltatext to a delta. Assume the delta already exists. */ 1316int 1317rcsdelta_addtext(struct delta *d, char *text, int len) 1318{ 1319 struct stream *dest; 1320 int nbytes; 1321 1322 assert(d != NULL); 1323 /* Strip away '@' at beginning and end. */ 1324 text++; 1325 len--; 1326 text[len - 1] = '\0'; 1327 1328 dest = stream_open_buf(d->text); 1329 nbytes = stream_write(dest, text, len - 1); 1330 stream_close(dest); 1331 return ((nbytes == -1) ? -1 : 0); 1332} 1333 1334/* Add a deltatext logline to a delta. */ 1335int 1336rcsdelta_appendlog(struct delta *d, char *logline, size_t size) 1337{ 1338 struct stream *dest; 1339 int error; 1340 1341 assert(d != NULL); 1342 dest = stream_open_buf(d->log); 1343 error = rcsdelta_writestring(logline, size, dest); 1344 stream_close(dest); 1345 return (error); 1346} 1347 1348/* Add a deltatext textline to a delta. */ 1349int 1350rcsdelta_appendtext(struct delta *d, char *textline, size_t size) 1351{ 1352 struct stream *dest; 1353 int error; 1354 1355 assert(d != NULL); 1356 dest = stream_open_buf(d->text); 1357 error = rcsdelta_writestring(textline, size, dest); 1358 stream_close(dest); 1359 return (error); 1360} 1361 1362static int 1363rcsdelta_writestring(char *textline, size_t size, struct stream *dest) 1364{ 1365 char buf[3]; 1366 size_t i; 1367 int count; 1368 1369 for (i = 0; i < size; i++) { 1370 buf[0] = textline[i]; 1371 buf[1] = '\0'; 1372 count = 1; 1373 /* Expand @'s */ 1374 if (buf[0] == '@') { 1375 buf[1] = '@'; 1376 buf[2] = '\0'; 1377 count = 2; 1378 } 1379 if (stream_write(dest, buf, count) == -1) 1380 return (-1); 1381 } 1382 return (0); 1383} 1384 1385/* Set delta state. */ 1386void 1387rcsdelta_setstate(struct delta *d, char *state) 1388{ 1389 1390 if (d->state != NULL) 1391 free(state); 1392 if (state != NULL) { 1393 d->state = xstrdup(state); 1394 return; 1395 } 1396 d->state = NULL; 1397} 1398 1399/* Truncate the deltalog with a certain offset. */ 1400void 1401rcsdelta_truncatelog(struct delta *d, off_t offset) 1402{ 1403 1404 stream_truncate_buf(d->log, offset); 1405} 1406 1407/* Truncate the deltatext with a certain offset. */ 1408void 1409rcsdelta_truncatetext(struct delta *d, off_t offset) 1410{ 1411 1412 stream_truncate_buf(d->text, offset); 1413} 1414