1156230Smux/*- 2156230Smux * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> 3156230Smux * All rights reserved. 4156230Smux * 5156230Smux * Redistribution and use in source and binary forms, with or without 6156230Smux * modification, are permitted provided that the following conditions 7156230Smux * are met: 8156230Smux * 1. Redistributions of source code must retain the above copyright 9156230Smux * notice, this list of conditions and the following disclaimer. 10156230Smux * 2. Redistributions in binary form must reproduce the above copyright 11156230Smux * notice, this list of conditions and the following disclaimer in the 12156230Smux * documentation and/or other materials provided with the distribution. 13156230Smux * 14156230Smux * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15156230Smux * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16156230Smux * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17156230Smux * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18156230Smux * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19156230Smux * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20156230Smux * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21156230Smux * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22156230Smux * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23156230Smux * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24156230Smux * SUCH DAMAGE. 25156230Smux * 26156230Smux * $FreeBSD$ 27156230Smux */ 28156230Smux 29156230Smux#include <sys/types.h> 30156230Smux#include <sys/stat.h> 31156230Smux 32156230Smux#include <assert.h> 33156230Smux#include <zlib.h> 34156230Smux#include <err.h> 35156230Smux#include <errno.h> 36156230Smux#include <fcntl.h> 37156230Smux#include <stdarg.h> 38156230Smux#include <stdio.h> 39156230Smux#include <stdlib.h> 40156230Smux#include <string.h> 41156230Smux#include <unistd.h> 42156230Smux 43156230Smux#include "misc.h" 44156230Smux#include "stream.h" 45156230Smux 46156230Smux/* 47156230Smux * Simple stream API to make my life easier. If the fgetln() and 48156230Smux * funopen() functions were standard and if funopen() wasn't using 49156230Smux * wrong types for the function pointers, I could have just used 50156230Smux * stdio, but life sucks. 51156230Smux * 52156230Smux * For now, streams are always block-buffered. 53156230Smux */ 54156230Smux 55156230Smux/* 56156230Smux * Try to quiet warnings as much as possible with GCC while staying 57156230Smux * compatible with other compilers. 58156230Smux */ 59156230Smux#ifndef __unused 60156230Smux#if defined(__GNUC__) && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7) 61156230Smux#define __unused __attribute__((__unused__)) 62156230Smux#else 63156230Smux#define __unused 64156230Smux#endif 65156230Smux#endif 66156230Smux 67156230Smux/* 68156230Smux * Flags passed to the flush methods. 69156230Smux * 70156230Smux * STREAM_FLUSH_CLOSING is passed during the last flush call before 71156230Smux * closing a stream. This allows the zlib filter to emit the EOF 72156230Smux * marker as appropriate. In all other cases, STREAM_FLUSH_NORMAL 73156230Smux * should be passed. 74156230Smux * 75156230Smux * These flags are completely unused in the default flush method, 76156230Smux * but they are very important for the flush method of the zlib 77156230Smux * filter. 78156230Smux */ 79156230Smuxtypedef enum { 80156230Smux STREAM_FLUSH_NORMAL, 81156230Smux STREAM_FLUSH_CLOSING 82156230Smux} stream_flush_t; 83156230Smux 84156230Smux/* 85156230Smux * This is because buf_new() will always allocate size + 1 bytes, 86156230Smux * so our buffer sizes will still be power of 2 values. 87156230Smux */ 88156230Smux#define STREAM_BUFSIZ 1023 89156230Smux 90156230Smuxstruct buf { 91156230Smux char *buf; 92156230Smux size_t size; 93156230Smux size_t in; 94156230Smux size_t off; 95156230Smux}; 96156230Smux 97156230Smuxstruct stream { 98156230Smux void *cookie; 99156230Smux int fd; 100186781Slulf int buf; 101156230Smux struct buf *rdbuf; 102156230Smux struct buf *wrbuf; 103156230Smux stream_readfn_t *readfn; 104156230Smux stream_writefn_t *writefn; 105156230Smux stream_closefn_t *closefn; 106156230Smux int eof; 107156230Smux struct stream_filter *filter; 108156230Smux void *fdata; 109156230Smux}; 110156230Smux 111156230Smuxtypedef int stream_filter_initfn_t(struct stream *, void *); 112156230Smuxtypedef void stream_filter_finifn_t(struct stream *); 113156230Smuxtypedef int stream_filter_flushfn_t(struct stream *, struct buf *, 114156230Smux stream_flush_t); 115156230Smuxtypedef ssize_t stream_filter_fillfn_t(struct stream *, struct buf *); 116156230Smux 117156230Smuxstruct stream_filter { 118156230Smux stream_filter_t id; 119156230Smux stream_filter_initfn_t *initfn; 120156230Smux stream_filter_finifn_t *finifn; 121156230Smux stream_filter_fillfn_t *fillfn; 122156230Smux stream_filter_flushfn_t *flushfn; 123156230Smux}; 124156230Smux 125156230Smux/* Low-level buffer API. */ 126156230Smux#define buf_avail(buf) ((buf)->size - (buf)->off - (buf)->in) 127156230Smux#define buf_count(buf) ((buf)->in) 128156230Smux#define buf_size(buf) ((buf)->size) 129156230Smux 130156230Smuxstatic void buf_more(struct buf *, size_t); 131156230Smuxstatic void buf_less(struct buf *, size_t); 132156230Smuxstatic void buf_grow(struct buf *, size_t); 133156230Smux 134156230Smux/* Internal stream functions. */ 135156230Smuxstatic ssize_t stream_fill(struct stream *); 136156230Smuxstatic ssize_t stream_fill_default(struct stream *, struct buf *); 137156230Smuxstatic int stream_flush_int(struct stream *, stream_flush_t); 138156230Smuxstatic int stream_flush_default(struct stream *, struct buf *, 139156230Smux stream_flush_t); 140156230Smux 141156230Smux/* Filters specific functions. */ 142156230Smuxstatic struct stream_filter *stream_filter_lookup(stream_filter_t); 143156230Smuxstatic int stream_filter_init(struct stream *, void *); 144156230Smuxstatic void stream_filter_fini(struct stream *); 145156230Smux 146156230Smux/* The zlib stream filter declarations. */ 147156230Smux#define ZFILTER_EOF 1 /* Got Z_STREAM_END. */ 148156230Smux 149156230Smuxstruct zfilter { 150156230Smux int flags; 151156230Smux struct buf *rdbuf; 152156230Smux struct buf *wrbuf; 153156230Smux z_stream *rdstate; 154156230Smux z_stream *wrstate; 155156230Smux}; 156156230Smux 157156230Smuxstatic int zfilter_init(struct stream *, void *); 158156230Smuxstatic void zfilter_fini(struct stream *); 159156230Smuxstatic ssize_t zfilter_fill(struct stream *, struct buf *); 160156230Smuxstatic int zfilter_flush(struct stream *, struct buf *, 161156230Smux stream_flush_t); 162156230Smux 163156230Smux/* The MD5 stream filter. */ 164156230Smuxstruct md5filter { 165156230Smux MD5_CTX ctx; 166156230Smux char *md5; 167186781Slulf char lastc; 168186781Slulf#define PRINT 1 169186781Slulf#define WS 2 170186781Slulf#define STRING 3 171186781Slulf#define SEEN 4 172186781Slulf int state; 173156230Smux}; 174156230Smux 175156230Smuxstatic int md5filter_init(struct stream *, void *); 176156230Smuxstatic void md5filter_fini(struct stream *); 177156230Smuxstatic ssize_t md5filter_fill(struct stream *, struct buf *); 178156230Smuxstatic int md5filter_flush(struct stream *, struct buf *, 179156230Smux stream_flush_t); 180186781Slulfstatic int md5rcsfilter_flush(struct stream *, struct buf *, 181186781Slulf stream_flush_t); 182156230Smux 183156230Smux/* The available stream filters. */ 184156230Smuxstruct stream_filter stream_filters[] = { 185156230Smux { 186156230Smux STREAM_FILTER_NULL, 187156230Smux NULL, 188156230Smux NULL, 189156230Smux stream_fill_default, 190156230Smux stream_flush_default 191156230Smux }, 192156230Smux { 193156230Smux STREAM_FILTER_ZLIB, 194156230Smux zfilter_init, 195156230Smux zfilter_fini, 196156230Smux zfilter_fill, 197156230Smux zfilter_flush 198156230Smux }, 199156230Smux { 200156230Smux STREAM_FILTER_MD5, 201156230Smux md5filter_init, 202156230Smux md5filter_fini, 203156230Smux md5filter_fill, 204156230Smux md5filter_flush 205186781Slulf }, 206186781Slulf { 207186781Slulf STREAM_FILTER_MD5RCS, 208186781Slulf md5filter_init, 209186781Slulf md5filter_fini, 210186781Slulf md5filter_fill, 211186781Slulf md5rcsfilter_flush 212156230Smux } 213186781Slulf 214156230Smux}; 215156230Smux 216156230Smux 217156230Smux/* Create a new buffer. */ 218186781Slulfstruct buf * 219156230Smuxbuf_new(size_t size) 220156230Smux{ 221156230Smux struct buf *buf; 222156230Smux 223156230Smux buf = xmalloc(sizeof(struct buf)); 224156230Smux /* 225156230Smux * We keep one spare byte so that stream_getln() can put a '\0' 226156230Smux * there in case the stream doesn't have an ending newline. 227156230Smux */ 228156230Smux buf->buf = xmalloc(size + 1); 229186781Slulf memset(buf->buf, 0, size + 1); 230156230Smux buf->size = size; 231156230Smux buf->in = 0; 232156230Smux buf->off = 0; 233156230Smux return (buf); 234156230Smux} 235156230Smux 236156230Smux/* 237156230Smux * Grow the size of the buffer. If "need" is 0, bump its size to the 238156230Smux * next power of 2 value. Otherwise, bump it to the next power of 2 239156230Smux * value bigger than "need". 240156230Smux */ 241156230Smuxstatic void 242156230Smuxbuf_grow(struct buf *buf, size_t need) 243156230Smux{ 244156230Smux 245156230Smux if (need == 0) 246156230Smux buf->size = buf->size * 2 + 1; /* Account for the spare byte. */ 247156230Smux else { 248156230Smux assert(need > buf->size); 249156230Smux while (buf->size < need) 250156230Smux buf->size = buf->size * 2 + 1; 251156230Smux } 252156230Smux buf->buf = xrealloc(buf->buf, buf->size + 1); 253156230Smux} 254156230Smux 255156230Smux/* Make more room in the buffer if needed. */ 256156230Smuxstatic void 257156230Smuxbuf_prewrite(struct buf *buf) 258156230Smux{ 259156230Smux 260156230Smux if (buf_count(buf) == buf_size(buf)) 261156230Smux buf_grow(buf, 0); 262156230Smux if (buf_count(buf) > 0 && buf_avail(buf) == 0) { 263156230Smux memmove(buf->buf, buf->buf + buf->off, buf_count(buf)); 264156230Smux buf->off = 0; 265156230Smux } 266156230Smux} 267156230Smux 268156230Smux/* Account for "n" bytes being added in the buffer. */ 269156230Smuxstatic void 270156230Smuxbuf_more(struct buf *buf, size_t n) 271156230Smux{ 272156230Smux 273156230Smux assert(n <= buf_avail(buf)); 274156230Smux buf->in += n; 275156230Smux} 276156230Smux 277156230Smux/* Account for "n" bytes having been read in the buffer. */ 278156230Smuxstatic void 279156230Smuxbuf_less(struct buf *buf, size_t n) 280156230Smux{ 281156230Smux 282156230Smux assert(n <= buf_count(buf)); 283156230Smux buf->in -= n; 284156230Smux if (buf->in == 0) 285156230Smux buf->off = 0; 286156230Smux else 287156230Smux buf->off += n; 288156230Smux} 289156230Smux 290156230Smux/* Free a buffer. */ 291186781Slulfvoid 292156230Smuxbuf_free(struct buf *buf) 293156230Smux{ 294156230Smux 295156230Smux free(buf->buf); 296156230Smux free(buf); 297156230Smux} 298156230Smux 299156230Smuxstatic struct stream * 300156230Smuxstream_new(stream_readfn_t *readfn, stream_writefn_t *writefn, 301156230Smux stream_closefn_t *closefn) 302156230Smux{ 303156230Smux struct stream *stream; 304156230Smux 305156230Smux stream = xmalloc(sizeof(struct stream)); 306156230Smux if (readfn == NULL && writefn == NULL) { 307156230Smux errno = EINVAL; 308156230Smux return (NULL); 309156230Smux } 310156230Smux if (readfn != NULL) 311156230Smux stream->rdbuf = buf_new(STREAM_BUFSIZ); 312156230Smux else 313156230Smux stream->rdbuf = NULL; 314156230Smux if (writefn != NULL) 315156230Smux stream->wrbuf = buf_new(STREAM_BUFSIZ); 316156230Smux else 317156230Smux stream->wrbuf = NULL; 318156230Smux stream->cookie = NULL; 319156230Smux stream->fd = -1; 320186781Slulf stream->buf = 0; 321156230Smux stream->readfn = readfn; 322156230Smux stream->writefn = writefn; 323156230Smux stream->closefn = closefn; 324156230Smux stream->filter = stream_filter_lookup(STREAM_FILTER_NULL); 325156230Smux stream->fdata = NULL; 326156230Smux stream->eof = 0; 327156230Smux return (stream); 328156230Smux} 329156230Smux 330156230Smux/* Create a new stream associated with a void *. */ 331156230Smuxstruct stream * 332156230Smuxstream_open(void *cookie, stream_readfn_t *readfn, stream_writefn_t *writefn, 333156230Smux stream_closefn_t *closefn) 334156230Smux{ 335156230Smux struct stream *stream; 336156230Smux 337156230Smux stream = stream_new(readfn, writefn, closefn); 338156230Smux stream->cookie = cookie; 339156230Smux return (stream); 340156230Smux} 341156230Smux 342156230Smux/* Associate a file descriptor with a stream. */ 343156230Smuxstruct stream * 344156230Smuxstream_open_fd(int fd, stream_readfn_t *readfn, stream_writefn_t *writefn, 345156230Smux stream_closefn_t *closefn) 346156230Smux{ 347156230Smux struct stream *stream; 348156230Smux 349156230Smux stream = stream_new(readfn, writefn, closefn); 350156230Smux stream->cookie = &stream->fd; 351156230Smux stream->fd = fd; 352156230Smux return (stream); 353156230Smux} 354156230Smux 355186781Slulf/* Associate a buf with a stream. */ 356186781Slulfstruct stream * 357186781Slulfstream_open_buf(struct buf *b) 358186781Slulf{ 359186781Slulf struct stream *stream; 360186781Slulf 361186781Slulf stream = stream_new(stream_read_buf, stream_append_buf, stream_close_buf); 362186781Slulf stream->cookie = b; 363186781Slulf stream->buf = 1; 364186781Slulf b->in = 0; 365186781Slulf return (stream); 366186781Slulf} 367186781Slulf 368186781Slulf/* 369186781Slulf * Truncate a buffer, just decrease offset pointer. 370186781Slulf * XXX: this can be dangerous if not used correctly. 371186781Slulf */ 372186781Slulfvoid 373186781Slulfstream_truncate_buf(struct buf *b, off_t off) 374186781Slulf{ 375186781Slulf b->off += off; 376186781Slulf} 377186781Slulf 378156230Smux/* Like open() but returns a stream. */ 379156230Smuxstruct stream * 380156230Smuxstream_open_file(const char *path, int flags, ...) 381156230Smux{ 382156230Smux struct stream *stream; 383156230Smux stream_readfn_t *readfn; 384156230Smux stream_writefn_t *writefn; 385156230Smux va_list ap; 386156230Smux mode_t mode; 387156230Smux int fd; 388156230Smux 389156230Smux va_start(ap, flags); 390156230Smux if (flags & O_CREAT) { 391156230Smux /* 392156230Smux * GCC says I should not be using mode_t here since it's 393156230Smux * promoted to an int when passed through `...'. 394156230Smux */ 395156230Smux mode = va_arg(ap, int); 396156230Smux fd = open(path, flags, mode); 397156230Smux } else 398156230Smux fd = open(path, flags); 399156230Smux va_end(ap); 400156230Smux if (fd == -1) 401156230Smux return (NULL); 402156230Smux 403156230Smux flags &= O_ACCMODE; 404156230Smux if (flags == O_RDONLY) { 405156230Smux readfn = stream_read_fd; 406156230Smux writefn = NULL; 407156230Smux } else if (flags == O_WRONLY) { 408156230Smux readfn = NULL; 409156230Smux writefn = stream_write_fd; 410156230Smux } else if (flags == O_RDWR) { 411156230Smux assert(flags == O_RDWR); 412156230Smux readfn = stream_read_fd; 413156230Smux writefn = stream_write_fd; 414156230Smux } else { 415156230Smux errno = EINVAL; 416156230Smux close(fd); 417156230Smux return (NULL); 418156230Smux } 419156230Smux 420156230Smux stream = stream_open_fd(fd, readfn, writefn, stream_close_fd); 421156230Smux if (stream == NULL) 422156230Smux close(fd); 423156230Smux return (stream); 424156230Smux} 425156230Smux 426156230Smux/* Return the file descriptor associated with this stream, or -1. */ 427156230Smuxint 428156230Smuxstream_fileno(struct stream *stream) 429156230Smux{ 430156230Smux 431156230Smux return (stream->fd); 432156230Smux} 433156230Smux 434186781Slulf/* Convenience read function for character buffers. */ 435186781Slulfssize_t 436186781Slulfstream_read_buf(void *cookie, void *buf, size_t size) 437186781Slulf{ 438186781Slulf struct buf *b; 439186781Slulf size_t avail; 440186781Slulf 441186781Slulf /* Use in to be read offset. */ 442186781Slulf b = (struct buf *)cookie; 443186781Slulf /* Just return what we have if the request is to large. */ 444186781Slulf avail = b->off - b->in; 445186781Slulf if (avail < size) { 446186781Slulf memcpy(buf, (b->buf + b->in), avail); 447186781Slulf b->in += avail; 448186781Slulf return (avail); 449186781Slulf } 450186781Slulf memcpy(buf, (b->buf + b->in), size); 451186781Slulf b->in += size; 452186781Slulf return (size); 453186781Slulf} 454186781Slulf 455186781Slulf/* Convenience write function for appending character buffers. */ 456186781Slulfssize_t 457186781Slulfstream_append_buf(void *cookie, const void *buf, size_t size) 458186781Slulf{ 459186781Slulf struct buf *b; 460186781Slulf size_t avail; 461186781Slulf 462186781Slulf /* Use off to be write offset. */ 463186781Slulf b = (struct buf *)cookie; 464186781Slulf 465186781Slulf avail = b->size - b->off; 466186781Slulf if (size > avail) 467186781Slulf buf_grow(b, b->size + size); 468186781Slulf memcpy((b->buf + b->off), buf, size); 469186781Slulf b->off += size; 470186781Slulf b->buf[b->off] = '\0'; 471186781Slulf return (size); 472186781Slulf} 473186781Slulf 474186781Slulf/* Convenience close function for freeing character buffers. */ 475186781Slulfint 476186781Slulfstream_close_buf(void *cookie) 477186781Slulf{ 478186781Slulf void *data; 479186781Slulf 480186781Slulf data = cookie; 481186781Slulf /* Basically a NOP. */ 482186781Slulf return (0); 483186781Slulf} 484186781Slulf 485156230Smux/* Convenience read function for file descriptors. */ 486156230Smuxssize_t 487156230Smuxstream_read_fd(void *cookie, void *buf, size_t size) 488156230Smux{ 489156230Smux ssize_t nbytes; 490156230Smux int fd; 491156230Smux 492156230Smux fd = *(int *)cookie; 493156230Smux nbytes = read(fd, buf, size); 494156230Smux return (nbytes); 495156230Smux} 496156230Smux 497156230Smux/* Convenience write function for file descriptors. */ 498156230Smuxssize_t 499156230Smuxstream_write_fd(void *cookie, const void *buf, size_t size) 500156230Smux{ 501156230Smux ssize_t nbytes; 502156230Smux int fd; 503156230Smux 504156230Smux fd = *(int *)cookie; 505156230Smux nbytes = write(fd, buf, size); 506156230Smux return (nbytes); 507156230Smux} 508156230Smux 509156230Smux/* Convenience close function for file descriptors. */ 510156230Smuxint 511156230Smuxstream_close_fd(void *cookie) 512156230Smux{ 513156230Smux int fd, ret; 514156230Smux 515156230Smux fd = *(int *)cookie; 516156230Smux ret = close(fd); 517156230Smux return (ret); 518156230Smux} 519156230Smux 520156230Smux/* Read some bytes from the stream. */ 521156230Smuxssize_t 522156230Smuxstream_read(struct stream *stream, void *buf, size_t size) 523156230Smux{ 524156230Smux struct buf *rdbuf; 525156230Smux ssize_t ret; 526156230Smux size_t n; 527156230Smux 528156230Smux rdbuf = stream->rdbuf; 529156230Smux if (buf_count(rdbuf) == 0) { 530156230Smux ret = stream_fill(stream); 531156230Smux if (ret <= 0) 532156230Smux return (-1); 533156230Smux } 534156230Smux n = min(size, buf_count(rdbuf)); 535156230Smux memcpy(buf, rdbuf->buf + rdbuf->off, n); 536156230Smux buf_less(rdbuf, n); 537156230Smux return (n); 538156230Smux} 539156230Smux 540186781Slulf/* A blocking stream_read call. */ 541186781Slulfssize_t 542186781Slulfstream_read_blocking(struct stream *stream, void *buf, size_t size) 543186781Slulf{ 544186781Slulf struct buf *rdbuf; 545186781Slulf ssize_t ret; 546186781Slulf size_t n; 547186781Slulf 548186781Slulf rdbuf = stream->rdbuf; 549186781Slulf while (buf_count(rdbuf) <= size) { 550186781Slulf ret = stream_fill(stream); 551186781Slulf if (ret <= 0) 552186781Slulf return (-1); 553186781Slulf } 554186781Slulf /* XXX: Should be at least size bytes in the buffer, right? */ 555186781Slulf /* Just do this to make sure. */ 556186781Slulf n = min(size, buf_count(rdbuf)); 557186781Slulf memcpy(buf, rdbuf->buf + rdbuf->off, n); 558186781Slulf buf_less(rdbuf, n); 559186781Slulf return (n); 560186781Slulf} 561186781Slulf 562156230Smux/* 563156230Smux * Read a line from the stream and return a pointer to it. 564156230Smux * 565156230Smux * If "len" is non-NULL, the length of the string will be put into it. 566156230Smux * The pointer is only valid until the next stream API call. The line 567156230Smux * can be modified by the caller, provided he doesn't write before or 568156230Smux * after it. 569156230Smux * 570156230Smux * This is somewhat similar to the BSD fgetln() function, except that 571156230Smux * "len" can be NULL here. In that case the string is terminated by 572156230Smux * overwriting the '\n' character with a NUL character. If it's the 573156230Smux * last line in the stream and it has no ending newline, we can still 574156230Smux * add '\0' after it, because we keep one spare byte in the buffers. 575156230Smux * 576156230Smux * However, be warned that one can't handle binary lines properly 577156230Smux * without knowing the size of the string since those can contain 578156230Smux * NUL characters. 579156230Smux */ 580156230Smuxchar * 581156230Smuxstream_getln(struct stream *stream, size_t *len) 582156230Smux{ 583156230Smux struct buf *buf; 584156230Smux char *cp, *line; 585156230Smux ssize_t n; 586156230Smux size_t done, size; 587156230Smux 588156230Smux buf = stream->rdbuf; 589156230Smux if (buf_count(buf) == 0) { 590156230Smux n = stream_fill(stream); 591156230Smux if (n <= 0) 592156230Smux return (NULL); 593156230Smux } 594156230Smux cp = memchr(buf->buf + buf->off, '\n', buf_count(buf)); 595156230Smux for (done = buf_count(buf); cp == NULL; done += n) { 596156230Smux n = stream_fill(stream); 597156230Smux if (n < 0) 598156230Smux return (NULL); 599156230Smux if (n == 0) 600156230Smux /* Last line of the stream. */ 601156230Smux cp = buf->buf + buf->off + buf->in - 1; 602156230Smux else 603156230Smux cp = memchr(buf->buf + buf->off + done, '\n', 604156230Smux buf_count(buf) - done); 605156230Smux } 606156230Smux line = buf->buf + buf->off; 607156230Smux assert(cp >= line); 608156230Smux size = cp - line + 1; 609156230Smux buf_less(buf, size); 610156230Smux if (len != NULL) { 611156230Smux *len = size; 612156230Smux } else { 613156230Smux /* Terminate the string when len == NULL. */ 614156230Smux if (line[size - 1] == '\n') 615156230Smux line[size - 1] = '\0'; 616156230Smux else 617156230Smux line[size] = '\0'; 618156230Smux } 619156230Smux return (line); 620156230Smux} 621156230Smux 622156230Smux/* Write some bytes to a stream. */ 623156230Smuxssize_t 624156230Smuxstream_write(struct stream *stream, const void *src, size_t nbytes) 625156230Smux{ 626156230Smux struct buf *buf; 627156230Smux int error; 628156230Smux 629156230Smux buf = stream->wrbuf; 630156230Smux if (nbytes > buf_size(buf)) 631156230Smux buf_grow(buf, nbytes); 632156230Smux if (nbytes > buf_avail(buf)) { 633156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 634156230Smux if (error) 635156230Smux return (-1); 636156230Smux } 637156230Smux memcpy(buf->buf + buf->off + buf->in, src, nbytes); 638156230Smux buf_more(buf, nbytes); 639156230Smux return (nbytes); 640156230Smux} 641156230Smux 642156230Smux/* Formatted output to a stream. */ 643156230Smuxint 644156230Smuxstream_printf(struct stream *stream, const char *fmt, ...) 645156230Smux{ 646156230Smux struct buf *buf; 647156230Smux va_list ap; 648156230Smux int error, ret; 649156230Smux 650156230Smux buf = stream->wrbuf; 651156230Smuxagain: 652156230Smux va_start(ap, fmt); 653156230Smux ret = vsnprintf(buf->buf + buf->off + buf->in, buf_avail(buf), fmt, ap); 654156230Smux va_end(ap); 655156230Smux if (ret < 0) 656156230Smux return (ret); 657156230Smux if ((unsigned)ret >= buf_avail(buf)) { 658156230Smux if ((unsigned)ret >= buf_size(buf)) 659156230Smux buf_grow(buf, ret + 1); 660156230Smux if ((unsigned)ret >= buf_avail(buf)) { 661156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 662156230Smux if (error) 663156230Smux return (-1); 664156230Smux } 665156230Smux goto again; 666156230Smux } 667156230Smux buf_more(buf, ret); 668156230Smux return (ret); 669156230Smux} 670156230Smux 671156230Smux/* Flush the entire write buffer of the stream. */ 672156230Smuxint 673156230Smuxstream_flush(struct stream *stream) 674156230Smux{ 675156230Smux int error; 676156230Smux 677156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 678156230Smux return (error); 679156230Smux} 680156230Smux 681156230Smux/* Internal flush API. */ 682156230Smuxstatic int 683156230Smuxstream_flush_int(struct stream *stream, stream_flush_t how) 684156230Smux{ 685156230Smux struct buf *buf; 686156230Smux int error; 687156230Smux 688156230Smux buf = stream->wrbuf; 689156230Smux error = (*stream->filter->flushfn)(stream, buf, how); 690156230Smux assert(buf_count(buf) == 0); 691156230Smux return (error); 692156230Smux} 693156230Smux 694156230Smux/* The default flush method. */ 695156230Smuxstatic int 696156230Smuxstream_flush_default(struct stream *stream, struct buf *buf, 697156230Smux stream_flush_t __unused how) 698156230Smux{ 699156230Smux ssize_t n; 700156230Smux 701156230Smux while (buf_count(buf) > 0) { 702156230Smux do { 703156230Smux n = (*stream->writefn)(stream->cookie, 704156230Smux buf->buf + buf->off, buf_count(buf)); 705156230Smux } while (n == -1 && errno == EINTR); 706156230Smux if (n <= 0) 707156230Smux return (-1); 708156230Smux buf_less(buf, n); 709156230Smux } 710156230Smux return (0); 711156230Smux} 712156230Smux 713156230Smux/* Flush the write buffer and call fsync() on the file descriptor. */ 714156230Smuxint 715156230Smuxstream_sync(struct stream *stream) 716156230Smux{ 717156230Smux int error; 718156230Smux 719156230Smux if (stream->fd == -1) { 720156230Smux errno = EINVAL; 721156230Smux return (-1); 722156230Smux } 723156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 724156230Smux if (error) 725156230Smux return (-1); 726156230Smux error = fsync(stream->fd); 727156230Smux return (error); 728156230Smux} 729156230Smux 730156230Smux/* Like truncate() but on a stream. */ 731156230Smuxint 732156230Smuxstream_truncate(struct stream *stream, off_t size) 733156230Smux{ 734156230Smux int error; 735156230Smux 736156230Smux if (stream->fd == -1) { 737156230Smux errno = EINVAL; 738156230Smux return (-1); 739156230Smux } 740156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 741156230Smux if (error) 742156230Smux return (-1); 743156230Smux error = ftruncate(stream->fd, size); 744156230Smux return (error); 745156230Smux} 746156230Smux 747156230Smux/* Like stream_truncate() except the off_t parameter is an offset. */ 748156230Smuxint 749156230Smuxstream_truncate_rel(struct stream *stream, off_t off) 750156230Smux{ 751156230Smux struct stat sb; 752156230Smux int error; 753156230Smux 754186781Slulf if (stream->buf) { 755186781Slulf stream_truncate_buf(stream->cookie, off); 756186781Slulf return (0); 757186781Slulf } 758156230Smux if (stream->fd == -1) { 759156230Smux errno = EINVAL; 760156230Smux return (-1); 761156230Smux } 762156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 763156230Smux if (error) 764156230Smux return (-1); 765156230Smux error = fstat(stream->fd, &sb); 766156230Smux if (error) 767156230Smux return (-1); 768156230Smux error = stream_truncate(stream, sb.st_size + off); 769156230Smux return (error); 770156230Smux} 771156230Smux 772156230Smux/* Rewind the stream. */ 773156230Smuxint 774156230Smuxstream_rewind(struct stream *stream) 775156230Smux{ 776156230Smux int error; 777156230Smux 778156230Smux if (stream->fd == -1) { 779156230Smux errno = EINVAL; 780156230Smux return (-1); 781156230Smux } 782156230Smux if (stream->rdbuf != NULL) 783156230Smux buf_less(stream->rdbuf, buf_count(stream->rdbuf)); 784156230Smux if (stream->wrbuf != NULL) { 785156230Smux error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); 786156230Smux if (error) 787156230Smux return (error); 788156230Smux } 789156230Smux error = lseek(stream->fd, 0, SEEK_SET); 790156230Smux return (error); 791156230Smux} 792156230Smux 793156230Smux/* Return EOF status. */ 794156230Smuxint 795156230Smuxstream_eof(struct stream *stream) 796156230Smux{ 797156230Smux 798156230Smux return (stream->eof); 799156230Smux} 800156230Smux 801156230Smux/* Close a stream and free any resources held by it. */ 802156230Smuxint 803156230Smuxstream_close(struct stream *stream) 804156230Smux{ 805156230Smux int error; 806156230Smux 807156230Smux if (stream == NULL) 808156230Smux return (0); 809156230Smux 810156230Smux error = 0; 811156230Smux if (stream->wrbuf != NULL) 812156230Smux error = stream_flush_int(stream, STREAM_FLUSH_CLOSING); 813156230Smux stream_filter_fini(stream); 814156230Smux if (stream->closefn != NULL) 815156230Smux /* 816156230Smux * We might overwrite a previous error from stream_flush(), 817156230Smux * but we have no choice, because wether it had worked or 818156230Smux * not, we need to close the file descriptor. 819156230Smux */ 820156230Smux error = (*stream->closefn)(stream->cookie); 821156230Smux if (stream->rdbuf != NULL) 822156230Smux buf_free(stream->rdbuf); 823156230Smux if (stream->wrbuf != NULL) 824156230Smux buf_free(stream->wrbuf); 825156230Smux free(stream); 826156230Smux return (error); 827156230Smux} 828156230Smux 829156230Smux/* The default fill method. */ 830156230Smuxstatic ssize_t 831156230Smuxstream_fill_default(struct stream *stream, struct buf *buf) 832156230Smux{ 833156230Smux ssize_t n; 834156230Smux 835156230Smux if (stream->eof) 836156230Smux return (0); 837156230Smux assert(buf_avail(buf) > 0); 838156230Smux n = (*stream->readfn)(stream->cookie, buf->buf + buf->off + buf->in, 839156230Smux buf_avail(buf)); 840156230Smux if (n < 0) 841156230Smux return (-1); 842156230Smux if (n == 0) { 843156230Smux stream->eof = 1; 844156230Smux return (0); 845156230Smux } 846156230Smux buf_more(buf, n); 847156230Smux return (n); 848156230Smux} 849156230Smux 850156230Smux/* 851156230Smux * Refill the read buffer. This function is not permitted to return 852156230Smux * without having made more bytes available, unless there was an error. 853156230Smux * Moreover, stream_fill() returns the number of bytes added. 854156230Smux */ 855156230Smuxstatic ssize_t 856156230Smuxstream_fill(struct stream *stream) 857156230Smux{ 858156230Smux struct stream_filter *filter; 859156230Smux struct buf *buf; 860156230Smux#ifndef NDEBUG 861156230Smux size_t oldcount; 862156230Smux#endif 863156230Smux ssize_t n; 864156230Smux 865156230Smux filter = stream->filter; 866156230Smux buf = stream->rdbuf; 867156230Smux buf_prewrite(buf); 868156230Smux#ifndef NDEBUG 869156230Smux oldcount = buf_count(buf); 870156230Smux#endif 871156230Smux n = (*filter->fillfn)(stream, buf); 872156230Smux assert((n > 0 && n == (signed)(buf_count(buf) - oldcount)) || 873156230Smux (n <= 0 && buf_count(buf) == oldcount)); 874156230Smux return (n); 875156230Smux} 876156230Smux 877156230Smux/* 878156230Smux * Lookup a stream filter. 879156230Smux * 880156230Smux * We are not supposed to get passed an invalid filter id, since 881156230Smux * filter ids are an enum type and we don't have invalid filter 882156230Smux * ids in the enum :-). Thus, we are not checking for out of 883156230Smux * bounds access here. If it happens, it's the caller's fault 884156230Smux * anyway. 885156230Smux */ 886156230Smuxstatic struct stream_filter * 887156230Smuxstream_filter_lookup(stream_filter_t id) 888156230Smux{ 889156230Smux struct stream_filter *filter; 890156230Smux 891156230Smux filter = stream_filters; 892156230Smux while (filter->id != id) 893156230Smux filter++; 894156230Smux return (filter); 895156230Smux} 896156230Smux 897156230Smuxstatic int 898156230Smuxstream_filter_init(struct stream *stream, void *data) 899156230Smux{ 900156230Smux struct stream_filter *filter; 901156230Smux int error; 902156230Smux 903156230Smux filter = stream->filter; 904156230Smux if (filter->initfn == NULL) 905156230Smux return (0); 906156230Smux error = (*filter->initfn)(stream, data); 907156230Smux return (error); 908156230Smux} 909156230Smux 910156230Smuxstatic void 911156230Smuxstream_filter_fini(struct stream *stream) 912156230Smux{ 913156230Smux struct stream_filter *filter; 914156230Smux 915156230Smux filter = stream->filter; 916156230Smux if (filter->finifn != NULL) 917156230Smux (*filter->finifn)(stream); 918156230Smux} 919156230Smux 920156230Smux/* 921156230Smux * Start a filter on a stream. 922156230Smux */ 923156230Smuxint 924156230Smuxstream_filter_start(struct stream *stream, stream_filter_t id, void *data) 925156230Smux{ 926156230Smux struct stream_filter *filter; 927156230Smux int error; 928156230Smux 929156230Smux filter = stream->filter; 930156230Smux if (id == filter->id) 931156230Smux return (0); 932156230Smux stream_filter_fini(stream); 933156230Smux stream->filter = stream_filter_lookup(id); 934156230Smux stream->fdata = NULL; 935156230Smux error = stream_filter_init(stream, data); 936156230Smux return (error); 937156230Smux} 938156230Smux 939156230Smux 940156230Smux/* Stop a filter, this is equivalent to setting the null filter. */ 941156230Smuxvoid 942156230Smuxstream_filter_stop(struct stream *stream) 943156230Smux{ 944156230Smux 945156230Smux stream_filter_start(stream, STREAM_FILTER_NULL, NULL); 946156230Smux} 947156230Smux 948156230Smux/* The zlib stream filter implementation. */ 949156230Smux 950156230Smux/* Take no chances with zlib... */ 951156230Smuxstatic void * 952156230Smuxzfilter_alloc(void __unused *opaque, unsigned int items, unsigned int size) 953156230Smux{ 954156230Smux 955156230Smux return (xmalloc(items * size)); 956156230Smux} 957156230Smux 958156230Smuxstatic void 959156230Smuxzfilter_free(void __unused *opaque, void *ptr) 960156230Smux{ 961156230Smux 962156230Smux free(ptr); 963156230Smux} 964156230Smux 965156230Smuxstatic int 966156230Smuxzfilter_init(struct stream *stream, void __unused *data) 967156230Smux{ 968156230Smux struct zfilter *zf; 969156230Smux struct buf *buf; 970156230Smux z_stream *state; 971156230Smux int rv; 972156230Smux 973156230Smux zf = xmalloc(sizeof(struct zfilter)); 974156230Smux memset(zf, 0, sizeof(struct zfilter)); 975156230Smux if (stream->rdbuf != NULL) { 976156230Smux state = xmalloc(sizeof(z_stream)); 977156230Smux state->zalloc = zfilter_alloc; 978156230Smux state->zfree = zfilter_free; 979156230Smux state->opaque = Z_NULL; 980156230Smux rv = inflateInit(state); 981156230Smux if (rv != Z_OK) 982156230Smux errx(1, "inflateInit: %s", state->msg); 983156230Smux buf = buf_new(buf_size(stream->rdbuf)); 984156230Smux zf->rdbuf = stream->rdbuf; 985156230Smux stream->rdbuf = buf; 986156230Smux zf->rdstate = state; 987156230Smux } 988156230Smux if (stream->wrbuf != NULL) { 989156230Smux state = xmalloc(sizeof(z_stream)); 990156230Smux state->zalloc = zfilter_alloc; 991156230Smux state->zfree = zfilter_free; 992156230Smux state->opaque = Z_NULL; 993156230Smux rv = deflateInit(state, Z_DEFAULT_COMPRESSION); 994156230Smux if (rv != Z_OK) 995156230Smux errx(1, "deflateInit: %s", state->msg); 996156230Smux buf = buf_new(buf_size(stream->wrbuf)); 997156230Smux zf->wrbuf = stream->wrbuf; 998156230Smux stream->wrbuf = buf; 999156230Smux zf->wrstate = state; 1000156230Smux } 1001156230Smux stream->fdata = zf; 1002156230Smux return (0); 1003156230Smux} 1004156230Smux 1005156230Smuxstatic void 1006156230Smuxzfilter_fini(struct stream *stream) 1007156230Smux{ 1008156230Smux struct zfilter *zf; 1009156230Smux struct buf *zbuf; 1010156230Smux z_stream *state; 1011156230Smux ssize_t n; 1012156230Smux 1013156230Smux zf = stream->fdata; 1014156230Smux if (zf->rdbuf != NULL) { 1015156230Smux state = zf->rdstate; 1016156230Smux zbuf = zf->rdbuf; 1017156230Smux /* 1018156230Smux * Even if it has produced all the bytes, zlib sometimes 1019156230Smux * hasn't seen the EOF marker, so we need to call inflate() 1020156230Smux * again to make sure we have eaten all the zlib'ed bytes. 1021156230Smux */ 1022156230Smux if ((zf->flags & ZFILTER_EOF) == 0) { 1023156230Smux n = zfilter_fill(stream, stream->rdbuf); 1024156230Smux assert(n == 0 && zf->flags & ZFILTER_EOF); 1025156230Smux } 1026156230Smux inflateEnd(state); 1027156230Smux free(state); 1028156230Smux buf_free(stream->rdbuf); 1029156230Smux stream->rdbuf = zbuf; 1030156230Smux } 1031156230Smux if (zf->wrbuf != NULL) { 1032156230Smux state = zf->wrstate; 1033156230Smux zbuf = zf->wrbuf; 1034156230Smux /* 1035156230Smux * Compress the remaining bytes in the buffer, if any, 1036156230Smux * and emit an EOF marker as appropriate. We ignore 1037156230Smux * the error because we can't do anything about it at 1038156230Smux * this point, and it can happen if we're getting 1039156230Smux * disconnected. 1040156230Smux */ 1041156230Smux (void)zfilter_flush(stream, stream->wrbuf, 1042156230Smux STREAM_FLUSH_CLOSING); 1043156230Smux deflateEnd(state); 1044156230Smux free(state); 1045156230Smux buf_free(stream->wrbuf); 1046156230Smux stream->wrbuf = zbuf; 1047156230Smux } 1048156230Smux free(zf); 1049156230Smux} 1050156230Smux 1051156230Smuxstatic int 1052156230Smuxzfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) 1053156230Smux{ 1054156230Smux struct zfilter *zf; 1055156230Smux struct buf *zbuf; 1056156230Smux z_stream *state; 1057156230Smux size_t lastin, lastout, ate, prod; 1058156230Smux int done, error, flags, rv; 1059156230Smux 1060156230Smux zf = stream->fdata; 1061156230Smux state = zf->wrstate; 1062156230Smux zbuf = zf->wrbuf; 1063156230Smux 1064156230Smux if (how == STREAM_FLUSH_NORMAL) 1065156230Smux flags = Z_SYNC_FLUSH; 1066156230Smux else 1067156230Smux flags = Z_FINISH; 1068156230Smux 1069156230Smux done = 0; 1070156230Smux rv = Z_OK; 1071156230Smux 1072156230Smuxagain: 1073156230Smux /* 1074156230Smux * According to zlib.h, we should have at least 6 bytes 1075156230Smux * available when using deflate() with Z_SYNC_FLUSH. 1076156230Smux */ 1077156230Smux if ((buf_avail(zbuf) < 6 && flags == Z_SYNC_FLUSH) || 1078156230Smux rv == Z_BUF_ERROR || buf_avail(buf) == 0) { 1079156230Smux error = stream_flush_default(stream, zbuf, how); 1080156230Smux if (error) 1081156230Smux return (error); 1082156230Smux } 1083156230Smux 1084156230Smux state->next_in = (Bytef *)(buf->buf + buf->off); 1085156230Smux state->avail_in = buf_count(buf); 1086156230Smux state->next_out = (Bytef *)(zbuf->buf + zbuf->off + zbuf->in); 1087156230Smux state->avail_out = buf_avail(zbuf); 1088156230Smux lastin = state->avail_in; 1089156230Smux lastout = state->avail_out; 1090156230Smux rv = deflate(state, flags); 1091156230Smux if (rv != Z_BUF_ERROR && rv != Z_OK && rv != Z_STREAM_END) 1092156230Smux errx(1, "deflate: %s", state->msg); 1093156230Smux ate = lastin - state->avail_in; 1094156230Smux prod = lastout - state->avail_out; 1095156230Smux buf_less(buf, ate); 1096156230Smux buf_more(zbuf, prod); 1097156230Smux if ((flags == Z_SYNC_FLUSH && buf_count(buf) > 0) || 1098156230Smux (flags == Z_FINISH && rv != Z_STREAM_END) || 1099156230Smux (rv == Z_BUF_ERROR)) 1100156230Smux goto again; 1101156230Smux 1102156230Smux assert(rv == Z_OK || (rv == Z_STREAM_END && flags == Z_FINISH)); 1103156230Smux error = stream_flush_default(stream, zbuf, how); 1104156230Smux return (error); 1105156230Smux} 1106156230Smux 1107156230Smuxstatic ssize_t 1108156230Smuxzfilter_fill(struct stream *stream, struct buf *buf) 1109156230Smux{ 1110156230Smux struct zfilter *zf; 1111156230Smux struct buf *zbuf; 1112156230Smux z_stream *state; 1113156230Smux size_t lastin, lastout, new; 1114156230Smux ssize_t n; 1115156230Smux int rv; 1116156230Smux 1117156230Smux zf = stream->fdata; 1118156230Smux state = zf->rdstate; 1119156230Smux zbuf = zf->rdbuf; 1120156230Smux 1121156230Smux assert(buf_avail(buf) > 0); 1122156230Smux if (buf_count(zbuf) == 0) { 1123156230Smux n = stream_fill_default(stream, zbuf); 1124156230Smux if (n <= 0) 1125156230Smux return (n); 1126156230Smux } 1127156230Smuxagain: 1128156230Smux assert(buf_count(zbuf) > 0); 1129156230Smux state->next_in = (Bytef *)(zbuf->buf + zbuf->off); 1130156230Smux state->avail_in = buf_count(zbuf); 1131156230Smux state->next_out = (Bytef *)(buf->buf + buf->off + buf->in); 1132156230Smux state->avail_out = buf_avail(buf); 1133156230Smux lastin = state->avail_in; 1134156230Smux lastout = state->avail_out; 1135156230Smux rv = inflate(state, Z_SYNC_FLUSH); 1136156230Smux buf_less(zbuf, lastin - state->avail_in); 1137156230Smux new = lastout - state->avail_out; 1138156230Smux if (new == 0 && rv != Z_STREAM_END) { 1139156230Smux n = stream_fill_default(stream, zbuf); 1140156230Smux if (n == -1) 1141156230Smux return (-1); 1142156230Smux if (n == 0) 1143156230Smux return (0); 1144156230Smux goto again; 1145156230Smux } 1146156230Smux if (rv != Z_STREAM_END && rv != Z_OK) 1147156230Smux errx(1, "inflate: %s", state->msg); 1148156230Smux if (rv == Z_STREAM_END) 1149156230Smux zf->flags |= ZFILTER_EOF; 1150156230Smux buf_more(buf, new); 1151156230Smux return (new); 1152156230Smux} 1153156230Smux 1154156230Smux/* The MD5 stream filter implementation. */ 1155156230Smuxstatic int 1156156230Smuxmd5filter_init(struct stream *stream, void *data) 1157156230Smux{ 1158156230Smux struct md5filter *mf; 1159156230Smux 1160156230Smux mf = xmalloc(sizeof(struct md5filter)); 1161156230Smux MD5_Init(&mf->ctx); 1162156230Smux mf->md5 = data; 1163186781Slulf mf->lastc = ';'; 1164186781Slulf mf->state = PRINT; 1165156230Smux stream->fdata = mf; 1166156230Smux return (0); 1167156230Smux} 1168156230Smux 1169156230Smuxstatic void 1170156230Smuxmd5filter_fini(struct stream *stream) 1171156230Smux{ 1172156230Smux struct md5filter *mf; 1173156230Smux 1174156230Smux mf = stream->fdata; 1175156230Smux MD5_End(mf->md5, &mf->ctx); 1176156230Smux free(stream->fdata); 1177156230Smux} 1178156230Smux 1179156230Smuxstatic ssize_t 1180156230Smuxmd5filter_fill(struct stream *stream, struct buf *buf) 1181156230Smux{ 1182156230Smux ssize_t n; 1183156230Smux 1184156230Smux assert(buf_avail(buf) > 0); 1185156230Smux n = stream_fill_default(stream, buf); 1186156230Smux return (n); 1187156230Smux} 1188156230Smux 1189156230Smuxstatic int 1190156230Smuxmd5filter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) 1191156230Smux{ 1192156230Smux struct md5filter *mf; 1193156230Smux int error; 1194156230Smux 1195156230Smux mf = stream->fdata; 1196156230Smux MD5_Update(&mf->ctx, buf->buf + buf->off, buf->in); 1197156230Smux error = stream_flush_default(stream, buf, how); 1198156230Smux return (error); 1199156230Smux} 1200186781Slulf 1201186781Slulf/* MD5 flush for RCS, where whitespaces are omitted. */ 1202186781Slulfstatic int 1203186781Slulfmd5rcsfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) 1204186781Slulf{ 1205186781Slulf struct md5filter *mf; 1206186781Slulf char *ptr, *end; 1207186781Slulf char *start; 1208186781Slulf char space[2]; 1209186781Slulf int error; 1210186781Slulf 1211186781Slulf mf = stream->fdata; 1212186781Slulf space[0] = ' '; 1213186781Slulf space[1] = '\0'; 1214186781Slulf ptr = buf->buf + buf->off; 1215186781Slulf end = buf->buf + buf->off + buf->in; 1216186781Slulf 1217186781Slulf#define IS_WS(var) ((var) == ' ' || (var) == '\n' || (var) == '\t' || \ 1218186781Slulf (var) == '\010' || (var) == '\013' || (var) == '\f' || \ 1219186781Slulf (var) == '\r') 1220186781Slulf 1221186781Slulf#define IS_SPECIAL(var) ((var) == '$' || (var) == ',' || (var) == ':' || \ 1222186781Slulf (var) == ';' || (var) == '@') 1223186781Slulf 1224186781Slulf#define IS_PRINT(var) (!IS_WS(var) && (var) != '@') 1225186781Slulf 1226186781Slulf /* XXX: We can do better than this state machine. */ 1227186781Slulf while (ptr < end) { 1228186781Slulf switch (mf->state) { 1229186781Slulf /* Outside RCS statements. */ 1230186781Slulf case PRINT: 1231186781Slulf start = ptr; 1232186781Slulf while (ptr < end && IS_PRINT(*ptr)) { 1233186781Slulf mf->lastc = *ptr; 1234186781Slulf ptr++; 1235186781Slulf } 1236186781Slulf MD5_Update(&mf->ctx, start, (ptr - start)); 1237186781Slulf if (ptr < end) { 1238186781Slulf if (*ptr == '@') { 1239186781Slulf MD5_Update(&mf->ctx, ptr, 1); 1240186781Slulf ptr++; 1241186781Slulf mf->state = STRING; 1242186781Slulf } else { 1243186781Slulf mf->state = WS; 1244186781Slulf } 1245186781Slulf } 1246186781Slulf break; 1247186781Slulf case WS: 1248186781Slulf while (ptr < end && IS_WS(*ptr)) { 1249186781Slulf ptr++; 1250186781Slulf } 1251186781Slulf if (ptr < end) { 1252186781Slulf if (*ptr == '@') { 1253186781Slulf if (mf->lastc == '@') { 1254186781Slulf MD5_Update(&mf->ctx, 1255186781Slulf space, 1); 1256186781Slulf } 1257186781Slulf MD5_Update(&mf->ctx, ptr, 1); 1258186781Slulf ptr++; 1259186781Slulf mf->state = STRING; 1260186781Slulf } else { 1261186781Slulf if (!IS_SPECIAL(*ptr) && 1262186781Slulf !IS_SPECIAL(mf->lastc)) { 1263186781Slulf MD5_Update(&mf->ctx, 1264186781Slulf space, 1); 1265186781Slulf } 1266186781Slulf mf->state = PRINT; 1267186781Slulf } 1268186781Slulf } 1269186781Slulf break; 1270186781Slulf case STRING: 1271186781Slulf start = ptr; 1272186781Slulf while (ptr < end && *ptr != '@') { 1273186781Slulf ptr++; 1274186781Slulf } 1275186781Slulf MD5_Update(&mf->ctx, start, (ptr - start)); 1276186781Slulf if (ptr < end) { 1277186781Slulf MD5_Update(&mf->ctx, ptr, 1); 1278186781Slulf ptr++; 1279186781Slulf mf->state = SEEN; 1280186781Slulf } 1281186781Slulf break; 1282186781Slulf case SEEN: 1283186781Slulf if (*ptr == '@') { 1284186781Slulf MD5_Update(&mf->ctx, ptr, 1); 1285186781Slulf ptr++; 1286186781Slulf mf->state = STRING; 1287186781Slulf } else if(IS_WS(*ptr)) { 1288186781Slulf mf->lastc = '@'; 1289186781Slulf mf->state = WS; 1290186781Slulf } else { 1291186781Slulf mf->state = PRINT; 1292186781Slulf } 1293186781Slulf break; 1294186781Slulf default: 1295186781Slulf err(1, "Invalid state"); 1296186781Slulf break; 1297186781Slulf } 1298186781Slulf } 1299186781Slulf 1300186781Slulf error = stream_flush_default(stream, buf, how); 1301186781Slulf return (error); 1302186781Slulf} 1303186781Slulf 1304