/*
 * This file contains routines for printing various types of base 
 * values (byte, half word, word and double word).
 *
 * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */
#include <klib.h>

/* Some architectures will cause a fault if there is an attempt
 * to reference an incorrectly aligned memory address. Others 
 * have no trouble with this and do care how you treat memory
 * access. If an architecture needs to check for proper data
 * type alignment, it should set the global align_chk variable
 * to 1 duing libklib initialization...
 */
int align_chk = 0;

/*
 * kl_binary_print()
 */
void
kl_binary_print(uint64_t num, FILE *ofp)
{
	int i, pre = 1;

	for (i = 63; i >= 0; i--) {
		if (num & ((uint64_t)1 << i)) {
			fprintf(ofp, "1");
			if (pre) {
				pre = 0;
			}
		} else {
			if (!pre) { 
				fprintf(ofp, "0");
			}
		}
	}
	if (pre) {
		fprintf(ofp, "0");
	}
}

/*
 * kl_print_bit_value()
 *
 * x = byte_size, y = bit_size, z = bit_offset
 */
void
kl_print_bit_value(void *ptr, int x, int y, int z, int flags, FILE *ofp)
{
	uint64_t value;

	value = kl_get_bit_value(ptr, x, y, z);
	if (flags & K_HEX) {
		fprintf(ofp, "0x%"FMT64"x", value);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%"FMT64"o", value);
	} else if (flags & K_BINARY) {
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%"FMT64"d", value);
	}
}

/*
 * kl_print_char()
 */
void
kl_print_char(void *ptr, int flags, FILE *ofp)
{
	char c;

	if (flags & K_HEX) {
		fprintf(ofp, "0x%x", (*(char *)ptr) & 0xff);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%o", (*(char *)ptr) & 0xff);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(char *)ptr) & 0xff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		c = *(char *)ptr;

		fprintf(ofp, "\'\\%03o\'", (unsigned char)c);
		switch (c) {
			case '\a' :
				fprintf(ofp, " = \'\\a\'");
				break;	
			case '\b' :
				fprintf(ofp, " = \'\\b\'");
				break;	
			case '\t' :
				fprintf(ofp, " = \'\\t\'");
				break;	
			case '\n' :
				fprintf(ofp, " = \'\\n\'");
				break;	
			case '\f' :
				fprintf(ofp, " = \'\\f\'");
				break;	
			case '\r' :
				fprintf(ofp, " = \'\\r\'");
				break;	
			case '\e' :
				fprintf(ofp, " = \'\\e\'");
				break;	
			default :
				if( !iscntrl((unsigned char) c) ) {
					fprintf(ofp, " = \'%c\'", c);
				}
				break;
		}
	}
}

/*
 * kl_print_uchar()
 */
void
kl_print_uchar(void *ptr, int flags, FILE *ofp)
{
	if (flags & K_HEX) {
		fprintf(ofp, "0x%x", *(unsigned char *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%o", *(unsigned char *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(unsigned char *)ptr) & 0xff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%u", *(unsigned char *)ptr);
	}
}

/*
 * kl_print_int2()
 */
void
kl_print_int2(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core 
	 */
	if (align_chk && (uaddr_t)ptr % 2) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%hx", *(int16_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%ho", *(int16_t *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(int16_t *)ptr) & 0xffff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%hd", *(int16_t *)ptr);
	}
}

/*
 * kl_print_uint2()
 */
void
kl_print_uint2(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core 
	 */
	if (align_chk && (uaddr_t)ptr % 2) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%hx", *(uint16_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%ho", *(uint16_t *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(uint16_t *)ptr) & 0xffff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%hu", *(uint16_t *)ptr);
	}
}

/*
 * kl_print_int4()
 */
void
kl_print_int4(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core 
	 */
	if (align_chk && (uaddr_t)ptr % 4) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%x", *(int32_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%o", *(int32_t *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(int32_t *)ptr) & 0xffffffff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%d", *(int32_t *)ptr);
	}
}

/*
 * kl_print_uint4()
 */
void
kl_print_uint4(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core) 
	 */
	if (align_chk && (uaddr_t)ptr % 4) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%x", *(uint32_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%o", *(uint32_t *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(uint32_t *)ptr) & 0xffffffff;
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%u", *(uint32_t *)ptr);
	}
}

