/*
 * Copyright (C) 2008-2015 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#define DEBUG	0

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

struct sig {
	struct sig *next;
	struct sig *prev;

	const char *name;
	const char *type;
	int is_port;
};
struct port {
	struct port *next;
	struct port *prev;

	char *name;
	char *type;

	struct sig *sig;
};

struct comp {
	int x;
	int y;
	unsigned int rotate;
	unsigned int mirror;
	char *sym_file;

	struct el *con_first;
	struct el *con_last;
};
struct net {
	int x0;
	int y0;
	int x1;
	int y1;

	struct sig *sig;
};
struct pin {
	int x0;
	int y0;
	int x1;
	int y1;
	unsigned int color;

	struct sig *sig;
};
struct text {
	int x;
	int y;
	unsigned int color;
	unsigned int size;
	unsigned int visible;
	unsigned int hide;	/* 1: Hide name, 2: Hide value */
	unsigned int angle;
	unsigned int align;
	char *str[128];
};
struct bus {
	int x0;
	int y0;
	int x1;
	int y1;
	unsigned int color;

	struct sig *sig;
};
struct el {
	struct el *prev;
	struct el *next;

	struct el *child_first;
	struct el *child_last;

	enum {
		COMP, NET, PIN, TEXT, BUS
	} type;
	union {
		struct comp comp;
		struct net net;
		struct pin pin;
		struct text text;
		struct bus bus;
	} u;
};

char *progname;
char *idir[100];
unsigned int nidirs = 0;
int opt_M;
char *inname;

static const struct {
	const char *from;
	const char *to;
	const char *include;
} sig_translate[] = {
	/* Keep sorted! */
	{ "agp_bus", "sig_agp_bus", "sig_agp_bus.h" },
	{ "agp_conn", "sig_agp_conn", "sig_agp_conn.h" },
	{ "at_conn", "sig_at_conn", "sig_at_conn.h" },
	{ "atx_conn", "sig_atx_conn", "sig_atx_conn.h" },
	{ "boolean", "sig_boolean", "sig_boolean.h" },
	{ "cardbus_conn", "sig_cardbus_conn", "sig_cardbus_conn.h" },
	{ "centronics_conn", "sig_centronics_conn", "sig_centronics_conn.h" },
	{ "cinch_stereo_conn", "sig_cinch_stereo_conn", "sig_cinch_stereo_conn.h" },
	{ "cs", "sig_cs", "sig_cs.h" },
	{ "cs8", "sig_cs8", "sig_cs8.h" },
	{ "cs16", "sig_cs16", "sig_cs16.h" },
	{ "cs32", "sig_cs32", "sig_cs32.h" },
	{ "cs64", "sig_cs64", "sig_cs64.h" },
	{ "dimm168_bus", "sig_dimm168_bus", "sig_dimm168_bus.h" },
	{ "dimm168_conn", "sig_dimm168_conn", "sig_dimm168_conn.h" },
	{ "dimm240_bus", "sig_dimm240_bus", "sig_dimm240_bus.h" },
	{ "dimm240_conn", "sig_dimm240_conn", "sig_dimm240_conn.h" },
	{ "dio24_conn", "sig_dio24_conn", "sig_dio24_conn.h" },
	{ "dio48_conn", "sig_dio48_conn", "sig_dio48_conn.h" },
	{ "dmi_bus", "sig_dmi_bus", "sig_dmi_bus.h" },
	{ "eth", "sig_eth", "sig_eth.h" },
	{ "eth_conn", "sig_eth_conn", "sig_eth_conn.h" },
	{ "floppy", "sig_floppy", "sig_floppy.h" },
	{ "fault", "sig_fault", "sig_fault.h" },
	{ "host_bus", "sig_host_bus", "sig_host_bus.h" },
	{ "host775_bus", "sig_host775_bus", "sig_host775_bus.h" },
	{ "host775_conn", "sig_host775_conn", "sig_host775_conn.h" },
	{ "i2c_bus", "sig_i2c_bus", "sig_i2c_bus.h" },
	{ "icc_bus", "sig_icc_bus", "sig_icc_bus.h" },
	{ "ide_bus", "sig_ide_bus", "sig_ide_bus.h" },
	{ "ide_conn", "sig_ide_conn", "sig_ide_conn.h" },
	{ "integer", "sig_integer", "sig_integer.h" },
	{ "isa_bus", "sig_isa_bus", "sig_isa_bus.h" },
	{ "isa_dma", "sig_isa_dma", "sig_isa_dma.h" },
	{ "isa_conn", "sig_isa_conn", "sig_isa_conn.h" },
	{ "magneto_optical", "sig_magneto_optical", "sig_magneto_optical.h" },
	{ "manage", "sig_manage", "sig_manage.h" },
	{ "match", "sig_match", "sig_match.h" },
	{ "mdi", "sig_mdi", "sig_mdi.h" },
	{ "mem_bus", "sig_mem_bus", "sig_mem_bus.h" },
	{ "opt_rgb", "sig_opt_rgb", "sig_opt_rgb.h" },
	{ "parallel", "sig_parallel", "sig_parallel.h" },
	{ "pci_conn", "sig_pci_conn", "sig_pci_conn.h" },
	{ "pci_bus", "sig_pci_bus", "sig_pci_bus.h" },
	{ "pci_idsel", "sig_pci_idsel", "sig_pci_idsel.h" },
	{ "molex_ide_power_conn", "sig_molex_ide_power_conn", "sig_molex_ide_power_conn.h" },
	{ "ps2_main", "sig_ps2_main", "sig_ps2_main.h" },
	{ "ps2_conn", "sig_ps2_conn", "sig_ps2_conn.h" },
	{ "rs232_conn", "sig_rs232_conn", "sig_rs232_conn.h" },
	{ "sata_bus", "sig_sata_bus", "sig_sata_bus.h" },
	{ "sata_conn", "sig_sata_conn", "sig_sata_conn.h" },
	{ "scsi_bus", "sig_scsi_bus", "sig_scsi_bus.h" },
	{ "scsi_conn", "sig_scsi_conn", "sig_scsi_conn.h" },
	{ "serial", "sig_serial", "sig_serial.h" },
	{ "shugart_bus", "sig_shugart_bus", "sig_shugart_bus.h" },
	{ "shugart_conn", "sig_shugart_conn", "sig_shugart_conn.h" },
	{ "slot1_conn", "sig_slot1_conn", "sig_slot1_conn.h" },
	{ "sound", "sig_sound", "sig_sound.h" },
	{ "std_logic", "sig_std_logic", "sig_std_logic.h" },
	{ "std_logic_conn", "sig_std_logic_conn", "sig_std_logic_conn.h" },
	{ "string", "sig_string", "sig_string.h" },
	{ "telephone", "sig_telephone", "sig_telephone.h" },
	{ "usb_bus", "sig_usb_bus", "sig_usb_bus.h" },
	{ "usb_conn", "sig_usb_conn", "sig_usb_conn.h" },
	{ "vga_conn", "sig_vga_conn", "sig_vga_conn.h" },
	{ "video", "sig_video", "sig_video.h" },
	{ NULL, NULL, NULL }
};

static struct entry {
	const char *port;
	unsigned int pinseq;
	const char *pintype;
	const char *type;
	const char *gender;
	struct sig *sig;
} entry[10000];
static unsigned int nentries;

static void
table_init(void)
{
	nentries = 0;
}

static void
table_add(
	const char *label,
	const char *pinseq,
	const char *pintype,
	const char *type,
	const char *gender,
	struct sig *sig
)
{
	entry[nentries].port = label;
	if (pinseq) {
		entry[nentries].pinseq = atoi(pinseq);
	} else {
		entry[nentries].pinseq = 0;
	}
	entry[nentries].pintype = pintype;
	entry[nentries].type = type;
	entry[nentries].gender = gender;
	entry[nentries].sig = sig;
	nentries++;
}

