/* ** Copyright (C) 2006-2007 by Carnegie Mellon University. ** ** @OPENSOURCE_HEADER_START@ ** ** Use of the SILK system and related source code is subject to the terms ** of the following licenses: ** ** GNU Public License (GPL) Rights pursuant to Version 2, June 1991 ** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013 ** ** NO WARRANTY ** ** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER ** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY ** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN ** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY ** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT ** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE ** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, ** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY ** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF ** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. ** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF ** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON ** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE ** DELIVERABLES UNDER THIS LICENSE. ** ** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie ** Mellon University, its trustees, officers, employees, and agents from ** all claims or demands made against them (and any related losses, ** expenses, or attorney's fees) arising out of, or relating to Licensee's ** and/or its sub licensees' negligent use or willful misuse of or ** negligent conduct or willful misconduct regarding the Software, ** facilities, or other rights or assistance granted by Carnegie Mellon ** University under this License, including, but not limited to, any ** claims of product liability, personal injury, death, damage to ** property, or violation of any laws or regulations. ** ** Carnegie Mellon University Software Engineering Institute authored ** documents are sponsored by the U.S. Department of Defense under ** Contract F19628-00-C-0003. Carnegie Mellon University retains ** copyrights in all material produced under this contract. The U.S. ** Government retains a non-exclusive, royalty-free license to publish or ** reproduce these documents, or allow others to do so, for U.S. ** Government purposes only pursuant to the copyright license under the ** contract clause at 252.227.7013. ** ** @OPENSOURCE_HEADER_END@ */ /* ** skstream.c ** Mark Thomas July-2006 ** ** skstream provides a wrapper around file pointers and file ** descriptors. It handles both textual and binary data. */ #include "silk.h" RCSIDENT("$SiLK: skstream.c 7696 2007-06-28 17:39:18Z mthomas $"); #include "skstream.h" #include "skiobuf.h" #include "sksite.h" struct _skstream { int fd; FILE *fp; #ifdef HAVE_ZLIB_H /* When the entire file has been compressed, we use gzread/gzwrite * to process the file, this is interface to those functions */ gzFile gz; #endif sk_iobuf_t *iobuf; skstream_mode_t io_mode; void *errobj; int errnum; char pathname[PATH_MAX+1]; silk_endian_t byte_order; fileFormat_t silk_format; fileVersion_t silk_version; sk_compmethod_t silk_compmethod; uint16_t rec_len; uint16_t rec_version; uint32_t rec_offset; char *pager; char *comment_start; skcontent_t content_type; unsigned is_seekable :1; unsigned is_big_endian :1; unsigned is_silk :1; unsigned is_pager_active :1; unsigned is_binary :1; unsigned is_terminal :1; unsigned is_dirty :1; unsigned is_closed :1; unsigned is_mpi :1; }; /* LOCAL FUNCTIONS */ static int _streamAssertModifiable( skstream_t *stream); static int _streamAssertOpen( const skstream_t *stream); static int _streamAssertUnopened( const skstream_t *stream); static int _streamCreateIOBuffer( skstream_t *stream); static int _streamInvokePager( skstream_t *stream); static int _streamOpenAppend( skstream_t *stream); static int _streamOpenRead( skstream_t *stream); static int _streamOpenWrite( skstream_t *stream); static int _streamPathnameIsCompressed( const char *pathname, skstream_mode_t read_write_append); static int _streamPrepareText( skstream_t *stream); /* FUNCTION DEFINITIONS */ /* * status = _streamAssertAttributes(stream, io_mode_list, content_type_list); * */ static int _streamAssertAttributes( skstream_t *stream, int io_mode_list, int content_type_list) { if (stream == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } else if ( !(stream->io_mode & io_mode_list)) { return SKSTREAM_ERR_UNSUPPORT_IOMODE; } else if ( !(stream->content_type & content_type_list)) { return SKSTREAM_ERR_UNSUPPORT_CONTENT; } else { return SKSTREAM_OK; } } /* * status = _streamAssertModifiable(stream); * * Return SKSTREAM_OK if the caller is still allowed to set aspects * of 'stream'; otherwise return the reason why 'stream' cannot be * modified. */ static int _streamAssertModifiable( skstream_t *stream) { if (stream == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } else if (stream->is_closed) { return SKSTREAM_ERR_CLOSED; } else if (stream->is_dirty) { return SKSTREAM_ERR_PREV_DATA; } else { return SKSTREAM_OK; } } /* * status = _streamAssertOpen(stream); * * Call this function on a stream which you expect to be open; it * will return SKSTREAM_OK if 'stream' is open, or an error code * explaining why 'stream' is not open. * * A stream that has been opened and closed is neither open nor * unopened. */ static int _streamAssertOpen( const skstream_t *stream) { if (stream == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } else if (stream->is_closed) { return SKSTREAM_ERR_CLOSED; } else if (stream->fd == -1) { return SKSTREAM_ERR_NOT_OPEN; } else { return SKSTREAM_OK; } } /* * status = _streamAssertUnopened(stream); * * Call this function on a stream which you expect to be * unopened---i.e., not yet open. It will return SKSTREAM_OK if * 'stream' is unopened, or an error code explaining why 'stream' * is not considered unopened. * * A stream that has been opened and closed is neither open nor * unopened. */ static int _streamAssertUnopened( const skstream_t *stream) { if (stream == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } else if (stream->is_closed) { return SKSTREAM_ERR_CLOSED; } else if (stream->fd != -1) { return SKSTREAM_ERR_PREV_OPEN; } else { return SKSTREAM_OK; } } /* * status = _streamCreateIOBuffer(stream); * * Create the skIOBuf that 'stream' will read-from/write-to, and * bind it to the file descriptor or gzfile. Return SKSTREAM_OK on * success, or an error code on failure. */ static int _streamCreateIOBuffer( skstream_t *stream) { int rv = SKSTREAM_OK; assert(stream); assert(stream->fd != -1); /* create the iobuf */ switch (stream->io_mode) { case SK_IO_READ: /* create the buffered reader */ stream->iobuf = skIOBufCreateReader(); break; case SK_IO_WRITE: case SK_IO_APPEND: stream->iobuf = skIOBufCreateWriter(); break; } if (stream->iobuf == NULL) { rv = SKSTREAM_ERR_ALLOC; goto END; } /* set the record size */ if (skIOBufSetRecordSize(stream->iobuf, stream->rec_len) == -1) { /* stream->errobj.str = skIOBufStrError(stream->iobuf); */ rv = SKSTREAM_ERR_IOBUF; goto END; } /* bind it to the file descriptor or gzfile */ #ifdef HAVE_ZLIB_H if (stream->gz) { if (skIOBufBindGzip(stream->iobuf,stream->gz,stream->silk_compmethod) == -1) { /* stream->errobj.str = skIOBufStrError(stream->iobuf); */ rv = SKSTREAM_ERR_IOBUF; goto END; } } else #endif { if (skIOBufBind(stream->iobuf, stream->fd, stream->silk_compmethod) == -1) { /* stream->errobj.str = skIOBufStrError(stream->iobuf); */ rv = SKSTREAM_ERR_IOBUF; goto END; } } END: return rv; } /* * status = _streamIOBufGetLine(stream, out_buffer, buf_size); * * Fill 'out_buffer' with the next '\n'-delimited line of text from * the IOBuf associated with the 'stream'. The '\n' is replaced * with '\0'. If the final input is smaller than 'buf_size' and * does not contain a '\n' it will be copied into 'out_buffer'. * Return SKSTREAM_OK on success. * * If there is no '\n' within the first 'buf_size' characters of the * input, return SKSTREAM_ERR_LONG_LINE and read from the IOBuf * until a '\n' is found or until end-of-file is reached. * * Return SKSTREAM_ERR_EOF when all input data has been processed. * * Return SKSTREAM_ERR_IOBUF is there is a problem reading from the * IOBuf. * * This function mimics fgets(). */ static int _streamIOBufGetLine( skstream_t *stream, char *out_buffer, size_t buf_size) { char *eol = NULL; ssize_t sz; int rv = SKSTREAM_OK; while (eol == NULL) { /* substract 1 for final '\0' */ sz = skIOBufReadToChar(stream->iobuf, out_buffer, buf_size-1, '\n'); if (sz == -1) { rv = SKSTREAM_ERR_IOBUF; break; } if (sz == 0) { rv = SKSTREAM_ERR_EOF; break; } if ((sz == (ssize_t)buf_size-1) && ('\n' != out_buffer[sz-1])) { /* Found no newline in 'buf_size' characters... */ rv = SKSTREAM_ERR_LONG_LINE; /* need to read more from skiobuf to find next '\n' */ continue; } /* NUL terminate the string, either by replacing '\n' with a * '\0', or by putting a '\0' after the final character. */ eol = &out_buffer[sz-1]; if (*eol != '\n') { ++eol; } *eol = '\0'; } return rv; } static int _streamInvokePager( skstream_t *stream) { int rv; pid_t pid; int wait_status; rv = _streamAssertModifiable(stream); if (rv) { goto END; } if (stream->pager == NULL) { goto END; } if ( !stream->is_terminal) { goto END; } #if 1 /* invoke the pager */ stream->fp = popen(stream->pager, "w"); if (NULL == stream->fp) { rv = SKSTREAM_ERR_NOPAGER; goto END; } /* see if pager started. There is a race condition here, and this * assumes we have only one child, which should be true. */ pid = wait4(0, &wait_status, WNOHANG, NULL); if (pid) { rv = SKSTREAM_ERR_NOPAGER; goto END; } #else { int pipe_des[2]; /* create pipe and fork */ if (pipe(pipe_des) == -1) { skAppPrintErr("Cannot create pipe: %s", strerror(errno)); return -1; } pid = fork(); if (pid < 0) { skAppPrintErr("Cannot fork: %s", strerror(errno)); return -1; } if (pid == 0) { /* CHILD */ /* close output side of pipe; set input to stdin */ close(pipe_des[1]); if (pipe_des[0] != STDIN_FILENO) { dup2(pipe_des[0], STDIN_FILENO); close(pipe_des[0]); } /* invoke pager */ execlp(pager, NULL); skAppPrintErr("Unable to invoke pager '%s': %s", pager, strerror(errno)); _exit(EXIT_FAILURE); } /* PARENT */ /* close input side of pipe */ close(pipe_des[0]); /* try to open the write side of the pipe */ out = fdopen(pipe_des[1], "w"); if (NULL == out) { skAppPrintErr("Cannot fdopen: %s", strerror(errno)); return -1; } /* it'd be nice to have a slight pause here to give child time to * die if command cannot be exec'ed, but it's not worth the * trouble to use select(), and sleep(1) is too long. */ /* see if child died unexpectedly */ if (waitpid(pid, &wait_status, WNOHANG)) { skAppPrintErr("Unable to invoke pager '%s'", pager); return -1; } } #endif /* 1: whether to use popen() */ /* looks good. */ stream->is_pager_active = 1; END: return rv; } /* * status = _streamOpenAppend(stream); * * Open the stream for appending. */ static int _streamOpenAppend( skstream_t *stream) { int rv = SKSTREAM_OK; int flags = O_RDWR | O_APPEND; assert(stream); assert(stream->pathname); /* Open file for read and write; position at start. */ stream->fd = open(stream->pathname, flags, 0); if (stream->fd == -1) { stream->errnum = errno; rv = SKSTREAM_ERR_SYS_OPEN; goto END; } END: return rv; } static int _streamOpenGzip( skstream_t *stream) { int is_compressed = 1; int num_read; uint8_t magic[3]; int rv = LIBRW_OK; if (stream->io_mode == SK_IO_READ && stream->is_seekable) { /* Read the first two characters to look for the GZIP magic * number (31 139 (see RFC1952)) to see if the stream really * is compressed. */ num_read = read(stream->fd, magic, 2); if ((num_read != 2) || (magic[0] != 31u) || (magic[1] != 139u)) { /* File does not contain the gzip magic number. */ is_compressed = 0; } if (0 != lseek(stream->fd, 0, SEEK_SET)) { rv = SKSTREAM_ERR_SYS_LSEEK; goto END; } } if (is_compressed) { #ifdef HAVE_ZLIB_H stream->gz = gzdopen(stream->fd, "wb"); if (stream->gz == NULL) { rv = SKSTREAM_ERR_ALLOC; goto END; } #else /* compression not supported */ rv = SKSTREAM_ERR_UNSUPPORT_COMPRESS; goto END; #endif /* HAVE_ZLIB_H */ } END: return rv; } /* * status = _streamOpenRead(stream); * * Open the stream for reading. */ static int _streamOpenRead( skstream_t *stream) { int rv = SKSTREAM_OK; assert(stream); assert(stream->pathname[0]); assert(stream->io_mode == SK_IO_READ); assert(-1 == stream->fd); if (0 == strcmp(stream->pathname, "stdin")) { stream->fd = STDIN_FILENO; } else if (stream->is_mpi) { /* for now, just set to a valid value. we should replace the * checks of 'fd' with an 'is_open' flag */ stream->fd = INT32_MAX; } else { stream->fd = open(stream->pathname, O_RDONLY); if (stream->fd == -1) { rv = SKSTREAM_ERR_SYS_OPEN; stream->errnum = errno; goto END; } } END: /* if something went wrong, close the file */ if (rv != SKSTREAM_OK) { if (stream->fd != -1) { close(stream->fd); stream->fd = -1; } } return rv; } static int _streamOpenWrite( skstream_t *stream) { int rv = SKSTREAM_OK; assert(stream); assert(stream->pathname[0]); assert(stream->io_mode == SK_IO_WRITE); if (0 == strcmp(stream->pathname, "stdout")) { stream->fd = STDOUT_FILENO; } else if (0 == strcmp(stream->pathname, "stderr")) { stream->fd = STDERR_FILENO; } else { struct stat stbuf; int mode, flags; /* standard mode of 0666 */ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; /* assume creating previously non-existent file */ flags = O_WRONLY | O_CREAT | O_EXCL; /* try to open as a brand new file */ stream->fd = open(stream->pathname, flags, mode); if (stream->fd == -1) { stream->errnum = errno; if ((stream->errnum == EEXIST) && (0 == stat(stream->pathname, &stbuf))) { /* file exists. If it is a FIFO or a character device * ("/dev/null"), try again with different flags */ if (S_ISFIFO(stbuf.st_mode)) { flags = O_WRONLY; } else if (S_ISCHR(stbuf.st_mode)) { flags = O_WRONLY | O_NOCTTY; } else { rv = SKSTREAM_ERR_FILE_EXISTS; goto END; } /* try again with the new flags */ stream->fd = open(stream->pathname, flags, mode); } /* if we (still) have an error, return */ if (stream->fd == -1) { /* we set errnum above */ rv = SKSTREAM_ERR_SYS_OPEN; goto END; } } } END: return rv; } /* * is_compressed = _streamPathnameIsCompressed(pathname, io_mode); * * Return TRUE if 'pathname' should be considered a compressed file * for the given IO mode---that is, where the entire file is * compressed---or FALSE otherwise. * * Basically, returns TRUE when 'pathname' ends in ".gz" or when it * is open for reading and contains the substring ".gz."---assuming * the pathname has had a mkstemp() suffix added to it. In all * other cases, it returns FALSE. */ static int _streamPathnameIsCompressed( const char *pathname, skstream_mode_t read_write_append) { const char *gz; gz = strstr(pathname, ".gz"); if (gz != NULL) { switch (read_write_append) { case SK_IO_READ: case SK_IO_APPEND: if (gz[3] == '.') { return 1; } /* FALLTHROUGH */ case SK_IO_WRITE: if (gz[3] == '\0') { return 1; } break; } } return 0; } static int _streamPostOpen( skstream_t *stream) { int rv = SKSTREAM_OK; assert(stream); assert(stream->fd != -1); if (!stream->is_mpi) { if (isatty(stream->fd)) { stream->is_terminal = 1; } else if (lseek(stream->fd, 0, SEEK_CUR) != (off_t)-1) { stream->is_seekable = 1; } /* handle compressed files */ if (_streamPathnameIsCompressed(stream->pathname, stream->io_mode)) { rv = _streamOpenGzip(stream); if (rv) { goto END; } } } /* create the IOBuf now if this is a non-silk binary file, or if * it is a text file we are reading. */ if ((stream->content_type == SK_CONTENT_OTHERBINARY) || (stream->content_type == SK_CONTENT_TEXT && stream->io_mode == SK_IO_READ)) { rv = _streamCreateIOBuffer(stream); if (rv) { goto END; } } END: return rv; } static int _streamPrepareText( skstream_t *stream) { int rv; rv = _streamAssertOpen(stream); if (rv) { goto END; } if (stream->fp == NULL) { const char *mode = NULL; switch (stream->io_mode) { case SK_IO_READ: break; case SK_IO_WRITE: if (stream->pager) { rv = _streamInvokePager(stream); if (rv) { goto END; } } if (stream->fp == NULL) { mode = "w"; } break; case SK_IO_APPEND: mode = "r+"; break; } if (mode) { stream->fp = fdopen(stream->fd, mode); if (stream->fp == NULL) { stream->errnum = errno; rv = SKSTREAM_ERR_SYS_FDOPEN; goto END; } } } stream->is_dirty = 1; END: return rv; } /* * status = skStreamBind(stream, path); * * Set 'stream' to operate on the file specified in 'path'; 'path' * may also be one of "stdin", "stdout", or "stderr". Returns * SKSTREAM_OK on success, or an error code on failure. */ int skStreamBind( skstream_t *stream, const char *pathname) { int rv = SKSTREAM_OK; FILE *s = NULL; /* check name */ if (NULL == stream || NULL == pathname || '\0' == *pathname) { rv = SKSTREAM_ERR_NULL_ARGUMENT; goto END; } if (stream->pathname[0]) { rv = SKSTREAM_ERR_PREV_BOUND; goto END; } /* copy it into place; if it is too long, truncate it and return * an error */ strncpy(stream->pathname, pathname, sizeof(stream->pathname)); if (stream->pathname[sizeof(stream->pathname)-1] != '\0') { stream->pathname[sizeof(stream->pathname)-1] = '\0'; rv = SKSTREAM_ERR_INVALID_INPUT; goto END; } if (0 == strcmp(pathname, "stdin")) { switch (stream->io_mode) { case SK_IO_READ: if (!stream->is_mpi && stream->is_binary && FILEIsATty(stdin)) { rv = SKSTREAM_ERR_ISTERMINAL; goto END; } break; case SK_IO_WRITE: case SK_IO_APPEND: /* cannot write or append to stdin */ rv = SKSTREAM_ERR_UNSUPPORT_IOMODE; goto END; } } else if (0 == strcmp(pathname, "stdout")) { s = stdout; } else if (0 == strcmp(pathname, "stderr")) { s = stderr; } if (s) { switch (stream->io_mode) { case SK_IO_READ: case SK_IO_APPEND: /* cannot read or append to stdout/stderr */ rv = SKSTREAM_ERR_UNSUPPORT_IOMODE; goto END; case SK_IO_WRITE: if (!stream->is_mpi && stream->is_binary && FILEIsATty(s)) { rv = SKSTREAM_ERR_ISTERMINAL; goto END; } break; } } /* cannot append to FIFOs or to gzipped files */ if (stream->io_mode == SK_IO_APPEND) { if (_streamPathnameIsCompressed(stream->pathname, stream->io_mode)) { rv = SKSTREAM_ERR_UNSUPPORT_IOMODE; goto END; } if (isFIFO(pathname)) { /* Cannot append to a FIFO */ rv = SKSTREAM_ERR_UNSUPPORT_IOMODE; goto END; } } END: return rv; } int skStreamCheckCompmethod( skstream_t *stream, sk_msg_fn_t err_fn) { if (sksiteCompmethodIsAvailable(stream->silk_compmethod)) { return SKSTREAM_OK; } if (err_fn) { if (sksiteCompmethodIsValid(stream->silk_compmethod)) { char name[64]; sksiteCompmethodGetName(name, sizeof(name), stream->silk_compmethod); err_fn("The %s compression method used by '%s' is not available", name, stream->pathname); } else { err_fn("File '%s' is compressed with an unrecognized method %d", stream->pathname, stream->silk_compmethod); } } return SKSTREAM_ERR_UNSUPPORT_COMPRESS; } int skStreamClose( skstream_t *stream) { int rv; rv = _streamAssertOpen(stream); if (rv) { goto END; } if (stream->fp) { if (stream->io_mode != SK_IO_READ){ if (EOF == fflush(stream->fp)) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } } if (stream->is_pager_active) { if (pclose(stream->fp) == -1) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } } else { if (EOF == fclose(stream->fp)) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } } } else if (stream->fd != -1) { /* Flush the skIOBuf is we're not reading */ if ((stream->io_mode != SK_IO_READ) && (stream->iobuf)) { if (skIOBufFlush(stream->iobuf) == -1) { rv = SKSTREAM_ERR_IOBUF; } } #ifdef HAVE_ZLIB_H /* Close the gzFile */ if (stream->gz) { int zerr = gzclose(stream->gz); if (zerr != Z_OK) { if (zerr == Z_ERRNO) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } else { rv = SKSTREAM_ERR_WRITE; } } /* gzclose() closes the file */ stream->fd = -1; } else #endif /* HAVE_ZLIB_H */ { if (stream->fd != STDIN_FILENO) { if (close(stream->fd) == -1) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } } } } stream->fd = -1; stream->fp = NULL; stream->is_closed = 1; END: return rv; } /* * status = skStreamCreate(&out_stream, io_mode, content_type); * * Create a stream (skstream_t*) and fill 'out_stream' with the * address of the newly allocated stream. In addition, bind the * stream to the given 'path', with IO in the specified 'io_mode'. * Return SKSTREAM_OK on success, or an error code on failure. */ int skStreamCreate( skstream_t **new_stream, skstream_mode_t read_write_append, skcontent_t content_type) { if (new_stream == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } *new_stream = calloc(1, sizeof(skstream_t)); if (NULL == *new_stream) { return SKSTREAM_ERR_ALLOC; } (*new_stream)->io_mode = read_write_append; (*new_stream)->content_type = content_type; (*new_stream)->fd = -1; (*new_stream)->rec_len = 1; (*new_stream)->silk_compmethod = SK_COMPMETHOD_NONE; switch (content_type) { case SK_CONTENT_TEXT: break; case SK_CONTENT_OTHERBINARY: (*new_stream)->is_binary = 1; break; case SK_CONTENT_SILK: (*new_stream)->is_silk = 1; (*new_stream)->is_binary = 1; (*new_stream)->byte_order = SILK_ENDIAN_NATIVE; (*new_stream)->silk_format = UINT8_MAX; (*new_stream)->silk_version = SK_FILE_VERSION_ANY; (*new_stream)->silk_compmethod = sksiteCompmethodGetDefault(); break; } return SKSTREAM_OK; } int skStreamDestroy( skstream_t **stream) { int rv; if ((NULL == stream) || (NULL == *stream)) { return SKSTREAM_OK; } rv = skStreamUnbind(*stream); /* Destroy the iobuf */ if ((*stream)->iobuf) { skIOBufDestroy((*stream)->iobuf); (*stream)->iobuf = NULL; } free(*stream); *stream = NULL; return rv; } int skStreamFDOpen( skstream_t *stream, int file_desc) { int rv; rv = _streamAssertUnopened(stream); if (rv) { goto END; } if (stream->pathname[0] == '\0') { rv = SKSTREAM_ERR_NOT_BOUND; goto END; } if (file_desc == -1) { rv = SKSTREAM_ERR_INVALID_INPUT; goto END; } stream->fd = file_desc; rv = _streamPostOpen(stream); if (rv) { goto END; } END: return rv; } int skStreamFlush( skstream_t *stream) { int rv; rv = _streamAssertOpen(stream); if (rv) { goto END; } if (stream->io_mode == SK_IO_READ) { /* nothing to do for a reader */ goto END; } if (stream->fp) { if (EOF == fflush(stream->fp)) { stream->errnum = errno; rv = SKSTREAM_ERR_WRITE; } } else if (stream->iobuf) { if (skIOBufFlush(stream->iobuf) == -1) { rv = SKSTREAM_ERR_IOBUF; goto END; } } END: return rv; } sk_compmethod_t skStreamGetCompressionMethod( skstream_t *stream) { return stream->silk_compmethod; } /* Get the next line from a text file */ int skStreamGetLine( skstream_t *stream, char *out_buffer, size_t buf_size, int *lines_read) { size_t len; int rv = SKSTREAM_OK; assert(stream); if ( !stream->is_dirty) { rv = _streamAssertOpen(stream); if (rv) { goto END; } rv = _streamAssertAttributes(stream, SK_IO_READ, SK_CONTENT_TEXT); if (rv) { goto END; } rv = _streamPrepareText(stream); if (rv) { goto END; } } assert(out_buffer && buf_size); out_buffer[0] = '\0'; /* read from the stream until we get a good line */ while (1) { rv = _streamIOBufGetLine(stream, out_buffer, buf_size); if (rv != SKSTREAM_OK) { if ((rv == SKSTREAM_ERR_LONG_LINE) && lines_read) { ++*lines_read; } break; } if (lines_read) { ++*lines_read; } /* Terminate line at first comment char */ if (stream->comment_start) { char *cp = strstr(out_buffer, stream->comment_start); if (cp) { *cp = '\0'; } } /* find first non-space character in the line */ len = strspn(out_buffer, " \t\v\f\r\n"); if (out_buffer[len] == '\0') { /* line contained whitespace only; ignore */ continue; } /* got a line, break out of loop */ break; } END: return rv; } const char *skStreamGetPager( const skstream_t *stream) { if (stream->is_closed) { return NULL; } else if (stream->is_pager_active) { /* stream is open and pager is in use */ return stream->pager; } else if (stream->fd == -1) { /* unopened, return pager we *may* use */ return stream->pager; } else { /* stream is open and not using pager */ return NULL; } } const char *skStreamGetPathname( const skstream_t *stream) { if (stream->pathname[0]) { return stream->pathname; } return NULL; } fileFormat_t skStreamGetSilkFormat( const skstream_t *stream) { return stream->silk_format; } fileVersion_t skStreamGetSilkVersion( const skstream_t *stream) { return stream->silk_version; } int skStreamIsNativeByteOrder( const skstream_t *stream) { /* handle the cases where we know we have to swap */ #if IS_LITTLE_ENDIAN if (stream->byte_order == SILK_ENDIAN_BIG) { return 0; } #else if (stream->byte_order == SILK_ENDIAN_LITTLE) { return 0; } #endif return 1; } int skStreamLockFile( skstream_t *stream) { int rv; rv = _streamAssertOpen(stream); if (rv) { goto END; } /* Don't try to lock anything that is not a real file */ if ( !stream->is_seekable) { goto END; } if (stream->io_mode == SK_IO_READ) { rv = getRLockFD_Wait(stream->fd); if (rv != 0) { rv = SKSTREAM_ERR_RLOCK; stream->errnum = errno; } } else { rv = getWLockFD_Wait(stream->fd); if (rv != 0) { rv = SKSTREAM_ERR_WLOCK; stream->errnum = errno; } } END: return rv; } int skStreamMakeTemp( skstream_t *stream) { int rv; rv = _streamAssertUnopened(stream); if (rv) { goto END; } /* Temp files only make sense for writing */ if (stream->io_mode != SK_IO_WRITE) { rv = SKSTREAM_ERR_UNSUPPORT_IOMODE; goto END; } if (stream->pathname[0] == '\0') { rv = SKSTREAM_ERR_NOT_BOUND; goto END; } /* open file */ stream->fd = mkstemp(stream->pathname); if (stream->fd == -1) { rv = SKSTREAM_ERR_SYS_MKSTEMP; stream->errnum = errno; goto END; } rv = _streamPostOpen(stream); if (rv) { goto END; } END: return rv; } int skStreamOpen( skstream_t *stream) { int rv; rv = _streamAssertUnopened(stream); if (rv) { goto END; } if (stream->pathname[0] == '\0') { rv = SKSTREAM_ERR_NOT_BOUND; goto END; } switch (stream->io_mode) { case SK_IO_WRITE: rv = _streamOpenWrite(stream); if (rv) { goto END; } break; case SK_IO_READ: rv = _streamOpenRead(stream); if (rv) { goto END; } break; case SK_IO_APPEND: rv = _streamOpenAppend(stream); if (rv) { goto END; } break; } rv = _streamPostOpen(stream); if (rv) { goto END; } END: return rv; } int skStreamPageOutput( skstream_t *stream, const char *pager) { int rv; rv = _streamAssertModifiable(stream); if (rv) { goto END; } rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_TEXT); if (rv) { goto END; } /* get pager from environment if not passed in */ if (NULL == pager) { pager = getenv("SILK_PAGER"); if (NULL == pager) { pager = getenv("PAGER"); } } /* a NULL or an empty string pager means do nothing */ if ((NULL == pager) || ('\0' == pager[0])) { if (stream->pager) { free(stream->pager); stream->pager = NULL; } goto END; } if (stream->pager) { free(stream->pager); } stream->pager = strdup(pager); if (stream->pager == NULL) { rv = SKSTREAM_ERR_ALLOC; goto END; } /* if the stream is open, go ahead and invoke the pager now */ if (stream->fd != -1) { rv = _streamPrepareText(stream); if (rv) { goto END; } } END: return rv; } #if !defined(skStreamPrint) int skStreamPrint( skstream_t *stream, const char *format, ...) { int rv = SKSTREAM_OK; va_list args; va_start(args, format); if ( !stream->fp) { rv = _streamAssertOpen(stream); if (rv) { goto END; } rv = _streamAssertAttributes(stream, SK_IO_WRITE | SK_IO_APPEND, SK_CONTENT_TEXT); if (rv) { goto END; } rv = _streamPrepareText(stream); if (rv) { goto END; } } if (vfprintf(stream->fp, format, args) == -1) { rv = SKSTREAM_ERR_WRITE; stream->errnum = errno; } END: va_end(args); return rv; } #endif /* !defined(skStreamPrint) */ void skStreamPrintLastErr( const skstream_t *stream, int errcode, sk_msg_fn_t errfn) { const char *msg; if (errfn == NULL) { errfn = &skAppPrintErr; } #ifdef TEST_PRINTF_FORMATS #define errfn printf #endif /* macros for common error messages */ #define FILENAME_MSG(_msg) \ msg = (_msg); \ if (!stream) { errfn("%s", msg); } \ else { errfn("%s '%s'", msg, stream->pathname); } #define STRERROR_MSG(_msg) \ msg = (_msg); \ if (!stream) { errfn("%s", msg); } \ else if (stream->errnum == 0) { \ errfn("%s '%s'", msg, stream->pathname); } \ else { \ errfn("%s '%s': %s", \ msg, stream->pathname, strerror(stream->errnum)); \ } switch ((skstream_err_t)errcode) { case SKSTREAM_OK: errfn("Command completed successfully"); return; case SKSTREAM_ERR_ALLOC: errfn("Memory allocation failed"); return; case SKSTREAM_ERR_PREV_DATA: FILENAME_MSG("Initial data has already been read or written"); return; case SKSTREAM_ERR_BAD_MAGIC: FILENAME_MSG("File does not appear to be a SiLK data file"); return; case SKSTREAM_ERR_CLOSED: FILENAME_MSG("Cannot modify a stream once it is closed"); return; case SKSTREAM_ERR_EOF: FILENAME_MSG("Reached end of stream"); return; case SKSTREAM_ERR_FILE_EXISTS: STRERROR_MSG("Will not create new file over existing file"); return; case SKSTREAM_ERR_INVALID_INPUT: errfn("Argument's value is invalid"); return; case SKSTREAM_ERR_IOBUF: msg = "Cannot read-from/write-to a buffer"; if (!stream) { errfn("%s", msg); } else { errfn("%s '%s': %s", msg, stream->pathname, skIOBufStrError(stream->iobuf)); } return; case SKSTREAM_ERR_ISTERMINAL: FILENAME_MSG("Will not read/write binary data on a terminal"); return; case SKSTREAM_ERR_LONG_LINE: errfn("Input string is too long"); return; case SKSTREAM_ERR_NOPAGER: msg = "Unable to invoke pager"; if (!stream) { errfn("%s", msg); } else { errfn("%s '%s'", msg, stream->pager); } return; case SKSTREAM_ERR_NOT_BOUND: errfn("Stream is not bound to a file"); return; case SKSTREAM_ERR_NOT_OPEN: FILENAME_MSG("Cannot read/write/close an unopened stream"); return; case SKSTREAM_ERR_NULL_ARGUMENT: errfn("Unexpected NULL or empty argument"); return; case SKSTREAM_ERR_PREV_BOUND: errfn("Cannot bind stream because it is already bound"); return; case SKSTREAM_ERR_PREV_OPEN: FILENAME_MSG("Stream is already open"); return; case SKSTREAM_ERR_READ: STRERROR_MSG("Error reading from stream"); return; case SKSTREAM_ERR_RLOCK: STRERROR_MSG("Cannot get read lock on file"); return; case SKSTREAM_ERR_SYS_FDOPEN: STRERROR_MSG("Cannot convert file descriptor"); return; case SKSTREAM_ERR_SYS_LSEEK: STRERROR_MSG("Cannot seek on stream"); return; case SKSTREAM_ERR_SYS_MKSTEMP: STRERROR_MSG("Cannot create temporary file"); return; case SKSTREAM_ERR_SYS_OPEN: STRERROR_MSG("Error opening file"); return; case SKSTREAM_ERR_UNSUPPORT_COMPRESS: FILENAME_MSG("Specified compression method is not supported"); return; case SKSTREAM_ERR_UNSUPPORT_CONTENT: msg = "Action not supported on stream's content type"; if (!stream) { errfn("%s", msg); } else { const char *ct = ""; switch (stream->content_type) { case SK_CONTENT_SILK: ct = " is SiLK"; break; case SK_CONTENT_TEXT: ct = " is text"; break; case SK_CONTENT_OTHERBINARY: ct = " is binary"; break; } errfn("%s '%s'%s", msg, stream->pathname, ct); } return; case SKSTREAM_ERR_UNSUPPORT_IOMODE: msg = "Action not permitted on stream"; if (!stream) { errfn("%s", msg); } else { const char *io = ""; switch (stream->io_mode) { case SK_IO_READ: io = ": read from"; break; case SK_IO_WRITE: io = ": write to"; break; case SK_IO_APPEND: io = ": append to"; break; } errfn("%s%s '%s'", msg, io, stream->pathname); } return; case SKSTREAM_ERR_WLOCK: STRERROR_MSG("Cannot get write lock on file"); return; case SKSTREAM_ERR_WRITE: STRERROR_MSG("Error writing to stream"); return; case SKSTREAM_ERR_IO: STRERROR_MSG("Bad read/write"); return; } errfn("Unrecognized error code %d", errcode); } ssize_t skStreamRead( skstream_t *stream, void *buf, size_t count) { size_t left; ssize_t s; assert(stream); assert(buf); assert(stream->io_mode == SK_IO_READ || stream->io_mode == SK_IO_APPEND); assert(stream->content_type != SK_CONTENT_TEXT); assert(stream->is_binary); assert(stream->fd != -1); if (stream->iobuf) { return skIOBufRead(stream->iobuf, buf, count); } #ifdef HAVE_ZLIB_H if (stream->gz != NULL) { return gzread(stream->gz, buf, (unsigned)count); } #endif left = count; while (left > 0) { s = read(stream->fd, buf, left); if (s > 0) { left -= s; buf = (uint8_t*)buf + s; } else if (s == -1) { stream->errnum = errno; return -1; } else if (s == 0) { break; } else { /* s is negative? */ return -1; } } return (count - left); } int skStreamReadSilkHeader( skstream_t *stream, void *hdr, size_t hdr_len) { int rv; ssize_t bytes; rv = _streamAssertOpen(stream); if (rv) { return rv; } if (stream->is_dirty) { rv = SKSTREAM_ERR_PREV_DATA; goto END; } rv = _streamAssertAttributes(stream, SK_IO_READ, SK_CONTENT_SILK); if (rv) { return rv; } if (hdr_len < sizeof(genericHeader)) { goto END; } bytes = skStreamRead(stream, hdr, hdr_len); if (bytes != (ssize_t)hdr_len) { rv = SKSTREAM_ERR_READ; goto END; } if (CHECKMAGIC((genericHeader*)hdr)) { rv = SKSTREAM_ERR_BAD_MAGIC; goto END; } stream->is_big_endian = ((genericHeader*)hdr)->isBigEndian; stream->byte_order = (stream->is_big_endian ? SILK_ENDIAN_BIG : SILK_ENDIAN_LITTLE); stream->silk_format = ((genericHeader*)hdr)->type; stream->silk_version = ((genericHeader*)hdr)->version; stream->silk_compmethod = ((genericHeader*)hdr)->compMethod; stream->is_dirty = 1; if ( !sksiteCompmethodIsAvailable(stream->silk_compmethod)) { rv = SKSTREAM_ERR_UNSUPPORT_COMPRESS; goto END; } rv = _streamCreateIOBuffer(stream); if (rv) { goto END; } END: return rv; } int skStreamSetByteOrder( skstream_t *stream, silk_endian_t byte_order) { int rv; rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_SILK); if (rv) { goto END; } stream->byte_order = byte_order; END: return rv; } int skStreamSetCommentStart( skstream_t *stream, char *comment_start) { int rv; rv = _streamAssertAttributes(stream, SK_IO_READ, SK_CONTENT_TEXT); if (rv) { goto END; } /* clear existing value */ if (stream->comment_start) { free(stream->comment_start); } /* set to new value */ if (comment_start == NULL) { stream->comment_start = NULL; } else { stream->comment_start = strdup(comment_start); if (stream->comment_start == NULL) { rv = SKSTREAM_ERR_ALLOC; goto END; } } END: return rv; } int skStreamSetCompressionMethod( skstream_t *stream, sk_compmethod_t comp_method) { int rv; rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_SILK); if (rv) { goto END; } if (sksiteCompmethodIsAvailable(comp_method)) { stream->silk_compmethod = comp_method; } else if (sksiteCompmethodIsValid(comp_method)) { rv = SKSTREAM_ERR_UNSUPPORT_COMPRESS; } else { rv = SKSTREAM_ERR_INVALID_INPUT; } END: return rv; } int skStreamSetSilkFormat( skstream_t *stream, fileFormat_t file_format) { int rv; rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_SILK); if (rv) { goto END; } if (sksiteFileformatIsValid(file_format)) { stream->silk_format = file_format; } else { rv = SKSTREAM_ERR_INVALID_INPUT; } END: return rv; } int skStreamSetSilkVersion( skstream_t *stream, fileVersion_t version) { int rv; rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_SILK); if (rv) { goto END; } stream->silk_version = version; END: return rv; } int skStreamUnbind( skstream_t *stream) { int rv = SKSTREAM_OK; if (!stream) { return rv; } if (stream->fd != -1) { rv = skStreamClose(stream); } if (stream->comment_start) { free(stream->comment_start); stream->comment_start = NULL; } if (stream->pager) { free(stream->pager); stream->pager = NULL; } stream->pathname[0] = '\0'; return rv; } ssize_t skStreamWrite( skstream_t *stream, const void *buf, size_t count) { size_t left; ssize_t s; assert(stream); assert(stream->io_mode == SK_IO_WRITE || stream->io_mode == SK_IO_APPEND); assert(stream->is_binary); assert(stream->fd != -1); assert(buf); if (stream->iobuf) { return skIOBufWrite(stream->iobuf, buf, count); } #ifdef HAVE_ZLIB_H if (stream->gz != NULL) { return gzwrite(stream->gz, buf, (unsigned)count); } #endif left = count; while (left > 0) { s = write(stream->fd, buf, left); if (s > 0) { left -= s; buf = (uint8_t*)buf + s; } else if (s == -1) { stream->errnum = errno; return -1; } else if (s == 0) { break; } else { /* s is negative? */ return -1; } } return (count - left); } int skStreamWriteSilkHeader( skstream_t *stream, void *hdr, size_t hdr_len) { int rv; ssize_t bytes; rv = _streamAssertOpen(stream); if (rv) { return rv; } if (stream->is_dirty) { rv = SKSTREAM_ERR_PREV_DATA; goto END; } rv = _streamAssertAttributes(stream, SK_IO_WRITE, SK_CONTENT_SILK); if (rv) { return rv; } if (hdr_len < sizeof(genericHeader)) { rv = SKSTREAM_ERR_INVALID_INPUT; goto END; } PREPHEADER((genericHeader*)hdr); ((genericHeader*)hdr)->type = stream->silk_format; ((genericHeader*)hdr)->version = stream->silk_version; ((genericHeader*)hdr)->compMethod = stream->silk_compmethod; switch (stream->byte_order) { case SILK_ENDIAN_BIG: ((genericHeader*)hdr)->isBigEndian = 1; break; case SILK_ENDIAN_LITTLE: ((genericHeader*)hdr)->isBigEndian = 0; break; case SILK_ENDIAN_NATIVE: case SILK_ENDIAN_ANY: #if IS_LITTLE_ENDIAN ((genericHeader*)hdr)->isBigEndian = 0; #else ((genericHeader*)hdr)->isBigEndian = 1; #endif break; } bytes = skStreamWrite(stream, hdr, hdr_len); if (bytes != (ssize_t)hdr_len) { rv = SKSTREAM_ERR_WRITE; goto END; } stream->is_dirty = 1; rv = _streamCreateIOBuffer(stream); if (rv) { goto END; } END: return rv; } /* ********** STREAM GROUPS ********** */ #if 0 struct _skstreamgroup; typedef struct _skstreamgroup skstreamgroup_t; struct _skstreamgroup { sk_vector_t *streams; int *shared_stdin; int *shared_stdout; int *shared_stderr; unsigned is_input :1; unsigned is_text :1; unsigned allow_stdin :1; unsigned allow_stdout:1; unsigned allow_stderr:1; unsigned used_stdin; unsigned used_stdout; unsigned used_stderr; }; int skStreamGroupCreate( skstreamgroup_t **sg) { if (sg == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } *sg = calloc(1, sizeof(skstreamgroup_t)); if (NULL == *sg) { return SKSTREAM_ERR_ALLOC; } (*sg)->streams = skVectorNew(sizeof(skstream_t*)); if (NULL == (*sg)->streams) { free(*sg); return SKSTREAM_ERR_ALLOC; } (*sg)->allow_stdin = 1; (*sg)->allow_stdout = 1; return OK; } int skStreamGroupAddPath( skstreamgroup_t *sg, const char *pathname) { if (pathname == NULL) { return SKSTREAM_ERR_NULL_ARGUMENT; } return skStreamGroupAddPaths(sg, 1, &pathname); } int skStreamGroupAddPaths( skstreamgroup_t *sg, int argc, const char * const *argv) { int rv; int i; const char *path; if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (0 == argc) { return OK; } if (NULL == argv) { return SKSTREAM_ERR_NULL_ARGUMENT; } /* for argc<0, treat as a NULL-terminated list. compute argc */ if (argc < 0) { for (argc = 0; argv[argc]; ++argc) ; /* empty */ if (argc == 0) { return SKSTREAM_OK; } } for (i = 0; i < argc; ++i) { path = argv[i]; if (NULL == path || '\0' == path[0]) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (0 == strcmp(path, "stdin")) { if (!sg->is_input) { return INVALID_IO; } if (!sg->allow_stdin) { return NOT_ALLOW; } if (sg->used_stdin) { return ALREADY_USED; } if (sg->shared_stdin && *sg->shared_stdin) { return ALREADY_USED; } if (!sg->is_text && FILEIsATty(stdin)) { return SKSTREAM_ERR_ISTERMINAL; } sg->used_stdin = 1; if (sg->shared_stdin) { *sg->shared_stdin = 1; } } else if (0 == strcmp(path, "stdout")) { if (sg->is_input) { return INVALID_IO; } if (!sg->allow_stdout) { return NOT_ALLOW; } if (sg->used_stdout) { return ALREADY_USED; } if (sg->shared_stdout && *sg->shared_stdout) { return ALREADY_USED; } if (!sg->is_text && FILEIsATty(stdout)) { return SKSTREAM_ERR_ISTERMINAL; } sg->used_stdout = 1; if (sg->shared_stdout) { *sg->shared_stdout = 1; } } else if (0 == strcmp(path, "stderr")) { if (sg->is_input) { return INVALID_IO; } if (!sg->allow_stderr) { return NOT_ALLOW; } if (sg->used_stderr) { return ALREADY_USED; } if (sg->shared_stderr && *sg->shared_stderr) { return ALREADY_USED; } if (!sg->is_text && FILEIsATty(stderr)) { return SKSTREAM_ERR_ISTERMINAL; } sg->used_stderr = 1; if (sg->shared_stderr) { *sg->shared_stderr = 1; } } else if (fileExists(path)) { if (!sg->is_input && !isFIFO(path)) { return FILE_EXISTS; } } else if (sg->is_input) { return NOT_EXIST; } if ((rv = skStreamCreate(&s, GET_MODE(sg), GET_CONTENT(sg))) || (rv = skStreamBind(s, path))) { return rv; } if (skVectorAppendValue(sg->streams, s)) { return VECTOR_ERROR; } } END: return rv; } size_t skStreamGroupGetCount( skstreamgroup_t *sg) { if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } return skVectorGetCount(sg->streams); } int skStreamGroupGetNextStream( skstreamgroup_t *sg, skstream_t **stream) { if (NULL == sg || NULL == stream) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (skVectorGetValue(stream, sg->streams, sg->v_pos)) { return NO_MORE_ENTRIES; } ++sg->v_pos; return SKSTREAM_OK; } int skStreamGroupRefuseStdin( skstreamgroup_t *sg) { if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (!sg->is_input) { return BAD_INPUT; } sg->allow_stdin = 0; return SKSTREAM_OK; } int skStreamGroupRefuseStdout( skstreamgroup_t *sg) { if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (sg->is_input) { return BAD_INPUT; } sg->allow_stdout = 0; return SKSTREAM_OK; } int skStreamGroupAllowStderr( skstreamgroup_t *sg) { if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } if (sg->is_input) { return BAD_INPUT; } sg->allow_stderr = 1; return SKSTREAM_OK; } int skStreamGroupSetIsText( skstreamgroup_t *sg) { if (NULL == sg) { return SKSTREAM_ERR_NULL_ARGUMENT; } sg->is_text = 1; return SKSTREAM_OK; } int skStreamGroupSetDefault( skstreamgroup_t *sg) { int rv; END: return rv; } #endif /* 0 */ /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */