#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <ctype.h>
#include <sys/ioctl.h>

#include "struct-dump.h"

#ifndef PRId64
# warning Hmm, your C99 support is incomplete, will guess (should be ok for 32bit archs)
# define PRId64 "lld"
# define PRIx64 "llx"
# define PRIu64 "llu"
#endif

/* ---------------------------------------------------------------------- */

struct struct_desc desc_int[] = {{
	.type   = SINT32,
	.name   = "int",
},{
	/* end of list */
}};

struct struct_desc desc_long[] = {{
	.type   = SINT32,
	.name   = "long",
},{
	/* end of list */
}};

struct struct_desc desc_timeval[] = {{
	/* FIXME */
	/* end of list */
}};

/* ---------------------------------------------------------------------- */

int print_struct(FILE *fp, struct struct_desc *desc, void *data,
		 char *prefix, int tab)
{
	char name[256];
	unsigned char *ptr = data;
	uint64_t u64;
	int64_t  s64;
	uint32_t u32;
	int32_t  s32;
	uint16_t u16;
	int16_t  s16;
	uint8_t  u8;
	int8_t   s8;
	int al = sizeof(long)-1; /* struct + union + 64bit alignment */
	void *p;
	unsigned int i,j,first;

	for (i = 0; desc[i].name != NULL; i++) {
		sprintf(name,"%s%s",prefix,desc[i].name);
		if (STRUCT == desc[i].type) {
			strcat(name,".");
			ptr = (void*)(((intptr_t)ptr + al) & ~al);
			print_struct(fp,desc[i].desc, ptr, name, tab);
			ptr += desc[i].length;
			if (!tab && desc[i+1].name != NULL)
				fprintf(fp,";");
			continue;
		}
		if (UNION == desc[i].type) {
			u32 = *((uint32_t*)(ptr-4));
			ptr = (void*)(((intptr_t)ptr + al) & ~al);
			for (j = 0; desc[i].u[j].name != NULL; j++)
				if (desc[i].u[j].value == u32)
					break;
			if (desc[i].u[j].name != NULL) {
				strcat(name,".");
				strcat(name,desc[i].u[j].name);
				strcat(name,".");
				print_struct(fp,desc[i].u[j].desc,
					     ptr, name, tab);
			}
			return 0; /* FIXME */
		}
		if (desc[i].type != PADDING) {
			if (tab)
				fprintf(fp,"\t%-24s: ",name);
			else
				fprintf(fp,"%s=",name);
		}
		switch (desc[i].type) {
		case STRING:
			fprintf(fp,"\"%-.*s\"",desc[i].length,ptr);
			ptr += desc[i].length;
			break;
		case PTR:
			p = *(void**)ptr;
			fprintf(fp,"%p",p);
			ptr += sizeof(p);
			break;
		case VER:
			u32 = *((uint32_t*)ptr);
			fprintf(fp,"%d.%d.%d",
				(u32 >> 16) & 0xff,
				(u32 >>  8) & 0xff,
				u32         & 0xff);
			ptr += 4;
			break;
		case FOURCC:
			u32 = *((uint32_t*)ptr);
			fprintf(fp,"0x%08x [%c%c%c%c]", u32,
				isprint(ptr[0]) ? ptr[0] : '.',
				isprint(ptr[1]) ? ptr[1] : '.',
				isprint(ptr[2]) ? ptr[2] : '.',
				isprint(ptr[3]) ? ptr[3] : '.');
			ptr += 4;
			break;

		case ENUM16:
			u16 = *((uint16_t*)ptr);
			fprintf(fp,"%s", (u16 < desc[i].length && desc[i].enums[u16])
				? desc[i].enums[u16] : "unknown");
			ptr += 2;
			break;
		case ENUM32:
			u32 = *((uint32_t*)ptr);
			fprintf(fp,"%s", (u32 < desc[i].length && desc[i].enums[u32])
				? desc[i].enums[u32] : "unknown");
			ptr += 4;
			break;

		case BITS16:
			u16 = *((uint16_t*)ptr);
			first = 1;
			fprintf(fp,"0x%x [",u16);
			for (j = 0; j < 16; j++) {
				if (0 == (u16 & (1 << j)))
					continue;
				fprintf(fp,"%s%s",
					first ? "" : ",",
					desc[i].bits[j]);
				first = 0;
			}
			fprintf(fp,"]");
			ptr += 2;
			break;
		case BITS32:
			u32 = *((uint32_t*)ptr);
			first = 1;
			fprintf(fp,"0x%x [",u32);
			for (j = 0; j < 32; j++) {
				if (0 == (u32 & (1 << j)))
					continue;
				fprintf(fp,"%s%s",
					first ? "" : ",",
					desc[i].bits[j]);
				first = 0;
			}
			fprintf(fp,"]");
			ptr += 4;
			break;
		case BITS64:
			ptr = (void*)(((intptr_t)ptr + al) & ~al);
			u64 = *((uint64_t*)ptr);
			first = 1;
			fprintf(fp,"0x%" PRIx64 " [",u64);
			for (j = 0; j < 64; j++) {
				if (0 == (u64 & ((int64_t)1 << j)))
					continue;
				fprintf(fp,"%s%s",
					first ? "" : ",",
					desc[i].bits[j]);
				first = 0;
			}
			fprintf(fp,"]");
			ptr += 8;
			break;

		case UINT64:
			ptr = (void*)(((intptr_t)ptr + al) & ~al);
			u64 = *((uint64_t*)ptr);
			fprintf(fp,"%" PRIu64,u64);
			ptr += 8;
			break;
		case SINT64:
			ptr = (void*)(((intptr_t)ptr + al) & ~al);
			s64 = *((int64_t*)ptr);
			fprintf(fp,"%" PRId64,s64);
			ptr += 8;
			break;
		case UINT32:
			u32 = *((uint32_t*)ptr);
			fprintf(fp,"%u",u32);
			ptr += 4;
			break;
		case SINT32:
			s32 = *((int32_t*)ptr);
			fprintf(fp,"%d",s32);
			ptr += 4;
			break;
		case UINT16:
			u16 = *((uint16_t*)ptr);
			fprintf(fp,"%u",u16);
			ptr += 2;
			break;
		case SINT16:
			s16 = *((int16_t*)ptr);
			fprintf(fp,"%d",s16);
			ptr += 2;
			break;
		case UINT8:
			u8 = *((uint8_t*)ptr);
			fprintf(fp,"%u",u8);
			ptr += 1;
			break;
		case SINT8:
			s8 = *((int8_t*)ptr);
			fprintf(fp,"%d",s8);
			ptr += 1;
			break;

		case PADDING:
			ptr += desc[i].length;
			break;

		case STRUCT:
		case UNION:
			/* shouldn't happen */
			fprintf(fp,"FIXME [type=%d]\n",desc[i].type);
			exit(1);
		}
		if  (tab)
			fprintf(fp,"\n");
		else if (desc[i+1].name != NULL)
			fprintf(fp,";");
	}
	return 0;
}

/* ---------------------------------------------------------------------- */

int print_ioctl(FILE *fp, struct ioctl_desc *ioctls, char *prefix,
		int cmd, void *ptr)
{
	int  index               = _IOC_NR(cmd);
	char *name               = ioctls[index].name;
	struct struct_desc *desc = ioctls[index].desc;

	fprintf(fp,"%s%s(", prefix, name ? name : "UNKNOWN");
	if (desc) {
		print_struct(fp,desc,ptr,"",0);
	} else {
		fprintf(stderr,"%p",ptr);
	}
	fprintf(fp,")");
	return 0;
}

/* ---------------------------------------------------------------------- */
/*
 * Local variables:
 * c-basic-offset: 8
 * End:
 */