static void
table_sort(void)
{
	unsigned int i;
	int done;

	if (nentries <= 1) {
		/* Nothing to sort... */
		return;
	}

	do {
		done = 1;
		for (i = 0; i < nentries - 1; i++) {
			if (entry[i + 1].pinseq < entry[i].pinseq
			 || (entry[i + 1].pinseq == entry[i].pinseq
			  && strcmp(entry[i + 1].port, entry[i].port) < 0)) {
				const char *port_tmp;
				unsigned int pinseq_tmp;
				const char *pintype_tmp;
				const char *type_tmp;
				const char *gender_tmp;
				struct sig *sig_tmp;

				port_tmp = entry[i].port;
				pinseq_tmp = entry[i].pinseq;
				pintype_tmp = entry[i].pintype;
				type_tmp = entry[i].type;
				gender_tmp = entry[i].gender;
				sig_tmp = entry[i].sig;
				entry[i].port = entry[i + 1].port;
				entry[i].pinseq = entry[i + 1].pinseq;
				entry[i].pintype = entry[i + 1].pintype;
				entry[i].type = entry[i + 1].type;
				entry[i].gender = entry[i + 1].gender;
				entry[i].sig = entry[i + 1].sig;
				entry[i + 1].port = port_tmp;
				entry[i + 1].pinseq = pinseq_tmp;
				entry[i + 1].pintype = pintype_tmp;
				entry[i + 1].type = type_tmp;
				entry[i + 1].gender = gender_tmp;
				entry[i + 1].sig = sig_tmp;
				done = 0;
			}
		}
	} while (! done);
}

static const char *
sig_type(const char *s)
{
	unsigned int i;

	for (i = 0; ; i++) {
		if (sig_translate[i].from == NULL) {
			fprintf(stderr, "ERROR: %s: %s: unknown signal type.\n",
					progname, s);
			return s;
		}
		if (strcmp(sig_translate[i].from, s) == 0) {
			return sig_translate[i].to;
		}
	}
}

static const char *
vhdl_sig_type(const char *s)
{
	return s;
}

static const char *
sig_include(const char *s)
{
	unsigned int i;

	for (i = 0; ; i++) {
		if (sig_translate[i].from == NULL) {
			fprintf(stderr, "ERROR: %s: %s: unknown signal type.\n",
					progname, s);
			return s;
		}
		if (strcmp(sig_translate[i].from, s) == 0) {
			return sig_translate[i].include;
		}
	}
}

static void
mangle(char *buf, const char *real)
{
	char c;

	while ((c = *real++) != '\0') {
		switch (c) {
		case '0' ... '9':
		case 'A' ... 'Z':
		case 'a' ... 'z':
		case '_':
			*buf++ = c;
			break;
		case '.':
			*buf++ = '_';
			break;
		case '#':
			*buf++ = 'X';
			*buf++ = 'h';
			*buf++ = 'a';
			*buf++ = 's';
			*buf++ = 'h';
			*buf++ = 'X';
			break;
		case '+':
			*buf++ = 'X';
			*buf++ = 'p';
			*buf++ = 'l';
			*buf++ = 'u';
			*buf++ = 's';
			*buf++ = 'X';
			break;
		case '-':
			*buf++ = 'X';
			*buf++ = 'm';
			*buf++ = 'i';
			*buf++ = 'n';
			*buf++ = 'u';
			*buf++ = 's';
			*buf++ = 'X';
			break;
		default:
			fprintf(stderr, "ERROR: %s: Bad character '%c' in signal name.\n",
					progname, c);
			break;
		}
	}
	*buf = '\0';
}

static const char *
name(const char *real)
{
	static char buf[10][100];
	static unsigned int bufno = 0;

	bufno %= 10;

	mangle(&buf[bufno][0], real);

	return buf[bufno++];
}

#if 0
static const char *
sig_name(const char *real)
{
	static char buf[10][100];
	static unsigned int bufno = 0;

	bufno %= 10;

	strcpy(buf[bufno], "sig_");
	mangle(&buf[bufno][strlen("sig_")], real);

	return buf[bufno++];
}
#endif

static const char *
vhdl_sig_name(const char *real)
{
	static char buf[10][100];
	static unsigned int bufno = 0;

	bufno %= 10;

	mangle(&buf[bufno][0], real);

	return buf[bufno++];
}

static const char *
port_name(const char *real)
{
	static char buf[10][100];
	static unsigned int bufno = 0;

	bufno %= 10;

	strcpy(buf[bufno], "port_");
	mangle(&buf[bufno][strlen("port_")], real);

	return buf[bufno++];
}

static const char *
ident_tmp(void)
{
	static unsigned int nr = 0;
	char tmp[100];

	sprintf(tmp, "tmp%06u", nr++);
	return strdup(tmp);
}

int
_ungetc(int c, FILE *stream)
{
	if (c != EOF) {
		return ungetc(c, stream);
	} else {
		return c;
	}
}

void
skip_white(FILE *fp)
{
	int c;

	c = fgetc(fp);
	while (c == '\t'
	    || c == '\n'
	    || c == '\r'
	    || c == ' ') {
		c = fgetc(fp);
	}
	_ungetc(c, fp);
}

int
read_char(FILE *fp, char *cp)
{
	int c;

	skip_white(fp);

	c = fgetc(fp);

	if (c == EOF
	 || c == '\0') {
		return EOF;
	} else {
		*cp = c;
		return 0;
	}
}

int
read_int(FILE *fp, int *ip)
{
	int sign;
	int c;

	skip_white(fp);

	*ip = 0;
	c = fgetc(fp);
	if (c == '-') {
		sign = -1;
		c = fgetc(fp);
	} else {
		sign = 1;
	}
	while ('0' <= c && c <= '9') {
		*ip *= 10;
		*ip += c - '0';
		c = fgetc(fp);
	}
	_ungetc(c, fp);

	*ip *= sign;

	return 0;
}

int
read_ident(FILE *fp, char *ident)
{
	int c;
	int x;

	skip_white(fp);

	x = 0;
	c = fgetc(fp);
	while (c != EOF
	    && c != '\0'
	    && c != '\t'
	    && c != '\n'
	    && c != '\r'
	    && c != ' ') {
		ident[x++] = c;
		c = fgetc(fp);
	}
	ident[x] = '\0';
	_ungetc(c, fp);

	return 0;
}

int
read_line(FILE *fp, char *ident)
{
	int c;
	int x;

	c = fgetc(fp);
	while (c != '\n') {
		c = fgetc(fp);
	}

	x = 0;
	c = fgetc(fp);
	while (c != EOF
	    && c != '\0'
	    && c != '\n'
	    && c != '\r') {
		ident[x++] = c;
		c = fgetc(fp);
	}
	ident[x] = '\0';
	_ungetc(c, fp);

	return 0;
}

/*forward*/ static int
readp_sub(FILE *fp, struct el **firstp, struct el **lastp);

