/* radare - LGPL - Copyright 2007-2016 - pancake & Skia */

#include "r_cons.h"
#include "r_util.h"
#include "r_print.h"
#include "r_reg.h"

#define NOPTR 0
#define PTRSEEK 1
#define PTRBACK 2
#define NULLPTR 3
#define STRUCTPTR 100
#define NESTEDSTRUCT 1
#define STRUCTFLAG 10000
#define NESTDEPTH 14
#define ARRAYINDEX_COEF 10000

#define MUSTSEE (mode & R_PRINT_MUSTSEE && mode & R_PRINT_ISFIELD && !(mode & R_PRINT_JSON))
#define MUSTSET (mode & R_PRINT_MUSTSET && mode & R_PRINT_ISFIELD && setval)
#define SEEVALUE (mode & R_PRINT_VALUE)
#define MUSTSEEJSON (mode & R_PRINT_JSON && mode & R_PRINT_ISFIELD)

static float updateAddr(const ut8 *buf, int i, int endian, ut64 *addr, ut64 *addr64) {
	float f;
	// assert sizeof (float) == sizeof (ut32))
	ut32 tmpaddr;
	r_mem_swaporcopy ((ut8*)&f, buf + i, sizeof (float), endian);
	
	if (addr) {
		tmpaddr = r_read_ble32 (buf + i, endian);
		*addr = (ut64)tmpaddr;
	}
	if (addr64) {
		*addr64 = r_read_ble64 (buf + i, endian);
	}
	return f;
}

static int r_get_size(RNum *num, ut8 *buf, int endian, const char *s) {
	int size = 0, len = strlen (s);
	ut64 addr;

	if (s[0] == '*' && len >= 4) { // value pointed by the address
		int offset = (int)r_num_math (num, s + 1);
		(void)updateAddr (buf, offset, endian, &addr, NULL);
		return addr;
	} else {
		// flag handling doesnt seems to work here
		size = r_num_math (num, s);
	}
	return size;
}

