struct: pack/unpack binary structs; fcntl: fcntl(), ioctl().
diff --git a/Modules/structmodule.c b/Modules/structmodule.c
new file mode 100644
index 0000000..61122e4
--- /dev/null
+++ b/Modules/structmodule.c
@@ -0,0 +1,447 @@
+/***********************************************************
+Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
+Netherlands.
+
+                        All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and its 
+documentation for any purpose and without fee is hereby granted, 
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in 
+supporting documentation, and that the names of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+******************************************************************/
+
+/* struct module -- pack values into and (out of) strings */
+
+#include "allobjects.h"
+#include "modsupport.h"
+
+static object *StructError;
+
+
+/* Define various structs to figure out the alignments of types */
+
+typedef struct { char c; short x; } s_short;
+typedef struct { char c; int x; } s_int;
+typedef struct { char c; long x; } s_long;
+typedef struct { char c; float x; } s_float;
+typedef struct { char c; double x; } s_double;
+
+#define SHORT_ALIGN (sizeof(s_short) - sizeof(short))
+#define INT_ALIGN (sizeof(s_int) - sizeof(int))
+#define LONG_ALIGN (sizeof(s_long) - sizeof(long))
+#define FLOAT_ALIGN (sizeof(s_float) - sizeof(float))
+#define DOUBLE_ALIGN (sizeof(s_double) - sizeof(double))
+
+
+/* Align a size according to a format code */
+
+static int
+align(size, c)
+	int size;
+	int c;
+{
+	int a;
+
+	switch (c) {
+	case 'h': a = SHORT_ALIGN; break;
+	case 'i': a = INT_ALIGN; break;
+	case 'l': a = LONG_ALIGN; break;
+	case 'f': a = FLOAT_ALIGN; break;
+	case 'd': a = DOUBLE_ALIGN; break;
+	default: return size;
+	}
+	return (size + a - 1) / a * a;
+}
+
+
+/* calculate the size of a format string */
+
+static int
+calcsize(fmt)
+	char *fmt;
+{
+	char *s;
+	char c;
+	int size,  num, itemsize, x;
+
+	s = fmt;
+	size = 0;
+	while ((c = *s++) != '\0') {
+		if ('0' <= c && c <= '9') {
+			num = c - '0';
+			while ('0' <= (c = *s++) && c <= '9') {
+				x = num*10 + (c - '0');
+				if (x/10 != num) {
+					err_setstr(StructError,
+						   "int overflow in fmt");
+					return -1;
+				}
+				num = x;
+			}
+			if (c == '\0')
+				break;
+		}
+		else
+			num = 1;
+		
+		size = align(size, c);
+		
+		switch (c) {
+			
+		case 'x': /* pad byte */
+		case 'b': /* byte-sized int */
+		case 'c': /* char */
+			itemsize = 1;
+			break;
+
+		case 'h': /* short ("half-size)" int */
+			itemsize = sizeof(short);
+			break;
+
+		case 'i': /* natural-size int */
+			itemsize = sizeof(int);
+			break;
+
+		case 'l': /* long int */
+			itemsize = sizeof(long);
+			break;
+
+		case 'f': /* float */
+			itemsize = sizeof(float);
+			break;
+
+		case 'd': /* double */
+			itemsize = sizeof(double);
+			break;
+
+		default:
+			err_setstr(StructError, "bad char in fmt");
+			return -1;
+
+		}
+		
+		x = num * itemsize;
+		size += x;
+		if (x/itemsize != num || size < 0) {
+			err_setstr(StructError, "total struct size too long");
+			return -1;
+		}
+			
+	}
+
+	return size;
+}
+
+
+/* pack(fmt, v1, v2, ...) --> string */
+
+static object *
+struct_calcsize(self, args)
+	object *self; /* Not used */
+	object *args;
+{
+	char *fmt;
+	int size;
+
+	if (!getargs(args, "s", &fmt))
+		return NULL;
+	size = calcsize(fmt);
+	if (size < 0)
+		return NULL;
+	return newintobject((long)size);
+}
+
+
+/* pack(fmt, v1, v2, ...) --> string */
+
+static object *
+struct_pack(self, args)
+	object *self; /* Not used */
+	object *args;
+{
+	object *format, *result, *v;
+	char *fmt;
+	int size, num;
+	int i, n;
+	char *s, *res, *restart;
+	char c;
+	long ival;
+	double fval;
+
+	if (args == NULL || !is_tupleobject(args) ||
+	    (n = gettuplesize(args)) < 1) {
+		err_badarg();
+		return NULL;
+	}
+	format = gettupleitem(args, 0);
+	if (!getargs(format, "s", &fmt))
+		return NULL;
+	size = calcsize(fmt);
+	if (size < 0)
+		return NULL;
+	result = newsizedstringobject((char *)NULL, size);
+	if (result == NULL)
+		return NULL;
+
+	s = fmt;
+	i = 1;
+	res = restart = getstringvalue(result);
+
+	while ((c = *s++) != '\0') {
+		if ('0' <= c && c <= '9') {
+			num = c - '0';
+			while ('0' <= (c = *s++) && c <= '9')
+			       num = num*10 + (c - '0');
+			if (c == '\0')
+				break;
+		}
+		else
+			num = 1;
+
+		res = restart + align((int)(res-restart), c);
+
+		while (--num >= 0) {
+			switch (c) {
+
+			case 'x': /* pad byte */
+				*res++ = '\0';
+				break;
+
+			case 'l':
+			case 'i':
+			case 'h':
+			case 'b':
+				if (i >= n) {
+					err_setstr(StructError,
+					   "insufficient arguments to pack");
+					goto fail;
+				}
+				v = gettupleitem(args, i++);
+				if (!is_intobject(v)) {
+					err_setstr(StructError,
+					   "bad argument type to pack");
+					goto fail;
+				}
+				ival = getintvalue(v);
+				switch (c) {
+				case 'b':
+					*res++ = ival;
+					break;
+				case 'h':
+					*(short*)res = ival;
+					res += sizeof(short);
+					break;
+				case 'i':
+					*(int*)res = ival;
+					res += sizeof(int);
+					break;
+				case 'l':
+					*(long*)res = ival;
+					res += sizeof(long);
+					break;
+				}
+				break;
+
+			case 'c':
+				if (i >= n) {
+					err_setstr(StructError,
+					   "insufficient arguments to pack");
+					goto fail;
+				}
+				v = gettupleitem(args, i++);
+				if (!is_stringobject(v) ||
+				    getstringsize(v) != 1) {
+					err_setstr(StructError,
+					   "bad argument type to pack");
+					goto fail;
+				}
+				*res++ = getstringvalue(v)[0];
+				break;
+
+			case 'd':
+			case 'f':
+				if (i >= n) {
+					err_setstr(StructError,
+					   "insufficient arguments to pack");
+					goto fail;
+				}
+				v = gettupleitem(args, i++);
+				if (!is_floatobject(v)) {
+					err_setstr(StructError,
+					   "bad argument type to pack");
+					goto fail;
+				}
+				fval = getfloatvalue(v);
+				switch (c) {
+				case 'f':
+					*(float*)res = (float)fval;
+					res += sizeof(float);
+					break;
+				case 'd':
+					*(double*)res = fval;
+					res += sizeof(double);
+					break;
+				}
+				break;
+
+			default:
+				err_setstr(StructError, "bad char in fmt");
+				goto fail;
+
+			}
+		}
+	}
+
+	if (i < n) {
+		err_setstr(StructError, "too many arguments for pack fmt");
+		goto fail;
+	}
+
+	return result;
+
+ fail:
+	DECREF(result);
+	return NULL;
+}
+
+
+/* unpack(fmt, string) --> (v1, v2, ...) */
+
+static object *
+struct_unpack(self, args)
+	object *self; /* Not used */
+	object *args;
+{
+	char *str, *start, *fmt, *s;
+	char c;
+	int len, size, num, x;
+	object *res, *v;
+
+	if (!getargs(args, "(ss#)", &fmt, &start, &len))
+		return NULL;
+	size = calcsize(fmt);
+	if (size != len) {
+		err_setstr(StructError, "unpack str size does not match fmt");
+		return NULL;
+	}
+	res = newlistobject(0);
+	if (res == NULL)
+		return NULL;
+	str = start;
+	s = fmt;
+	while ((c = *s++) != '\0') {
+		if ('0' <= c && c <= '9') {
+			num = c - '0';
+			while ('0' <= (c = *s++) && c <= '9')
+			       num = num*10 + (c - '0');
+			if (c == '\0')
+				break;
+		}
+		else
+			num = 1;
+
+		str = start + align((int)(str-start), c);
+
+		while (--num >= 0) {
+			switch (c) {
+
+			case 'x':
+				str++;
+				continue;
+
+			case 'b':
+				x = *str++;
+				if (x >= 128)
+					x -= 256;
+				v = newintobject((long)x);
+				break;
+
+			case 'c':
+				v = newsizedstringobject(str, 1);
+				str++;
+				break;
+
+			case 'h':
+				v = newintobject((long)*(short*)str);
+				str += sizeof(short);
+				break;
+
+			case 'i':
+				v = newintobject((long)*(int*)str);
+				str += sizeof(int);
+				break;
+
+			case 'l':
+				v = newintobject(*(long*)str);
+				str += sizeof(long);
+				break;
+
+			case 'f':
+				v = newfloatobject((double)*(float*)str);
+				str += sizeof(float);
+				break;
+
+			case 'd':
+				v = newfloatobject(*(double*)str);
+				str += sizeof(double);
+				break;
+
+			default:
+				err_setstr(StructError, "bad char in fmt");
+				goto fail;
+
+			}
+			if (v == NULL || addlistitem(res, v) < 0)
+				goto fail;
+			DECREF(v);
+		}
+	}
+
+	return res;
+
+ fail:
+	DECREF(res);
+	return NULL;
+}
+
+/* List of functions */
+
+static struct methodlist struct_methods[] = {
+	{"calcsize",	struct_calcsize},
+	{"pack",	struct_pack,	1/*varargs*/},
+	{"unpack",	struct_unpack},
+	{NULL,		NULL}		/* sentinel */
+};
+
+
+/* Module initialization */
+
+void
+initstruct()
+{
+	object *m, *d;
+
+	/* Create the module and add the functions */
+	m = initmodule("struct", struct_methods);
+
+	/* Add some symbolic constants to the module */
+	d = getmoduledict(m);
+	StructError = newstringobject("struct.error");
+	dictinsert(d, "error", StructError);
+
+	/* Check for errors */
+	if (err_occurred())
+		fatal("can't initialize module struct");
+}