static int
read_list(FILE *fp, struct el **firstp, struct el **lastp)
{
	int ret;
	char c;
	int num;
	int i;
	char ident[1024];
	struct el *e;

	*firstp = NULL;
	*lastp = NULL;

	for (;;) {
		ret = read_char(fp, &c);
		if (ret == EOF) {
			_ungetc(EOF, fp);
			break;
		}
		if (c == ']'
		 || c == '}') {
			_ungetc(c, fp);
			break;
		}

		switch (c) {
		case '{':
		case '[':
			assert(0);
			break;
		case 'A':
			/* Arc */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'B':
			/* Box */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'C':
			/* Component */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = COMP;
			read_int(fp, &e->u.comp.x); /* X Coord */
			read_int(fp, &e->u.comp.y); /* Y Coord */
			read_int(fp, &num);
			read_int(fp, &e->u.comp.rotate); /* Rotate */
			read_int(fp, &e->u.comp.mirror); /* Mirror */
			read_ident(fp, ident); /* Sym File */
			e->u.comp.sym_file = strdup(ident);
			assert(e->u.comp.sym_file);
			assert(e->u.comp.sym_file[0]);
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'L':
			e = NULL;

			read_int(fp, &num); /* Start X */
			read_int(fp, &num); /* Start Y */
			read_int(fp, &num); /* End X */
			read_int(fp, &num); /* End Y */
			read_int(fp, &num); /* Color */
			read_int(fp, &num); /* Width */
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'N':
			/* Single Signal */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = NET;
			read_int(fp, &e->u.net.x0); /* Start X */
			read_int(fp, &e->u.net.y0); /* Start Y */
			read_int(fp, &e->u.net.x1); /* End Y */
			read_int(fp, &e->u.net.y1); /* End Y */
			read_int(fp, &num); /* Color */
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'P':
			/* Pin */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = PIN;
			read_int(fp, &e->u.pin.x0); /* Start X */
			read_int(fp, &e->u.pin.y0); /* Start Y */
			read_int(fp, &e->u.pin.x1); /* End X */
			read_int(fp, &e->u.pin.y1); /* End Y */
			read_int(fp, &e->u.pin.color); /* Color */
			read_int(fp, &num); /* ? */
			read_int(fp, &num); /* ? */
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'T': /* Text */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = TEXT;
			read_int(fp, &e->u.text.x);
			read_int(fp, &e->u.text.y);
			read_int(fp, &e->u.text.color);
			read_int(fp, &e->u.text.size);
			read_int(fp, &e->u.text.visible);
			read_int(fp, &e->u.text.hide);
			read_int(fp, &e->u.text.angle);
			read_int(fp, &e->u.text.align);
			read_int(fp, &num); /* Number of Lines */
			for (i = 0; i < num; i++) {
				read_line(fp, ident);
				e->u.text.str[i] = strdup(ident);
				assert(e->u.text.str[i]);
			}
			break;
		case 'U':
			/* Bus */
			e = malloc(sizeof(*e));
			assert(e);
			memset(e, 0, sizeof(*e));
			e->type = BUS;
			read_int(fp, &e->u.bus.x0); /* Start X */
			read_int(fp, &e->u.bus.y0); /* Start Y */
			read_int(fp, &e->u.bus.x1); /* End X */
			read_int(fp, &e->u.bus.y1); /* End Y */
			read_int(fp, &e->u.bus.color); /* Color */
			read_int(fp, &num);
			readp_sub(fp, &e->child_first, &e->child_last);
			break;
		case 'V':
			/* Circle */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			read_int(fp, &num);
			break;
		case 'v': /* Version of gschem */
			e = NULL;

			read_int(fp, &num);
			read_int(fp, &num);
			break;
		default:
			fprintf(stderr, "c=%c\n", c);
			assert(0);
		}

		if (e) {
			e->prev = *lastp;
			e->next = NULL;
			if (e->prev) {
				e->prev->next = e;
			} else {
				*firstp = e;
			}
			*lastp = e;
		}
	}

	return 0;
}

static int
readp_sub(FILE *fp, struct el **firstp, struct el **lastp)
{
	char c;
	int ret;

	ret = read_char(fp, &c);
	if (ret == EOF) {
		_ungetc(EOF, fp);
		*firstp = NULL;
		*lastp = NULL;
		return EOF;
	}
	if (c != '{') {
		_ungetc(c, fp);
		*firstp = NULL;
		*lastp = NULL;
		return EOF;
	}

	read_list(fp, firstp, lastp);

	ret = read_char(fp, &c);
	assert(ret != EOF
	    && c == '}');

	return 0;
}

static const char *
lookup_str(struct el *e, const char *n)
{
	if (e->type == TEXT
	 && e->u.text.str[0]
	 && strncmp(e->u.text.str[0], n, strlen(n)) == 0
	 && e->u.text.str[0][strlen(n)] == '=') {
		return &e->u.text.str[0][strlen(n) + 1];
	} else {
		return NULL;
	}
}

static const char *
lookup_n(struct el *e, const char *str, unsigned int n)
{
	struct el *ce;
	const char *value;

	for (ce = e->child_first; ce; ce = ce->next) {
		value = lookup_str(ce, str);
		if (value) {
			if (n == 0) {
				return value;
			} else {
				n--;
			}
		}
	}
#if 0
	if (e->type == COMP) {
		for (ce = e->u.comp.con_first; ce; ce = ce->next) {
			value = lookup_str(ce, str);
			if (value) {
				if (n == 0) {
					return value;
				} else {
					n--;
				}
			}
		}
	}
#endif
	return NULL;
}

static const char *
lookup(struct el *e, const char *str)
{
	return lookup_n(e, str, 0);
}

#define MIN(x, y)	(((x) < (y)) ? (x) : (y))
#define MAX(x, y)	(((x) < (y)) ? (y) : (x))

static int
connected_point_line(
	int x,
	int y,
	int x0,
	int y0,
	int x1,
	int y1
)
{
	if (x == x0 && y == y0) {
		return 1;
	} else if (x == x1 && y == y1) {
		return 1;
	} else if (x0 == x1) {
		if (x == x0
		 && MIN(y0, y1) <= y && y <= MAX(y0, y1)) {
			return 1;
		} else {
			return 0;
		}
	} else if (y0 == y1) {
		if (y == y0
		 && MIN(x0, x1) <= x && x <= MAX(x0, x1)) {
			return 1;
		} else {
			return 0;
		}
	} else {
#if DEBUG
		static int count = 0;

		if (count < 10) {
			fprintf(stderr, "WARNING: x0=%d, y0=%d, x1=%d, y1=%d\n",
					x0, y0, x1, y1);
			count++;
		} else if (count == 10) {
			fprintf(stderr, "WARNING: more warnings following...\n");
			count++;
		}
#endif

		return 0;
	}
}

static int
connected_line_line(
	int xa0,
	int ya0,
	int xa1,
	int ya1,
	int xb0,
	int yb0,
	int xb1,
	int yb1
)
{
	return connected_point_line(xa0, ya0, xb0, yb0, xb1, yb1)
	    || connected_point_line(xa1, ya1, xb0, yb0, xb1, yb1)
	    || connected_point_line(xb0, yb0, xa0, ya0, xa1, ya1)
	    || connected_point_line(xb1, yb1, xa0, ya0, xa1, ya1);
}

