#include <stdlib.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>

#include <mba/hashmap.h>
#include <mba/csv.h>
#include <mba/msgno.h>

#define INMAX 32

struct ent {
	char ident[32];
	char value[32];
	char desc[512];
};

int
load_map(struct hashmap *map, char *fname)
{
	FILE *in;
	unsigned char buf[8192];
	unsigned char *row[3];
	int n, i = 0;
	struct ent *ents;

	if ((in = fopen(fname, "r")) == NULL) {
		PMNF(errno, ": %s", fname);
		return -1;
	}

	ents = malloc(255 * sizeof *ents);

	while ((n = csv_row_fread(in, buf, 8192, row, 3, '\t', CSV_TRIM | CSV_QUOTES)) > 0) {
		struct ent *ent = ents + i++;
		if (!row[0] || strcmp(row[0], "") == 0) {
			PMSG("Identifer cannot be NULL or blank: %s:%d", fname, i);
			return -1;
		}
		strcpy(ent->ident, row[0]);
		strcpy(ent->value, row[1] ? row[1] : (unsigned char *)"");
		strcpy(ent->desc, row[2] ? row[2] : (unsigned char *)"");
		if (hashmap_put(map, ent->ident, ent) == -1) {
			AMSG("");
			return -1;
		}
	}
	fclose(in);
	if (n == -1) {
		AMSG("");
		return -1;
	}

	return 0;
}

int
run(int incount, char *in[])
{
	struct hashmap maps[INMAX];
	struct ent *line[INMAX];
	iter_t iter;
	char *ident;
	int p, i;

	for (i = 0; i < incount; i++) {
		hashmap_init(&maps[i], 0, hash_str, cmp_str, NULL, NULL);
		if (load_map(&maps[i], in[i]) == -1) {
			AMSG("");
			return -1;
		}
	}

/* For each systems' errno map iterate over each key and use it to remove
 * any matches in the other maps placing the entries in the corresponding
 * index of the line array and print the line. Then move to the next map
 * and repeat. With each iteration of the outer loop the number of keys in
 * the remaining maps should decrease until only errno identifiers unique
 * to the last map remain.
 */
	for (p = 0; p < incount; p++) {
		hashmap_iterate(&maps[p], &iter);
		while ((ident = hashmap_next(&maps[p], &iter))) {
			float portability;
			char *id;

			memset(line, 0, sizeof(line));
			line[p] = hashmap_get(&maps[p], ident);
                   /* Go through all maps after p and remove matches
                    */
			for (i = (p + 1); i < incount; i++) {
				char *key = ident;
				struct ent *ent;

				if (hashmap_remove(&maps[i], (void **)&key, (void **)&ent) == 0) {
					line[i] = ent;
				} else {
					line[i] = NULL;
				}
			}
                   /* Now go through each ent in the line and
                    * print the tuple
                    */
			portability = 0.0;
			for (i = 0; i < incount; i++) {
				if (line[i]) {
					portability += 1.0;
					id = line[i]->ident;
				}
			}
			fprintf(stdout, "%s\t%.2f\t", id, portability / incount);
			for (i = 0; i < incount; i++) {
				if (line[i]) {
					fprintf(stdout, "%s\t%s\t", line[i]->value, line[i]->desc);
				} else {
					fprintf(stdout, "\t\t");
				}
			}
			fprintf(stdout, "\n");
		}
	}

	return 0;
}
int
main(int argc, char *argv[])
{
	if (argc < 2) {
		fprintf(stderr, "usage: %s <file1> [<file2 [...]]\n", argv[0]);
		return EXIT_FAILURE;
	}
	if (run(argc - 1, argv + 1) == -1) {
		MMSG("");
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}



syntax highlighted by Code2HTML, v. 0.9.1