/*
 * kl_print_float4()
 */
void
kl_print_float4(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core)
	 */
	if (align_chk && (uaddr_t)ptr % 4) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	fprintf(ofp, "%f", *(float *)ptr);
}

/*
 * kl_print_int8()
 */
void
kl_print_int8(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core)
	 */
	if (align_chk && (uaddr_t)ptr % 8) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%"FMT64"x", *(int64_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%"FMT64"o", *(int64_t *)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(int64_t *)ptr);
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%"FMT64"d", *(int64_t *)ptr);
	}
}

/*
 * kl_print_uint8()
 */
void
kl_print_uint8(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core)
	 */
	if (align_chk && (uaddr_t)ptr % 8) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	if (flags & K_HEX) {
		fprintf(ofp, "0x%"FMT64"x", *(uint64_t *)ptr);
	} else if (flags & K_OCTAL) {
		fprintf(ofp, "0%"FMT64"o", *(uint64_t*)ptr);
	} else if (flags & K_BINARY) {
		uint64_t value = (*(uint64_t *)ptr);
		fprintf(ofp, "0b");
		kl_binary_print(value, ofp);
	} else {
		fprintf(ofp, "%"FMT64"u", *(uint64_t *)ptr);
	}
}

/*
 * kl_print_float8()
 */
void
kl_print_float8(void *ptr, int flags, FILE *ofp)
{
	/* Make sure the pointer is properly aligned (or we will 
	 * dump core)
	 */
	if (align_chk && (uaddr_t)ptr % 8) {
		fprintf(ofp, "ILLEGAL ADDRESS (%"FMTPTR"x)", UADDR(ptr));
	}
	fprintf(ofp, "%f", *(double *)ptr);
}

/*
 * kl_print_base()
 */
void
kl_print_base(void *ptr, int size, int encoding, int flags, FILE *ofp)
{
	switch (size) {

		case 1:
			if (encoding == ENC_UNSIGNED) {
				kl_print_uchar(ptr, flags, ofp);
			} else {
				kl_print_char(ptr, flags, ofp);
			}
			break;

		case 2:
			if (encoding == ENC_UNSIGNED) {
				kl_print_uint2(ptr, flags, ofp);
			} else {
				kl_print_int2(ptr, flags, ofp);
			}
			break;

		case 4:
			if (encoding == ENC_UNSIGNED) {
				kl_print_uint4(ptr, flags, ofp);
			} else if (encoding == ENC_FLOAT) {
				kl_print_float4(ptr, flags, ofp);
			} else {
				kl_print_int4(ptr, flags, ofp);
			}
			break;

		case 8:
			if (encoding == ENC_UNSIGNED) {
				kl_print_uint8(ptr, flags, ofp);
			} else if (encoding == ENC_FLOAT) {
				kl_print_float8(ptr, flags, ofp);
			} else {
				kl_print_int8(ptr, flags, ofp);
			}
			break;

		default:
			break;
	}
}

/*
 * kl_print_string()
 *
 *   print out a string, translating all embeded control characters
 *   (e.g., '\n' for newline, '\t' for tab, etc.)
 */
void
kl_print_string(char *s, FILE *ofp)
{
	char *sp, *cp;

	kl_reset_error();

	if (!(sp = s)) {
		klib_error = KLE_BAD_STRING;
		return;
	}

	while (sp) {
		if ((cp = strchr(sp, '\\'))) {
			switch (*(cp + 1)) {

				case 'n' :
					*cp++ = '\n';
					*cp++ = 0;
					break;

				case 't' :
					*cp++ = '\t';
					*cp++ = 0;
					break;

				default :
					if (*(cp + 1) == 0) {
						klib_error = KLE_BAD_STRING;
						return;
					}
					/* Change the '\' character to a zero 
					 * and then print the string (the rest 
					 * of the string will be picked
					 * up on the next pass).
					 */
					*cp++ = 0;
					break;
			}
			fprintf(ofp, "%s", sp);
			sp = cp;
		} else {
			fprintf(ofp, "%s", sp);
			sp = 0;
		}
	}
}