static void
connect_to(
	const char *comp_type,
	const char *comp_name,
	unsigned int level,
	struct sig *sig,
	int x0,
	int y0,
	int x1,
	int y1,
	struct el *first,
	struct el *last
)
{
	struct el *e;

	for (e = first; e; e = e->next) {
		switch (e->type) {
		case COMP: {
			int x0_new;
			int y0_new;
			int x1_new;
			int y1_new;
			int tmp;

			x0_new = x0 - e->u.comp.x;
			y0_new = y0 - e->u.comp.y;
			x1_new = x1 - e->u.comp.x;
			y1_new = y1 - e->u.comp.y;

			/*
			 * Note:
			 * If component was rotated by 90 counterclockwise
			 * we must back-rotate by 90 clockwise!
			 */
			switch (e->u.comp.rotate) {
			case 0:
				/* Nothing to do... */
				break;
			case 90:
				/* x'=y, y'=-x */
				tmp = x0_new;
				x0_new = y0_new;
				y0_new = -tmp;

				tmp = x1_new;
				x1_new = y1_new;
				y1_new = -tmp;
				break;
			case 180:
				/* x'=-x, y'=-y */
				x0_new = -x0_new;
				y0_new = -y0_new;

				x1_new = -x1_new;
				y1_new = -y1_new;
				break;
			case 270:
				/* x'=-y, y'=x */
				tmp = x0_new;
				x0_new = -y0_new;
				y0_new = tmp;

				tmp = x1_new;
				x1_new = -y1_new;
				y1_new = tmp;
				break;
			default:
				assert(0);
			}

			assert(e->u.comp.mirror == 0);
			connect_to(lookup(e, "device"), lookup(e, "refdes"),
					level + 1, sig,
					x0_new, y0_new, x1_new, y1_new,
					e->u.comp.con_first, e->u.comp.con_last);
			break;
		    }
		case NET:
			if ((sig->name
			  && lookup(e, "netname")
			  && strcmp(sig->name, lookup(e, "netname")) == 0)
			 || connected_line_line(
					e->u.net.x0, e->u.net.y0,
					e->u.net.x1, e->u.net.y1,
					x0, y0, x1, y1)) {
				if (e->u.net.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.net.sig != NULL) {
					fprintf(stderr, "%s <-> %s\n",
							sig->name,
							e->u.net.sig->name);
					assert(0);
				} else {
					const char *netname;

					e->u.net.sig = sig;

					netname = lookup(e, "netname");
					if (netname) {
						if (sig->name) {
							if (strcmp(sig->name, netname) != 0) {
								fprintf(stderr, "ERROR: signal \"%s\" connected to signal \"%s\".\n", netname, sig->name);
							}
						} else {
							sig->name = netname;
						}
					}

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.net.x0, e->u.net.y0,
							e->u.net.x1, e->u.net.y1,
							first, last);
				}
			}
			break;
		case PIN:
			if (connected_line_line(
					e->u.pin.x0, e->u.pin.y0,
					e->u.pin.x1, e->u.pin.y1,
					x0, y0, x1, y1)) {
				if (e->u.pin.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.pin.sig != NULL) {
					fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" already connected.\n",
							lookup(e, "pinlabel"),
							comp_name);
				} else {
					const char *label;
					const char *type;

					e->u.pin.sig = sig;

					if (level == 0) {
						sig->is_port = 1;
					}

					label = lookup(e, "pinlabel");
					if (! label) {
						fprintf(stderr, "ERROR: Pin of \"%s\" has no pinlabel attribute.\n", comp_type);
						label = "unknown";
					}
					assert(label);
					if (level == 0) {
						if (sig->name) {
							if (strcmp(sig->name, label) != 0) {
								fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" connected to named signal \"%s\".\n", label, comp_name, sig->name);
							}
						} else {
							sig->name = label;
						}
					}

					type = lookup(e, "type");
					if (! type) {
						fprintf(stderr, "ERROR: Pin \"%s\" in \"%s\" has no type attribute.\n", label, comp_type);
						type = "boolean";
					}
					assert(type);
					if (sig->type) {
						if (strcmp(sig->type, type) != 0) {
							fprintf(stderr, "ERROR: Port \"%s\" of \"%s\" has type \"%s\" and is connected to signal of type \"%s\".\n", label, comp_type, type, sig->type);
						}
					} else {
						sig->type = type;
					}

#if DEBUG
					fprintf(stderr, " %s/%s", comp_name, label);
#endif

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.pin.x0, e->u.pin.y0,
							e->u.pin.x1, e->u.pin.y1,
							first, last);
				}
			}
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if ((sig->name
			  && lookup(e, "netname")
			  && strcmp(sig->name, lookup(e, "netname")) == 0)
			 || connected_line_line(
					e->u.bus.x0, e->u.bus.y0,
					e->u.bus.x1, e->u.bus.y1,
					x0, y0, x1, y1)) {
				if (e->u.bus.sig == sig) {
					/* Nothing to do... */
				} else if (e->u.bus.sig != NULL) {
					assert(0);
				} else {
					const char *netname;

					e->u.bus.sig = sig;

					netname = lookup(e, "netname");
					if (netname) {
						if (sig->name) {
							assert(strcmp(sig->name, netname) == 0);
						} else {
							sig->name = netname;
						}
					}

					connect_to(comp_type, comp_name,
							level, sig,
							e->u.bus.x0, e->u.bus.y0,
							e->u.bus.x1, e->u.bus.y1,
							first, last);
				}
			}
			break;
		}
	}
}

static void
connect(
	const char *comp_type,
	const char *comp_name,
	unsigned int level,
	struct el *first,
	struct el *last,
	struct sig **sig_firstp,
	struct sig **sig_lastp
)
{
	struct el *e;
	struct sig *sig;

	/*
	 * First round:
	 * connect all *named* nets/busses.
	 */
	for (e = first; e; e = e->next) {
		sig = NULL;
		switch (e->type) {
		case COMP:
			break;
		case NET:
			if (! lookup(e, "netname")) {
				break;
			}
			if (! e->u.net.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				sig->name = lookup(e, "netname");

				connect_to(comp_type, comp_name, level, sig,
						e->u.net.x0, e->u.net.y0,
						e->u.net.x1, e->u.net.y1,
						first, last);
			}
			break;
		case PIN:
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if (! lookup(e, "netname")) {
				break;
			}
			if (! e->u.bus.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				sig->name = lookup(e, "netname");

				connect_to(comp_type, comp_name, level, sig,
						e->u.bus.x0, e->u.bus.y0,
						e->u.bus.x1, e->u.bus.y1,
						first, last);
			}
			break;
		}
		if (sig) {
			assert(sig->name);
			if (! sig->type) {
				fprintf(stderr, "ERROR: signal %s not connected to any pin.\n",
						sig->name);
				sig->type = "boolean";
			}

#if DEBUG
			fprintf(stderr, " -> %s (%s)\n", sig->name, sig->type);
#endif

			sig->prev = *sig_lastp;
			sig->next = NULL;
			if (sig->prev) {
				sig->prev->next = sig;
			} else {
				*sig_firstp = sig;
			}
			*sig_lastp = sig;
		}
	}

	/*
	 * Second round:
	 * Connect all *unnamed* nets/busses.
	 */
	for (e = first; e; e = e->next) {
		sig = NULL;
		switch (e->type) {
		case COMP:
			break;
		case NET:
			if (! e->u.net.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.net.x0, e->u.net.y0,
						e->u.net.x1, e->u.net.y1,
						first, last);
			}
			break;
		case PIN:
			if (! e->u.pin.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.pin.x0, e->u.pin.y0,
						e->u.pin.x1, e->u.pin.y1,
						first, last);
			}
			break;
		case TEXT:
			/* Nothing to connect... */
			break;
		case BUS:
			if (! e->u.bus.sig) {
				sig = malloc(sizeof(*sig));
				assert(sig);
				memset(sig, 0, sizeof(*sig));

				connect_to(comp_type, comp_name, level, sig,
						e->u.bus.x0, e->u.bus.y0,
						e->u.bus.x1, e->u.bus.y1,
						first, last);
			}
			break;
		}
		if (sig) {
			if (! sig->name) {
				sig->name = ident_tmp();
			}
			if (! sig->type) {
				fprintf(stderr, "ERROR: signal %s not connected to any pin.\n",
						sig->name);
				sig->type = "boolean";
			}

#if DEBUG
			fprintf(stderr, " -> %s (%s)\n", sig->name, sig->type);
#endif

			sig->prev = *sig_lastp;
			sig->next = NULL;
			if (sig->prev) {
				sig->prev->next = sig;
			} else {
				*sig_firstp = sig;
			}
			*sig_lastp = sig;
		}
	}

	/*
	 * Third round:
	 * Connect nets/busses in subcomponents.
	 */
	for (e = first; e; e = e->next) {
		if (e->type != COMP) continue;

		connect(lookup(e, "device"), lookup(e, "refdes"), level + 1,
				e->u.comp.con_first, e->u.comp.con_last,
				sig_firstp, sig_lastp);
	}
}

static int
gen_test(const char *type, const char *suffix)
{
	char path[1024];
	int fd;
	int ret;

	strcpy(path, type);
	strcat(path, suffix);

	fd = open(path, O_RDONLY);
	if (0 <= fd) {
		ret = close(fd);
		assert(0 <= ret);
		return 1;
	} else {
		return 0;
	}
}

