Create libosmovty as library from OpenBSC VTY functions
diff --git a/src/vty/buffer.c b/src/vty/buffer.c
new file mode 100644
index 0000000..a5655b9
--- /dev/null
+++ b/src/vty/buffer.c
@@ -0,0 +1,463 @@
+/*
+ * Buffering of output and input.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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 2, or (at your
+ * option) any later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING.  If not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <stddef.h>
+#include <sys/uio.h>
+
+#include <osmocore/talloc.h>
+#include <osmocom/vty/buffer.h>
+#include <osmocom/vty/vty.h>
+
+/* Buffer master. */
+struct buffer {
+	/* Data list. */
+	struct buffer_data *head;
+	struct buffer_data *tail;
+
+	/* Size of each buffer_data chunk. */
+	size_t size;
+};
+
+/* Data container. */
+struct buffer_data {
+	struct buffer_data *next;
+
+	/* Location to add new data. */
+	size_t cp;
+
+	/* Pointer to data not yet flushed. */
+	size_t sp;
+
+	/* Actual data stream (variable length). */
+	unsigned char data[0];	/* real dimension is buffer->size */
+};
+
+/* It should always be true that: 0 <= sp <= cp <= size */
+
+/* Default buffer size (used if none specified).  It is rounded up to the
+   next page boundery. */
+#define BUFFER_SIZE_DEFAULT		4096
+
+#define BUFFER_DATA_FREE(D) talloc_free((D))
+
+/* Make new buffer. */
+struct buffer *buffer_new(void *ctx, size_t size)
+{
+	struct buffer *b;
+
+	b = talloc_zero(ctx, struct buffer);
+
+	if (size)
+		b->size = size;
+	else {
+		static size_t default_size;
+		if (!default_size) {
+			long pgsz = sysconf(_SC_PAGESIZE);
+			default_size =
+			    ((((BUFFER_SIZE_DEFAULT - 1) / pgsz) + 1) * pgsz);
+		}
+		b->size = default_size;
+	}
+
+	return b;
+}
+
+/* Free buffer. */
+void buffer_free(struct buffer *b)
+{
+	buffer_reset(b);
+	talloc_free(b);
+}
+
+/* Make string clone. */
+char *buffer_getstr(struct buffer *b)
+{
+	size_t totlen = 0;
+	struct buffer_data *data;
+	char *s;
+	char *p;
+
+	for (data = b->head; data; data = data->next)
+		totlen += data->cp - data->sp;
+	if (!(s = _talloc_zero(tall_vty_ctx, (totlen + 1), "buffer_getstr")))
+		return NULL;
+	p = s;
+	for (data = b->head; data; data = data->next) {
+		memcpy(p, data->data + data->sp, data->cp - data->sp);
+		p += data->cp - data->sp;
+	}
+	*p = '\0';
+	return s;
+}
+
+/* Return 1 if buffer is empty. */
+int buffer_empty(struct buffer *b)
+{
+	return (b->head == NULL);
+}
+
+/* Clear and free all allocated data. */
+void buffer_reset(struct buffer *b)
+{
+	struct buffer_data *data;
+	struct buffer_data *next;
+
+	for (data = b->head; data; data = next) {
+		next = data->next;
+		BUFFER_DATA_FREE(data);
+	}
+	b->head = b->tail = NULL;
+}
+
+/* Add buffer_data to the end of buffer. */
+static struct buffer_data *buffer_add(struct buffer *b)
+{
+	struct buffer_data *d;
+
+	d = _talloc_zero(b,
+			 offsetof(struct buffer_data, data[b->size]),
+			 "buffer_add");
+	if (!d)
+		return NULL;
+	d->cp = d->sp = 0;
+	d->next = NULL;
+
+	if (b->tail)
+		b->tail->next = d;
+	else
+		b->head = d;
+	b->tail = d;
+
+	return d;
+}
+
+/* Write data to buffer. */
+void buffer_put(struct buffer *b, const void *p, size_t size)
+{
+	struct buffer_data *data = b->tail;
+	const char *ptr = p;
+
+	/* We use even last one byte of data buffer. */
+	while (size) {
+		size_t chunk;
+
+		/* If there is no data buffer add it. */
+		if (data == NULL || data->cp == b->size)
+			data = buffer_add(b);
+
+		chunk =
+		    ((size <=
+		      (b->size - data->cp)) ? size : (b->size - data->cp));
+		memcpy((data->data + data->cp), ptr, chunk);
+		size -= chunk;
+		ptr += chunk;
+		data->cp += chunk;
+	}
+}
+
+/* Insert character into the buffer. */
+void buffer_putc(struct buffer *b, u_char c)
+{
+	buffer_put(b, &c, 1);
+}
+
+/* Put string to the buffer. */
+void buffer_putstr(struct buffer *b, const char *c)
+{
+	buffer_put(b, c, strlen(c));
+}
+
+/* Keep flushing data to the fd until the buffer is empty or an error is
+   encountered or the operation would block. */
+buffer_status_t buffer_flush_all(struct buffer *b, int fd)
+{
+	buffer_status_t ret;
+	struct buffer_data *head;
+	size_t head_sp;
+
+	if (!b->head)
+		return BUFFER_EMPTY;
+	head_sp = (head = b->head)->sp;
+	/* Flush all data. */
+	while ((ret = buffer_flush_available(b, fd)) == BUFFER_PENDING) {
+		if ((b->head == head) && (head_sp == head->sp)
+		    && (errno != EINTR))
+			/* No data was flushed, so kernel buffer must be full. */
+			return ret;
+		head_sp = (head = b->head)->sp;
+	}
+
+	return ret;
+}
+
+#if 0
+/* Flush enough data to fill a terminal window of the given scene (used only
+   by vty telnet interface). */
+buffer_status_t
+buffer_flush_window(struct buffer * b, int fd, int width, int height,
+		    int erase_flag, int no_more_flag)
+{
+	int nbytes;
+	int iov_alloc;
+	int iov_index;
+	struct iovec *iov;
+	struct iovec small_iov[3];
+	char more[] = " --More-- ";
+	char erase[] =
+	    { 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08,
+		' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
+		0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
+	};
+	struct buffer_data *data;
+	int column;
+
+	if (!b->head)
+		return BUFFER_EMPTY;
+
+	if (height < 1) {
+		zlog_warn
+		    ("%s called with non-positive window height %d, forcing to 1",
+		     __func__, height);
+		height = 1;
+	} else if (height >= 2)
+		height--;
+	if (width < 1) {
+		zlog_warn
+		    ("%s called with non-positive window width %d, forcing to 1",
+		     __func__, width);
+		width = 1;
+	}
+
+	/* For erase and more data add two to b's buffer_data count. */
+	if (b->head->next == NULL) {
+		iov_alloc = sizeof(small_iov) / sizeof(small_iov[0]);
+		iov = small_iov;
+	} else {
+		iov_alloc = ((height * (width + 2)) / b->size) + 10;
+		iov = XMALLOC(MTYPE_TMP, iov_alloc * sizeof(*iov));
+	}
+	iov_index = 0;
+
+	/* Previously print out is performed. */
+	if (erase_flag) {
+		iov[iov_index].iov_base = erase;
+		iov[iov_index].iov_len = sizeof erase;
+		iov_index++;
+	}
+
+	/* Output data. */
+	column = 1;		/* Column position of next character displayed. */
+	for (data = b->head; data && (height > 0); data = data->next) {
+		size_t cp;
+
+		cp = data->sp;
+		while ((cp < data->cp) && (height > 0)) {
+			/* Calculate lines remaining and column position after displaying
+			   this character. */
+			if (data->data[cp] == '\r')
+				column = 1;
+			else if ((data->data[cp] == '\n') || (column == width)) {
+				column = 1;
+				height--;
+			} else
+				column++;
+			cp++;
+		}
+		iov[iov_index].iov_base = (char *)(data->data + data->sp);
+		iov[iov_index++].iov_len = cp - data->sp;
+		data->sp = cp;
+
+		if (iov_index == iov_alloc)
+			/* This should not ordinarily happen. */
+		{
+			iov_alloc *= 2;
+			if (iov != small_iov) {
+				zlog_warn("%s: growing iov array to %d; "
+					  "width %d, height %d, size %lu",
+					  __func__, iov_alloc, width, height,
+					  (u_long) b->size);
+				iov =
+				    XREALLOC(MTYPE_TMP, iov,
+					     iov_alloc * sizeof(*iov));
+			} else {
+				/* This should absolutely never occur. */
+				zlog_err
+				    ("%s: corruption detected: iov_small overflowed; "
+				     "head %p, tail %p, head->next %p",
+				     __func__, b->head, b->tail, b->head->next);
+				iov =
+				    XMALLOC(MTYPE_TMP,
+					    iov_alloc * sizeof(*iov));
+				memcpy(iov, small_iov, sizeof(small_iov));
+			}
+		}
+	}
+
+	/* In case of `more' display need. */
+	if (b->tail && (b->tail->sp < b->tail->cp) && !no_more_flag) {
+		iov[iov_index].iov_base = more;
+		iov[iov_index].iov_len = sizeof more;
+		iov_index++;
+	}
+#ifdef IOV_MAX
+	/* IOV_MAX are normally defined in <sys/uio.h> , Posix.1g.
+	   example: Solaris2.6 are defined IOV_MAX size at 16.     */
+	{
+		struct iovec *c_iov = iov;
+		nbytes = 0;	/* Make sure it's initialized. */
+
+		while (iov_index > 0) {
+			int iov_size;
+
+			iov_size =
+			    ((iov_index > IOV_MAX) ? IOV_MAX : iov_index);
+			if ((nbytes = writev(fd, c_iov, iov_size)) < 0) {
+				zlog_warn("%s: writev to fd %d failed: %s",
+					  __func__, fd, safe_strerror(errno));
+				break;
+			}
+
+			/* move pointer io-vector */
+			c_iov += iov_size;
+			iov_index -= iov_size;
+		}
+	}
+#else				/* IOV_MAX */
+	if ((nbytes = writev(fd, iov, iov_index)) < 0)
+		zlog_warn("%s: writev to fd %d failed: %s",
+			  __func__, fd, safe_strerror(errno));
+#endif				/* IOV_MAX */
+
+	/* Free printed buffer data. */
+	while (b->head && (b->head->sp == b->head->cp)) {
+		struct buffer_data *del;
+		if (!(b->head = (del = b->head)->next))
+			b->tail = NULL;
+		BUFFER_DATA_FREE(del);
+	}
+
+	if (iov != small_iov)
+		XFREE(MTYPE_TMP, iov);
+
+	return (nbytes < 0) ? BUFFER_ERROR :
+	    (b->head ? BUFFER_PENDING : BUFFER_EMPTY);
+}
+#endif
+
+/* This function (unlike other buffer_flush* functions above) is designed
+to work with non-blocking sockets.  It does not attempt to write out
+all of the queued data, just a "big" chunk.  It returns 0 if it was
+able to empty out the buffers completely, 1 if more flushing is
+required later, or -1 on a fatal write error. */
+buffer_status_t buffer_flush_available(struct buffer * b, int fd)
+{
+
+/* These are just reasonable values to make sure a significant amount of
+data is written.  There's no need to go crazy and try to write it all
+in one shot. */
+#ifdef IOV_MAX
+#define MAX_CHUNKS ((IOV_MAX >= 16) ? 16 : IOV_MAX)
+#else
+#define MAX_CHUNKS 16
+#endif
+#define MAX_FLUSH 131072
+
+	struct buffer_data *d;
+	size_t written;
+	struct iovec iov[MAX_CHUNKS];
+	size_t iovcnt = 0;
+	size_t nbyte = 0;
+
+	for (d = b->head; d && (iovcnt < MAX_CHUNKS) && (nbyte < MAX_FLUSH);
+	     d = d->next, iovcnt++) {
+		iov[iovcnt].iov_base = d->data + d->sp;
+		nbyte += (iov[iovcnt].iov_len = d->cp - d->sp);
+	}
+
+	if (!nbyte)
+		/* No data to flush: should we issue a warning message? */
+		return BUFFER_EMPTY;
+
+	/* only place where written should be sign compared */
+	if ((ssize_t) (written = writev(fd, iov, iovcnt)) < 0) {
+		if (ERRNO_IO_RETRY(errno))
+			/* Calling code should try again later. */
+			return BUFFER_PENDING;
+		return BUFFER_ERROR;
+	}
+
+	/* Free printed buffer data. */
+	while (written > 0) {
+		struct buffer_data *d;
+		if (!(d = b->head))
+			break;
+		if (written < d->cp - d->sp) {
+			d->sp += written;
+			return BUFFER_PENDING;
+		}
+
+		written -= (d->cp - d->sp);
+		if (!(b->head = d->next))
+			b->tail = NULL;
+		BUFFER_DATA_FREE(d);
+	}
+
+	return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+
+#undef MAX_CHUNKS
+#undef MAX_FLUSH
+}
+
+buffer_status_t
+buffer_write(struct buffer * b, int fd, const void *p, size_t size)
+{
+	ssize_t nbytes;
+
+#if 0
+	/* Should we attempt to drain any previously buffered data?  This could help reduce latency in pushing out the data if we are stuck in a long-running thread that is preventing the main select loop from calling the flush thread... */
+
+	if (b->head && (buffer_flush_available(b, fd) == BUFFER_ERROR))
+		return BUFFER_ERROR;
+#endif
+	if (b->head)
+		/* Buffer is not empty, so do not attempt to write the new data. */
+		nbytes = 0;
+	else if ((nbytes = write(fd, p, size)) < 0) {
+		if (ERRNO_IO_RETRY(errno))
+			nbytes = 0;
+		else
+			return BUFFER_ERROR;
+	}
+	/* Add any remaining data to the buffer. */
+	{
+		size_t written = nbytes;
+		if (written < size)
+			buffer_put(b, ((const char *)p) + written,
+				   size - written);
+	}
+	return b->head ? BUFFER_PENDING : BUFFER_EMPTY;
+}