make use of a callback table instead of undefined functions for integration
diff --git a/README b/README
index 97c84e7..c3bd6de 100644
--- a/README
+++ b/README
@@ -50,13 +50,7 @@
 libtelnet processor.  The third part is the libtelnet_send_*()
 functions, which generate TELNET commands and ensure data is properly
 formatted before sending over the wire.  The final part is the
-libtelnet_*_cb() functions.
-
-The libtelnet_*_cb() functions (callback fuctions from here out) are
-not actually implemented by libtelnet, but are used by it.  The
-libtelnet.h header includes declarations for the functions but it is
-the application's responsibility to provide implementations of these
-functions.
+callback structure libtelnet_cb_t.
 
 IIa. Initialization
 
@@ -66,13 +60,22 @@
    its own libtelnet_t structure, which is passed to all libtelnet
    API calls.
 
+ struct libtelnet_cb_t;
+   An instance of this structure must be initialized and have all
+   mandatory and desired optional callbacks set.  See section IId
+   for more information.
+
  void libtelnet_init(struct libtelnet_t *telnet,
-     enum libtelnet_mode_t mode);
+     struct libtelnet_cb_t *cb, enum libtelnet_mode_t mode);
    The libtelnet_init() function is responsible for initializing
    the data in a libtelnet_t structure.  It must be called
    immediately after establishing a connection and before any other
    libtelnet API calls are made.
 
+   The cb parameter must be a pointer to a fully initialized
+   instance of libtelnet_cb_t.  A single instance of the structure
+   can be shared between any number of libtelnet_t instances.
+
    The mode parameter must be one of LIBTELNET_MODE_SERVER or
    LIBTELNET_MODE_CLIENT.  These slightly alter the behavior of
    libtelnet in certain instances.  If you are implementing a
@@ -96,8 +99,9 @@
 IIc. Sending Data
 
  Note that all of the libtelnet_send_*() functions will invoke
- the libtelnet_send_cb() callback function.  The user_data parameter
- to each of these functions is passed through to the callback.
+ the send callback function attached to the libtelnet_t instance.
+ The user_data parameter to each of these functions is passed
+ through to the callback.
 
  void libtelnet_send_command(struct libtelnet_t *telnet,
      unsigned char cmd, void *user_data);
@@ -124,12 +128,39 @@
 
 IId. Callbacks
 
- The application is responsible for implementing these functions.
- The function definitions are included in libtelnet.h.  If any of
- these functions are not implemented and linked into the application
- then libtelnet.o will generate linker errors for undefined symbols.
+ The libtelnet_cb_t structure containers a number of callback
+ entry points.  Of these, only the send and data callbacks are
+ absolutely required.  All others are optional.  The declarations
+ below show the signature of the callback functions.
 