static void
gen_h(
	FILE *fp,
	const char *type,
	const char *suffix,
	int chip,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	struct el *e;
	struct sig *sig;
	char include[100][100];
	unsigned int nincludes;
	unsigned int i;

	/*
	 * Generate Header
	 */
	fprintf(fp, "/*\n");
	fprintf(fp, " * WARNING:\n");
	fprintf(fp, " *\n");
	fprintf(fp, " * Automatically generated from %s.\n", inname);
	fprintf(fp, " */\n");

	fprintf(fp, "\n");

	/*
	 * Generate Signal Include List
	 */
	fprintf(fp, "#include <stdio.h>\n");
	
	nincludes = 0;
	strcpy(include[nincludes], sig_include("manage"));
	nincludes++;
	for (sig = sig_first; sig; sig = sig->next) {
		if (! sig->is_port) continue;

		for (i = 0; ; i++) {
			if (i == nincludes) {
				strcpy(include[nincludes], sig_include(sig->type));
				nincludes++;
				break;
			}
			if (strcmp(include[i], sig_include(sig->type)) == 0) {
				break;
			}
		}
	}
	if (1 < nincludes) {
		for (;;) {
			int done;

			done = 1;

			for (i = 0; i < nincludes - 1; i++) {
				if (strcmp(include[i + 1], include[i]) < 0) {
					char tmp[100];

					strcpy(tmp, include[i]);
					strcpy(include[i], include[i + 1]);
					strcpy(include[i + 1], tmp);
					done = 0;
				}
			}

			if (done) {
				break;
			}
		}
	}
	for (i = 0; i < nincludes; i++) {
		fprintf(fp, "#include \"%s\"\n", include[i]);
	}

	fprintf(fp, "\n");

	/*
	 * Generate <type>_create Function Prototype
	 */
	fprintf(fp, "extern void *\n");
	fprintf(fp, "%s%s_create(", type, suffix);
	if (strncmp(suffix, "_gui", 4) == 0) {
		fprintf(fp, "\n\tunsigned int page,");
	}
	fprintf(fp, "\n\tconst char *name");

	/* Simulation Setups */
	for (e = first; e; e = e->next) {
		const char *simsetup;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "simsetup=", strlen("simsetup=")) != 0)
			continue;

		simsetup = e->u.text.str[0] + strlen("simsetup=");
		assert(strchr(simsetup, '='));

		fprintf(fp, ",\n\t");
		equal = strchr(simsetup, '=');
		*equal = '\0';
		fprintf(fp, "const char *%s", simsetup);
		*equal = '=';
	}

	/* Generics */
	for (e = first; e; e = e->next) {
		const char *generic;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "generic=", strlen("generic=")) != 0)
			continue;

		generic = e->u.text.str[0] + strlen("generic=");
		assert(strchr(generic, '='));

		fprintf(fp, ",\n\t");
		equal = strchr(generic, '=');
		*equal = '\0';
		fprintf(fp, "const char *%s", generic);
		*equal = '=';
	}

	/* Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	fprintf(fp, ",\n\tstruct sig_manage *port_manage");
	for (i = 0; i < nentries; i++) {
		fprintf(fp, ",\n\t");
		fprintf(fp, "struct %s *%s",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
	}
	fprintf(fp, "\n");
	fprintf(fp, ");\n");

	/*
	 * Generate <type>_destroy Function Prototype
	 */
	fprintf(fp, "extern void\n");
	fprintf(fp, "%s%s_destroy(void *_cpssp);\n", type, suffix);

	/*
	 * Generate <type>_suspend Function Prototype
	 */
	fprintf(fp, "extern void\n");
	fprintf(fp, "%s%s_suspend(void *_cpssp, FILE *fp);\n", type, suffix);
	
	/*
	 * Generate <type>_resume Function Prototype
	 */
	fprintf(fp, "extern void\n");
	fprintf(fp, "%s%s_resume(void *_cpssp, FILE *fp);\n", type, suffix);
}

