--- /dev/null
+/* PSPP - a program for statistical analysis.
+ Copyright (C) 2017 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_FBUF_H
+#define LIBPSPP_FBUF_H 1
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include "compiler.h"
+
+/* Data structure for buffered file I/O.
+
+ fbuf is much like stdio and serves the same purpose. The main difference is
+ that it is defined entirely in PSPP and thus it is possible to extend it in
+ a portable way, in particular to have a portable way to read and write a
+ memory buffer instead of a file descriptor, since GNU and BSD libc provide
+ ways to do that, but other libcs don't. */
+
+struct fbuf;
+
+struct fbuf *fbuf_open_fd (int fd);
+int fbuf_open_file (const char *filename, int flags, mode_t mode,
+ struct fbuf **) WARN_UNUSED_RESULT;
+struct fbuf *fbuf_open_memory (void *, size_t);
+
+int fbuf_close (struct fbuf *);
+
+int fbuf_get_status (const struct fbuf *);
+void fbuf_clear_status (struct fbuf *);
+
+int fbuf_is_seekable (const struct fbuf *);
+off_t fbuf_get_size (const struct fbuf *);
+
+int fbuf_flush (struct fbuf *);
+
+off_t fbuf_tell (const struct fbuf *);
+int fbuf_seek (struct fbuf *, off_t);
+
+static inline int fbuf_putc (struct fbuf *, uint8_t);
+ssize_t fbuf_write (struct fbuf *, const void *, size_t);
+
+static inline int fbuf_getc (struct fbuf *);
+ssize_t fbuf_read (struct fbuf *, void *, size_t);
+\f
+/* Implementation details. */
+
+struct fbuf
+ {
+ const struct fbuf_class *class;
+ uint8_t *buffer;
+
+ /* Offset in the underlying file descriptor:
+
+ - In read mode, this is the offset of 'read_head'.
+
+ - In write mode, this is the offset of 'write_tail'.
+
+ Starts out at TYPE_MINIMUM (off_t), which indicates that the underlying
+ descriptor offset is not known. Negative errno values indicate
+ errors. */
+ off_t offset;
+
+ /*
+ In read mode, buffered data is read into the start of 'buffer'.
+ Initially 'read_tail' points to 'buffer' and 'read_head' just past the
+ last byte of buffered data. As the client reads data, 'read_tail'
+ advances until it reaches 'read_head', then the buffer is re-filled and
+ the process repeats.
+
+ offset in fd
+ |
+ v
+ +----------+------------------------+
+ | | ...data to be read... |
+ +-----------------------------------+
+ ^ ^ ^
+ | | |
+ buffer read_tail read_head
+
+ In write mode, read_tail and read_head are both NULL. */
+ uint8_t *read_tail, *read_head;
+
+ /*
+ In write mode, write_tail and write_head initially point to 'buffer' and
+ 'write_end' to 'buffer + FBUF_SIZE'. As the client writes, its data is
+ copied to and advances 'write_head', limited by 'write_end'. As the
+ fbuf flushes data to the fd, 'write_tail' advances, and when
+ 'write_tail' catches 'write_head', both reset to 'buffer'.
+
+ offset in fd
+ |
+ v
+ +----------+------------------------+-------------------+
+ | |...data to be flushed...| |
+ +-----------------------------------+-------------------+
+ ^ ^ ^ ^
+ | | | |
+ buffer write_tail write_head write_end
+
+ In read mode, write_tail, write_head, and write_end are all NULL. */
+ uint8_t *write_tail, *write_head, *write_end;
+ int status;
+ };
+
+static inline int
+fbuf_putc (struct fbuf *fbuf, uint8_t byte)
+{
+ if (fbuf->write_head < fbuf->write_end)
+ {
+ *fbuf->write_head++ = byte;
+ return 0;
+ }
+ else
+ return fbuf_write (fbuf, &byte, 1);
+}
+
+int fbuf_getc__ (struct fbuf *);
+int
+fbuf_getc (struct fbuf *fbuf)
+{
+ return (fbuf->read_tail < fbuf->read_head
+ ? *fbuf->read_tail++
+ : fbuf_getc__ (fbuf));
+}
+
+#endif /* libpspp/fbuf.h */