static void r_print_format_quadword(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr64;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	updateAddr (buf, i, endian, NULL, &addr64);
	if (MUSTSET) {
		p->cb_printf ("wv8 %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*8:0));
	} else if (MUSTSEE) {
		if (!SEEVALUE) {
			p->cb_printf ("0x%08"PFMT64x" = (qword)",
				seeki + ((elem >= 0)? elem * 2: 0));
		}
		if (size == -1) {
			p->cb_printf ("0x%016"PFMT64x, addr64);
		} else {
			if (!SEEVALUE) {
				p->cb_printf ("[ ");
			}
			while (size--) {
				updateAddr (buf, i, endian, NULL, &addr64);
				if (elem == -1 || elem == 0) {
					p->cb_printf ("0x%016"PFMT64x, addr64);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (", ");
				}
				if (elem > -1) {
					elem--;
				}
				i += 8;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1) {
			p->cb_printf ("%"PFMT64d, addr64);
		} else {
			p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, NULL, &addr64);
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%"PFMT64d, addr64);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (", ");
				}
				if (elem > -1) elem--;
				i += 8;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static void r_print_format_byte(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size / ARRAYINDEX_COEF - 1;
		size %= ARRAYINDEX_COEF;
	}
	if (MUSTSET) {
		p->cb_printf ("\"w %s\" @ 0x%08"PFMT64x"\n", setval, seeki + ((elem >= 0) ? elem : 0));
	} else if (MUSTSEE) {
		if (!SEEVALUE) {
			p->cb_printf ("0x%08"PFMT64x" = ", seeki + ((elem >= 0) ? elem : 0));
		}
		if (size == -1) {
			p->cb_printf ("0x%02x", buf[i]);
		} else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf ("0x%02x", buf[i]);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i++;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1) {
			p->cb_printf ("%d", buf[i]);
		} else {
			p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf (", %d", buf[i]);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (", ");
				}
				if (elem > -1) {
					elem--;
				}
				i++;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static int r_print_format_uleb(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	int elem = -1;
	int s = 0, sum = 0;
	ut64 value = 0, offset = 0;
	if (size >= ARRAYINDEX_COEF) {
		elem = size / ARRAYINDEX_COEF - 1;
		size %= ARRAYINDEX_COEF;
	}
	// offset = seeki+((elem>=0)?16*elem:0);
	if (MUSTSET) {
		ut8 *tmp;
		char *nbr;
		do {
			offset += s;
			r_uleb128_decode (buf+offset, &s, &value);
		} while (elem--);
		tmp = (ut8*) r_uleb128_encode (r_num_math (NULL, setval), &s);
		nbr = r_hex_bin2strdup (tmp, s);
		p->cb_printf ("\"wx %s\" @ 0x%08"PFMT64x"\n", nbr, seeki+offset);
		free (tmp);
		free (nbr);
		// sum = size of the converted number
	} else if (MUSTSEE) {
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		if (size==-1) {
			r_uleb128_decode (buf+i, &s, &value);
			p->cb_printf ("%"PFMT64d, value);
			sum = s;
		} else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					r_uleb128_decode (buf+i, &s, &value);
					sum += s;
					p->cb_printf ("%"PFMT64d, value);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=s;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1) {
			r_uleb128_decode (buf+i, &s, &value);
			p->cb_printf ("\"%"PFMT64d"\"", value);
			sum = s;
		} else {
			p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					r_uleb128_decode (buf+i, &s, &value);
					sum += s;
					p->cb_printf ("\"%"PFMT64d"\"", value);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=s;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
	return sum;
}

static void r_print_format_char(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	if (MUSTSET) {
		p->cb_printf ("\"w %s\" @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem:0));
	} else if (MUSTSEE) {
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*2:0));
		if (size==-1)
			p->cb_printf ("'%c'", IS_PRINTABLE (buf[i])?buf[i]:'.');
		else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf ("'%c'", IS_PRINTABLE (buf[i])?buf[i]:'.');
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i++;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1)
			p->cb_printf ("\"%c\"", IS_PRINTABLE (buf[i])?buf[i]:'.');
		else {
			p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf ("\"%c\"", IS_PRINTABLE (buf[i])?buf[i]:'.');
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i++;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static void r_print_format_decchar(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	if (MUSTSET) {
		p->cb_printf ("\"w %s\" @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem:0));
	} else if (MUSTSEE) {
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem:0));
		if (size==-1)
			p->cb_printf ("%d", buf[i]);
		else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%d", buf[i]);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i++;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1)
			p->cb_printf ("\"%d\"", buf[i]);
		else {
			p->cb_printf ("[ ");
			while (size--) {
				if (elem == -1 || elem == 0) {
					p->cb_printf ("\"%d\"", buf[i]);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i++;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static int r_print_format_string(const RPrint* p, ut64 seeki, ut64 addr64, ut64 addr, int is64, int mode) {
	ut8 buffer[255];
	buffer[0] = 0;
	if (p->iob.read_at) {
		if (is64 == 1)
			p->iob.read_at (p->iob.io, addr64, buffer, sizeof (buffer)-8);
		else
			p->iob.read_at (p->iob.io, (ut64)addr, buffer, sizeof (buffer)-8);
	} else {
		eprintf ("(cannot read memory)\n");
		return -1;
	}
	if (MUSTSEEJSON) {
		p->cb_printf ("%d,\"string\":\"%s\"}", seeki, buffer);
	} else if (MUSTSEE) {
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" -> 0x%08"PFMT64x" ", seeki, addr);
		p->cb_printf ("%s", buffer);
	}
	return 0;
}

static void r_print_format_time(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	updateAddr (buf, i, endian, &addr, NULL);
	if (MUSTSET) {
		p->cb_printf ("wv4 %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*4:0));
	} else if (MUSTSEE) {
		char *timestr = strdup(asctime (gmtime ((time_t*)&addr)));
		*(timestr+24) = '\0';
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*4:0));
		if (size==-1) {
			p->cb_printf ("%s", timestr);
		} else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				free (timestr);
				timestr = strdup (asctime (gmtime ((time_t*)&addr)));
				*(timestr+24) = '\0';
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%s", timestr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i += 4;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
		free (timestr);
	} else if (MUSTSEEJSON) {
		char *timestr = strdup (asctime (gmtime ((time_t*)&addr)));
		*(timestr+24) = '\0';
		if (size==-1) {
			p->cb_printf ("\"%s\"", timestr);
		} else {
			p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				free (timestr);
				timestr = strdup (asctime (gmtime ((time_t*)&addr)));
				*(timestr+24) = '\0';
				if (elem == -1 || elem == 0) {
					p->cb_printf ("\"%s\"", timestr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i += 4;
			}
			p->cb_printf (" ]");
		}
		free (timestr);
		p->cb_printf ("}");
	}
}

// TODO: support unsigned int?
static void r_print_format_hex(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	updateAddr (buf, i, endian, &addr, NULL);
	if (MUSTSET) {
		p->cb_printf ("wv4 %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*4:0));
	} else if (mode & R_PRINT_DOT) {
		p->cb_printf ("%"PFMT64d, addr);
	} else if (MUSTSEE) {
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*4:0));
		if (size==-1)
			p->cb_printf ("%"PFMT64d, addr);
		else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%"PFMT64d, addr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=4;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		if (size==-1)
			p->cb_printf ("%d", addr);
		else {
			p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%d", addr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=4;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static int r_print_format_disasm(const RPrint* p, ut64 seeki, int size) {
	ut64 prevseeki = seeki;

	if (!p->disasm || !p->user) return 0;

	size = R_MAX (1, size);

	while (size-- > 0) {
		seeki += p->disasm (p->user, seeki);
	}

	return seeki - prevseeki;
}

static void r_print_format_octal (const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	updateAddr (buf, i, endian, &addr, NULL);
	if (MUSTSET) {
		p->cb_printf ("wv4 %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*4:0));
	} else if (mode & R_PRINT_DOT) {
		p->cb_printf ("0%"PFMT64o, addr);
	} else if (MUSTSEE) {
		ut32 addr32 = (ut32)addr;
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*4:0));
		if (!SEEVALUE) p->cb_printf ("(octal) ");
		if (size==-1)
			p->cb_printf (" 0%08"PFMT64o, addr32);
		else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				addr32 = (ut32)addr;
				if (elem == -1 || elem == 0) {
					p->cb_printf ("0%08"PFMT64o, addr32);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=4;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		ut32 addr32 = (ut32)addr;
		if (size==-1)
			p->cb_printf ("%d", addr32);
		else {
			p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				addr32 = (ut32)addr;
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%d", addr32);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=4;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static void r_print_format_hexflag(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	updateAddr (buf, i, endian, &addr, NULL);
	if (MUSTSET) {
		p->cb_printf ("wv4 %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*4:0));
	} else if (mode & R_PRINT_DOT) {
		p->cb_printf ("0x%08"PFMT64x, addr & UT32_MAX);
	} else if (MUSTSEE) {
		ut32 addr32 = (ut32)addr;
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*4:0));
		if (size==-1) {
			p->cb_printf ("0x%08"PFMT64x, addr32);
		} else {
			if (!SEEVALUE) p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				addr32 = (ut32)addr;
				if (elem == -1 || elem == 0) {
					p->cb_printf ("0x%08"PFMT64x, addr32);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (", ");
				if (elem > -1) elem--;
				i+=4;
			}
			if (!SEEVALUE) p->cb_printf (" ]");
		}
	} else if (MUSTSEEJSON) {
		ut32 addr32 = (ut32)addr;
		if (size==-1)
			p->cb_printf ("%d", addr32);
		else {
			p->cb_printf ("[ ");
			while (size--) {
				updateAddr (buf, i, endian, &addr, NULL);
				addr32 = (ut32)addr;
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%d", addr32);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1)
					p->cb_printf (",");
				if (elem > -1) elem--;
				i+=4;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static int r_print_format_10bytes(const RPrint* p, int mode, const char* setval,
		ut64 seeki, ut64 addr, ut8* buf) {
	ut8 buffer[255];
	int j;
	if (MUSTSET) {
		p->cb_printf ("?e pf B not yet implemented\n");
	} else if (mode & R_PRINT_DOT) {
		for (j = 0; j<10; j++)
			p->cb_printf ("%02x ", buf[j]);
	} else if (MUSTSEE) {
		if (!p->iob.read_at) {
			printf ("(cannot read memory)\n");
			return -1;
		}
		p->iob.read_at (p->iob.io, (ut64)addr, buffer, 248);
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		for (j=0; j<10; j++)
			p->cb_printf ("%02x ", buf[j]);
		if (!SEEVALUE) p->cb_printf (" ... (");
		for (j=0; j<10; j++) {
			if (!SEEVALUE) {
				if (IS_PRINTABLE (buf[j]))
					p->cb_printf ("%c", buf[j]);
				else
					p->cb_printf (".");
			}
		}
		if (!SEEVALUE) p->cb_printf (")");
	} else if (MUSTSEEJSON) {
		if (!p->iob.read_at) {
			printf ("(cannot read memory)\n");
			return -1;
		} else
			p->iob.read_at (p->iob.io, (ut64)addr, buffer, 248);
		p->cb_printf ("[ %d", buf[0]);
		j=1;
		for (; j<10; j++)
			p->cb_printf (", %d", buf[j]);
		p->cb_printf ("]}");
		return 0;
	}
	return 0;
}

static int r_print_format_hexpairs(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	int j;
	size = (size==-1) ? 1 : size;
	if (MUSTSET) {
		p->cb_printf ("?e pf X not yet implemented\n");
	} else if (mode & R_PRINT_DOT) {
		for (j = 0; j < size; j++)
			p->cb_printf ("%02x", buf[i + j]);
	} else if (MUSTSEE) {
		size = (size < 1) ? 1 : size;
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		for (j = 0; j < size; j++) {
			p->cb_printf ("%02x ", buf[i + j]);
		}
		if (!SEEVALUE) p->cb_printf (" ... (");
		for (j=0; j<size; j++) {
			if (!SEEVALUE) {
				if (IS_PRINTABLE (buf[j]))
					p->cb_printf ("%c", buf[i+j]);
				else
					p->cb_printf (".");
			}
		}
		p->cb_printf (")");
	} else if (MUSTSEEJSON) {
		size = (size < 1) ? 1 : size;
		p->cb_printf ("[ %d", buf[0]);
		j = 1;
		for (; j < 10; j++)
			p->cb_printf (", %d", buf[j]);
		p->cb_printf ("]}");
		return size;
	}
	return size;
}

static void r_print_format_float(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	float val_f = 0.0f;
	ut64 addr = 0;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF - 1;
		size %= ARRAYINDEX_COEF;
	}
	val_f = updateAddr (buf, i, endian, &addr, NULL);
	if (MUSTSET) {
		p->cb_printf ("wv4 %s @ 0x%08"PFMT64x"\n", setval,
			seeki + ((elem >= 0) ? elem * 4 : 0));
	} else if (mode & R_PRINT_DOT) {
		p->cb_printf ("%f", val_f);
	} else {
		if (MUSTSEE) {
			if (!SEEVALUE) {
				p->cb_printf ("0x%08"PFMT64x" = ",
					seeki + ((elem >= 0) ? elem * 4 : 0));
			}
		}
		if (size == -1) {
			p->cb_printf ("%f", val_f);
		} else {
			if (!SEEVALUE) {
				p->cb_printf ("[ ");
			}
			while (size--) {
				val_f = updateAddr (buf, i, endian, &addr, NULL);
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%f", val_f);
					if (elem == 0) {
						elem = -2;
					}
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (", ");
				}
				if (elem > -1) {
					elem--;
				}
				i += 4;
			}
			if (!SEEVALUE) {
				p->cb_printf (" ]");
			}
		}
		if (MUSTSEEJSON) {
			p->cb_printf ("}");
		}
	}
}

static void r_print_format_word(const RPrint* p, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	ut64 addr;
	int elem = -1;
	if (size >= ARRAYINDEX_COEF) {
		elem = size/ARRAYINDEX_COEF-1;
		size %= ARRAYINDEX_COEF;
	}
	addr = endian
		? (*(buf + i)) << 8 | (*(buf + i + 1))
		: (*(buf + i + 1)) << 8 | (*(buf + i));
	if (MUSTSET) {
		p->cb_printf ("wx %s @ 0x%08"PFMT64x"\n", setval, seeki+((elem>=0)?elem*2:0));
	} else if (mode & R_PRINT_DOT) {
		if (size == -1) {
			p->cb_printf ("0x%04x", addr);
		}
		while ((size -= 2) > 0) {
			addr = endian
				? (*(buf+i))<<8 | (*(buf+i+1))
				: (*(buf+i+1))<<8 | (*(buf+i));
			if (elem == -1 || elem == 0) {
				p->cb_printf ("%d", addr);
				if (elem == 0) elem = -2;
			}
			if (size != 0 && elem == -1) {
				p->cb_printf (",");
			}
			if (elem > -1) {
				elem--;
			}
			i += 2;
		}
	} else if (MUSTSEE) {
		if (!SEEVALUE) {
			p->cb_printf ("0x%08"PFMT64x" = ", seeki+((elem>=0)?elem*2:0));
		}
		if (size==-1) {
			p->cb_printf ("0x%04x", addr);
		} else {
			if (!SEEVALUE) {
				p->cb_printf ("[ ");
			}
			while (size--) {
				addr = endian
					? (*(buf+i))<<8 | (*(buf+i+1))
					: (*(buf+i+1))<<8 | (*(buf+i));
				if (elem == -1 || elem == 0) {
					p->cb_printf ("0x%04x", addr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (", ");
				}
				if (elem > -1) {
					elem--;
				}
				i += 2;
			}
			if (!SEEVALUE) {
				p->cb_printf (" ]");
			}
		}
	} else if (MUSTSEEJSON) {
		if (size==-1) {
			p->cb_printf ("%d", addr);
		} else {
			p->cb_printf ("[ ");
			while ( (size -= 2) >0) {
				addr = endian
					? (*(buf+i))<<8 | (*(buf+i+1))
					: (*(buf+i+1))<<8 | (*(buf+i));
				if (elem == -1 || elem == 0) {
					p->cb_printf ("%d", addr);
					if (elem == 0) elem = -2;
				}
				if (size != 0 && elem == -1) {
					p->cb_printf (",");
				}
				if (elem > -1) elem--;
				i += 2;
			}
			p->cb_printf (" ]");
		}
		p->cb_printf ("}");
	}
}

static void r_print_format_nulltermstring(const RPrint* p, const int len, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	if (MUSTSET) {
		int buflen = strlen ((const char *)buf+seeki), vallen = strlen(setval);
		char *newstring, *ons;
		newstring = ons = strdup(setval);
		if ((newstring[0] == '\"' && newstring[vallen-1] == '\"')
				|| (newstring[0] == '\'' && newstring[vallen-1] == '\'')) {
			newstring[vallen-1] = '\0';
			newstring++;
			vallen -= 2;
		}
		if (vallen > buflen) {
			eprintf ("Warning: new string is longer than previous one\n");
		}
		p->cb_printf ("wx ");
		for (i = 0; i < vallen; i++) {
			if (i < vallen - 3 && newstring[i] == '\\' && newstring[i + 1] == 'x') {
				p->cb_printf ("%c%c", newstring[i + 2], newstring[i + 3]);
				i += 3;
			} else {
				p->cb_printf ("%2x", newstring[i]);
			}
		}
		p->cb_printf (" @ 0x%08"PFMT64x"\n", seeki);
		free(ons);
	} else if (mode & R_PRINT_DOT) {
		int j = i;
		p->cb_printf ("\\\"", seeki);
		for (; j<len && ((size==-1 || size-- >0) && buf[j]) ; j++) {
			char ch = buf[j];
			if (ch == '"') {
				p->cb_printf ("\\\"");
			} else if (IS_PRINTABLE (ch)) {
				p->cb_printf ("%c", ch);
			} else p->cb_printf (".");
		}
		p->cb_printf ("\\\"");
	} else if (MUSTSEE) {
		int j = i;
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		for (; j < len && ((size == -1 || size-- > 0) && buf[j]) ; j++) {
			if (IS_PRINTABLE (buf[j])) {
				p->cb_printf ("%c", buf[j]);
			} else {
				p->cb_printf (".");
			}
		}
	} else if (MUSTSEEJSON) {
		int j = i;
		p->cb_printf ("%d,\"string\":\"", seeki);
		for (; j < len && ((size == -1 || size-- > 0) && buf[j]) ; j++) {
			if (IS_PRINTABLE (buf[j])) {
				p->cb_printf ("%c", buf[j]);
			} else {
				p->cb_printf (".");
			}
		}
		p->cb_printf ("\"}");
	}
}

static void r_print_format_nulltermwidestring(const RPrint* p, const int len, int endian, int mode,
		const char* setval, ut64 seeki, ut8* buf, int i, int size) {
	if (MUSTSET) {
		int vallen = strlen(setval);
		char *newstring, *ons;
		newstring = ons = strdup(setval);
		if ((newstring[0] == '\"' && newstring[vallen-1] == '\"')
				|| (newstring[0] == '\'' && newstring[vallen-1] == '\'')) {
			newstring[vallen - 1] = '\0';
			newstring++;
			vallen -= 2;
		}
		if ((size = strlen (setval)) > r_wstr_clen((char*)(buf+seeki))) {
			eprintf ("Warning: new string is longer than previous one\n");
		}
		p->cb_printf ("ww %s @ 0x%08"PFMT64x"\n", newstring, seeki);
		free(ons);
	} else if (MUSTSEE) {
		int j = i;
		if (!SEEVALUE) {
			p->cb_printf ("0x%08"PFMT64x" = ", seeki);
		}
		for (; j<len && ((size==-1 || size-->0) && buf[j]) ; j+=2) {
			if (IS_PRINTABLE (buf[j])) {
				p->cb_printf ("%c", buf[j]);
			} else {
				p->cb_printf (".");
			}
		}
	}
}

static void r_print_format_bitfield(const RPrint* p, ut64 seeki, char* fmtname,
		char* fieldname, ut64 addr, int mode, int size) {
	char *bitfield = NULL;
	switch (size) {
	case 1: addr &= UT8_MAX; break;
	case 2: addr &= UT16_MAX; break;
	case 4: addr &= UT32_MAX; break;
	}
	if (MUSTSEE && !SEEVALUE) {
		p->cb_printf ("0x%08"PFMT64x" = ", seeki);
	}
	if (p->get_bitfield) {
		bitfield = p->get_bitfield (p->user, fmtname, addr);
	}
	if (bitfield && *bitfield) {
		if (MUSTSEEJSON) p->cb_printf ("\"%s\"}", bitfield);
		else if (MUSTSEE) p->cb_printf (" %s (bitfield) = %s\n", fieldname, bitfield);
	} else {
		if (MUSTSEEJSON) p->cb_printf ("\"`tb %s 0x%x`\"}", fmtname, addr);
		else if (MUSTSEE) p->cb_printf (" %s (bitfield) = `tb %s 0x%x`\n",
				fieldname, fmtname, addr);
	}
	free (bitfield);
}

static void r_print_format_enum (const RPrint* p, ut64 seeki, char* fmtname,
		char* fieldname, ut64 addr, int mode, int size) {
	char *enumvalue = NULL;
	switch (size) {
	case 1: addr &= UT8_MAX; break;
	case 2: addr &= UT16_MAX; break;
	case 4: addr &= UT32_MAX; break;
	}
	if (MUSTSEE)
		if (!SEEVALUE) p->cb_printf ("0x%08"PFMT64x" = ", seeki);
	if (p->get_enumname)
		enumvalue = p->get_enumname (p->user, fmtname, addr);
	if (enumvalue && *enumvalue) {
		if (mode & R_PRINT_DOT) {
			p->cb_printf ("%s.%s", fmtname, enumvalue);
		} else if (MUSTSEEJSON) p->cb_printf ("\"%s\"}", fmtname);
		else if (MUSTSEE) p->cb_printf (" %s (enum) = 0x%"PFMT64x" ; %s\n",
				fieldname, addr, enumvalue);
	} else {
		if (MUSTSEEJSON) p->cb_printf ("\"`te %s 0x%x`\"}", fmtname, addr);
		else if (MUSTSEE) p->cb_printf (" %s (enum) = %s\n",//`te %s 0x%x`\n",
				fieldname, enumvalue); //fmtname, addr);
	}
	free (enumvalue);
}

static void r_print_format_register (const RPrint* p, int mode,
		const char *name, const char* setval) {
	RRegItem *ri = p->get_register (p->reg, name, R_REG_TYPE_ALL);
	if (ri) {
		if (MUSTSET) {
			p->cb_printf ("dr %s=%s\n", name, setval);
		} else if (MUSTSEE) {
			if (!SEEVALUE) p->cb_printf("%s : 0x%08"PFMT64x"\n", ri->name, p->get_register_value (p->reg, ri));
			else p->cb_printf("0x%08"PFMT64x"\n", p->get_register_value (p->reg, ri));
		} else if (MUSTSEEJSON) {
			p->cb_printf ("%d}", p->get_register_value (p->reg, ri));
		}
	} else {
		p->cb_printf ("Register %s does not exists\n", name);
	}
}

// XXX: this is very incomplete. must be updated to handle all format chars
int r_print_format_struct_size(const char *f, RPrint *p, int mode) {
	char *o, *end, *args, *fmt;
	int size = 0, tabsize = 0, i, idx = 0, biggest = 0;
	if (!f) return -1;
	o = strdup (f);
	if (!o) return -1;
	end = strchr (o, ' ');
	fmt = o;
	if (!end && !(end = strchr (o, '\0'))) {
		free (o);
		return -1;
	}
	if (*end) {
		*end = 0;
		args = strdup (end + 1);
	} else {
		args = strdup ("");
	}
	if (fmt[0] == '0') {
		mode |= R_PRINT_UNIONMODE;
		fmt++;
	} else {
		mode &= ~R_PRINT_UNIONMODE;
	}

	r_str_word_set0 (args);
	for (i=0; i < strlen (fmt); i++) {
		if (fmt[i] == '[') {
			char *end = strchr (fmt+i,']');
			if (!end) {
				eprintf ("No end bracket.\n");
				continue;
			}
			*end = '\0';
			tabsize = r_num_math (NULL, fmt+i+1);
			*end = ']';
			while (fmt[i++]!=']');
		} else {
			tabsize = 1;
		}

		switch (fmt[i]) {
		case 'c':
		case 'b':
		case '.':
		case 'X':
			size += tabsize*1;
			break;
		case 'w':
			size += tabsize*2;
			break;
		case 'd':
		case 'o':
		case 'i':
		case 'x':
		case 'f':
		case 's':
		case 't':
		case ':':
			size += tabsize * 4;
			break;
		case 'S':
		case 'q':
			size += tabsize * 8;
			break;
		case 'z':
		case 'Z':
			size += tabsize;
			break;
		case '*':
			size += tabsize * 4;
			i++;
			break;
		case 'B':
		case 'E':
			switch (tabsize) {
			case 1: size += 1; break;
			case 2: size += 2; break;
			case 4: size += 4; break;
			case 8: size += 8; break;
			default:
				eprintf ("Unknown enum format size: %d\n", tabsize);
				break;
			}
			break;
		case '?':
			{
			const char *format = NULL;
			char *endname = NULL, *structname = NULL;
			structname = strdup (r_str_word_get0 (args, idx));
			if (*structname == '(') {
				endname = strchr (structname, ')');
			} else {
				eprintf ("Struct name missing (%s)\n", structname);
				free (structname);
				break;
			}
			if (endname) *endname = '\0';
			format = r_strht_get (p->formats, structname + 1);
			free (structname);
			size += tabsize * r_print_format_struct_size (format, p, mode);
			}
			break;
			// TODO continue list
		default:
			break;
		}
		idx++;
		if (mode & R_PRINT_UNIONMODE) {
			if (size > biggest) biggest = size;
			size = 0;
		}
	}
	free (o);
	free (args);
	return (mode & R_PRINT_UNIONMODE)? biggest : size;
}

static int r_print_format_struct(RPrint* p, ut64 seek, const ut8* b, int len,
		char *name, int slide, int mode, const char *setval, char *field) {
	const char *fmt;
	char namefmt[128];
	if ((slide % STRUCTPTR) > NESTDEPTH || (slide%STRUCTFLAG)/STRUCTPTR > NESTDEPTH) {
		eprintf ("Too much nested struct, recursion too deep...\n");
		return 0;
	}
	fmt = r_strht_get (p->formats, name);
	if (!fmt || !*fmt) {
		eprintf ("Undefined struct '%s'.\n", name);
		return 0;
	}
	if (MUSTSEE && !SEEVALUE) {
		snprintf (namefmt, sizeof (namefmt), "%%%ds", 10+6*slide%STRUCTPTR);
		if (fmt[0] == '0')
			p->cb_printf (namefmt, "union");
		else p->cb_printf (namefmt, "struct");
		p->cb_printf ("<%s>\n", name);
	}
	r_print_format (p, seek, b, len, fmt, mode, setval, field);
	return r_print_format_struct_size (fmt, p, mode);
}

static char* get_args_offset(const char *arg) {
	char *args = strchr (arg, ' ');
	char *sq_bracket = strchr (arg, '[');
	int max = 30;
	if (args && sq_bracket) {
		char *csq_bracket = strchr (arg, ']');
		while (args && csq_bracket && csq_bracket > args && max--) {
			args = strchr (csq_bracket, ' ');
		}
	}
	return args;
}

#define MINUSONE ((void*)(size_t)-1)
#define ISSTRUCT (tmp == '?' || (tmp == '*' && *(arg+1) == '?'))
R_API int r_print_format(RPrint *p, ut64 seek, const ut8* b, const int len,
		const char *formatname, int mode, const char *setval, char *ofield) {
	int nargs, i, j, invalid, nexti, idx, times, otimes, endian, isptr = 0;
	const int old_bits = p->bits;
	char *args = NULL, *bracket, tmp, last = 0;
	ut64 addr = 0, addr64 = 0, seeki = 0;
	static int slide = 0, oldslide = 0;
	char namefmt[8], *field = NULL;
	const char *arg = NULL;
	const char *fmt = NULL;
	const char *argend;
	int viewflags = 0;
	char *oarg = NULL;
	ut8 *buf;

	/* Load format from name into fmt */
	if (!formatname) return 0;
	fmt = r_strht_get (p->formats, formatname);
	if (!fmt) fmt = formatname;
	while (*fmt && iswhitechar (*fmt)) fmt++;
	argend = fmt+strlen (fmt);
	arg = fmt;

	nexti = nargs = i = j = 0;

	if (len < 1) return 0;
	// len+2 to save space for the null termination in wide strings
	buf = calloc (1,len + 2);
	if (!buf) return 0;
	memcpy (buf, b, len);
	endian = p->big_endian;

	if (ofield && ofield != MINUSONE) field = strdup (ofield);

	/* get times */
	otimes = times = atoi (arg);
	if (times > 0) {
		while (*arg >= '0' && *arg <= '9') arg++;
	}

	bracket = strchr (arg,'{');
	if (bracket) {
		char *end = strchr (arg, '}');
		if (end == NULL) {
			eprintf ("No end bracket. Try pm {ecx}b @ esi\n");
			goto beach;
		}
		*end = '\0';
		times = r_num_math (NULL, bracket + 1);
		arg = end + 1;
	}

	if (*arg == '\0') {
		goto beach;
	}

	/* get args */
	args = get_args_offset (arg);
	if (args) {
		int l = 0, maxl = 0;
		argend = args;
		args = strdup (args + 1);
		nargs = r_str_word_set0 (args);
		if (nargs == 0) {
			R_FREE (args);
		}
		for (i = 0; i < nargs; i++) {
			const int len = strlen (r_str_word_get0 (args, i));
			if (len > maxl) {
				maxl = len;
			}
		}
		l++;
		const char *ends = " "; // XXX trailing space warning
		snprintf (namefmt, sizeof (namefmt), "%%%ds :%s",
			maxl + 6 * slide % STRUCTPTR, ends);
	}
#define ISPOINTED ((slide%STRUCTFLAG)/STRUCTPTR<=(oldslide%STRUCTFLAG)/STRUCTPTR)
#define ISNESTED ((slide%STRUCTPTR)<=(oldslide%STRUCTPTR))
	if (mode == R_PRINT_JSON && slide == 0) {
		p->cb_printf ("[");
	}
	if (arg[0] == '0') {
		mode |= R_PRINT_UNIONMODE;
		arg++;
	} else {
		mode &= ~R_PRINT_UNIONMODE;
	}
	if (mode & R_PRINT_DOT) {
		char *fmtname;
		if (formatname && *formatname) {
			if (strchr (formatname, ' ')) {
				fmtname = r_str_newf ("0x%"PFMT64x, seek);
			} else {
				fmtname = strdup (formatname);
			}
		} else {
			fmtname = r_str_newf ("0x%"PFMT64x, seek);
		}
		p->cb_printf ("digraph g { graph [ rank=same; rankdir=LR; ];\n");
		p->cb_printf ("root [ rank=1; shape=record\nlabel=\"%s", fmtname);
	}

	/* go format */
	i = 0;
	if (!times) {
		otimes = times = 1;
	}
	for (; times; times--) { // repeat N times
		const char * orig = arg;
		int first = 1;
		if (otimes > 1) {
			if (mode & R_PRINT_JSON) {
				if (otimes > times) p->cb_printf (",");
				p->cb_printf ("[{\"index\":%d,\"offset\":%d},", otimes-times, seek+i);
			} else {
				p->cb_printf ("0x%08"PFMT64x" [%d] {\n", seek+i, otimes-times);
			}
		}
		arg = orig;
		int flag = 0;
		for (idx = 0; i < len && arg < argend && *arg; arg++) {
			int size = 0, elem = 0; /* size of the array, element of the array */
			char *fieldname = NULL, *fmtname = NULL;
			if (mode & R_PRINT_UNIONMODE) {
				i = 0;
			}
			seeki = seek + i;
			addr = 0LL;
			invalid = 0;
			p->bits = old_bits;
			if (arg[0] == '[') {
				char *end = strchr (arg,']');
				if (!end) {
					eprintf ("No end bracket.\n");
					goto beach;
				}
				*end = '\0';
				size = r_get_size (p->num, buf, endian, arg+1);
				arg = end + 1;
				*end = ']';
			} else {
				size = -1;
			}
			if (i+7<len) { // Max byte number where updateAddr will look into
				updateAddr (buf, i, endian, &addr, &addr64);
			} else {
				eprintf ("Likely a heap buffer overflow in %s at %d\n", __FILE__, __LINE__);
				goto beach;
			}

			tmp = *arg;

			if (args == NULL) {
				mode |= R_PRINT_ISFIELD;
			}
			if (mode & R_PRINT_MUSTSEE && otimes > 1) {
				p->cb_printf ("  ");
			}
			if (idx < nargs && tmp != 'e' && isptr == 0) {
				char *dot = NULL, *bracket = NULL;
				if (field)
					dot = strchr (field, '.');
				if (dot)
					*dot = '\0';
				free (oarg);
				oarg = fieldname = strdup (r_str_word_get0 (args, idx));
				if (ISSTRUCT || tmp=='E' || tmp=='B' || tmp=='r') {
					if (*fieldname == '(') {
						fmtname = fieldname+1;
						fieldname = strchr (fieldname, ')');
						if (fieldname) *fieldname++ = '\0';
						else {
							eprintf ("Missing closing parenthesis in format ')'\n");
							goto beach;
						}
					} else {
						eprintf ("Missing name (%s)\n", fieldname);
						goto beach;
					}
				}
				if (!args || (!field && ofield != MINUSONE)
						|| (field && !strncmp (field, fieldname, \
							strchr (field, '[')
						? strchr (field, '[') - field
						: strlen (field) + 1))) {
					mode |= R_PRINT_ISFIELD;
				} else {
					mode &= ~R_PRINT_ISFIELD;
				}

				/* There we handle specific element in array */
				if (field && (bracket = strchr (field, '[')) && mode & R_PRINT_ISFIELD) {
					char *end = strchr (field, ']');
					if (!end) {
						eprintf ("Missing closing bracket\n");
						goto beach;
					}
					*end = '\0';
					elem = r_num_math (NULL, bracket+1)+1; // +1 to handle 0 index easily
					for ( ; bracket < end; bracket++)
						*bracket = '\0';
					size += elem*ARRAYINDEX_COEF;
				} else {
					elem = -1;
				}
				idx++;
				if (MUSTSEE && !SEEVALUE && !flag) {
					p->cb_printf (namefmt, fieldname);
					flag = 1;
				}
			}

		feed_me_again:
			switch (isptr) {
			case PTRSEEK:
				{
				nexti = i + (p->bits/8);
				i = 0;
				if (tmp == '?' ) seeki = addr;
				memset (buf, '\0', len);
				if (MUSTSEE) {
					p->cb_printf ("(*0x%"PFMT64x")", addr);
				}
				isptr = (addr)? PTRBACK: NULLPTR;
				if (/*addr<(b+len) && addr>=b && */p->iob.read_at) { /* The test was here to avoid segfault in the next line,
						but len make it doesnt work... */
					p->iob.read_at (p->iob.io, (ut64)addr, buf, len-4);
					if ( (i + 3) < len || (i + 7) < len) {
						updateAddr (buf, i, endian, &addr, &addr64);
					} else {
						eprintf ("Likely a heap buffer overflow at %s at %d\n", __FILE__, __LINE__);
						goto beach;
					}
				} else {
					eprintf ("(SEGFAULT: cannot read memory at 0x%08"PFMT64x", Block: %s, blocksize: 0x%x)\n",
							addr, b, len);
					p->cb_printf ("\n");
					goto beach;
				}
				}
				break;
			case PTRBACK:
				// restore state after pointer seek
				i = nexti;
				memcpy (buf, b, len);
				isptr = NOPTR;
				arg--;
				continue;
			}
			if (tmp == 0 && last != '*') {
				break;
			}

			/* skip chars */
			switch (tmp) {
			case '*': // next char is a pointer
				isptr = PTRSEEK;
				arg++;
				tmp = *arg; //last;
				goto feed_me_again;
			case '+': // toggle view flags
				viewflags = !viewflags;
				continue;
			case 'e': // tmp swap endian
				endian ^= 1;
				continue;
			case ':': // skip 4 bytes
				if (size == -1) i+=4;
				else while (size--) i+=4;
				idx--;
				continue;
			case '.': // skip 1 byte
				i += (size == -1)? 1: size;
				idx--;
				continue;
			case 'p': // pointer reference
				if (*(arg+1) == '2') {
					p->bits = 16;
					arg++;
				} else if (*(arg+1) == '4') {
					p->bits = 32;
					arg++;
				} else if (*(arg+1) == '8') {
					p->bits = 64;
					arg++;
				}
				switch (p->bits) {
					case 16: tmp = 'w'; break;
					case 32: tmp = 'x'; break;
					default: tmp = 'q'; break;
				}
				break;
			}
			flag = 0;

			/* flags */
			if (mode & R_PRINT_SEEFLAGS && isptr != NULLPTR) {
				char *newname = NULL;
				if (!fieldname) {
					newname = fieldname = r_str_newf ("pf.%d", seeki);
				}
				if (mode & R_PRINT_UNIONMODE) {
					p->cb_printf ("f %s=0x%08"PFMT64x"\n", formatname, seeki);
					goto beach;
				} else if (tmp == '?') {
					p->cb_printf ("f %s.%s_", fmtname, fieldname);
				} else if (tmp == 'E') {
					p->cb_printf ("f %s=0x%08"PFMT64x"\n", fieldname, seeki);
				} else if (slide/STRUCTFLAG>0 && idx==1) {
					p->cb_printf ("%s=0x%08"PFMT64x"\n", fieldname, seeki);
				} else p->cb_printf ("f %s=0x%08"PFMT64x"\n", fieldname , seeki);
				if (newname) {
					free (newname);
					newname = fieldname = NULL;
				}
			}

			/* dot */
			if (mode & R_PRINT_DOT) {
				if (fieldname) {
					p->cb_printf ("|{0x%"PFMT64x"|%c|%s|<%s>",
						seeki, tmp, fieldname, fieldname);
				} else {
					p->cb_printf ("|{0x%"PFMT64x"|%c|",
						seeki, tmp);
				}
			}

			/* json */
			if (MUSTSEEJSON && mode & R_PRINT_JSON) {
				if (oldslide <= slide) {
					if (first) first = 0;
					else p->cb_printf (",");
				} else if (oldslide) {
					p->cb_printf ("]},");
				}
				p->cb_printf ("{\"name\":\"%s\",\"type\":\"", fieldname);
				if (ISSTRUCT) {
					p->cb_printf ("%s", fmtname);
				} else {
					p->cb_printf ("%c", tmp);
				}
				if (isptr) p->cb_printf ("*");
				p->cb_printf ("\",\"offset\":%d,\"value\":",(isptr)?(seek+nexti-(p->bits/8)):seek+i);
			}

			if (isptr == NULLPTR) {
				if (MUSTSEEJSON) p->cb_printf ("\"NULL\"}", tmp, seek+i);
				else if (MUSTSEE) p->cb_printf (" NULL\n");
				isptr = PTRBACK;
			} else
			/* format chars */
			// before to enter in the switch statement check buf boundaries due to  updateAddr
			// might go beyond its len and it's usually called in each of the following functions
			if (((i+3)<len) || (i+7)<len) {
				switch (tmp) {
				case 'u':
					i+= r_print_format_uleb (p, endian, mode, setval, seeki, buf, i, size);
					break;
				case 't':
					r_print_format_time (p, endian, mode, setval, seeki, buf, i, size);
					i+= (size==-1) ? 4 : 4*size;
					break;
				case 'q':
					r_print_format_quadword (p, endian, mode, setval, seeki, buf, i, size);
					i += (size==-1) ? 8 : 8*size;
					break;
				case 'b':
					r_print_format_byte (p, endian, mode, setval, seeki, buf, i, size);
					i+= (size==-1) ? 1 : size;
					break;
				case 'C':
					r_print_format_decchar (p, endian, mode,
						setval, seeki, buf, i, size);
					i+= (size==-1) ? 1 : size;
					break;
				case 'c':
					r_print_format_char (p, endian, mode,
						setval, seeki, buf, i, size);
					i+= (size==-1) ? 1 : size;
					break;
				case 'X':
					size = r_print_format_hexpairs (p, endian, mode,
						setval, seeki, buf, i, size);
					i += size;
					break;
				case 'T':
					if (r_print_format_10bytes (p, mode,
						setval, seeki, addr, buf) == 0)
						i += (size==-1) ? 4 : 4*size;
					break;
				case 'f':
					r_print_format_float (p, endian, mode, setval, seeki, buf, i, size);
					i += (size==-1) ? 4 : 4*size;
					break;
				case 'i':
				case 'd':
					r_print_format_hex (p, endian, mode, setval, seeki, buf, i, size);
					i+= (size==-1) ? 4 : 4*size;
					break;
				case 'D':
					i += r_print_format_disasm (p, seeki, size);
					break;
				case 'o':
					r_print_format_octal (p, endian, mode, setval, seeki, buf, i, size);
					i += (size==-1) ? 4 : 4*size;
					break;
				case 'x':
					r_print_format_hexflag (p, endian, mode, setval, seeki, buf, i, size);
					i += (size==-1) ? 4 : 4*size;
					break;
				case 'w':
					r_print_format_word (p, endian, mode, setval, seeki, buf, i, size);
					i += (size==-1) ? 2 : 2*size;
					break;
				case 'z': // zero terminated string
					r_print_format_nulltermstring (p, len, endian, mode, setval, seeki, buf, i, size);
					if (size == -1)
						i+=strlen((char*)buf+i)+1;
					else
						while (size--) i++;
					break;
				case 'Z': // zero terminated wide string
					r_print_format_nulltermwidestring (p, len, endian, mode, setval, seeki, buf, i, size);
					if (size == -1)
						i += r_wstr_clen((char*)(buf+i))*2+2;
					else
						while (size--) i+=2;
					break;
				case 's':
					if (r_print_format_string (p, seeki, addr64, addr, 0, mode) == 0)
						i += (size==-1) ? 4 : 4*size;
					break;
				case 'S':
					if (r_print_format_string (p, seeki, addr64, addr, 1, mode) == 0)
						i += (size==-1) ? 8 : 8*size;
					break;
				case 'B': // resolve bitfield
					if (size >= ARRAYINDEX_COEF) size %= ARRAYINDEX_COEF;
					r_print_format_bitfield (p, seeki, fmtname, fieldname, addr, mode, size);
					i+=(size==-1)?1:size;
					break;
				case 'E': // resolve enum
					if (size >= ARRAYINDEX_COEF) size %= ARRAYINDEX_COEF;
					r_print_format_enum (p, seeki, fmtname, fieldname, addr, mode, size);
					i += (size==-1)? 1: size;
					break;
				case 'r':
					r_print_format_register (p, mode, fmtname, setval);
					break;
				case '?':
					{
					int s = 0;
					char *nxtfield = NULL;
					if (size >= ARRAYINDEX_COEF) {
						elem = size / ARRAYINDEX_COEF - 1;
						size %= ARRAYINDEX_COEF;
					}
					if (!(mode & R_PRINT_ISFIELD)) nxtfield = MINUSONE;
					else if (field) nxtfield = strchr (ofield, '.');
					if (nxtfield != MINUSONE && nxtfield) nxtfield++;

					if (MUSTSEE) {
						if (!SEEVALUE) p->cb_printf ("\n");
					}
					if (MUSTSEEJSON) {
						if (isptr)
							p->cb_printf ("%d},", seeki);
						else
							p->cb_printf ("[");
					}
					if (mode & R_PRINT_SEEFLAGS) slide += STRUCTFLAG;
					oldslide = slide;
					slide += (isptr) ? STRUCTPTR : NESTEDSTRUCT;
					if (size == -1) {
						s = r_print_format_struct (p, seeki,
							buf+i, len-i, fmtname, slide,
							mode, setval, nxtfield);
						i+= (isptr) ? 4 : s;
					} else {
						if (mode & R_PRINT_ISFIELD)
							if (!SEEVALUE) p->cb_printf ("[\n");
						while (size--) {
							if (elem == -1 || elem == 0) {
								mode |= R_PRINT_MUSTSEE;
								if (elem == 0) elem = -2;
							} else {
								mode &= ~R_PRINT_MUSTSEE;
							}
							s = r_print_format_struct (p, seek+i,
								buf+i, len-i, fmtname, slide, mode, setval, nxtfield);
							if ((MUSTSEE || MUSTSEEJSON) && size != 0 && elem == -1) {
								p->cb_printf (",");
								if (MUSTSEE) p->cb_printf ("\n");
							}
							if (elem > -1) elem--;
							i+= (isptr) ? 4 : s;
						}
						if (mode & R_PRINT_ISFIELD)
							if (!SEEVALUE) p->cb_printf ("]");
						if (MUSTSEEJSON) p->cb_printf ("]}]}");
					}
					oldslide = slide;
					slide -= (isptr) ? STRUCTPTR : NESTEDSTRUCT;
					if (mode & R_PRINT_SEEFLAGS) {
						oldslide = slide;
						slide-=STRUCTFLAG;
					}
					break;
					}
				default:
					/* ignore unknown chars */
					invalid = 1;
					break;
				} //switch
			} else {
				eprintf ("Likely a heap buffer overflow in %s at %d\n", __FILE__, __LINE__);
				goto beach;
			}
			if (mode & R_PRINT_DOT) {
				p->cb_printf ("}");
			}
			if (viewflags && p->offname) {
				const char *s = p->offname (p->user, seeki);
				if (s) p->cb_printf ("@(%s)", s);
				s = p->offname (p->user, addr);
				if (s) p->cb_printf ("*(%s)", s);
			}
			if (tmp != 'D' && !invalid && !fmtname && MUSTSEE) {
				p->cb_printf ("\n");
			}
			last = tmp;
		}
		if (otimes > 1) {
			if (MUSTSEEJSON) p->cb_printf ("]");
			else p->cb_printf ("}\n");
		}
		arg = orig;
		oldslide = 0;
	}
	if (mode & R_PRINT_JSON && slide==0) {
		p->cb_printf("]\n");
	}
	if (mode & R_PRINT_DOT) {
		p->cb_printf ("\"];\n}\n");
		// TODO: show nested structs and field reference lines
	}
beach:
	free (oarg);
	free (buf);
	free (field);
	free (args);
	return i;
}