- void libtelnet_data_cb(struct libtelnet_t *telnet,
+ An example of initializing a libtelnet_cb_t structure:
+
+  /* illustrative data callback */
+  void my_data_cb(libtelnet_t *telnet, unsigned char *buffer,
+      unsigned int size, void *user_data) {
+    /* print number of bytes received and then show the
+     * whole buffer */
+    printf("RECV(%d): %.*s\n", size, size, buffer);
+  }
+
+  /* illustrative variable definitions */
+  libtelnet_t conn;
+  libtelnet_cb_t callbacks;
+
+  /* clear all callbacks and set just the ones we want */
+  memset(&callbacks, 0, sizeof(callbacks));
+  callbacks->send = my_send_cb;
+  callbacks->data = my_data_cb;
+
+  /* initialize the connection with our callbacks */
+  libtelnet_init(&conn, &callbacks, LIBTELNET_MODE_SERVER);
+
+ Remember that a single libtelnet_cb_t structure can be shared
+ between any number of libtelnet_t instances.  There is no reason
+ to make multiple copies of the data if all of your connections
+ use the same callback functions.
+
+ void libtelnet_cb_t->data(struct libtelnet_t *telnet,
      unsigned char *buffer, unsigned int size, void *user_data);
    Regular data has been received by the remote end.  For a server,
    this would be input typed by the client; for a client, this is
@@ -141,7 +172,7 @@
    line-based processing of data, it is your responsibility to
    buffer data and find the line breaks.
 
- void libtelnet_send_cb(struct libtelnet_t *telnet,
+ void libtelnet_cb_t->send(struct libtelnet_t *telnet,
      unsigned char *buffer, unsigned int size, void *user_data);
    This is called whenever libtelnet has generated output to be
    send to the remote end of the connection.  In most cases this
@@ -152,14 +183,14 @@
    parameter to libtelnet calls so that it is available in this
    callback.
 
- void libtelnet_command_cb(struct libtelnet_t *telnet,
+ void libtelnet_cb_t->command(struct libtelnet_t *telnet,
      unsigned char cmd, void *user_data);
    Called whenever a "simpler" TELNET command has arrived, such
    as GO-AHEAD commands (255 249).  The necessary processing
    depends on the specific commands; see the TELNET RFC for
    more information.
 
- void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
+ void libtelnet_cb_t->negotiate(struct libtelnet_t *telnet,
      unsigned char cmd, unsigned char opt, void *user_data);
    This function is called whenever a TELNET negotiation command
    has been received.  The cmd parameter will be one of
@@ -171,7 +202,7 @@
 
     http://www.faqs.org/rfcs/rfc1143.html
 
- void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
+ void libtelnet_cb_t->subnegotiation(struct libtelnet_t *telnet,
      unsigned char opt, unsigned char *data, unsigned int size,
      void *user_data);
    Called whenever a TELNET sub-negotiation has been received.
@@ -183,7 +214,7 @@
    The opt parameter is the option under sub-negotiation.  The
    remaining data (if any) is passed in the buffer.
 
- void libtelnet_compress_cb(struct libtelnet_t *telnet,
+ void libtelnet_cb_t->compress(struct libtelnet_t *telnet,
      char enabled, void *user_data);
    The callback is invoked whenever the COMPRESS2 (MCCP2)
    feature is enabled or disabled.  For servers, this is called
diff --git a/libtelnet.c b/libtelnet.c
index 0a4a9f0..544c0ef 100644
--- a/libtelnet.c
+++ b/libtelnet.c
@@ -30,8 +30,10 @@
 	sizeof(_buffer_sizes) / sizeof(_buffer_sizes[0]);
 
 /* initialize a telnet state tracker */
-void libtelnet_init(struct libtelnet_t *telnet, enum libtelnet_mode_t mode) {
+void libtelnet_init(struct libtelnet_t *telnet, struct libtelnet_cb_t *cb,
+		enum libtelnet_mode_t mode) {
 	memset(telnet, 0, sizeof(struct libtelnet_t));
+	telnet->cb = cb;
 	telnet->mode = mode;
 }
 
@@ -72,7 +74,7 @@
 
 		/* overflow -- can't grow any more */
 		if (i >= _buffer_sizes_count - 1) {
-			libtelnet_error_cb(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
+			telnet->cb->error(telnet, LIBTELNET_ERROR_OVERFLOW, user_data);
 			libtelnet_free(telnet);
 			return LIBTELNET_ERROR_OVERFLOW;
 		}
@@ -81,7 +83,7 @@
 		new_buffer = (unsigned char *)realloc(telnet->buffer,
 				_buffer_sizes[i + 1]);
 		if (new_buffer == 0) {
-			libtelnet_error_cb(telnet, LIBTELNET_ERROR_NOMEM,
+			telnet->cb->error(telnet, LIBTELNET_ERROR_NOMEM,
 				user_data);
 			libtelnet_free(telnet);
 			return LIBTELNET_ERROR_NOMEM;
@@ -109,7 +111,7 @@
 			 * switch states */
 			if (byte == LIBTELNET_IAC) {
 				if (i != start)
-					libtelnet_data_cb(telnet, &buffer[start], i - start,
+					telnet->cb->data(telnet, &buffer[start], i - start,
 							user_data);
 				telnet->state = LIBTELNET_STATE_IAC;
 			}
@@ -137,13 +139,13 @@
 				break;
 			/* IAC escaping */
 			case LIBTELNET_IAC:
-				libtelnet_data_cb(telnet, &byte, 1, user_data);
+				telnet->cb->data(telnet, &byte, 1, user_data);
 				start = i + 1;
 				telnet->state = LIBTELNET_STATE_DATA;
 				break;
 			/* some other command */
 			default:
-				libtelnet_command_cb(telnet, byte, user_data);
+				telnet->cb->command(telnet, byte, user_data);
 				start = i + 1;
 				telnet->state = LIBTELNET_STATE_DATA;
 			}
@@ -151,22 +153,22 @@
 
 		/* negotiation commands */
 		case LIBTELNET_STATE_DO:
-			libtelnet_negotiate_cb(telnet, LIBTELNET_DO, byte, user_data);
+			telnet->cb->negotiate(telnet, LIBTELNET_DO, byte, user_data);
 			start = i + 1;
 			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_DONT:
-			libtelnet_negotiate_cb(telnet, LIBTELNET_DONT, byte, user_data);
+			telnet->cb->negotiate(telnet, LIBTELNET_DONT, byte, user_data);
 			start = i + 1;
 			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_WILL:
-			libtelnet_negotiate_cb(telnet, LIBTELNET_WILL, byte, user_data);
+			telnet->cb->negotiate(telnet, LIBTELNET_WILL, byte, user_data);
 			start = i + 1;
 			telnet->state = LIBTELNET_STATE_DATA;
 			break;
 		case LIBTELNET_STATE_WONT:
-			libtelnet_negotiate_cb(telnet, LIBTELNET_WONT, byte, user_data);
+			telnet->cb->negotiate(telnet, LIBTELNET_WONT, byte, user_data);
 			start = i + 1;
 			telnet->state = LIBTELNET_STATE_DATA;
 			break;
@@ -195,13 +197,13 @@
 
 				/* zero-size buffer is a protocol error */
 				if (telnet->length == 0) {
-					libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
+					telnet->cb->error(telnet, LIBTELNET_ERROR_PROTOCOL,
 						user_data);
 					break;
 				}
 
 				/* invoke callback */
-				libtelnet_subnegotiation_cb(telnet, telnet->buffer[0],
+				telnet->cb->subnegotiation(telnet, telnet->buffer[0],
 					telnet->buffer + 1, telnet->length - 1, user_data);
 				telnet->length = 0;
 
@@ -215,7 +217,7 @@
 					/* allocate zstream box */
 					if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
 							== 0) {
-						libtelnet_error_cb(telnet,
+						telnet->cb->error(telnet,
 							LIBTELNET_ERROR_NOMEM, user_data);
 					}
 
@@ -224,13 +226,13 @@
 					if (inflateInit(telnet->zlib) != Z_OK) {
 						free(telnet->zlib);
 						telnet->zlib = 0;
-						libtelnet_error_cb(telnet,
+						telnet->cb->error(telnet,
 							LIBTELNET_ERROR_UNKNOWN, user_data);
 						break;
 					}
 
 					/* notify app that compression was enabled */
-					libtelnet_compress_cb(telnet, 1, user_data);
+					telnet->cb->compress(telnet, 1, user_data);
 
 					/* any remaining bytes in the buffer are compressed.
 					 * we have to re-invoke libtelnet_push to get those
@@ -258,7 +260,7 @@
 				break;
 			/* something else -- protocol error */
 			default:
-				libtelnet_error_cb(telnet, LIBTELNET_ERROR_PROTOCOL,
+				telnet->cb->error(telnet, LIBTELNET_ERROR_PROTOCOL,
 						user_data);
 				start = i + 1;
 				telnet->state = LIBTELNET_STATE_DATA;
@@ -270,7 +272,7 @@
 
 	/* pass through any remaining bytes */ 
 	if (telnet->state == LIBTELNET_STATE_DATA && i != start)
-		libtelnet_data_cb(telnet, &buffer[start], i - start, user_data);
+		telnet->cb->data(telnet, &buffer[start], i - start, user_data);
 }
 
 /* push a bytes into the state tracker */
@@ -302,7 +304,7 @@
 				_process(telnet, inflate_buffer, sizeof(inflate_buffer) -
 						telnet->zlib->avail_out, user_data);
 			else
-				libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+				telnet->cb->error(telnet, LIBTELNET_ERROR_UNKNOWN,
 						user_data);
 
 			/* prepare output buffer for next run */
@@ -311,7 +313,7 @@
 
 			/* on error (or on end of stream) disable further inflation */
 			if (rs != Z_OK) {
-				libtelnet_compress_cb(telnet, 0, user_data);
+				telnet->cb->compress(telnet, 0, user_data);
 
 				inflateEnd(telnet->zlib);
 				free(telnet->zlib);
@@ -347,7 +349,7 @@
 
 			/* compress */
 			if (deflate(telnet->zlib, Z_SYNC_FLUSH) != Z_OK) {
-				libtelnet_error_cb(telnet, LIBTELNET_ERROR_UNKNOWN,
+				telnet->cb->error(telnet, LIBTELNET_ERROR_UNKNOWN,
 						user_data);
 				deflateEnd(telnet->zlib);
 				free(telnet->zlib);
@@ -355,7 +357,7 @@
 				break;
 			}
 
-			libtelnet_send_cb(telnet, deflate_buffer, sizeof(deflate_buffer) -
+			telnet->cb->send(telnet, deflate_buffer, sizeof(deflate_buffer) -
 					telnet->zlib->avail_out, user_data);
 
 			/* prepare output buffer for next run */
@@ -366,7 +368,7 @@
 	/* COMPRESS2 is not negotiated, just send */
 	} else
 #endif /* HAVE_ZLIB */
-		libtelnet_send_cb(telnet, buffer, size, user_data);
+		telnet->cb->send(telnet, buffer, size, user_data);
 }
 
 /* send an iac command */
@@ -423,7 +425,7 @@
 		/* allocate zstream box */
 		if ((telnet->zlib = (z_stream *)malloc(sizeof(z_stream)))
 				== 0) {
-			libtelnet_error_cb(telnet,
+			telnet->cb->error(telnet,
 				LIBTELNET_ERROR_NOMEM, user_data);
 		}
 
@@ -432,12 +434,12 @@
 		if (deflateInit(telnet->zlib, Z_DEFAULT_COMPRESSION) != Z_OK) {
 			free(telnet->zlib);
 			telnet->zlib = 0;
-			libtelnet_error_cb(telnet,
+			telnet->cb->error(telnet,
 				LIBTELNET_ERROR_UNKNOWN, user_data);
 		}
 
 		/* notify app that compression was enabled */
-		libtelnet_compress_cb(telnet, 1, user_data);
+		telnet->cb->compress(telnet, 1, user_data);
 	}
 #endif /* HAVE_ZLIB */
 }
diff --git a/libtelnet.h b/libtelnet.h
index 729bd50..821839f 100644
--- a/libtelnet.h
+++ b/libtelnet.h
@@ -12,6 +12,10 @@
 #if !defined(LIBTELNET_INCLUDE)
 #define LIBTELNET_INCLUDE 1
 
+/* forward declarations */
+struct libtelnet_t;
+struct libtelnet_cb_t;
+
 /* telnet special values */
 #define LIBTELNET_IAC 255
 #define LIBTELNET_DONT 254
@@ -108,10 +112,40 @@
 	LIBTELNET_ERROR_UNKNOWN /* some crazy unexplainable unknown error */
 };
 
+/* libtelnet callback declarations */
+struct libtelnet_cb_t {
+	/* received (processed) data */
+	void (*data)(struct libtelnet_t *telnet,
+			unsigned char *buffer, unsigned int size, void *user_data);
+	/* processed data to buffer for sending */
+	void (*send)(struct libtelnet_t *telnet,
+			unsigned char *buffer, unsigned int size, void *user_data);
+	/* unknown command notification */
+	void (*command)(struct libtelnet_t *telnet,
+			unsigned char cmd, void *user_data);
+	/* negotiation notification */
+	void (*negotiate)(struct libtelnet_t *telnet,
+			unsigned char cmd, unsigned char opt, void *user_data);
+	/* unknown subnegotiation notification */
+	void (*subnegotiation)(struct libtelnet_t *telnet,
+			unsigned char opt, unsigned char *data, unsigned int size,
+			void *user_data);
+	/* error handler */
+	void (*error)(struct libtelnet_t *telnet,
+			enum libtelnet_error_t error, void *user_data);
+
+	#ifdef HAVE_ZLIB
+	void (*compress)(struct libtelnet_t *telnet,
+			char enabled, void *user_data);
+	#endif
+};
+
 /* state tracker */
 struct libtelnet_t {
-	/* zlib (mccp2) compression */
+	/* callback table */
+	struct libtelnet_cb_t *cb;
 #ifdef HAVE_ZLIB
+	/* zlib (mccp2) compression */
 	z_stream *zlib;
 #endif
 	/* sub-request buffer */
@@ -126,30 +160,9 @@
 	enum libtelnet_mode_t mode;
 };
 
-/* libtelnet callback declarations
- * APPLICATION MUST IMPLEMENT THESE FUNCTIONS!!
- */
-extern void libtelnet_data_cb(struct libtelnet_t *telnet,
-		unsigned char *buffer, unsigned int size, void *user_data);
-extern void libtelnet_send_cb(struct libtelnet_t *telnet,
-		unsigned char *buffer, unsigned int size, void *user_data);
-extern void libtelnet_command_cb(struct libtelnet_t *telnet,
-		unsigned char cmd, void *user_data);
-extern void libtelnet_negotiate_cb(struct libtelnet_t *telnet,
-		unsigned char cmd, unsigned char opt, void *user_data);
-extern void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
-		unsigned char opt, unsigned char *data, unsigned int size,
-		void *user_data);
-#ifdef HAVE_ZLIB
-extern void libtelnet_compress_cb(struct libtelnet_t *telnet,
-		char enabled, void *user_data);
-#endif
-extern void libtelnet_error_cb(struct libtelnet_t *telnet,
-		enum libtelnet_error_t error, void *user_data);
-
 /* initialize a telnet state tracker */
 extern void libtelnet_init(struct libtelnet_t *telnet,
-		enum libtelnet_mode_t mode);
+		struct libtelnet_cb_t *cb, enum libtelnet_mode_t mode);
 
 /* free up any memory allocated by a state tracker */
 extern void libtelnet_free(struct libtelnet_t *telnet);
diff --git a/telnet-proxy.c b/telnet-proxy.c
index 96a1fe3..8179253 100644
--- a/telnet-proxy.c
+++ b/telnet-proxy.c
@@ -126,7 +126,7 @@
 	}
 }
 
-void libtelnet_data_cb(struct libtelnet_t *telnet, unsigned char *buffer,
+static void _data_cb(struct libtelnet_t *telnet, unsigned char *buffer,
 		unsigned int size, void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 
@@ -138,7 +138,7 @@
 			conn->remote);
 }
 
-void libtelnet_send_cb(struct libtelnet_t *telnet, unsigned char *buffer,
+static void _send_cb(struct libtelnet_t *telnet, unsigned char *buffer,
 		unsigned int size, void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 	int rs;
@@ -165,7 +165,7 @@
 	}
 }
 
-void libtelnet_command_cb(struct libtelnet_t *telnet, unsigned char cmd,
+static void _command_cb(struct libtelnet_t *telnet, unsigned char cmd,
 		void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 
@@ -174,7 +174,7 @@
 	libtelnet_send_command(&conn->remote->telnet, cmd, conn->remote);
 }
 
-void libtelnet_negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd,
+static void _negotiate_cb(struct libtelnet_t *telnet, unsigned char cmd,
 		unsigned char opt, void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 
@@ -185,7 +185,7 @@
 			conn->remote);
 }
 
-void libtelnet_subnegotiation_cb(struct libtelnet_t *telnet,
+static void _subnegotiation_cb(struct libtelnet_t *telnet,
 		unsigned char type, unsigned char *buffer, unsigned int size,
 		void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
@@ -201,14 +201,14 @@
 			conn->remote);
 }
 
-void libtelnet_compress_cb(struct libtelnet_t *telnet, char enabled,
+static void _compress_cb(struct libtelnet_t *telnet, char enabled,
 		void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 
 	printf("%s COMPRESSION %s\e[0m\n", conn->name, enabled ? "ON" : "OFF");
 }
 
-void libtelnet_error_cb(struct libtelnet_t *telnet,
+static void _error_cb(struct libtelnet_t *telnet,
 		enum libtelnet_error_t error, void *user_data) {
 	struct conn_t *conn = (struct conn_t*)user_data;
 
@@ -225,6 +225,7 @@
 	struct pollfd pfd[2];
 	struct conn_t server;
 	struct conn_t client;
+	struct libtelnet_cb_t cb_table;
 
 	/* check usage */
 	if (argc != 4) {
@@ -298,13 +299,23 @@
 	client.name = "\e[34mCLIENT";
 	client.remote = &server;
 
+	/* initialize libtelnet callback table */
+	memset(&cb_table, 0, sizeof(cb_table));
+	cb_table.data = _data_cb;
+	cb_table.send = _send_cb;
+	cb_table.command = _command_cb;
+	cb_table.negotiate = _negotiate_cb;
+	cb_table.subnegotiation = _subnegotiation_cb;
+	cb_table.compress = _compress_cb;
+	cb_table.error = _error_cb;
+
 	/* initialize telnet boxes
 	 * NOTE: we set the server connect to the CLIENT mode because we
 	 * are acting as a client of the server; likewise, we set the
 	 * client connection to SERVER mode becauser we are acting as a
 	 * server to the client. */
-	libtelnet_init(&server.telnet, LIBTELNET_MODE_CLIENT);
-	libtelnet_init(&client.telnet, LIBTELNET_MODE_SERVER);
+	libtelnet_init(&server.telnet, &cb_table, LIBTELNET_MODE_CLIENT);
+	libtelnet_init(&client.telnet, &cb_table, LIBTELNET_MODE_SERVER);
 
 	/* initialize poll descriptors */
 	memset(pfd, 0, sizeof(pfd));