1/* 2 * stream.c: svn_stream operations 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24#include <assert.h> 25#include <stdio.h> 26 27#include <apr.h> 28#include <apr_pools.h> 29#include <apr_strings.h> 30#include <apr_file_io.h> 31#include <apr_errno.h> 32#include <apr_md5.h> 33 34#include <zlib.h> 35 36#include "svn_pools.h" 37#include "svn_io.h" 38#include "svn_error.h" 39#include "svn_string.h" 40#include "svn_utf.h" 41#include "svn_checksum.h" 42#include "svn_path.h" 43#include "svn_private_config.h" 44#include "private/svn_error_private.h" 45#include "private/svn_eol_private.h" 46#include "private/svn_io_private.h" 47#include "private/svn_subr_private.h" 48 49 50struct svn_stream_t { 51 void *baton; 52 svn_read_fn_t read_fn; 53 svn_stream_skip_fn_t skip_fn; 54 svn_write_fn_t write_fn; 55 svn_close_fn_t close_fn; 56 svn_stream_mark_fn_t mark_fn; 57 svn_stream_seek_fn_t seek_fn; 58 svn_stream__is_buffered_fn_t is_buffered_fn; 59 apr_file_t *file; /* Maybe NULL */ 60}; 61 62 63/*** Forward declarations. ***/ 64 65static svn_error_t * 66skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn); 67 68 69/*** Generic streams. ***/ 70 71svn_stream_t * 72svn_stream_create(void *baton, apr_pool_t *pool) 73{ 74 svn_stream_t *stream; 75 76 stream = apr_palloc(pool, sizeof(*stream)); 77 stream->baton = baton; 78 stream->read_fn = NULL; 79 stream->skip_fn = NULL; 80 stream->write_fn = NULL; 81 stream->close_fn = NULL; 82 stream->mark_fn = NULL; 83 stream->seek_fn = NULL; 84 stream->is_buffered_fn = NULL; 85 stream->file = NULL; 86 return stream; 87} 88 89 90void 91svn_stream_set_baton(svn_stream_t *stream, void *baton) 92{ 93 stream->baton = baton; 94} 95 96 97void 98svn_stream_set_read(svn_stream_t *stream, svn_read_fn_t read_fn) 99{ 100 stream->read_fn = read_fn; 101} 102 103void 104svn_stream_set_skip(svn_stream_t *stream, svn_stream_skip_fn_t skip_fn) 105{ 106 stream->skip_fn = skip_fn; 107} 108 109void 110svn_stream_set_write(svn_stream_t *stream, svn_write_fn_t write_fn) 111{ 112 stream->write_fn = write_fn; 113} 114 115void 116svn_stream_set_close(svn_stream_t *stream, svn_close_fn_t close_fn) 117{ 118 stream->close_fn = close_fn; 119} 120 121void 122svn_stream_set_mark(svn_stream_t *stream, svn_stream_mark_fn_t mark_fn) 123{ 124 stream->mark_fn = mark_fn; 125} 126 127void 128svn_stream_set_seek(svn_stream_t *stream, svn_stream_seek_fn_t seek_fn) 129{ 130 stream->seek_fn = seek_fn; 131} 132 133void 134svn_stream__set_is_buffered(svn_stream_t *stream, 135 svn_stream__is_buffered_fn_t is_buffered_fn) 136{ 137 stream->is_buffered_fn = is_buffered_fn; 138} 139 140svn_error_t * 141svn_stream_read(svn_stream_t *stream, char *buffer, apr_size_t *len) 142{ 143 SVN_ERR_ASSERT(stream->read_fn != NULL); 144 return svn_error_trace(stream->read_fn(stream->baton, buffer, len)); 145} 146 147 148svn_error_t * 149svn_stream_skip(svn_stream_t *stream, apr_size_t len) 150{ 151 if (stream->skip_fn == NULL) 152 return svn_error_trace( 153 skip_default_handler(stream->baton, len, stream->read_fn)); 154 155 return svn_error_trace(stream->skip_fn(stream->baton, len)); 156} 157 158 159svn_error_t * 160svn_stream_write(svn_stream_t *stream, const char *data, apr_size_t *len) 161{ 162 SVN_ERR_ASSERT(stream->write_fn != NULL); 163 return svn_error_trace(stream->write_fn(stream->baton, data, len)); 164} 165 166 167svn_error_t * 168svn_stream_reset(svn_stream_t *stream) 169{ 170 return svn_error_trace( 171 svn_stream_seek(stream, NULL)); 172} 173 174svn_boolean_t 175svn_stream_supports_mark(svn_stream_t *stream) 176{ 177 return stream->mark_fn != NULL; 178} 179 180svn_error_t * 181svn_stream_mark(svn_stream_t *stream, svn_stream_mark_t **mark, 182 apr_pool_t *pool) 183{ 184 if (stream->mark_fn == NULL) 185 return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL); 186 187 return svn_error_trace(stream->mark_fn(stream->baton, mark, pool)); 188} 189 190svn_error_t * 191svn_stream_seek(svn_stream_t *stream, const svn_stream_mark_t *mark) 192{ 193 if (stream->seek_fn == NULL) 194 return svn_error_create(SVN_ERR_STREAM_SEEK_NOT_SUPPORTED, NULL, NULL); 195 196 return svn_error_trace(stream->seek_fn(stream->baton, mark)); 197} 198 199svn_boolean_t 200svn_stream__is_buffered(svn_stream_t *stream) 201{ 202 if (stream->is_buffered_fn == NULL) 203 return FALSE; 204 205 return stream->is_buffered_fn(stream->baton); 206} 207 208svn_error_t * 209svn_stream_close(svn_stream_t *stream) 210{ 211 if (stream->close_fn == NULL) 212 return SVN_NO_ERROR; 213 return svn_error_trace(stream->close_fn(stream->baton)); 214} 215 216svn_error_t * 217svn_stream_puts(svn_stream_t *stream, 218 const char *str) 219{ 220 apr_size_t len; 221 len = strlen(str); 222 return svn_error_trace(svn_stream_write(stream, str, &len)); 223} 224 225svn_error_t * 226svn_stream_printf(svn_stream_t *stream, 227 apr_pool_t *pool, 228 const char *fmt, 229 ...) 230{ 231 const char *message; 232 va_list ap; 233 234 va_start(ap, fmt); 235 message = apr_pvsprintf(pool, fmt, ap); 236 va_end(ap); 237 238 return svn_error_trace(svn_stream_puts(stream, message)); 239} 240 241 242svn_error_t * 243svn_stream_printf_from_utf8(svn_stream_t *stream, 244 const char *encoding, 245 apr_pool_t *pool, 246 const char *fmt, 247 ...) 248{ 249 const char *message, *translated; 250 va_list ap; 251 252 va_start(ap, fmt); 253 message = apr_pvsprintf(pool, fmt, ap); 254 va_end(ap); 255 256 SVN_ERR(svn_utf_cstring_from_utf8_ex2(&translated, message, encoding, 257 pool)); 258 259 return svn_error_trace(svn_stream_puts(stream, translated)); 260} 261 262/* Size that 90% of the lines we encounter will be not longer than. 263 used by stream_readline_bytewise() and stream_readline_chunky(). 264 */ 265#define LINE_CHUNK_SIZE 80 266 267/* Guts of svn_stream_readline(). 268 * Returns the line read from STREAM in *STRINGBUF, and indicates 269 * end-of-file in *EOF. If DETECT_EOL is TRUE, the end-of-line indicator 270 * is detected automatically and returned in *EOL. 271 * If DETECT_EOL is FALSE, *EOL must point to the desired end-of-line 272 * indicator. STRINGBUF is allocated in POOL. */ 273static svn_error_t * 274stream_readline_bytewise(svn_stringbuf_t **stringbuf, 275 svn_boolean_t *eof, 276 const char *eol, 277 svn_stream_t *stream, 278 apr_pool_t *pool) 279{ 280 svn_stringbuf_t *str; 281 apr_size_t numbytes; 282 const char *match; 283 char c; 284 285 /* Since we're reading one character at a time, let's at least 286 optimize for the 90% case. 90% of the time, we can avoid the 287 stringbuf ever having to realloc() itself if we start it out at 288 80 chars. */ 289 str = svn_stringbuf_create_ensure(LINE_CHUNK_SIZE, pool); 290 291 /* Read into STR up to and including the next EOL sequence. */ 292 match = eol; 293 while (*match) 294 { 295 numbytes = 1; 296 SVN_ERR(svn_stream_read(stream, &c, &numbytes)); 297 if (numbytes != 1) 298 { 299 /* a 'short' read means the stream has run out. */ 300 *eof = TRUE; 301 *stringbuf = str; 302 return SVN_NO_ERROR; 303 } 304 305 if (c == *match) 306 match++; 307 else 308 match = eol; 309 310 svn_stringbuf_appendbyte(str, c); 311 } 312 313 *eof = FALSE; 314 svn_stringbuf_chop(str, match - eol); 315 *stringbuf = str; 316 317 return SVN_NO_ERROR; 318} 319 320static svn_error_t * 321stream_readline_chunky(svn_stringbuf_t **stringbuf, 322 svn_boolean_t *eof, 323 const char *eol, 324 svn_stream_t *stream, 325 apr_pool_t *pool) 326{ 327 /* Read larger chunks of data at once into this buffer and scan 328 * that for EOL. A good chunk size should be about 80 chars since 329 * most text lines will be shorter. However, don't use a much 330 * larger value because filling the buffer from the stream takes 331 * time as well. 332 */ 333 char buffer[LINE_CHUNK_SIZE+1]; 334 335 /* variables */ 336 svn_stream_mark_t *mark; 337 apr_size_t numbytes; 338 const char *eol_pos; 339 apr_size_t total_parsed = 0; 340 341 /* invariant for this call */ 342 const size_t eol_len = strlen(eol); 343 344 /* Remember the line start so this plus the line length will be 345 * the position to move to at the end of this function. 346 */ 347 SVN_ERR(svn_stream_mark(stream, &mark, pool)); 348 349 /* Read the first chunk. */ 350 numbytes = LINE_CHUNK_SIZE; 351 SVN_ERR(svn_stream_read(stream, buffer, &numbytes)); 352 buffer[numbytes] = '\0'; 353 354 /* Look for the EOL in this first chunk. If we find it, we are done here. 355 */ 356 eol_pos = strstr(buffer, eol); 357 if (eol_pos != NULL) 358 { 359 *stringbuf = svn_stringbuf_ncreate(buffer, eol_pos - buffer, pool); 360 total_parsed = eol_pos - buffer + eol_len; 361 } 362 else if (numbytes < LINE_CHUNK_SIZE) 363 { 364 /* We hit EOF but not EOL. 365 */ 366 *stringbuf = svn_stringbuf_ncreate(buffer, numbytes, pool); 367 *eof = TRUE; 368 return SVN_NO_ERROR; 369 } 370 else 371 { 372 /* A larger buffer for the string is needed. */ 373 svn_stringbuf_t *str; 374 str = svn_stringbuf_create_ensure(2*LINE_CHUNK_SIZE, pool); 375 svn_stringbuf_appendbytes(str, buffer, numbytes); 376 *stringbuf = str; 377 378 /* Loop reading chunks until an EOL was found. If we hit EOF, fall 379 * back to the standard implementation. */ 380 do 381 { 382 /* Append the next chunk to the string read so far. 383 */ 384 svn_stringbuf_ensure(str, str->len + LINE_CHUNK_SIZE); 385 numbytes = LINE_CHUNK_SIZE; 386 SVN_ERR(svn_stream_read(stream, str->data + str->len, &numbytes)); 387 str->len += numbytes; 388 str->data[str->len] = '\0'; 389 390 /* Look for the EOL in the new data plus the last part of the 391 * previous chunk because the EOL may span over the boundary 392 * between both chunks. 393 */ 394 eol_pos = strstr(str->data + str->len - numbytes - (eol_len-1), eol); 395 396 if ((numbytes < LINE_CHUNK_SIZE) && (eol_pos == NULL)) 397 { 398 /* We hit EOF instead of EOL. */ 399 *eof = TRUE; 400 return SVN_NO_ERROR; 401 } 402 } 403 while (eol_pos == NULL); 404 405 /* Number of bytes we actually consumed (i.e. line + EOF). 406 * We need to "return" the rest to the stream by moving its 407 * read pointer. 408 */ 409 total_parsed = eol_pos - str->data + eol_len; 410 411 /* Terminate the string at the EOL postion and return it. */ 412 str->len = eol_pos - str->data; 413 str->data[str->len] = 0; 414 } 415 416 /* Move the stream read pointer to the first position behind the EOL. 417 */ 418 SVN_ERR(svn_stream_seek(stream, mark)); 419 return svn_error_trace(svn_stream_skip(stream, total_parsed)); 420} 421 422/* Guts of svn_stream_readline(). 423 * Returns the line read from STREAM in *STRINGBUF, and indicates 424 * end-of-file in *EOF. EOL must point to the desired end-of-line 425 * indicator. STRINGBUF is allocated in POOL. */ 426static svn_error_t * 427stream_readline(svn_stringbuf_t **stringbuf, 428 svn_boolean_t *eof, 429 const char *eol, 430 svn_stream_t *stream, 431 apr_pool_t *pool) 432{ 433 *eof = FALSE; 434 435 /* Often, we operate on APR file or string-based streams and know what 436 * EOL we are looking for. Optimize that common case. 437 */ 438 if (svn_stream_supports_mark(stream) && 439 svn_stream__is_buffered(stream)) 440 { 441 /* We can efficiently read chunks speculatively and reposition the 442 * stream pointer to the end of the line once we found that. 443 */ 444 SVN_ERR(stream_readline_chunky(stringbuf, 445 eof, 446 eol, 447 stream, 448 pool)); 449 } 450 else 451 { 452 /* Use the standard byte-byte implementation. 453 */ 454 SVN_ERR(stream_readline_bytewise(stringbuf, 455 eof, 456 eol, 457 stream, 458 pool)); 459 } 460 461 return SVN_NO_ERROR; 462} 463 464svn_error_t * 465svn_stream_readline(svn_stream_t *stream, 466 svn_stringbuf_t **stringbuf, 467 const char *eol, 468 svn_boolean_t *eof, 469 apr_pool_t *pool) 470{ 471 return svn_error_trace(stream_readline(stringbuf, eof, eol, stream, 472 pool)); 473} 474 475svn_error_t *svn_stream_copy3(svn_stream_t *from, svn_stream_t *to, 476 svn_cancel_func_t cancel_func, 477 void *cancel_baton, 478 apr_pool_t *scratch_pool) 479{ 480 char *buf = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 481 svn_error_t *err; 482 svn_error_t *err2; 483 484 /* Read and write chunks until we get a short read, indicating the 485 end of the stream. (We can't get a short write without an 486 associated error.) */ 487 while (1) 488 { 489 apr_size_t len = SVN__STREAM_CHUNK_SIZE; 490 491 if (cancel_func) 492 { 493 err = cancel_func(cancel_baton); 494 if (err) 495 break; 496 } 497 498 err = svn_stream_read(from, buf, &len); 499 if (err) 500 break; 501 502 if (len > 0) 503 err = svn_stream_write(to, buf, &len); 504 505 if (err || (len != SVN__STREAM_CHUNK_SIZE)) 506 break; 507 } 508 509 err2 = svn_error_compose_create(svn_stream_close(from), 510 svn_stream_close(to)); 511 512 return svn_error_compose_create(err, err2); 513} 514 515svn_error_t * 516svn_stream_contents_same2(svn_boolean_t *same, 517 svn_stream_t *stream1, 518 svn_stream_t *stream2, 519 apr_pool_t *pool) 520{ 521 char *buf1 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 522 char *buf2 = apr_palloc(pool, SVN__STREAM_CHUNK_SIZE); 523 apr_size_t bytes_read1 = SVN__STREAM_CHUNK_SIZE; 524 apr_size_t bytes_read2 = SVN__STREAM_CHUNK_SIZE; 525 svn_error_t *err = NULL; 526 527 *same = TRUE; /* assume TRUE, until disproved below */ 528 while (bytes_read1 == SVN__STREAM_CHUNK_SIZE 529 && bytes_read2 == SVN__STREAM_CHUNK_SIZE) 530 { 531 err = svn_stream_read(stream1, buf1, &bytes_read1); 532 if (err) 533 break; 534 err = svn_stream_read(stream2, buf2, &bytes_read2); 535 if (err) 536 break; 537 538 if ((bytes_read1 != bytes_read2) 539 || (memcmp(buf1, buf2, bytes_read1))) 540 { 541 *same = FALSE; 542 break; 543 } 544 } 545 546 return svn_error_compose_create(err, 547 svn_error_compose_create( 548 svn_stream_close(stream1), 549 svn_stream_close(stream2))); 550} 551 552 553/*** Stream implementation utilities ***/ 554 555/* Skip data from any stream by reading and simply discarding it. */ 556static svn_error_t * 557skip_default_handler(void *baton, apr_size_t len, svn_read_fn_t read_fn) 558{ 559 apr_size_t bytes_read = 1; 560 char buffer[4096]; 561 apr_size_t to_read = len; 562 563 while ((to_read > 0) && (bytes_read > 0)) 564 { 565 bytes_read = sizeof(buffer) < to_read ? sizeof(buffer) : to_read; 566 SVN_ERR(read_fn(baton, buffer, &bytes_read)); 567 to_read -= bytes_read; 568 } 569 570 return SVN_NO_ERROR; 571} 572 573 574 575/*** Generic readable empty stream ***/ 576 577static svn_error_t * 578read_handler_empty(void *baton, char *buffer, apr_size_t *len) 579{ 580 *len = 0; 581 return SVN_NO_ERROR; 582} 583 584static svn_error_t * 585write_handler_empty(void *baton, const char *data, apr_size_t *len) 586{ 587 return SVN_NO_ERROR; 588} 589 590static svn_error_t * 591mark_handler_empty(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool) 592{ 593 *mark = NULL; /* Seek to start of stream marker */ 594 return SVN_NO_ERROR; 595} 596 597static svn_error_t * 598seek_handler_empty(void *baton, const svn_stream_mark_t *mark) 599{ 600 return SVN_NO_ERROR; 601} 602 603static svn_boolean_t 604is_buffered_handler_empty(void *baton) 605{ 606 return FALSE; 607} 608 609 610svn_stream_t * 611svn_stream_empty(apr_pool_t *pool) 612{ 613 svn_stream_t *stream; 614 615 stream = svn_stream_create(NULL, pool); 616 svn_stream_set_read(stream, read_handler_empty); 617 svn_stream_set_write(stream, write_handler_empty); 618 svn_stream_set_mark(stream, mark_handler_empty); 619 svn_stream_set_seek(stream, seek_handler_empty); 620 svn_stream__set_is_buffered(stream, is_buffered_handler_empty); 621 return stream; 622} 623 624 625 626/*** Stream duplication support ***/ 627struct baton_tee { 628 svn_stream_t *out1; 629 svn_stream_t *out2; 630}; 631 632 633static svn_error_t * 634write_handler_tee(void *baton, const char *data, apr_size_t *len) 635{ 636 struct baton_tee *bt = baton; 637 638 SVN_ERR(svn_stream_write(bt->out1, data, len)); 639 SVN_ERR(svn_stream_write(bt->out2, data, len)); 640 641 return SVN_NO_ERROR; 642} 643 644 645static svn_error_t * 646close_handler_tee(void *baton) 647{ 648 struct baton_tee *bt = baton; 649 650 SVN_ERR(svn_stream_close(bt->out1)); 651 SVN_ERR(svn_stream_close(bt->out2)); 652 653 return SVN_NO_ERROR; 654} 655 656 657svn_stream_t * 658svn_stream_tee(svn_stream_t *out1, 659 svn_stream_t *out2, 660 apr_pool_t *pool) 661{ 662 struct baton_tee *baton; 663 svn_stream_t *stream; 664 665 if (out1 == NULL) 666 return out2; 667 668 if (out2 == NULL) 669 return out1; 670 671 baton = apr_palloc(pool, sizeof(*baton)); 672 baton->out1 = out1; 673 baton->out2 = out2; 674 stream = svn_stream_create(baton, pool); 675 svn_stream_set_write(stream, write_handler_tee); 676 svn_stream_set_close(stream, close_handler_tee); 677 678 return stream; 679} 680 681 682 683/*** Ownership detaching stream ***/ 684 685static svn_error_t * 686read_handler_disown(void *baton, char *buffer, apr_size_t *len) 687{ 688 return svn_error_trace(svn_stream_read(baton, buffer, len)); 689} 690 691static svn_error_t * 692skip_handler_disown(void *baton, apr_size_t len) 693{ 694 return svn_error_trace(svn_stream_skip(baton, len)); 695} 696 697static svn_error_t * 698write_handler_disown(void *baton, const char *buffer, apr_size_t *len) 699{ 700 return svn_error_trace(svn_stream_write(baton, buffer, len)); 701} 702 703static svn_error_t * 704mark_handler_disown(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool) 705{ 706 return svn_error_trace(svn_stream_mark(baton, mark, pool)); 707} 708 709static svn_error_t * 710seek_handler_disown(void *baton, const svn_stream_mark_t *mark) 711{ 712 return svn_error_trace(svn_stream_seek(baton, mark)); 713} 714 715static svn_boolean_t 716is_buffered_handler_disown(void *baton) 717{ 718 return svn_stream__is_buffered(baton); 719} 720 721svn_stream_t * 722svn_stream_disown(svn_stream_t *stream, apr_pool_t *pool) 723{ 724 svn_stream_t *s = svn_stream_create(stream, pool); 725 726 svn_stream_set_read(s, read_handler_disown); 727 svn_stream_set_skip(s, skip_handler_disown); 728 svn_stream_set_write(s, write_handler_disown); 729 svn_stream_set_mark(s, mark_handler_disown); 730 svn_stream_set_seek(s, seek_handler_disown); 731 svn_stream__set_is_buffered(s, is_buffered_handler_disown); 732 733 return s; 734} 735 736 737 738/*** Generic stream for APR files ***/ 739struct baton_apr { 740 apr_file_t *file; 741 apr_pool_t *pool; 742}; 743 744/* svn_stream_mark_t for streams backed by APR files. */ 745struct mark_apr { 746 apr_off_t off; 747}; 748 749static svn_error_t * 750read_handler_apr(void *baton, char *buffer, apr_size_t *len) 751{ 752 struct baton_apr *btn = baton; 753 svn_error_t *err; 754 svn_boolean_t eof; 755 756 if (*len == 1) 757 { 758 err = svn_io_file_getc(buffer, btn->file, btn->pool); 759 if (err) 760 { 761 *len = 0; 762 if (APR_STATUS_IS_EOF(err->apr_err)) 763 { 764 svn_error_clear(err); 765 err = SVN_NO_ERROR; 766 } 767 } 768 } 769 else 770 err = svn_io_file_read_full2(btn->file, buffer, *len, len, 771 &eof, btn->pool); 772 773 return svn_error_trace(err); 774} 775 776static svn_error_t * 777skip_handler_apr(void *baton, apr_size_t len) 778{ 779 struct baton_apr *btn = baton; 780 apr_off_t offset = len; 781 782 return svn_error_trace( 783 svn_io_file_seek(btn->file, APR_CUR, &offset, btn->pool)); 784} 785 786static svn_error_t * 787write_handler_apr(void *baton, const char *data, apr_size_t *len) 788{ 789 struct baton_apr *btn = baton; 790 svn_error_t *err; 791 792 if (*len == 1) 793 { 794 err = svn_io_file_putc(*data, btn->file, btn->pool); 795 if (err) 796 *len = 0; 797 } 798 else 799 err = svn_io_file_write_full(btn->file, data, *len, len, btn->pool); 800 801 return svn_error_trace(err); 802} 803 804static svn_error_t * 805close_handler_apr(void *baton) 806{ 807 struct baton_apr *btn = baton; 808 809 return svn_error_trace(svn_io_file_close(btn->file, btn->pool)); 810} 811 812static svn_error_t * 813mark_handler_apr(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool) 814{ 815 struct baton_apr *btn = baton; 816 struct mark_apr *mark_apr; 817 818 mark_apr = apr_palloc(pool, sizeof(*mark_apr)); 819 mark_apr->off = 0; 820 SVN_ERR(svn_io_file_seek(btn->file, APR_CUR, &mark_apr->off, btn->pool)); 821 *mark = (svn_stream_mark_t *)mark_apr; 822 return SVN_NO_ERROR; 823} 824 825static svn_error_t * 826seek_handler_apr(void *baton, const svn_stream_mark_t *mark) 827{ 828 struct baton_apr *btn = baton; 829 apr_off_t offset = (mark != NULL) ? ((const struct mark_apr *)mark)->off : 0; 830 831 SVN_ERR(svn_io_file_seek(btn->file, APR_SET, &offset, btn->pool)); 832 833 return SVN_NO_ERROR; 834} 835 836static svn_boolean_t 837is_buffered_handler_apr(void *baton) 838{ 839 struct baton_apr *btn = baton; 840 return (apr_file_flags_get(btn->file) & APR_BUFFERED) != 0; 841} 842 843svn_error_t * 844svn_stream_open_readonly(svn_stream_t **stream, 845 const char *path, 846 apr_pool_t *result_pool, 847 apr_pool_t *scratch_pool) 848{ 849 apr_file_t *file; 850 851 SVN_ERR(svn_io_file_open(&file, path, APR_READ | APR_BUFFERED, 852 APR_OS_DEFAULT, result_pool)); 853 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); 854 855 return SVN_NO_ERROR; 856} 857 858 859svn_error_t * 860svn_stream_open_writable(svn_stream_t **stream, 861 const char *path, 862 apr_pool_t *result_pool, 863 apr_pool_t *scratch_pool) 864{ 865 apr_file_t *file; 866 867 SVN_ERR(svn_io_file_open(&file, path, 868 APR_WRITE 869 | APR_BUFFERED 870 | APR_CREATE 871 | APR_EXCL, 872 APR_OS_DEFAULT, result_pool)); 873 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); 874 875 return SVN_NO_ERROR; 876} 877 878 879svn_error_t * 880svn_stream_open_unique(svn_stream_t **stream, 881 const char **temp_path, 882 const char *dirpath, 883 svn_io_file_del_t delete_when, 884 apr_pool_t *result_pool, 885 apr_pool_t *scratch_pool) 886{ 887 apr_file_t *file; 888 889 SVN_ERR(svn_io_open_unique_file3(&file, temp_path, dirpath, 890 delete_when, result_pool, scratch_pool)); 891 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); 892 893 return SVN_NO_ERROR; 894} 895 896 897svn_stream_t * 898svn_stream_from_aprfile2(apr_file_t *file, 899 svn_boolean_t disown, 900 apr_pool_t *pool) 901{ 902 struct baton_apr *baton; 903 svn_stream_t *stream; 904 905 if (file == NULL) 906 return svn_stream_empty(pool); 907 908 baton = apr_palloc(pool, sizeof(*baton)); 909 baton->file = file; 910 baton->pool = pool; 911 stream = svn_stream_create(baton, pool); 912 svn_stream_set_read(stream, read_handler_apr); 913 svn_stream_set_write(stream, write_handler_apr); 914 svn_stream_set_skip(stream, skip_handler_apr); 915 svn_stream_set_mark(stream, mark_handler_apr); 916 svn_stream_set_seek(stream, seek_handler_apr); 917 svn_stream__set_is_buffered(stream, is_buffered_handler_apr); 918 stream->file = file; 919 920 if (! disown) 921 svn_stream_set_close(stream, close_handler_apr); 922 923 return stream; 924} 925 926apr_file_t * 927svn_stream__aprfile(svn_stream_t *stream) 928{ 929 return stream->file; 930} 931 932 933/* Compressed stream support */ 934 935#define ZBUFFER_SIZE 4096 /* The size of the buffer the 936 compressed stream uses to read from 937 the substream. Basically an 938 arbitrary value, picked to be about 939 page-sized. */ 940 941struct zbaton { 942 z_stream *in; /* compressed stream for reading */ 943 z_stream *out; /* compressed stream for writing */ 944 svn_read_fn_t read; /* substream's read function */ 945 svn_write_fn_t write; /* substream's write function */ 946 svn_close_fn_t close; /* substream's close function */ 947 void *read_buffer; /* buffer used for reading from 948 substream */ 949 int read_flush; /* what flush mode to use while 950 reading */ 951 apr_pool_t *pool; /* The pool this baton is allocated 952 on */ 953 void *subbaton; /* The substream's baton */ 954}; 955 956/* zlib alloc function. opaque is the pool we need. */ 957static voidpf 958zalloc(voidpf opaque, uInt items, uInt size) 959{ 960 apr_pool_t *pool = opaque; 961 962 return apr_palloc(pool, items * size); 963} 964 965/* zlib free function */ 966static void 967zfree(voidpf opaque, voidpf address) 968{ 969 /* Empty, since we allocate on the pool */ 970} 971 972/* Helper function to figure out the sync mode */ 973static svn_error_t * 974read_helper_gz(svn_read_fn_t read_fn, 975 void *baton, 976 char *buffer, 977 uInt *len, int *zflush) 978{ 979 uInt orig_len = *len; 980 981 /* There's no reason this value should grow bigger than the range of 982 uInt, but Subversion's API requires apr_size_t. */ 983 apr_size_t apr_len = (apr_size_t) *len; 984 985 SVN_ERR((*read_fn)(baton, buffer, &apr_len)); 986 987 /* Type cast back to uInt type that zlib uses. On LP64 platforms 988 apr_size_t will be bigger than uInt. */ 989 *len = (uInt) apr_len; 990 991 /* I wanted to use Z_FINISH here, but we need to know our buffer is 992 big enough */ 993 *zflush = (*len) < orig_len ? Z_SYNC_FLUSH : Z_SYNC_FLUSH; 994 995 return SVN_NO_ERROR; 996} 997 998/* Handle reading from a compressed stream */ 999static svn_error_t * 1000read_handler_gz(void *baton, char *buffer, apr_size_t *len) 1001{ 1002 struct zbaton *btn = baton; 1003 int zerr; 1004 1005 if (btn->in == NULL) 1006 { 1007 btn->in = apr_palloc(btn->pool, sizeof(z_stream)); 1008 btn->in->zalloc = zalloc; 1009 btn->in->zfree = zfree; 1010 btn->in->opaque = btn->pool; 1011 btn->read_buffer = apr_palloc(btn->pool, ZBUFFER_SIZE); 1012 btn->in->next_in = btn->read_buffer; 1013 btn->in->avail_in = ZBUFFER_SIZE; 1014 1015 SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, 1016 &btn->in->avail_in, &btn->read_flush)); 1017 1018 zerr = inflateInit(btn->in); 1019 SVN_ERR(svn_error__wrap_zlib(zerr, "inflateInit", btn->in->msg)); 1020 } 1021 1022 btn->in->next_out = (Bytef *) buffer; 1023 btn->in->avail_out = (uInt) *len; 1024 1025 while (btn->in->avail_out > 0) 1026 { 1027 if (btn->in->avail_in <= 0) 1028 { 1029 btn->in->avail_in = ZBUFFER_SIZE; 1030 btn->in->next_in = btn->read_buffer; 1031 SVN_ERR(read_helper_gz(btn->read, btn->subbaton, btn->read_buffer, 1032 &btn->in->avail_in, &btn->read_flush)); 1033 } 1034 1035 /* Short read means underlying stream has run out. */ 1036 if (btn->in->avail_in == 0) 1037 { 1038 *len = 0; 1039 return SVN_NO_ERROR; 1040 } 1041 1042 zerr = inflate(btn->in, btn->read_flush); 1043 if (zerr == Z_STREAM_END) 1044 break; 1045 else if (zerr != Z_OK) 1046 return svn_error_trace(svn_error__wrap_zlib(zerr, "inflate", 1047 btn->in->msg)); 1048 } 1049 1050 *len -= btn->in->avail_out; 1051 return SVN_NO_ERROR; 1052} 1053 1054/* Compress data and write it to the substream */ 1055static svn_error_t * 1056write_handler_gz(void *baton, const char *buffer, apr_size_t *len) 1057{ 1058 struct zbaton *btn = baton; 1059 apr_pool_t *subpool; 1060 void *write_buf; 1061 apr_size_t buf_size, write_len; 1062 int zerr; 1063 1064 if (btn->out == NULL) 1065 { 1066 btn->out = apr_palloc(btn->pool, sizeof(z_stream)); 1067 btn->out->zalloc = zalloc; 1068 btn->out->zfree = zfree; 1069 btn->out->opaque = btn->pool; 1070 1071 zerr = deflateInit(btn->out, Z_DEFAULT_COMPRESSION); 1072 SVN_ERR(svn_error__wrap_zlib(zerr, "deflateInit", btn->out->msg)); 1073 } 1074 1075 /* The largest buffer we should need is 0.1% larger than the 1076 compressed data, + 12 bytes. This info comes from zlib.h. */ 1077 buf_size = *len + (*len / 1000) + 13; 1078 subpool = svn_pool_create(btn->pool); 1079 write_buf = apr_palloc(subpool, buf_size); 1080 1081 btn->out->next_in = (Bytef *) buffer; /* Casting away const! */ 1082 btn->out->avail_in = (uInt) *len; 1083 1084 while (btn->out->avail_in > 0) 1085 { 1086 btn->out->next_out = write_buf; 1087 btn->out->avail_out = (uInt) buf_size; 1088 1089 zerr = deflate(btn->out, Z_NO_FLUSH); 1090 SVN_ERR(svn_error__wrap_zlib(zerr, "deflate", btn->out->msg)); 1091 write_len = buf_size - btn->out->avail_out; 1092 if (write_len > 0) 1093 SVN_ERR(btn->write(btn->subbaton, write_buf, &write_len)); 1094 } 1095 1096 svn_pool_destroy(subpool); 1097 1098 return SVN_NO_ERROR; 1099} 1100 1101/* Handle flushing and closing the stream */ 1102static svn_error_t * 1103close_handler_gz(void *baton) 1104{ 1105 struct zbaton *btn = baton; 1106 int zerr; 1107 1108 if (btn->in != NULL) 1109 { 1110 zerr = inflateEnd(btn->in); 1111 SVN_ERR(svn_error__wrap_zlib(zerr, "inflateEnd", btn->in->msg)); 1112 } 1113 1114 if (btn->out != NULL) 1115 { 1116 void *buf; 1117 apr_size_t write_len; 1118 1119 buf = apr_palloc(btn->pool, ZBUFFER_SIZE); 1120 1121 while (TRUE) 1122 { 1123 btn->out->next_out = buf; 1124 btn->out->avail_out = ZBUFFER_SIZE; 1125 1126 zerr = deflate(btn->out, Z_FINISH); 1127 if (zerr != Z_STREAM_END && zerr != Z_OK) 1128 return svn_error_trace(svn_error__wrap_zlib(zerr, "deflate", 1129 btn->out->msg)); 1130 write_len = ZBUFFER_SIZE - btn->out->avail_out; 1131 if (write_len > 0) 1132 SVN_ERR(btn->write(btn->subbaton, buf, &write_len)); 1133 if (zerr == Z_STREAM_END) 1134 break; 1135 } 1136 1137 zerr = deflateEnd(btn->out); 1138 SVN_ERR(svn_error__wrap_zlib(zerr, "deflateEnd", btn->out->msg)); 1139 } 1140 1141 if (btn->close != NULL) 1142 return svn_error_trace(btn->close(btn->subbaton)); 1143 else 1144 return SVN_NO_ERROR; 1145} 1146 1147 1148svn_stream_t * 1149svn_stream_compressed(svn_stream_t *stream, apr_pool_t *pool) 1150{ 1151 struct svn_stream_t *zstream; 1152 struct zbaton *baton; 1153 1154 assert(stream != NULL); 1155 1156 baton = apr_palloc(pool, sizeof(*baton)); 1157 baton->in = baton->out = NULL; 1158 baton->read = stream->read_fn; 1159 baton->write = stream->write_fn; 1160 baton->close = stream->close_fn; 1161 baton->subbaton = stream->baton; 1162 baton->pool = pool; 1163 baton->read_buffer = NULL; 1164 baton->read_flush = Z_SYNC_FLUSH; 1165 1166 zstream = svn_stream_create(baton, pool); 1167 svn_stream_set_read(zstream, read_handler_gz); 1168 svn_stream_set_write(zstream, write_handler_gz); 1169 svn_stream_set_close(zstream, close_handler_gz); 1170 1171 return zstream; 1172} 1173 1174 1175/* Checksummed stream support */ 1176 1177struct checksum_stream_baton 1178{ 1179 svn_checksum_ctx_t *read_ctx, *write_ctx; 1180 svn_checksum_t **read_checksum; /* Output value. */ 1181 svn_checksum_t **write_checksum; /* Output value. */ 1182 svn_stream_t *proxy; 1183 1184 /* True if more data should be read when closing the stream. */ 1185 svn_boolean_t read_more; 1186 1187 /* Pool to allocate read buffer and output values from. */ 1188 apr_pool_t *pool; 1189}; 1190 1191static svn_error_t * 1192read_handler_checksum(void *baton, char *buffer, apr_size_t *len) 1193{ 1194 struct checksum_stream_baton *btn = baton; 1195 apr_size_t saved_len = *len; 1196 1197 SVN_ERR(svn_stream_read(btn->proxy, buffer, len)); 1198 1199 if (btn->read_checksum) 1200 SVN_ERR(svn_checksum_update(btn->read_ctx, buffer, *len)); 1201 1202 if (saved_len != *len) 1203 btn->read_more = FALSE; 1204 1205 return SVN_NO_ERROR; 1206} 1207 1208 1209static svn_error_t * 1210write_handler_checksum(void *baton, const char *buffer, apr_size_t *len) 1211{ 1212 struct checksum_stream_baton *btn = baton; 1213 1214 if (btn->write_checksum && *len > 0) 1215 SVN_ERR(svn_checksum_update(btn->write_ctx, buffer, *len)); 1216 1217 return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); 1218} 1219 1220 1221static svn_error_t * 1222close_handler_checksum(void *baton) 1223{ 1224 struct checksum_stream_baton *btn = baton; 1225 1226 /* If we're supposed to drain the stream, do so before finalizing the 1227 checksum. */ 1228 if (btn->read_more) 1229 { 1230 char *buf = apr_palloc(btn->pool, SVN__STREAM_CHUNK_SIZE); 1231 apr_size_t len = SVN__STREAM_CHUNK_SIZE; 1232 1233 do 1234 { 1235 SVN_ERR(read_handler_checksum(baton, buf, &len)); 1236 } 1237 while (btn->read_more); 1238 } 1239 1240 if (btn->read_ctx) 1241 SVN_ERR(svn_checksum_final(btn->read_checksum, btn->read_ctx, btn->pool)); 1242 1243 if (btn->write_ctx) 1244 SVN_ERR(svn_checksum_final(btn->write_checksum, btn->write_ctx, btn->pool)); 1245 1246 return svn_error_trace(svn_stream_close(btn->proxy)); 1247} 1248 1249 1250svn_stream_t * 1251svn_stream_checksummed2(svn_stream_t *stream, 1252 svn_checksum_t **read_checksum, 1253 svn_checksum_t **write_checksum, 1254 svn_checksum_kind_t checksum_kind, 1255 svn_boolean_t read_all, 1256 apr_pool_t *pool) 1257{ 1258 svn_stream_t *s; 1259 struct checksum_stream_baton *baton; 1260 1261 if (read_checksum == NULL && write_checksum == NULL) 1262 return stream; 1263 1264 baton = apr_palloc(pool, sizeof(*baton)); 1265 if (read_checksum) 1266 baton->read_ctx = svn_checksum_ctx_create(checksum_kind, pool); 1267 else 1268 baton->read_ctx = NULL; 1269 1270 if (write_checksum) 1271 baton->write_ctx = svn_checksum_ctx_create(checksum_kind, pool); 1272 else 1273 baton->write_ctx = NULL; 1274 1275 baton->read_checksum = read_checksum; 1276 baton->write_checksum = write_checksum; 1277 baton->proxy = stream; 1278 baton->read_more = read_all; 1279 baton->pool = pool; 1280 1281 s = svn_stream_create(baton, pool); 1282 svn_stream_set_read(s, read_handler_checksum); 1283 svn_stream_set_write(s, write_handler_checksum); 1284 svn_stream_set_close(s, close_handler_checksum); 1285 return s; 1286} 1287 1288struct md5_stream_baton 1289{ 1290 const unsigned char **read_digest; 1291 const unsigned char **write_digest; 1292 svn_checksum_t *read_checksum; 1293 svn_checksum_t *write_checksum; 1294 svn_stream_t *proxy; 1295 apr_pool_t *pool; 1296}; 1297 1298static svn_error_t * 1299read_handler_md5(void *baton, char *buffer, apr_size_t *len) 1300{ 1301 struct md5_stream_baton *btn = baton; 1302 return svn_error_trace(svn_stream_read(btn->proxy, buffer, len)); 1303} 1304 1305static svn_error_t * 1306skip_handler_md5(void *baton, apr_size_t len) 1307{ 1308 struct md5_stream_baton *btn = baton; 1309 return svn_error_trace(svn_stream_skip(btn->proxy, len)); 1310} 1311 1312static svn_error_t * 1313write_handler_md5(void *baton, const char *buffer, apr_size_t *len) 1314{ 1315 struct md5_stream_baton *btn = baton; 1316 return svn_error_trace(svn_stream_write(btn->proxy, buffer, len)); 1317} 1318 1319static svn_error_t * 1320close_handler_md5(void *baton) 1321{ 1322 struct md5_stream_baton *btn = baton; 1323 1324 SVN_ERR(svn_stream_close(btn->proxy)); 1325 1326 if (btn->read_digest) 1327 *btn->read_digest 1328 = apr_pmemdup(btn->pool, btn->read_checksum->digest, 1329 APR_MD5_DIGESTSIZE); 1330 1331 if (btn->write_digest) 1332 *btn->write_digest 1333 = apr_pmemdup(btn->pool, btn->write_checksum->digest, 1334 APR_MD5_DIGESTSIZE); 1335 1336 return SVN_NO_ERROR; 1337} 1338 1339 1340svn_stream_t * 1341svn_stream_checksummed(svn_stream_t *stream, 1342 const unsigned char **read_digest, 1343 const unsigned char **write_digest, 1344 svn_boolean_t read_all, 1345 apr_pool_t *pool) 1346{ 1347 svn_stream_t *s; 1348 struct md5_stream_baton *baton; 1349 1350 if (! read_digest && ! write_digest) 1351 return stream; 1352 1353 baton = apr_palloc(pool, sizeof(*baton)); 1354 baton->read_digest = read_digest; 1355 baton->write_digest = write_digest; 1356 baton->pool = pool; 1357 1358 /* Set BATON->proxy to a stream that will fill in BATON->read_checksum 1359 * and BATON->write_checksum (if we want them) when it is closed. */ 1360 baton->proxy 1361 = svn_stream_checksummed2(stream, 1362 read_digest ? &baton->read_checksum : NULL, 1363 write_digest ? &baton->write_checksum : NULL, 1364 svn_checksum_md5, 1365 read_all, pool); 1366 1367 /* Create a stream that will forward its read/write/close operations to 1368 * BATON->proxy and will fill in *READ_DIGEST and *WRITE_DIGEST (if we 1369 * want them) after it closes BATON->proxy. */ 1370 s = svn_stream_create(baton, pool); 1371 svn_stream_set_read(s, read_handler_md5); 1372 svn_stream_set_skip(s, skip_handler_md5); 1373 svn_stream_set_write(s, write_handler_md5); 1374 svn_stream_set_close(s, close_handler_md5); 1375 return s; 1376} 1377 1378 1379 1380 1381/* Miscellaneous stream functions. */ 1382struct stringbuf_stream_baton 1383{ 1384 svn_stringbuf_t *str; 1385 apr_size_t amt_read; 1386}; 1387 1388/* svn_stream_mark_t for streams backed by stringbufs. */ 1389struct stringbuf_stream_mark { 1390 apr_size_t pos; 1391}; 1392 1393static svn_error_t * 1394read_handler_stringbuf(void *baton, char *buffer, apr_size_t *len) 1395{ 1396 struct stringbuf_stream_baton *btn = baton; 1397 apr_size_t left_to_read = btn->str->len - btn->amt_read; 1398 1399 *len = (*len > left_to_read) ? left_to_read : *len; 1400 memcpy(buffer, btn->str->data + btn->amt_read, *len); 1401 btn->amt_read += *len; 1402 return SVN_NO_ERROR; 1403} 1404 1405static svn_error_t * 1406skip_handler_stringbuf(void *baton, apr_size_t len) 1407{ 1408 struct stringbuf_stream_baton *btn = baton; 1409 apr_size_t left_to_read = btn->str->len - btn->amt_read; 1410 1411 len = (len > left_to_read) ? left_to_read : len; 1412 btn->amt_read += len; 1413 return SVN_NO_ERROR; 1414} 1415 1416static svn_error_t * 1417write_handler_stringbuf(void *baton, const char *data, apr_size_t *len) 1418{ 1419 struct stringbuf_stream_baton *btn = baton; 1420 1421 svn_stringbuf_appendbytes(btn->str, data, *len); 1422 return SVN_NO_ERROR; 1423} 1424 1425static svn_error_t * 1426mark_handler_stringbuf(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool) 1427{ 1428 struct stringbuf_stream_baton *btn; 1429 struct stringbuf_stream_mark *stringbuf_stream_mark; 1430 1431 btn = baton; 1432 1433 stringbuf_stream_mark = apr_palloc(pool, sizeof(*stringbuf_stream_mark)); 1434 stringbuf_stream_mark->pos = btn->amt_read; 1435 *mark = (svn_stream_mark_t *)stringbuf_stream_mark; 1436 return SVN_NO_ERROR; 1437} 1438 1439static svn_error_t * 1440seek_handler_stringbuf(void *baton, const svn_stream_mark_t *mark) 1441{ 1442 struct stringbuf_stream_baton *btn = baton; 1443 1444 if (mark != NULL) 1445 { 1446 const struct stringbuf_stream_mark *stringbuf_stream_mark; 1447 1448 stringbuf_stream_mark = (const struct stringbuf_stream_mark *)mark; 1449 btn->amt_read = stringbuf_stream_mark->pos; 1450 } 1451 else 1452 btn->amt_read = 0; 1453 1454 return SVN_NO_ERROR; 1455} 1456 1457static svn_boolean_t 1458is_buffered_handler_stringbuf(void *baton) 1459{ 1460 return TRUE; 1461} 1462 1463svn_stream_t * 1464svn_stream_from_stringbuf(svn_stringbuf_t *str, 1465 apr_pool_t *pool) 1466{ 1467 svn_stream_t *stream; 1468 struct stringbuf_stream_baton *baton; 1469 1470 if (! str) 1471 return svn_stream_empty(pool); 1472 1473 baton = apr_palloc(pool, sizeof(*baton)); 1474 baton->str = str; 1475 baton->amt_read = 0; 1476 stream = svn_stream_create(baton, pool); 1477 svn_stream_set_read(stream, read_handler_stringbuf); 1478 svn_stream_set_skip(stream, skip_handler_stringbuf); 1479 svn_stream_set_write(stream, write_handler_stringbuf); 1480 svn_stream_set_mark(stream, mark_handler_stringbuf); 1481 svn_stream_set_seek(stream, seek_handler_stringbuf); 1482 svn_stream__set_is_buffered(stream, is_buffered_handler_stringbuf); 1483 return stream; 1484} 1485 1486struct string_stream_baton 1487{ 1488 const svn_string_t *str; 1489 apr_size_t amt_read; 1490}; 1491 1492/* svn_stream_mark_t for streams backed by stringbufs. */ 1493struct string_stream_mark { 1494 apr_size_t pos; 1495}; 1496 1497static svn_error_t * 1498read_handler_string(void *baton, char *buffer, apr_size_t *len) 1499{ 1500 struct string_stream_baton *btn = baton; 1501 apr_size_t left_to_read = btn->str->len - btn->amt_read; 1502 1503 *len = (*len > left_to_read) ? left_to_read : *len; 1504 memcpy(buffer, btn->str->data + btn->amt_read, *len); 1505 btn->amt_read += *len; 1506 return SVN_NO_ERROR; 1507} 1508 1509static svn_error_t * 1510mark_handler_string(void *baton, svn_stream_mark_t **mark, apr_pool_t *pool) 1511{ 1512 struct string_stream_baton *btn; 1513 struct string_stream_mark *marker; 1514 1515 btn = baton; 1516 1517 marker = apr_palloc(pool, sizeof(*marker)); 1518 marker->pos = btn->amt_read; 1519 *mark = (svn_stream_mark_t *)marker; 1520 return SVN_NO_ERROR; 1521} 1522 1523static svn_error_t * 1524seek_handler_string(void *baton, const svn_stream_mark_t *mark) 1525{ 1526 struct string_stream_baton *btn = baton; 1527 1528 if (mark != NULL) 1529 { 1530 const struct string_stream_mark *marker; 1531 1532 marker = (const struct string_stream_mark *)mark; 1533 btn->amt_read = marker->pos; 1534 } 1535 else 1536 btn->amt_read = 0; 1537 1538 return SVN_NO_ERROR; 1539} 1540 1541static svn_error_t * 1542skip_handler_string(void *baton, apr_size_t len) 1543{ 1544 struct string_stream_baton *btn = baton; 1545 apr_size_t left_to_read = btn->str->len - btn->amt_read; 1546 1547 len = (len > left_to_read) ? left_to_read : len; 1548 btn->amt_read += len; 1549 return SVN_NO_ERROR; 1550} 1551 1552static svn_boolean_t 1553is_buffered_handler_string(void *baton) 1554{ 1555 return TRUE; 1556} 1557 1558svn_stream_t * 1559svn_stream_from_string(const svn_string_t *str, 1560 apr_pool_t *pool) 1561{ 1562 svn_stream_t *stream; 1563 struct string_stream_baton *baton; 1564 1565 if (! str) 1566 return svn_stream_empty(pool); 1567 1568 baton = apr_palloc(pool, sizeof(*baton)); 1569 baton->str = str; 1570 baton->amt_read = 0; 1571 stream = svn_stream_create(baton, pool); 1572 svn_stream_set_read(stream, read_handler_string); 1573 svn_stream_set_mark(stream, mark_handler_string); 1574 svn_stream_set_seek(stream, seek_handler_string); 1575 svn_stream_set_skip(stream, skip_handler_string); 1576 svn_stream__set_is_buffered(stream, is_buffered_handler_string); 1577 return stream; 1578} 1579 1580 1581svn_error_t * 1582svn_stream_for_stdin(svn_stream_t **in, apr_pool_t *pool) 1583{ 1584 apr_file_t *stdin_file; 1585 apr_status_t apr_err; 1586 1587 apr_err = apr_file_open_stdin(&stdin_file, pool); 1588 if (apr_err) 1589 return svn_error_wrap_apr(apr_err, "Can't open stdin"); 1590 1591 *in = svn_stream_from_aprfile2(stdin_file, TRUE, pool); 1592 1593 return SVN_NO_ERROR; 1594} 1595 1596 1597svn_error_t * 1598svn_stream_for_stdout(svn_stream_t **out, apr_pool_t *pool) 1599{ 1600 apr_file_t *stdout_file; 1601 apr_status_t apr_err; 1602 1603 apr_err = apr_file_open_stdout(&stdout_file, pool); 1604 if (apr_err) 1605 return svn_error_wrap_apr(apr_err, "Can't open stdout"); 1606 1607 *out = svn_stream_from_aprfile2(stdout_file, TRUE, pool); 1608 1609 return SVN_NO_ERROR; 1610} 1611 1612 1613svn_error_t * 1614svn_stream_for_stderr(svn_stream_t **err, apr_pool_t *pool) 1615{ 1616 apr_file_t *stderr_file; 1617 apr_status_t apr_err; 1618 1619 apr_err = apr_file_open_stderr(&stderr_file, pool); 1620 if (apr_err) 1621 return svn_error_wrap_apr(apr_err, "Can't open stderr"); 1622 1623 *err = svn_stream_from_aprfile2(stderr_file, TRUE, pool); 1624 1625 return SVN_NO_ERROR; 1626} 1627 1628 1629svn_error_t * 1630svn_string_from_stream(svn_string_t **result, 1631 svn_stream_t *stream, 1632 apr_pool_t *result_pool, 1633 apr_pool_t *scratch_pool) 1634{ 1635 svn_stringbuf_t *work = svn_stringbuf_create_ensure(SVN__STREAM_CHUNK_SIZE, 1636 result_pool); 1637 char *buffer = apr_palloc(scratch_pool, SVN__STREAM_CHUNK_SIZE); 1638 1639 while (1) 1640 { 1641 apr_size_t len = SVN__STREAM_CHUNK_SIZE; 1642 1643 SVN_ERR(svn_stream_read(stream, buffer, &len)); 1644 svn_stringbuf_appendbytes(work, buffer, len); 1645 1646 if (len < SVN__STREAM_CHUNK_SIZE) 1647 break; 1648 } 1649 1650 SVN_ERR(svn_stream_close(stream)); 1651 1652 *result = apr_palloc(result_pool, sizeof(**result)); 1653 (*result)->data = work->data; 1654 (*result)->len = work->len; 1655 1656 return SVN_NO_ERROR; 1657} 1658 1659 1660/* These are somewhat arbirary, if we ever get good empirical data as to 1661 actually valid values, feel free to update them. */ 1662#define BUFFER_BLOCK_SIZE 1024 1663#define BUFFER_MAX_SIZE 100000 1664 1665svn_stream_t * 1666svn_stream_buffered(apr_pool_t *result_pool) 1667{ 1668 return svn_stream__from_spillbuf(BUFFER_BLOCK_SIZE, BUFFER_MAX_SIZE, 1669 result_pool); 1670} 1671 1672 1673 1674/*** Lazyopen Streams ***/ 1675 1676/* Custom baton for lazyopen-style wrapper streams. */ 1677typedef struct lazyopen_baton_t { 1678 1679 /* Callback function and baton for opening the wrapped stream. */ 1680 svn_stream_lazyopen_func_t open_func; 1681 void *open_baton; 1682 1683 /* The wrapped stream, or NULL if the stream hasn't yet been 1684 opened. */ 1685 svn_stream_t *real_stream; 1686 apr_pool_t *pool; 1687 1688 /* Whether to open the wrapped stream on a close call. */ 1689 svn_boolean_t open_on_close; 1690 1691} lazyopen_baton_t; 1692 1693 1694/* Use B->open_func/baton to create and set B->real_stream iff it 1695 isn't already set. */ 1696static svn_error_t * 1697lazyopen_if_unopened(lazyopen_baton_t *b) 1698{ 1699 if (b->real_stream == NULL) 1700 { 1701 svn_stream_t *stream; 1702 apr_pool_t *scratch_pool = svn_pool_create(b->pool); 1703 1704 SVN_ERR(b->open_func(&stream, b->open_baton, 1705 b->pool, scratch_pool)); 1706 1707 svn_pool_destroy(scratch_pool); 1708 1709 b->real_stream = stream; 1710 } 1711 1712 return SVN_NO_ERROR; 1713} 1714 1715/* Implements svn_read_fn_t */ 1716static svn_error_t * 1717read_handler_lazyopen(void *baton, 1718 char *buffer, 1719 apr_size_t *len) 1720{ 1721 lazyopen_baton_t *b = baton; 1722 1723 SVN_ERR(lazyopen_if_unopened(b)); 1724 SVN_ERR(svn_stream_read(b->real_stream, buffer, len)); 1725 1726 return SVN_NO_ERROR; 1727} 1728 1729/* Implements svn_stream_skip_fn_t */ 1730static svn_error_t * 1731skip_handler_lazyopen(void *baton, 1732 apr_size_t len) 1733{ 1734 lazyopen_baton_t *b = baton; 1735 1736 SVN_ERR(lazyopen_if_unopened(b)); 1737 SVN_ERR(svn_stream_skip(b->real_stream, len)); 1738 1739 return SVN_NO_ERROR; 1740} 1741 1742/* Implements svn_write_fn_t */ 1743static svn_error_t * 1744write_handler_lazyopen(void *baton, 1745 const char *data, 1746 apr_size_t *len) 1747{ 1748 lazyopen_baton_t *b = baton; 1749 1750 SVN_ERR(lazyopen_if_unopened(b)); 1751 SVN_ERR(svn_stream_write(b->real_stream, data, len)); 1752 1753 return SVN_NO_ERROR; 1754} 1755 1756/* Implements svn_close_fn_t */ 1757static svn_error_t * 1758close_handler_lazyopen(void *baton) 1759{ 1760 lazyopen_baton_t *b = baton; 1761 1762 if (b->open_on_close) 1763 SVN_ERR(lazyopen_if_unopened(b)); 1764 if (b->real_stream) 1765 SVN_ERR(svn_stream_close(b->real_stream)); 1766 1767 return SVN_NO_ERROR; 1768} 1769 1770/* Implements svn_stream_mark_fn_t */ 1771static svn_error_t * 1772mark_handler_lazyopen(void *baton, 1773 svn_stream_mark_t **mark, 1774 apr_pool_t *pool) 1775{ 1776 lazyopen_baton_t *b = baton; 1777 1778 SVN_ERR(lazyopen_if_unopened(b)); 1779 SVN_ERR(svn_stream_mark(b->real_stream, mark, pool)); 1780 1781 return SVN_NO_ERROR; 1782} 1783 1784/* Implements svn_stream_seek_fn_t */ 1785static svn_error_t * 1786seek_handler_lazyopen(void *baton, 1787 const svn_stream_mark_t *mark) 1788{ 1789 lazyopen_baton_t *b = baton; 1790 1791 SVN_ERR(lazyopen_if_unopened(b)); 1792 SVN_ERR(svn_stream_seek(b->real_stream, mark)); 1793 1794 return SVN_NO_ERROR; 1795} 1796 1797/* Implements svn_stream__is_buffered_fn_t */ 1798static svn_boolean_t 1799is_buffered_lazyopen(void *baton) 1800{ 1801 lazyopen_baton_t *b = baton; 1802 1803 /* No lazy open as we cannot handle an open error. */ 1804 if (!b->real_stream) 1805 return FALSE; 1806 1807 return svn_stream__is_buffered(b->real_stream); 1808} 1809 1810svn_stream_t * 1811svn_stream_lazyopen_create(svn_stream_lazyopen_func_t open_func, 1812 void *open_baton, 1813 svn_boolean_t open_on_close, 1814 apr_pool_t *result_pool) 1815{ 1816 lazyopen_baton_t *lob = apr_pcalloc(result_pool, sizeof(*lob)); 1817 svn_stream_t *stream; 1818 1819 lob->open_func = open_func; 1820 lob->open_baton = open_baton; 1821 lob->real_stream = NULL; 1822 lob->pool = result_pool; 1823 lob->open_on_close = open_on_close; 1824 1825 stream = svn_stream_create(lob, result_pool); 1826 svn_stream_set_read(stream, read_handler_lazyopen); 1827 svn_stream_set_skip(stream, skip_handler_lazyopen); 1828 svn_stream_set_write(stream, write_handler_lazyopen); 1829 svn_stream_set_close(stream, close_handler_lazyopen); 1830 svn_stream_set_mark(stream, mark_handler_lazyopen); 1831 svn_stream_set_seek(stream, seek_handler_lazyopen); 1832 svn_stream__set_is_buffered(stream, is_buffered_lazyopen); 1833 1834 return stream; 1835} 1836