#pragma ident	"@(#)adbsub.c	1.8	98/01/28 SMI" 

/*
 * Copyright (c) 1983-1998 by Sun Microsystems, Inc.
 * All rights reserved.
 */

/*
 * Subroutines to be called by adbgen2.c, the C program generated
 * by adbgen1.c.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>

off_t last_off;
int warnings = 1;
int warns = 0;

/*
 * User claims offset is ok.
 * This usually follows call to another script, which we cannot handle.
 */
void
offsetok(void)
{
	last_off = -1;
}

/*
 * Get adb.s dot to the right offset.
 */
void
offset(off_t off)
{
	off_t off_diff;

	if (last_off == -1) {
		last_off = off;
		return;
	}
	off_diff = off - last_off;
	if (off_diff) {
		if (off_diff > 0) {
			if (off_diff > 1) {
				printf("%ld", off_diff);
			}
			printf("+");
		}
		if (off_diff < 0) {
			if (off_diff < -1) {
				printf("%ld", -off_diff);
			}
			printf("-");
		}
	}
	last_off = off;
}

/*
 * Emit the format command, return the size.
 */
int
do_fmt(char *acp)
{
	int rcount, width, sum, i;
	char *cp;

	cp = acp;
	sum = rcount = 0;
	do {
		while (*cp >= '0' && *cp <= '9') {
			rcount = rcount * 10 + *cp++ - '0';
		}
		if (rcount == 0) {
			rcount = 1;
		}
		switch (*cp) {
		case 'e':
		case 'E':
		case 'F':
		case 'g':
		case 'G':
		case 'J':
			width = 8;
			break;
		case 'K':
#ifdef	_LP64
			width = 8;
#else	/* _LP64 */
			width = 4;
#endif	/* _LP64 */
			break;
		case 'X':
		case 'O':
		case 'Q':
		case 'D':
		case 'U':
		case 'f':
		case 'Y':
		case 'p':
		case 'P':
			width = 4;
			break;
		case 'x':
		case 'o':
		case 'q':
		case 'd':
		case 'u':
			width = 2;
			break;
		case 'v':
		case 'V':
		case 'b':
		case 'B':
		case 'c':
		case 'C':
		case '+':
			width = 1;
			break;
		case 'I':
		case 'a':
		case 'A':
		case 't':
		case 'r':
		case 'n':
			width = 0;
			break;
		case '-':
			width = -1;
			break;
		case 's':
		case 'S':
		case 'i':
			if (warnings) {
				fprintf(stderr,
				"Unknown format size \"%s\", assuming zero\n",
				acp);
				warns++;
			}
			width = 0;
			break;
		default:
			fprintf(stderr, "Unknown format size: %s\n", acp);
			exit(1);
		}
		for (i = 0; i < rcount; i++) {
			putchar(*cp);
		}
		cp++;
		sum += width * rcount;
	} while (*cp);
	return (sum);
}

/*
 * Format struct member, checking size.
 */
void
format(char *name, size_t size, char *fmt)
{
	int fs;

	fs = do_fmt(fmt);
	if (fs != size && warnings) {
		fprintf(stderr,
			"warning: \"%s\" size is %ld, \"%s\" width is %d\n",
			name, size, fmt, fs);
		warns++;
	}
	last_off += fs;
}

/*
 * Get the value at offset based on base.
 */
void
indirect(off_t offset, size_t size, char *base, char *member)
{
	if (size == 8 || size == 4) {
		if (offset == 0) {
			printf("*%s", base);
		} else {
			printf("*(%s+0t%ld)", base, offset);
		}
	} else if (size == 2) {
		if (offset == 2) {
			printf("(*%s&0xffff)", base);
		} else {
			printf("(*(%s+0t%ld)&0xffff)", base, offset - 2);
		}
	} else if (size == 1) {
		if (offset == 3) {
			printf("(*%s&0xff)", base);
		} else {
			if ((offset & 0x1) == 0x1) {
				printf("(*(%s+0t%ld)&0xff)", base, offset - 3);
			} else {
				printf("((*(%s+0t%ld)&0xff00)/0x100)",
				    base, offset - 2);
			}
		}
	} else {
		fprintf(stderr, "Indirect size %ld not 1, 2, or 4: %s\n",
		    size, member);
		exit(1);
	}
}
