summaryrefslogtreecommitdiff
path: root/buffer.h
diff options
context:
space:
mode:
Diffstat (limited to 'buffer.h')
-rw-r--r--buffer.h864
1 files changed, 864 insertions, 0 deletions
diff --git a/buffer.h b/buffer.h
new file mode 100644
index 0000000..3b24d09
--- /dev/null
+++ b/buffer.h
@@ -0,0 +1,864 @@
+/*
+ * OpenVPN -- An application to securely tunnel IP networks
+ * over a single UDP port, with support for SSL/TLS-based
+ * session authentication and key exchange,
+ * packet encryption, packet authentication, and
+ * packet compression.
+ *
+ * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * 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 (see the file COPYING included with this
+ * distribution); if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef BUFFER_H
+#define BUFFER_H
+
+#include "basic.h"
+
+#define BUF_SIZE_MAX 1000000
+
+/*
+ * Define verify_align function, otherwise
+ * it will be a noop.
+ */
+/* #define VERIFY_ALIGNMENT */
+
+/*
+ * Keep track of source file/line of buf_init calls
+ */
+#ifdef VERIFY_ALIGNMENT
+#define BUF_INIT_TRACKING
+#endif
+
+/* basic buffer class for OpenVPN */
+
+struct buffer
+{
+ int capacity; /* size of buffer allocated by malloc */
+ int offset; /* data starts at data + offset, offset > 0 to allow for efficient prepending */
+ int len; /* length of data that starts at data + offset */
+ uint8_t *data;
+
+#ifdef BUF_INIT_TRACKING
+ const char *debug_file;
+ int debug_line;
+#endif
+};
+
+/* for garbage collection */
+
+struct gc_entry
+{
+ struct gc_entry *next;
+};
+
+struct gc_arena
+{
+ struct gc_entry *list;
+};
+
+#define BPTR(buf) (buf_bptr(buf))
+#define BEND(buf) (buf_bend(buf))
+#define BLAST(buf) (buf_blast(buf))
+#define BLEN(buf) (buf_len(buf))
+#define BDEF(buf) (buf_defined(buf))
+#define BSTR(buf) (buf_str(buf))
+#define BCAP(buf) (buf_forward_capacity (buf))
+
+void buf_clear (struct buffer *buf);
+
+struct buffer clear_buf (void);
+void free_buf (struct buffer *buf);
+
+bool buf_assign (struct buffer *dest, const struct buffer *src);
+
+void string_clear (char *str);
+int string_array_len (const char **array);
+
+size_t array_mult_safe (const size_t m1, const size_t m2, const size_t extra);
+
+#define PA_BRACKET (1<<0)
+char *print_argv (const char **p, struct gc_arena *gc, const unsigned int flags);
+
+void buf_size_error (const size_t size);
+
+/* for dmalloc debugging */
+
+#ifdef DMALLOC
+
+#define alloc_buf(size) alloc_buf_debug (size, __FILE__, __LINE__)
+#define alloc_buf_gc(size, gc) alloc_buf_gc_debug (size, gc, __FILE__, __LINE__);
+#define clone_buf(buf) clone_buf_debug (buf, __FILE__, __LINE__);
+#define gc_malloc(size, clear, arena) gc_malloc_debug (size, clear, arena, __FILE__, __LINE__)
+#define string_alloc(str, gc) string_alloc_debug (str, gc, __FILE__, __LINE__)
+#define string_alloc_buf(str, gc) string_alloc_buf_debug (str, gc, __FILE__, __LINE__)
+
+struct buffer alloc_buf_debug (size_t size, const char *file, int line);
+struct buffer alloc_buf_gc_debug (size_t size, struct gc_arena *gc, const char *file, int line);
+struct buffer clone_buf_debug (const struct buffer* buf, const char *file, int line);
+void *gc_malloc_debug (size_t size, bool clear, struct gc_arena *a, const char *file, int line);
+char *string_alloc_debug (const char *str, struct gc_arena *gc, const char *file, int line);
+struct buffer string_alloc_buf_debug (const char *str, struct gc_arena *gc, const char *file, int line);
+
+#else
+
+struct buffer alloc_buf (size_t size);
+struct buffer alloc_buf_gc (size_t size, struct gc_arena *gc); /* allocate buffer with garbage collection */
+struct buffer clone_buf (const struct buffer* buf);
+void *gc_malloc (size_t size, bool clear, struct gc_arena *a);
+char *string_alloc (const char *str, struct gc_arena *gc);
+struct buffer string_alloc_buf (const char *str, struct gc_arena *gc);
+
+#endif
+
+#ifdef BUF_INIT_TRACKING
+#define buf_init(buf, offset) buf_init_debug (buf, offset, __FILE__, __LINE__)
+bool buf_init_debug (struct buffer *buf, int offset, const char *file, int line);
+#else
+#define buf_init(buf, offset) buf_init_dowork (buf, offset)
+#endif
+
+
+/* inline functions */
+
+static inline bool
+buf_defined (const struct buffer *buf)
+{
+ return buf->data != NULL;
+}
+
+static inline bool
+buf_valid (const struct buffer *buf)
+{
+ return likely (buf->data != NULL) && likely (buf->len >= 0);
+}
+
+static inline uint8_t *
+buf_bptr (const struct buffer *buf)
+{
+ if (buf_valid (buf))
+ return buf->data + buf->offset;
+ else
+ return NULL;
+}
+
+static int
+buf_len (const struct buffer *buf)
+{
+ if (buf_valid (buf))
+ return buf->len;
+ else
+ return 0;
+}
+
+static inline uint8_t *
+buf_bend (const struct buffer *buf)
+{
+ return buf_bptr (buf) + buf_len (buf);
+}
+
+static inline uint8_t *
+buf_blast (const struct buffer *buf)
+{
+ if (buf_len (buf) > 0)
+ return buf_bptr (buf) + buf_len (buf) - 1;
+ else
+ return NULL;
+}
+
+static inline bool
+buf_size_valid (const size_t size)
+{
+ return likely (size < BUF_SIZE_MAX);
+}
+
+static inline bool
+buf_size_valid_signed (const int size)
+{
+ return likely (size >= -BUF_SIZE_MAX) && likely (size < BUF_SIZE_MAX);
+}
+
+static inline char *
+buf_str (const struct buffer *buf)
+{
+ return (char *)buf_bptr(buf);
+}
+
+static inline void
+buf_reset (struct buffer *buf)
+{
+ buf->capacity = 0;
+ buf->offset = 0;
+ buf->len = 0;
+ buf->data = NULL;
+}
+
+static inline void
+buf_reset_len (struct buffer *buf)
+{
+ buf->len = 0;
+ buf->offset = 0;
+}
+
+static inline bool
+buf_init_dowork (struct buffer *buf, int offset)
+{
+ if (offset < 0 || offset > buf->capacity || buf->data == NULL)
+ return false;
+ buf->len = 0;
+ buf->offset = offset;
+ return true;
+}
+
+static inline void
+buf_set_write (struct buffer *buf, uint8_t *data, int size)
+{
+ if (!buf_size_valid (size))
+ buf_size_error (size);
+ buf->len = 0;
+ buf->offset = 0;
+ buf->capacity = size;
+ buf->data = data;
+ if (size > 0 && data)
+ *data = 0;
+}
+
+static inline void
+buf_set_read (struct buffer *buf, const uint8_t *data, int size)
+{
+ if (!buf_size_valid (size))
+ buf_size_error (size);
+ buf->len = buf->capacity = size;
+ buf->offset = 0;
+ buf->data = (uint8_t *)data;
+}
+
+/* Like strncpy but makes sure dest is always null terminated */
+static inline void
+strncpynt (char *dest, const char *src, size_t maxlen)
+{
+ strncpy (dest, src, maxlen);
+ if (maxlen > 0)
+ dest[maxlen - 1] = 0;
+}
+
+/* return true if string contains at least one numerical digit */
+static inline bool
+has_digit (const unsigned char* src)
+{
+ unsigned char c;
+ while ((c = *src++))
+ {
+ if (isdigit(c))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * printf append to a buffer with overflow check
+ */
+bool buf_printf (struct buffer *buf, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 2, 3)))
+#endif
+ ;
+
+/*
+ * Like snprintf but guarantees null termination for size > 0
+ */
+int openvpn_snprintf(char *str, size_t size, const char *format, ...)
+#ifdef __GNUC__
+ __attribute__ ((format (printf, 3, 4)))
+#endif
+ ;
+
+/*
+ * remove/add trailing characters
+ */
+
+void buf_null_terminate (struct buffer *buf);
+void buf_chomp (struct buffer *buf);
+void buf_rmtail (struct buffer *buf, uint8_t remove);
+
+/*
+ * non-buffer string functions
+ */
+void chomp (char *str);
+void rm_trailing_chars (char *str, const char *what_to_delete);
+const char *skip_leading_whitespace (const char *str);
+void string_null_terminate (char *str, int len, int capacity);
+
+/*
+ * Write string in buf to file descriptor fd.
+ * NOTE: requires that string be null terminated.
+ */
+void buf_write_string_file (const struct buffer *buf, const char *filename, int fd);
+
+/*
+ * write a string to the end of a buffer that was
+ * truncated by buf_printf
+ */
+void buf_catrunc (struct buffer *buf, const char *str);
+
+/*
+ * convert a multi-line output to one line
+ */
+void convert_to_one_line (struct buffer *buf);
+
+/*
+ * Parse a string based on a given delimiter char
+ */
+bool buf_parse (struct buffer *buf, const int delim, char *line, const int size);
+
+/*
+ * Hex dump -- Output a binary buffer to a hex string and return it.
+ */
+char *
+format_hex_ex (const uint8_t *data, int size, int maxoutput,
+ int space_break, const char* separator,
+ struct gc_arena *gc);
+
+static inline char *
+format_hex (const uint8_t *data, int size, int maxoutput, struct gc_arena *gc)
+{
+ return format_hex_ex (data, size, maxoutput, 4, " ", gc);
+}
+
+/*
+ * Return a buffer that is a subset of another buffer.
+ */
+struct buffer buf_sub (struct buffer *buf, int size, bool prepend);
+
+/*
+ * Check if sufficient space to append to buffer.
+ */
+
+static inline bool
+buf_safe (const struct buffer *buf, int len)
+{
+ return buf_valid (buf) && buf_size_valid (len)
+ && buf->offset + buf->len + len <= buf->capacity;
+}
+
+static inline bool
+buf_safe_bidir (const struct buffer *buf, int len)
+{
+ if (buf_valid (buf) && buf_size_valid_signed (len))
+ {
+ const int newlen = buf->len + len;
+ return newlen >= 0 && buf->offset + newlen <= buf->capacity;
+ }
+ else
+ return false;
+}
+
+static inline int
+buf_forward_capacity (const struct buffer *buf)
+{
+ if (buf_valid (buf))
+ {
+ int ret = buf->capacity - (buf->offset + buf->len);
+ if (ret < 0)
+ ret = 0;
+ return ret;
+ }
+ else
+ return 0;
+}
+
+static inline int
+buf_forward_capacity_total (const struct buffer *buf)
+{
+ if (buf_valid (buf))
+ {
+ int ret = buf->capacity - buf->offset;
+ if (ret < 0)
+ ret = 0;
+ return ret;
+ }
+ else
+ return 0;
+}
+
+static inline int
+buf_reverse_capacity (const struct buffer *buf)
+{
+ if (buf_valid (buf))
+ return buf->offset;
+ else
+ return 0;
+}
+
+static inline bool
+buf_inc_len (struct buffer *buf, int inc)
+{
+ if (!buf_safe_bidir (buf, inc))
+ return false;
+ buf->len += inc;
+ return true;
+}
+
+/*
+ * Make space to prepend to a buffer.
+ * Return NULL if no space.
+ */
+
+static inline uint8_t *
+buf_prepend (struct buffer *buf, int size)
+{
+ if (!buf_valid (buf) || size < 0 || size > buf->offset)
+ return NULL;
+ buf->offset -= size;
+ buf->len += size;
+ return BPTR (buf);
+}
+
+static inline bool
+buf_advance (struct buffer *buf, int size)
+{
+ if (!buf_valid (buf) || size < 0 || buf->len < size)
+ return false;
+ buf->offset += size;
+ buf->len -= size;
+ return true;
+}
+
+/*
+ * Return a pointer to allocated space inside a buffer.
+ * Return NULL if no space.
+ */
+
+static inline uint8_t *
+buf_write_alloc (struct buffer *buf, int size)
+{
+ uint8_t *ret;
+ if (!buf_safe (buf, size))
+ return NULL;
+ ret = BPTR (buf) + buf->len;
+ buf->len += size;
+ return ret;
+}
+
+static inline uint8_t *
+buf_write_alloc_prepend (struct buffer *buf, int size, bool prepend)
+{
+ return prepend ? buf_prepend (buf, size) : buf_write_alloc (buf, size);
+}
+
+static inline uint8_t *
+buf_read_alloc (struct buffer *buf, int size)
+{
+ uint8_t *ret;
+ if (size < 0 || buf->len < size)
+ return NULL;
+ ret = BPTR (buf);
+ buf->offset += size;
+ buf->len -= size;
+ return ret;
+}
+
+static inline bool
+buf_write (struct buffer *dest, const void *src, int size)
+{
+ uint8_t *cp = buf_write_alloc (dest, size);
+ if (!cp)
+ return false;
+ memcpy (cp, src, size);
+ return true;
+}
+
+static inline bool
+buf_write_prepend (struct buffer *dest, const void *src, int size)
+{
+ uint8_t *cp = buf_prepend (dest, size);
+ if (!cp)
+ return false;
+ memcpy (cp, src, size);
+ return true;
+}
+
+static inline bool
+buf_write_u8 (struct buffer *dest, int data)
+{
+ uint8_t u8 = (uint8_t) data;
+ return buf_write (dest, &u8, sizeof (uint8_t));
+}
+
+static inline bool
+buf_write_u16 (struct buffer *dest, int data)
+{
+ uint16_t u16 = htons ((uint16_t) data);
+ return buf_write (dest, &u16, sizeof (uint16_t));
+}
+
+static inline bool
+buf_write_u32 (struct buffer *dest, int data)
+{
+ uint32_t u32 = htonl ((uint32_t) data);
+ return buf_write (dest, &u32, sizeof (uint32_t));
+}
+
+static inline bool
+buf_copy (struct buffer *dest, const struct buffer *src)
+{
+ return buf_write (dest, BPTR (src), BLEN (src));
+}
+
+static inline bool
+buf_copy_n (struct buffer *dest, struct buffer *src, int n)
+{
+ uint8_t *cp = buf_read_alloc (src, n);
+ if (!cp)
+ return false;
+ return buf_write (dest, cp, n);
+}
+
+static inline bool
+buf_copy_range (struct buffer *dest,
+ int dest_index,
+ const struct buffer *src,
+ int src_index,
+ int src_len)
+{
+ if (src_index < 0
+ || src_len < 0
+ || src_index + src_len > src->len
+ || dest_index < 0
+ || dest->offset + dest_index + src_len > dest->capacity)
+ return false;
+ memcpy (dest->data + dest->offset + dest_index, src->data + src->offset + src_index, src_len);
+ if (dest_index + src_len > dest->len)
+ dest->len = dest_index + src_len;
+ return true;
+}
+
+/* truncate src to len, copy excess data beyond len to dest */
+static inline bool
+buf_copy_excess (struct buffer *dest,
+ struct buffer *src,
+ int len)
+{
+ if (len < 0)
+ return false;
+ if (src->len > len)
+ {
+ struct buffer b = *src;
+ src->len = len;
+ if (!buf_advance (&b, len))
+ return false;
+ return buf_copy (dest, &b);
+ }
+ else
+ {
+ return true;
+ }
+}
+
+static inline bool
+buf_read (struct buffer *src, void *dest, int size)
+{
+ uint8_t *cp = buf_read_alloc (src, size);
+ if (!cp)
+ return false;
+ memcpy (dest, cp, size);
+ return true;
+}
+
+static inline int
+buf_read_u8 (struct buffer *buf)
+{
+ int ret;
+ if (BLEN (buf) < 1)
+ return -1;
+ ret = *BPTR(buf);
+ buf_advance (buf, 1);
+ return ret;
+}
+
+static inline int
+buf_read_u16 (struct buffer *buf)
+{
+ uint16_t ret;
+ if (!buf_read (buf, &ret, sizeof (uint16_t)))
+ return -1;
+ return ntohs (ret);
+}
+
+static inline uint32_t
+buf_read_u32 (struct buffer *buf, bool *good)
+{
+ uint32_t ret;
+ if (!buf_read (buf, &ret, sizeof (uint32_t)))
+ {
+ if (good)
+ *good = false;
+ return 0;
+ }
+ else
+ {
+ if (good)
+ *good = true;
+ return ntohl (ret);
+ }
+}
+
+static inline bool
+buf_string_match (const struct buffer *src, const void *match, int size)
+{
+ if (size != src->len)
+ return false;
+ return memcmp (BPTR (src), match, size) == 0;
+}
+
+static inline bool
+buf_string_match_head (const struct buffer *src, const void *match, int size)
+{
+ if (size < 0 || size > src->len)
+ return false;
+ return memcmp (BPTR (src), match, size) == 0;
+}
+
+bool buf_string_match_head_str (const struct buffer *src, const char *match);
+bool buf_string_compare_advance (struct buffer *src, const char *match);
+int buf_substring_len (const struct buffer *buf, int delim);
+
+/*
+ * Bitwise operations
+ */
+static inline void
+xor (uint8_t *dest, const uint8_t *src, int len)
+{
+ while (len-- > 0)
+ *dest++ ^= *src++;
+}
+
+/*
+ * Print a string which might be NULL
+ */
+const char *np (const char *str);
+
+/*#define CHARACTER_CLASS_DEBUG*/
+
+/* character classes */
+
+#define CC_ANY (1<<0)
+#define CC_NULL (1<<1)
+
+#define CC_ALNUM (1<<2)
+#define CC_ALPHA (1<<3)
+#define CC_ASCII (1<<4)
+#define CC_CNTRL (1<<5)
+#define CC_DIGIT (1<<6)
+#define CC_PRINT (1<<7)
+#define CC_PUNCT (1<<8)
+#define CC_SPACE (1<<9)
+#define CC_XDIGIT (1<<10)
+
+#define CC_BLANK (1<<11)
+#define CC_NEWLINE (1<<12)
+#define CC_CR (1<<13)
+
+#define CC_BACKSLASH (1<<14)
+#define CC_UNDERBAR (1<<15)
+#define CC_DASH (1<<16)
+#define CC_DOT (1<<17)
+#define CC_COMMA (1<<18)
+#define CC_COLON (1<<19)
+#define CC_SLASH (1<<20)
+#define CC_SINGLE_QUOTE (1<<21)
+#define CC_DOUBLE_QUOTE (1<<22)
+#define CC_REVERSE_QUOTE (1<<23)
+#define CC_AT (1<<24)
+#define CC_EQUAL (1<<25)
+
+/* macro classes */
+#define CC_NAME (CC_ALNUM|CC_UNDERBAR)
+#define CC_CRLF (CC_CR|CC_NEWLINE)
+
+bool char_class (const unsigned char c, const unsigned int flags);
+bool string_class (const char *str, const unsigned int inclusive, const unsigned int exclusive);
+bool string_mod (char *str, const unsigned int inclusive, const unsigned int exclusive, const char replace);
+
+const char *string_mod_const (const char *str,
+ const unsigned int inclusive,
+ const unsigned int exclusive,
+ const char replace,
+ struct gc_arena *gc);
+
+void string_replace_leading (char *str, const char match, const char replace);
+
+#ifdef CHARACTER_CLASS_DEBUG
+void character_class_debug (void);
+#endif
+
+/*
+ * Verify that a pointer is correctly aligned
+ */
+#ifdef VERIFY_ALIGNMENT
+ void valign4 (const struct buffer *buf, const char *file, const int line);
+# define verify_align_4(ptr) valign4(buf, __FILE__, __LINE__)
+#else
+# define verify_align_4(ptr)
+#endif
+
+/*
+ * Very basic garbage collection, mostly for routines that return
+ * char ptrs to malloced strings.
+ */
+
+void gc_transfer (struct gc_arena *dest, struct gc_arena *src);
+
+void x_gc_free (struct gc_arena *a);
+
+static inline bool
+gc_defined (struct gc_arena *a)
+{
+ return a->list != NULL;
+}
+
+static inline void
+gc_init (struct gc_arena *a)
+{
+ a->list = NULL;
+}
+
+static inline void
+gc_detach (struct gc_arena *a)
+{
+ gc_init (a);
+}
+
+static inline struct gc_arena
+gc_new (void)
+{
+ struct gc_arena ret;
+ ret.list = NULL;
+ return ret;
+}
+
+static inline void
+gc_free (struct gc_arena *a)
+{
+ if (a->list)
+ x_gc_free (a);
+}
+
+static inline void
+gc_reset (struct gc_arena *a)
+{
+ gc_free (a);
+}
+
+/*
+ * Allocate memory to hold a structure
+ */
+
+void out_of_memory (void);
+
+#define ALLOC_OBJ(dptr, type) \
+{ \
+ check_malloc_return ((dptr) = (type *) malloc (sizeof (type))); \
+}
+
+#define ALLOC_OBJ_CLEAR(dptr, type) \
+{ \
+ ALLOC_OBJ (dptr, type); \
+ memset ((dptr), 0, sizeof(type)); \
+}
+
+#define ALLOC_ARRAY(dptr, type, n) \
+{ \
+ check_malloc_return ((dptr) = (type *) malloc (array_mult_safe (sizeof (type), (n), 0))); \
+}
+
+#define ALLOC_ARRAY_GC(dptr, type, n, gc) \
+{ \
+ (dptr) = (type *) gc_malloc (array_mult_safe (sizeof (type), (n), 0), false, (gc)); \
+}
+
+#define ALLOC_ARRAY_CLEAR(dptr, type, n) \
+{ \
+ ALLOC_ARRAY (dptr, type, n); \
+ memset ((dptr), 0, (array_mult_safe (sizeof(type), (n), 0))); \
+}
+
+#define ALLOC_ARRAY_CLEAR_GC(dptr, type, n, gc) \
+{ \
+ (dptr) = (type *) gc_malloc (array_mult_safe (sizeof (type), (n), 0), true, (gc)); \
+}
+
+#define ALLOC_VAR_ARRAY_CLEAR_GC(dptr, type, atype, n, gc) \
+{ \
+ (dptr) = (type *) gc_malloc (array_mult_safe (sizeof (atype), (n), sizeof (type)), true, (gc)); \
+}
+
+#define ALLOC_OBJ_GC(dptr, type, gc) \
+{ \
+ (dptr) = (type *) gc_malloc (sizeof (type), false, (gc)); \
+}
+
+#define ALLOC_OBJ_CLEAR_GC(dptr, type, gc) \
+{ \
+ (dptr) = (type *) gc_malloc (sizeof (type), true, (gc)); \
+}
+
+static inline void
+check_malloc_return (void *p)
+{
+ void out_of_memory (void);
+ if (!p)
+ out_of_memory ();
+}
+
+/*
+ * Manage lists of buffers
+ */
+
+#ifdef ENABLE_BUFFER_LIST
+
+struct buffer_entry
+{
+ struct buffer buf;
+ struct buffer_entry *next;
+};
+
+struct buffer_list
+{
+ struct buffer_entry *head; /* next item to pop/peek */
+ struct buffer_entry *tail; /* last item pushed */
+ int size; /* current number of entries */
+ int max_size; /* maximum size list should grow to */
+};
+
+struct buffer_list *buffer_list_new (const int max_size);
+void buffer_list_free (struct buffer_list *ol);
+
+bool buffer_list_defined (const struct buffer_list *ol);
+void buffer_list_reset (struct buffer_list *ol);
+
+void buffer_list_push (struct buffer_list *ol, const unsigned char *str);
+struct buffer_entry *buffer_list_push_data (struct buffer_list *ol, const uint8_t *data, size_t size);
+struct buffer *buffer_list_peek (struct buffer_list *ol);
+void buffer_list_advance (struct buffer_list *ol, int n);
+void buffer_list_pop (struct buffer_list *ol);
+
+void buffer_list_aggregate (struct buffer_list *bl, const size_t max);
+
+struct buffer_list *buffer_list_file (const char *fn, int max_line_len);
+
+#endif
+
+#endif /* BUFFER_H */