static void
gen_c_tmp(
	FILE *fp,
	const char *type,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	time_t now;
	struct tm *nowtm;
	struct el *e;
	unsigned int i;

	now = time(NULL);

	nowtm = gmtime(&now);
	assert(nowtm);

	/*
	 * Generate header.
	 */
	fprintf(fp, "/*\n");
	fprintf(fp, " * Copyright (C) 2003-%d FAUmachine Team <info@faumachine.org>.\n", 1900 + nowtm->tm_year);
	fprintf(fp, " * This program is free software. You can redistribute it and/or modify it\n");
	fprintf(fp, " * under the terms of the GNU General Public License, either version 2 of\n");
	fprintf(fp, " * the License, or (at your option) any later version. See COPYING.\n");
	fprintf(fp, " */\n");

	fprintf(fp, "\n");

	/*
	 * Generate include file list.
	 */
	fprintf(fp, "#include \"config.h\"\n");
	fprintf(fp, "#include <assert.h>\n");
	fprintf(fp, "#include <stdio.h>\n");
	fprintf(fp, "#include <stdlib.h>\n");
	fprintf(fp, "\n");
	fprintf(fp, "#include \"%s.h\"\n", type);

	fprintf(fp, "\n");

	/*
	 * Define CHIP.
	 */
	fprintf(fp, "#define COMP_(x) %s_ ## x\n", type);

	fprintf(fp, "\n");

	/*
	 * Create cpssp struct.
	 */
	fprintf(fp, "struct cpssp {\n");

	/* Output Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "out") != 0) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tstruct %s *%s;\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
	}

	/* Input Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "in") != 0) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tunsigned int state_%s;\n",
				name(entry[i].port));
	}

	/* In/Out Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "io") != 0) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tstruct %s *%s;\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
		fprintf(fp, "\tunsigned int state_%s;\n",
				name(entry[i].port));
	}

	/* Call Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "incall") != 0
		 && strcmp(lookup(e, "pintype"), "outcall") != 0
		 && strcmp(lookup(e, "pintype"), "iocall") != 0
		 && strcmp(lookup(e, "pintype"), "call") != 0) {
			continue;
		}

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tstruct %s *%s;\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
	}

	fprintf(fp, "};\n");

	fprintf(fp, "\n");

	/*
	 * Create <type>_create function.
	 */
	fprintf(fp, "void *\n");
	fprintf(fp, "COMP_(create)(\n");
	fprintf(fp, "\tconst char *name");

	/* Simulation Setups */
	for (e = first; e; e = e->next) {
		const char *simsetup;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "simsetup=", strlen("simsetup=")) != 0)
			continue;

		simsetup = e->u.text.str[0] + strlen("simsetup=");
		assert(strchr(simsetup, '='));

		fprintf(fp, ",\n\t");
		equal = strchr(simsetup, '=');
		*equal = '\0';
		fprintf(fp, "const char *%s", simsetup);
		*equal = '=';
	}

	/* Generics */
	for (e = first; e; e = e->next) {
		const char *generic;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "generic=", strlen("generic=")) != 0)
			continue;

		generic = e->u.text.str[0] + strlen("generic=");
		assert(strchr(generic, '='));

		fprintf(fp, ",\n\t");
		equal = strchr(generic, '=');
		*equal = '\0';
		fprintf(fp, "const char *%s", generic);
		*equal = '=';
	}

	/* Ports */
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	fprintf(fp, ",\n\t");
	fprintf(fp, "struct sig_manage *manage");
	for (i = 0; i < nentries; i++) {
		fprintf(fp, ",\n\t");
		fprintf(fp, "struct %s *%s",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
	}

	fprintf(fp, "\n");
	fprintf(fp, ")\n");
	fprintf(fp, "{\n");

	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "in") != 0
		 && strcmp(lookup(e, "pintype"), "io") != 0
		 && strcmp(lookup(e, "pintype"), "incall") != 0
		 && strcmp(lookup(e, "pintype"), "iocall") != 0
		 && strcmp(lookup(e, "pintype"), "call") != 0) {
		 	continue;
		}

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tstatic const struct %s_funcs %s_funcs = {\n",
				sig_type(entry[i].sig->type),
				name(entry[i].port));
		fprintf(fp, "\t\t/* FIXME */\n");
		fprintf(fp, "\t};\n");
	}

	fprintf(fp, "\tstruct cpssp *cpssp;\n");
	fprintf(fp, "\n");
	fprintf(fp, "\tcpssp = malloc(sizeof(*cpssp));\n");
	fprintf(fp, "\tassert(cpssp);\n");
	fprintf(fp, "\n");
	fprintf(fp, "\t/* FIXME */\n");
	fprintf(fp, "\n");

	fprintf(fp, "\t/* Call */\n");

	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "incall") != 0
		 && strcmp(lookup(e, "pintype"), "outcall") != 0
		 && strcmp(lookup(e, "pintype"), "iocall") != 0
		 && strcmp(lookup(e, "pintype"), "call") != 0) {
			continue;
		}

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tcpssp->%s = %s;\n",
				port_name(entry[i].port),
				port_name(entry[i].port));
		fprintf(fp, "\t%s_connect(%s, cpssp, &%s_funcs);\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port),
				name(entry[i].port));
		fprintf(fp, "\n");
	}

	fprintf(fp, "\t/* Out */\n");

	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "out") != 0
		 && strcmp(lookup(e, "pintype"), "io") != 0) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tcpssp->%s = %s;\n",
				port_name(entry[i].port),
				port_name(entry[i].port));
		fprintf(fp, "\t%s_connect_out(%s, cpssp, 0);\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port));
		fprintf(fp, "\n");
	}

	fprintf(fp, "\t/* In */\n");

	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;
		if (strcmp(lookup(e, "pintype"), "in") != 0
		 && strcmp(lookup(e, "pintype"), "io") != 0) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\tcpssp->state_%s = 0;\n",
				name(entry[i].port));
		fprintf(fp, "\t%s_connect_in(%s, cpssp, &%s_funcs);\n",
				sig_type(entry[i].sig->type),
				port_name(entry[i].port),
				name(entry[i].port));
		fprintf(fp, "\n");
	}

	fprintf(fp, "\treturn cpssp;\n");
	fprintf(fp, "}\n");

	fprintf(fp, "\n");

	/*
	 * Create <type>_destroy Function
	 */
	fprintf(fp, "void\n");
	fprintf(fp, "COMP_(destroy)(void *_cpssp)\n");
	fprintf(fp, "{\n");
	fprintf(fp, "\tstruct cpssp *cpssp = _cpssp;\n");
	fprintf(fp, "\n");
	fprintf(fp, "\t/* FIXME */\n");
	fprintf(fp, "\n");
	fprintf(fp, "\tfree(cpssp);\n");
	fprintf(fp, "}\n");

	/*
	 * Generate <type>_suspend Function
	 */
	fprintf(fp, "void\n");
	fprintf(fp, "COMP_(suspend)(void *_cpssp, FILE *fp)\n");
	fprintf(fp, "{\n");
	fprintf(fp, "\tstruct cpssp *cpssp = _cpssp;\n");
	fprintf(fp, "\n");
	fprintf(fp, "\tgeneric_suspend(cpssp, sizeof(*cpssp), fp);\n");
	fprintf(fp, "}\n");
	
	/*
	 * Generate <type>_resume Function
	 */
	fprintf(fp, "void\n");
	fprintf(fp, "COMP_(resume)(void *_cpssp, FILE *fp)\n");
	fprintf(fp, "{\n");
	fprintf(fp, "\tstruct cpssp *cpssp = _cpssp;\n");
	fprintf(fp, "\n");
	fprintf(fp, "\tgeneric_resume(cpssp, sizeof(*cpssp), fp);\n");
	fprintf(fp, "}\n");
}

static void
gen_inc1(
	FILE *fp,
	const char *type,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	/*
	 * Generate Header
	 */
	fprintf(fp, "/*\n");
	fprintf(fp, " * WARNING:\n");
	fprintf(fp, " *\n");
	fprintf(fp, " * Automatically generated from %s.\n", inname);
	fprintf(fp, " */\n");

	fprintf(fp, "\n");

	/*
	 * Generate Includes
	 */
	if (gen_test(type, ".c")) {
		fprintf(fp, "#include \"%s.h\"\n", type);
	}
	if (gen_test(type, "_gui.c")) {
		fprintf(fp, "#include \"%s_gui.h\"\n", type);
	}
	if (gen_test(type, "_aui_gen.c")) {
		fprintf(fp, "#include \"%s_aui.h\"\n", type);
	}
}

