/* Copyright 2006 Garrett Rooney. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include "etl_stream.h" struct etl_stream_t { etl_stream_read_func_t rfunc; etl_stream_write_func_t wfunc; etl_stream_destroy_func_t dfunc; void *baton; }; etl_stream_t * etl_stream_create(etl_stream_read_func_t rfunc, etl_stream_write_func_t wfunc, etl_stream_destroy_func_t dfunc, void *baton) { etl_stream_t *stream = malloc(sizeof(*stream)); stream->rfunc = rfunc; stream->wfunc = wfunc; stream->dfunc = dfunc; stream->baton = baton; return stream; } etl_error_t * etl_stream_read(etl_stream_t *stream, char *buffer, size_t *len) { return stream->rfunc(stream->baton, buffer, len); } etl_error_t * etl_stream_write(etl_stream_t *stream, const char *data, size_t *len) { return stream->wfunc(stream->baton, data, len); } void * etl_stream_baton(etl_stream_t *stream) { return stream->baton; } void etl_stream_destroy(etl_stream_t *stream) { if (! stream) return; stream->dfunc(stream->baton); free(stream); } etl_error_t * etl_stream_readline(etl_stream_t *stream, char *buffer, size_t *len, bool *eof) { size_t maxlen = *len; size_t nread = 0; *eof = 0; while (nread < maxlen) { size_t one = 1; ETL_ERR(etl_stream_read(stream, &buffer[nread], &one)); if (one == 0) { *eof = true; break; } nread++; if (buffer[nread - 1] == '\n') break; } if (nread == maxlen) return etl_error_create(ETL_ENOSPACE, "Ran out of space in buffer"); *len = nread; buffer[nread] = 0; return ETL_SUCCESS; } etl_error_t * etl_stream_printf(etl_stream_t *stream, const char *fmt, ...) { etl_error_t *err; char *buffer; int nbytes; va_list ap; va_start(ap, fmt); nbytes = vasprintf(&buffer, fmt, ap); va_end(ap); { size_t st = nbytes; err = etl_stream_write(stream, buffer, &st); } free(buffer); return err; } static void file_destroy_func(void *baton) { close((int) baton); } static etl_error_t * file_read_func(void *baton, char *buffer, size_t *len) { int i; /* XXX single byte reads suck */ for (i = 0; i < *len; ++i) { ssize_t nread = read((int) baton, &buffer[i], 1); if (nread == 0) { *len = 0; return ETL_SUCCESS; } else if (nread == -1) { return etl_error_create(ETL_EIO, "Error reading from stream"); } } *len = i; return ETL_SUCCESS; } static etl_error_t * file_write_func(void *baton, const char *data, size_t *len) { int i; /* XXX single byte writes suck */ for (i = 0; i < *len; ++i) { ssize_t rv = write((int) baton, data + i, 1); if (rv == -1) return etl_error_create(ETL_EIO, "Error writing to stream"); } *len = i; return ETL_SUCCESS; } etl_error_t * etl_stream_file_open(etl_stream_t **stream, const char *filename, int flags) { int oflags = 0, fd; if (flags & etl_stream_open_read && flags & etl_stream_open_write) oflags |= O_RDWR; else if (flags & etl_stream_open_read) oflags |= O_RDONLY; else if (flags & etl_stream_open_write) oflags |= O_WRONLY; if (flags & etl_stream_open_create) oflags |= O_CREAT; if (flags & etl_stream_open_append) oflags |= O_APPEND; if (flags & etl_stream_open_truncate) oflags |= O_TRUNC; fd = open(filename, oflags, 0666); /* XXX mode control? */ if (fd == -1) return etl_error_createf(ETL_ENOENT, "Couldn't open file '%s'", filename); *stream = etl_stream_create(file_read_func, file_write_func, file_destroy_func, (void *) fd); return ETL_SUCCESS; } etl_error_t * etl_stream_stdout_open(etl_stream_t **out) { int newfd = dup(STDOUT_FILENO); *out = etl_stream_create(file_read_func, file_write_func, file_destroy_func, (void *) newfd); return ETL_SUCCESS; }