#include <stdio.h>
#include <string.h>
#include "fuse_kernel.h"

static struct {
	const char *name;
} fuse_ll_ops[] = {
	[FUSE_LOOKUP]	   = { "LOOKUP"	     },
	[FUSE_FORGET]	   = { "FORGET"	     },
	[FUSE_GETATTR]	   = { "GETATTR"     },
	[FUSE_SETATTR]	   = { "SETATTR"     },
	[FUSE_READLINK]	   = { "READLINK"    },
	[FUSE_SYMLINK]	   = { "SYMLINK"     },
	[FUSE_MKNOD]	   = { "MKNOD"	     },
	[FUSE_MKDIR]	   = { "MKDIR"	     },
	[FUSE_UNLINK]	   = { "UNLINK"	     },
	[FUSE_RMDIR]	   = { "RMDIR"	     },
	[FUSE_RENAME]	   = { "RENAME"	     },
	[FUSE_LINK]	   = { "LINK"	     },
	[FUSE_OPEN]	   = { "OPEN"	     },
	[FUSE_READ]	   = { "READ"	     },
	[FUSE_WRITE]	   = { "WRITE"	     },
	[FUSE_STATFS]	   = { "STATFS"	     },
	[FUSE_RELEASE]	   = { "RELEASE"     },
	[FUSE_FSYNC]	   = { "FSYNC"	     },
	[FUSE_SETXATTR]	   = { "SETXATTR"    },
	[FUSE_GETXATTR]	   = { "GETXATTR"    },
	[FUSE_LISTXATTR]   = { "LISTXATTR"   },
	[FUSE_REMOVEXATTR] = { "REMOVEXATTR" },
	[FUSE_FLUSH]	   = { "FLUSH"	     },
	[FUSE_INIT]	   = { "INIT"	     },
	[FUSE_OPENDIR]	   = { "OPENDIR"     },
	[FUSE_READDIR]	   = { "READDIR"     },
	[FUSE_RELEASEDIR]  = { "RELEASEDIR"  },
	[FUSE_FSYNCDIR]	   = { "FSYNCDIR"    },
	[FUSE_GETLK]	   = { "GETLK"	     },
	[FUSE_SETLK]	   = { "SETLK"	     },
	[FUSE_SETLKW]	   = { "SETLKW"	     },
	[FUSE_ACCESS]	   = { "ACCESS"	     },
	[FUSE_CREATE]	   = { "CREATE"	     },
	[FUSE_TMPFILE]	   = { "TMPFILE"	 },
	[FUSE_INTERRUPT]   = { "INTERRUPT"   },
	[FUSE_BMAP]	   = { "BMAP"	     },
	[FUSE_DESTROY]	   = { "DESTROY"     },
	[FUSE_READDIRPLUS] = { "READDIRPLUS" },
};

#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))

static const char *opname(enum fuse_opcode opcode)
{
	if (opcode >= FUSE_MAXOP || !fuse_ll_ops[opcode].name)
		return "???";
	else
		return fuse_ll_ops[opcode].name;
}


static void process_buf(int dir, char *buf, int len)
{
	static unsigned long long prevuniq = -1;
	static int prevopcode;

	if (!dir) {
		struct fuse_in_header *in = (struct fuse_in_header *) buf;
		buf += sizeof(struct fuse_in_header);

		printf("unique: %llu, opcode: %s (%i), nodeid: %lu, len: %i, insize: %i\n",
		       (unsigned long long) in->unique,
		       opname((enum fuse_opcode) in->opcode), in->opcode,
		       (unsigned long) in->nodeid, in->len, len);

		switch (in->opcode) {
		case FUSE_READ: {
			struct fuse_read_in *arg = (struct fuse_read_in *) buf;
			printf("-READ fh:%llu off:%llu siz:%u rfl:%u own:%llu fl:%u\n",
			       arg->fh, arg->offset, arg->size, arg->read_flags,
			       arg->lock_owner, arg->flags);
			break;
		}
		case FUSE_WRITE: {
			struct fuse_write_in *arg = (struct fuse_write_in *) buf;
			printf("-WRITE fh:%llu off:%llu siz:%u wfl:%u own:%llu fl:%u\n",
			       arg->fh, arg->offset, arg->size, arg->write_flags,
			       arg->lock_owner, arg->flags);
			break;
		}
		}
		prevuniq = in->unique;
		prevopcode = in->opcode;
	} else {
		struct fuse_out_header *out = (struct fuse_out_header *) buf;
		buf += sizeof(struct fuse_out_header);

		printf("   unique: %llu, error: %i (%s), len: %i, outsize: %i\n",
		       (unsigned long long) out->unique, out->error,
		       strerror(-out->error), out->len, len);

		if (out->unique == prevuniq) {
			switch (prevopcode) {
			case FUSE_GETATTR: {
				struct fuse_attr_out *arg = (struct fuse_attr_out *) buf;
				printf("+ATTR v:%llu.%09u i:%llu s:%llu b:%llu\n",
				       arg->attr_valid, arg->attr_valid_nsec,
				       arg->attr.ino, arg->attr.size, arg->attr.blocks);
				break;
			}
			case FUSE_LOOKUP: {
				struct fuse_entry_out *arg = (struct fuse_entry_out *) buf;
				printf("+ENTRY nodeid:%llu v:%llu.%09u i:%llu s:%llu b:%llu\n",
				       arg->nodeid, arg->attr_valid, arg->attr_valid_nsec,
				       arg->attr.ino, arg->attr.size, arg->attr.blocks);
				break;
			}
			}
		}
	}

}

int main(void)
{
	FILE *in = stdin;
	while (1) {
		int dir;
		int res;
		char buf[1048576];
		unsigned len = 0;

		memset(buf, 0, sizeof(buf));
		while (1) {
			char str[32];

			res = fscanf(in, "%30s", str);
			if (res != 1 && feof(in))
				return 0;

			if (res == 0)
				continue;

			if (strncmp(str, "read(", 5) == 0) {
				dir = 0;
				break;
			} else if (strncmp(str, "writev(", 7) == 0) {
				dir = 1;
				break;
			}
		}

		while (1) {
			int c = getc(in);
			if (c == '"') {
				while (1) {
					int val;

					c = getc(in);
					if (c == EOF) {
						fprintf(stderr, "eof in string\n");
						break;
					}
					if (c == '\n') {
						fprintf(stderr, "eol in string\n");
						break;
					}
					if (c == '"')
						break;
					if (c != '\\') {
						val = c;
					} else {
						c = getc(in);
						switch (c) {
						case 'n': val = '\n'; break;
						case 'r': val = '\r'; break;
						case 't': val = '\t'; break;
						case '"': val = '"'; break;
						case '\\': val = '\\'; break;
						case 'x':
							res = scanf("%x", &val);
							if (res != 1) {
								fprintf(stderr, "parse error\n");
								continue;
							}
							break;
						default:
							fprintf(stderr, "unknown sequence: '\\%c'\n", c);
							continue;
						}
					}
					buf[len++] = val;
				}
			}
			if (c == '\n')
				break;
		}
		process_buf(dir, buf, len);
		memset(buf, 0, len);
		len = 0;
	}
}