static void
gen_inc2(
	FILE *fp,
	const char *type,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	struct el *e;
	unsigned int i;

	/*
	 * Generate Header
	 */
	fprintf(fp, "/*\n");
	fprintf(fp, " * WARNING:\n");
	fprintf(fp, " *\n");
	fprintf(fp, " * Automatically generated from %s.\n", inname);
	fprintf(fp, " */\n");

	fprintf(fp, "\n");

	/*
	 * Generate Entry
	 */
	/* Name */
	fprintf(fp, "{ \"%s\",\n", type);

	/* Simulation Setups / Generics */
	fprintf(fp, "\t{\n");
	for (e = first; e; e = e->next) {
		const char *simsetup;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "simsetup=", strlen("simsetup=")) != 0) continue;

		simsetup = e->u.text.str[0] + strlen("simsetup=");
		assert(strchr(simsetup, '='));

		equal = strchr(simsetup, '=');
		*equal = '\0';
		fprintf(fp, "\t\t\"%s\",\n", simsetup);
		*equal = '=';
	}
	for (e = first; e; e = e->next) {
		const char *generic;
		char *equal;

		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "generic=", strlen("generic=")) != 0) continue;

		generic = e->u.text.str[0] + strlen("generic=");
		assert(strchr(generic, '='));

		equal = strchr(generic, '=');
		*equal = '\0';
		fprintf(fp, "\t\t\"%s\",\n", generic);
		*equal = '=';
	}
	fprintf(fp, "\t},\n");

	/* Ports */
	fprintf(fp, "\t{\n");
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	fprintf(fp, "\t\t\"%s\",\n", "manage");
	for (i = 0; i < nentries; i++) {
		fprintf(fp, "\t\t\"%s\",\n", name(entry[i].port));
	}
	fprintf(fp, "\t},\n");

	fprintf(fp, "\t{\n");
	table_init();
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;

		table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
				lookup(e, "pintype"), lookup(e, "type"),
				lookup(e, "gender"), e->u.pin.sig);
	}
	table_sort();
	fprintf(fp, "\t\t%s,\n", "SYSTEM_GENDER_NEUTRAL");
	for (i = 0; i < nentries; i++) {
		if (! entry[i].gender
		 || strcmp(entry[i].gender, "") == 0
		 || strcmp(entry[i].gender, "neutral") == 0) {
			fprintf(fp, "\t\t%s,\n", "SYSTEM_GENDER_NEUTRAL");
		} else if (strcmp(entry[i].gender, "female") == 0) {
			fprintf(fp, "\t\t%s,\n", "SYSTEM_GENDER_FEMALE");
		} else if (strcmp(entry[i].gender, "male") == 0) {
			fprintf(fp, "\t\t%s,\n", "SYSTEM_GENDER_MALE");
		} else {
			assert(0);
		}
	}
	fprintf(fp, "\t},\n");

	fprintf(fp, "\t(void *(*)(const char *, ...)) ");
	if (gen_test(type, ".c")) {
		fprintf(fp, "%s_create,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *)) ");
	if (gen_test(type, ".c")) {
		fprintf(fp, "%s_destroy,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	
	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, ".c")) {
		fprintf(fp, "%s_suspend,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, ".c")) {
		fprintf(fp, "%s_resume,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}

	fprintf(fp, "\t(void *(*)(unsigned int, const char *, ...)) ");
	if (gen_test(type, "_gui.c")) {
		fprintf(fp, "%s_gui_create,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *)) ");
	if (gen_test(type, "_gui.c")) {
		fprintf(fp, "%s_gui_destroy,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	
	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, "_gui.c")) {
		fprintf(fp, "%s_gui_suspend,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, "_gui.c")) {
		fprintf(fp, "%s_gui_resume,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}

	fprintf(fp, "\t(void *(*)(const char *, ...)) ");
	if (gen_test(type, "_aui_gen.c")) {
		fprintf(fp, "%s_aui_create,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *)) ");
	if (gen_test(type, "_aui_gen.c")) {
		fprintf(fp, "%s_aui_destroy,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}

	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, "_aui_gen.c")) {
		fprintf(fp, "%s_aui_suspend,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}
	fprintf(fp, "\t(void (*)(void *, FILE *)) ");
	if (gen_test(type, "_aui_gen.c")) {
		fprintf(fp, "%s_aui_resume,\n", type);
	} else {
		fprintf(fp, "NULL,\n");
	}

	fprintf(fp, "},\n");
}

static void
gen_vhdl_entity(
	FILE *fp,
	const char *type,
	int chip,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	struct el *e;
	unsigned int count;
	unsigned int i;

	/*
	 * Generate Header
	 */
	fprintf(fp, "--\n");
	fprintf(fp, "-- WARNING:\n");
	fprintf(fp, "--\n");
	fprintf(fp, "-- Automatically generated from %s.\n", inname);
	fprintf(fp, "--\n");

	fprintf(fp, "\n");

	/*
	 * Generate Entry
	 */
	/* Name */
	fprintf(fp, "USE types.all;\n");
	fprintf(fp, "LIBRARY ieee;\n");
	fprintf(fp, "USE ieee.std_logic_1164.all;\n");
	fprintf(fp, "ENTITY %s IS\n", type);

	/* Skip Simuation Setups */

	/* Generics */
	count = 0;
	for (e = first; e; e = e->next) {
		if (e->type != TEXT
		 || ! e->u.text.str[0]
		 || strncmp(e->u.text.str[0], "generic=", strlen("generic=")) != 0)
			continue;

		count++;
	}
	if (count) {
		fprintf(fp, "\tGENERIC (\n");
		for (e = first; e; e = e->next) {
			const char *generic;
			char *equal;
			const char *value;

			if (e->type != TEXT
			 || ! e->u.text.str[0]
			 || strncmp(e->u.text.str[0], "generic=", strlen("generic=")) != 0)
				continue;

			generic = e->u.text.str[0] + strlen("generic=");
			assert(strchr(generic, '='));

			equal = strchr(generic, '=');
			*equal = '\0';
			value = equal + 1;

			fprintf(fp, "\t\t");
			fprintf(fp, "%s", generic);
			fprintf(fp, " : ");
			if (*(equal + 1) == '"') {
				fprintf(fp, "string");
			} else {
				fprintf(fp, "integer");
			}
			fprintf(fp, " := ");
			if (*value == '"') {
				fprintf(fp, "%s", value);
			} else {
				fprintf(fp, "%ld", strtol(value, NULL, 0));
			}
			if (count != 1) {
				fprintf(fp, ";");
			}
			fprintf(fp, "\n");
			*equal = '=';
			count--;
		}
		fprintf(fp, "\t);\n");
	}

	/* Ports */
	count = 0;
	for (e = first; e; e = e->next) {
		if (e->type != PIN) continue;

		count++;
	}
	if (count) {
		fprintf(fp, "\tPORT (\n");
		table_init();
		for (e = first; e; e = e->next) {
			if (e->type != PIN) continue;

			table_add(lookup(e, "pinlabel"), lookup(e, "pinseq"),
					lookup(e, "pintype"), lookup(e, "type"),
					lookup(e, "gender"), e->u.pin.sig);
		}
		table_sort();
		for (i = 0; i < nentries; i++) {
			fprintf(fp, "\t\t");
			fprintf(fp, "%s", name(entry[i].port));
			fprintf(fp, " : ");
			if (strcmp(entry[i].pintype, "in") == 0
			 || strcmp(entry[i].pintype, "incall") == 0) {
				fprintf(fp, "IN");
			} else if (strcmp(entry[i].pintype, "out") == 0
				|| strcmp(entry[i].pintype, "outcall") == 0) {
				fprintf(fp, "OUT");
			} else if (strcmp(entry[i].pintype, "io") == 0
				|| strcmp(entry[i].pintype, "iocall") == 0
				|| strcmp(entry[i].pintype, "call") == 0) {
				fprintf(fp, "INOUT");
			} else {
				fprintf(stderr, "pintype=%s\n",
						entry[i].pintype);
				assert(0);
			}
			fprintf(fp, " ");
			if (strcmp(entry[i].type, "string") == 0) {
				fprintf(fp, "cstring");
			} else {
				fprintf(fp, "%s", entry[i].type);
			}
			if (count != 1) {
				fprintf(fp, ";");
			}
			fprintf(fp, "\n");
			count--;
		}
		fprintf(fp, "\t);\n");
	}

	if (chip) { /* FIXME */
		fprintf(fp, "\tATTRIBUTE foreign OF %s : ENTITY IS \"%s\";\n",
				type, type);
	}

	fprintf(fp, "END %s;\n", type);
}

static void
gen_vhdl_architecture(
	FILE *fp,
	const char *type,
	struct el *first,
	struct el *last,
	struct sig *sig_first,
	struct sig *sig_last
)
{
	struct el *e;
	struct sig *sig;
	unsigned int i;

	/*
	 * Generate Header
	 */
	fprintf(fp, "--\n");
	fprintf(fp, "-- WARNING:\n");
	fprintf(fp, "--\n");
	fprintf(fp, "-- Automatically generated from %s.\n", inname);
	fprintf(fp, "--\n");

	fprintf(fp, "\n");

	/* ARCHITECTURE */
	fprintf(fp, "ARCHITECTURE structural OF %s IS\n", type);

	if (gen_test(type, "_aui.c")
	 || gen_test(type, "_gui.c")) {
		fprintf(fp, "\tATTRIBUTE foreign OF structural : ARCHITECTURE IS \"%s\";\n", type);
	}

	/*
	 * Signals
	 */
	for (sig = sig_first; sig; sig = sig->next) {
		if (sig->is_port) continue;

		fprintf(fp, "\tSIGNAL %s : %s;\n",
				vhdl_sig_name(sig->name),
				vhdl_sig_type(sig->type));
	}

	/* BEGIN */
	fprintf(fp, "BEGIN\n");

	/*
	 * Components
	 */
	for (e = first; e; e = e->next) {
		struct el *ce;

		if (e->type != COMP
		 || lookup(e, "graphical")
		 || ! lookup(e, "refdes"))
			continue;

		fprintf(fp, "\t%s : %s\n",
				lookup(e, "refdes"), lookup(e, "device"));

		/* Generics */
		if (lookup_n(e, "simsetup", 0)
		 || lookup_n(e, "generic", 0)) {
			fprintf(fp, "\t\tGENERIC MAP (\n");
			for (i = 0; ; i++) {
				const char *simsetup;
				char *equal;
				const char *value;

				simsetup = lookup_n(e, "simsetup", i);
				if (! simsetup) {
					break;
				}
				equal = strchr(simsetup, '=');
				assert(equal);

				*equal = '\0';
				value = equal + 1;

				fprintf(fp, "\t\t\t%s => ", simsetup);

				if (*value == '"') {
					fprintf(fp, "%s", value);
				} else if ('0' <= *value && *value <= '9') {
					fprintf(fp, "%ld", strtol(value, NULL, 0));
				} else {
					fprintf(fp, "%s", value);
				}
				if (lookup_n(e, "simsetup", i + 1)) {
					fprintf(fp, ",");
				}
				fprintf(fp, "\n");

				*equal = '=';
			}
			for (i = 0; ; i++) {
				const char *generic;
				char *equal;
				const char *value;

				generic = lookup_n(e, "generic", i);
				if (! generic) {
					break;
				}
				equal = strchr(generic, '=');
				assert(equal);

				*equal = '\0';
				value = equal + 1;

				fprintf(fp, "\t\t\t%s => ", generic);

				if (*value == '"') {
					fprintf(fp, "%s", value);
				} else if ('0' <= *value && *value <= '9') {
					fprintf(fp, "%ld", strtol(value, NULL, 0));
				} else {
					fprintf(fp, "%s", value);
				}
				if (lookup_n(e, "generic", i + 1)) {
					fprintf(fp, ",");
				}
				fprintf(fp, "\n");

				*equal = '=';
			}
			fprintf(fp, "\t\t)\n");
		}

		/* Ports */
		table_init();
		for (ce = e->u.comp.con_first; ce; ce = ce->next) {
			if (ce->type != PIN) continue;

			table_add(lookup(ce, "pinlabel"), lookup(ce, "pinseq"),
					lookup(ce, "pintype"), lookup(ce, "type"),
					lookup(ce, "gender"), ce->u.pin.sig);
		}
		table_sort();
		if (nentries) {
			fprintf(fp, "\t\tPORT MAP (\n");
			for (i = 0; i < nentries; i++) {
				fprintf(fp, "\t\t\t%s => %s",
					vhdl_sig_name(entry[i].port),
					vhdl_sig_name(entry[i].sig->name));
				if (i != nentries - 1) {
					fprintf(fp, ",");
				}
				fprintf(fp, "\n");
			}
			fprintf(fp, "\t\t);\n");
		}
	}

	/* END */
	fprintf(fp, "END structural;\n");
}

static void __attribute__((noreturn))
usage(int retval)
{
	fprintf(stderr, "Usage: %s <*.sch or *.sym file>\n", progname);
	exit(retval);
}

int
main(int argc, char **argv)
{
	int c;
	FILE *fp;
	struct el *first;
	struct el *last;
	struct el *e;
	struct sig *sig_first;
	struct sig *sig_last;
	char type[1024];
	char outname[1024];
	int ret;
	int chip;
	int i;

	progname = *argv;

	while ((c = getopt(argc, argv, "I:M")) != -1) {
		switch (c) {
		case 'I':
			idir[nidirs++] = optarg;
			break;
		case 'M':
			opt_M = 1;
			break;
		default:
			usage(1);
		}
	}
	argc -= optind;
	argv += optind;

	if (0 < argc) {
		inname = *argv;
		argc--;
		argv++;
	} else {
		usage(1);
	}
	if (argc != 0) {
		usage(1);
	}
	if (! strchr(inname, '.')) {
		usage(1);
	}
	
	if (strchr(inname, '/')) {
		strcpy(type, strrchr(inname, '/') + 1);
	} else {
		strcpy(type, inname);
	}
	if (strchr(type, '.')) {
		*strchr(type, '.') = '\0';
	}

	/*
	 * Read schematic file.
	 */
#if DEBUG
	fprintf(stderr, "Reading %s...\n", inname);
#endif

	fp = fopen(inname, "r");
	if (! fp) {
		fprintf(stderr, "ERROR: %s: %s: %s.\n", progname,
				inname, strerror(errno));
		exit(1);
	}
	assert(fp);

	read_list(fp, &first, &last);

	fclose(fp);

	/*
	 * Read all component files.
	 */
	for (e = first; e; e = e->next) {
		if (e->type != COMP
		 || lookup(e, "graphical")) {
			continue;
		}

#if DEBUG
		fprintf(stderr, "Reading %s...\n", e->u.comp.sym_file);
#endif
		for (i = 0; ; i++) {
			char path[1024];

			if (i == nidirs) {
				/* Not found. */
				fprintf(stderr, "ERROR: %s: %s: Not found.\n",
						progname,
						e->u.comp.sym_file);
				break;
			}
			strcpy(path, idir[i]);
			strcat(path, "/");
			strcat(path, e->u.comp.sym_file);
			fp = fopen(path, "r");
			if (fp) {
				/* Found */
				if (opt_M) {
					printf(" %s", path);
				}
				read_list(fp, &e->u.comp.con_first,
						&e->u.comp.con_last);

				ret = fclose(fp);
				break;
			}
		}
	}

	if (opt_M) {
		printf("\n");
		exit(0);
	}

	/*
	 * Build signal/connection lists.
	 */
	sig_first = NULL;
	sig_last = NULL;
	connect(inname, "toplevel", 0, first, last, &sig_first, &sig_last);

	chip = (strcmp(strrchr(inname, '.'), ".sym") == 0);

	/*
	 * Generate <type>.inc1 File
	 */
	strcpy(outname, inname);
	strcpy(strrchr(outname, '.'), ".inc1");
	fp = fopen(outname, "w");
	if (! fp) {
		fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
				outname, strerror(errno));
		exit(1);
	}

	gen_inc1(fp, type, first, last, sig_first, sig_last);

	ret = fclose(fp);
	assert(ret == 0);

	/*
	 * Generate <type>.inc2 File
	 */
	strcpy(outname, inname);
	strcpy(strrchr(outname, '.'), ".inc2");
	fp = fopen(outname, "w");
	if (! fp) {
		fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
				outname, strerror(errno));
		exit(1);
	}

	gen_inc2(fp, type, first, last, sig_first, sig_last);

	ret = fclose(fp);
	assert(ret == 0);

	if (chip) {
		/*
		 * Generate <type>.c.tmp File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), ".c.tmp");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_c_tmp(fp, type, first, last, sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);

		/*
		 * Generate <type>.h File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), ".h");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_h(fp, type, "", chip, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);

	} else {
		/*
		 * Generate <type>.avhdl File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), ".avhdl");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_vhdl_architecture(fp, type, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);
	}

	/*
	 * Generate <type>.vhdl File
	 */
	strcpy(outname, inname);
	strcpy(strrchr(outname, '.'), ".vhdl");
	fp = fopen(outname, "w");
	if (! fp) {
		fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
				outname, strerror(errno));
		exit(1);
	}

	gen_vhdl_entity(fp, type, chip, first, last, sig_first, sig_last);

	ret = fclose(fp);
	assert(ret == 0);

	if (gen_test(type, "_gui.c")) {
		/*
		 * Generate <type>_gui.h File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), "_gui.h");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_h(fp, type, "_gui", chip, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);
	}
	if (gen_test(type, "_gui_gtk.c")) {
		/*
		 * Generate <type>_gui_gtk.h File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), "_gui_gtk.h");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_h(fp, type, "_gui_gtk", chip, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);
	}
	if (gen_test(type, "_gui_null.c")) {
		/*
		 * Generate <type>_gui_null.h File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), "_gui_null.h");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_h(fp, type, "_gui_null", chip, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);
	}
	if (gen_test(type, "_aui.c")) {
		/*
		 * Generate <type>_aui.h File
		 */
		strcpy(outname, inname);
		strcpy(strrchr(outname, '.'), "_aui.h");
		fp = fopen(outname, "w");
		if (! fp) {
			fprintf(stderr, "%s: ERROR: Can't create %s: %s.\n", progname,
					outname, strerror(errno));
			exit(1);
		}

		gen_h(fp, type, "_aui", chip, first, last,
				sig_first, sig_last);

		ret = fclose(fp);
		assert(ret == 0);
	}

	return 0;
}
