OpenSolaris_b135/cmd/file/magicutils.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*	Copyright (c) 1987, 1988 Microsoft Corporation	*/
/*	  All Rights Reserved	*/

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <inttypes.h>
#include <sys/types.h>
#include <libintl.h>

/*
 *	Types
 */

#define	BYTE	1
#define	SHORT	2
#define	LONG	4
#define	LLONG	8
#define	UBYTE	16
#define	USHORT	32
#define	ULONG	64
#define	ULLONG	128
#define	STR	256

/*
 *	Opcodes
 */

#define	EQ	0
#define	GT	1
#define	LT	2
#define	STRC	3	/* string compare */
#define	ANY	4
#define	AND	5
#define	NSET	6	/* True if bit is not set */
#define	SUB	64	/* or'ed in, SUBstitution string, for example */
			/* %ld, %s, %lo mask: with bit 6 on, used to locate */
			/* print formats */
/*
 *	Misc
 */

#define	BSZ	128
#define	NENT	200

/*
 *	Structure of magic file entry
 */

struct	entry	{
	char		e_level;	/* 0 or 1 */
	off_t		e_off;		/* in bytes */
	uint32_t	e_type;		/* BYTE, SHORT, STR, et al */
	char		e_opcode;	/* EQ, GT, LT, ANY, AND, NSET */
	uint64_t	e_mask;		/* if non-zero, mask value with this */
	union	{
		uint64_t	num;
		char		*str;
	}	e_value;
	const char	*e_str;
};

/* Non-localized string giving name of command.  Defined in file.c */
extern const char	*File;

typedef	struct entry	Entry;

static Entry	*mtab1;	/* 1st magic table, applied before default tests */

	/*
	 * 2nd magic table, includes default tests and magic entries
	 * to be applied after default position-sensitive tests
	 */
static Entry	*mtab2;

static Entry	*mend1;	/* one past last-allocated entry in mtab1 */
static Entry	*mend2;	/* one past last-allocated entry in mtab2 */

static Entry	*ep1;	/* current entry in mtab1 */
static Entry	*ep2;	/* current entry in mtab2 */

static char *
getstr(char *p, char *file)
{
	char	*newstr;
	char	*s;
	long	val;
	int	base;

	newstr = (char *)malloc((strlen(p) + 1) * sizeof (char));
	if (newstr == NULL) {
		int err = errno;
		(void) fprintf(stderr, gettext("%s: malloc failed: %s\n"),
		    File, strerror(err));
		return (NULL);
	}

	s = newstr;
	while (*p != '\0') {
		if (*p != '\\') {
			*s++ = *p++;
			continue;
		}
		p++;
		if (*p == '\0')
			break;
		if (isdigit(*p)) {
			if (*p == '0' && (*(p+1) == 'x' || *(p+1) == 'X')) {
				/* hex */
				base = 16;
			} else {
				base = 8;
			}
			errno = 0;
			val = strtol(p, &p, base);
			if (val > UCHAR_MAX || val < 0 || errno != 0) {
				(void) fprintf(stderr, gettext("%s: %s: magic "
				    "table invalid string value\n"), File,
				    file);
				return (NULL);
			}
			*s++ = (char)val;
		} else {
			/* escape the character */
			switch (*p) {
			case 'n':
				*s = '\n';
				break;
			case 'r':
				*s = '\r';
				break;
			case 'a':
				*s = '\a';
				break;
			case 'b':
				*s = '\b';
				break;
			case 'f':
				*s = '\f';
				break;
			case 't':
				*s = '\t';
				break;
			case 'v':
				*s = '\v';
				break;
			default:
				*s = *p;
				break;
			}
			p++;
			s++;
		}
	}
	*s = '\0';
	return (newstr);
}

/*
 * f_mkmtab - fills mtab array of magic table entries with
 *	values from the file magfile.
 *	May be called more than once if multiple magic
 * 	files were specified.
 *	Stores entries sequentially in one of two magic
 *	tables: mtab1, if first = 1; mtab2 otherwise.
 *
 *	If -c option is specified, cflg is non-zero, and
 *	f_mkmtab() reports on errors in the magic file.
 *
 *	Two magic tables may need to be created.  The first
 *	one (mtab1) contains magic entries to be checked before
 *	the programmatic default position-sensitive tests in
 *	def_position_tests().
 *	The second one (mtab2) should start with the default
 *	/etc/magic file entries and is to be checked after
 *	the programmatic default position-sensitive tests in
 *	def_position_tests().  The parameter "first" would
 *	be 1 for the former set of tables, 0 for the latter
 *	set of magic tables.
 *	No mtab2 should be created if file will not be
 *	applying default tests; in that case, all magic table
 *	entries should be in mtab1.
 *
 *	f_mkmtab returns 0 on success, -1 on error.  The calling
 *	program is not expected to proceed after f_mkmtab()
 *	returns an error.
 */

int
f_mkmtab(char *magfile, int cflg, int first)
{
	Entry	*mtab;	/* generic magic table pointer */
	Entry	*ep;	/* current magic table entry */
	Entry	*mend;	/* one past last-allocated entry of mtab */
	FILE	*fp;
	int	lcnt = 0;
	char	buf[BSZ];
	size_t	tbsize;
	size_t	oldsize;

	if (first) {
		mtab = mtab1;
		mend = mend1;
		ep = ep1;
	} else {
		mtab = mtab2;
		mend = mend2;
		ep = ep2;
	}

	/* mtab may have been allocated on a previous f_mkmtab call */
	if (mtab == (Entry *)NULL) {
		if ((mtab = calloc(sizeof (Entry), NENT)) == NULL) {
			int err = errno;
			(void) fprintf(stderr, gettext("%s: malloc "
			    "failed: %s\n"), File, strerror(err));
			return (-1);
		}

		ep = mtab;
		mend = &mtab[NENT];
	}

	errno = 0;
	if ((fp = fopen(magfile, "r")) == NULL) {
		int err = errno;
		(void) fprintf(stderr, gettext("%s: %s: cannot open magic "
		    "file: %s\n"), File, magfile, err ? strerror(err) : "");
		return (-1);
	}
	while (fgets(buf, BSZ, fp) != NULL) {
		char	*p = buf;
		char	*p2;
		char	*p3;
		char	opc;

		/*
		 * ensure we have one extra entry allocated
		 * to mark end of the table, after the while loop
		 */
		if (ep >= (mend - 1)) {
			oldsize = mend - mtab;
			tbsize = (NENT + oldsize) * sizeof (Entry);
			if ((mtab = realloc(mtab, tbsize)) == NULL) {
				int err = errno;
				(void) fprintf(stderr, gettext("%s: malloc "
				    "failed: %s\n"), File, strerror(err));
				return (-1);
			} else {
				(void) memset(mtab + oldsize, 0,
				    sizeof (Entry) * NENT);
				mend = &mtab[tbsize / sizeof (Entry)];
				ep = &mtab[oldsize-1];
			}
		}

		lcnt++;
		if (*p == '\n' || *p == '#')
			continue;


			/* LEVEL */
		if (*p == '>') {
			ep->e_level = 1;
			p++;
		}
			/* OFFSET */
		p2 = strchr(p, '\t');
		if (p2 == NULL) {
			if (cflg)
				(void) fprintf(stderr, gettext("%s: %s: format "
				    "error, no tab after %s on line %d\n"),
				    File, magfile, p, lcnt);
			continue;
		}
		*p2++ = NULL;
		ep->e_off = strtol((const char *)p, (char **)NULL, 0);
		while (*p2 == '\t')
			p2++;
			/* TYPE */
		p = p2;
		p2 = strchr(p, '\t');
		if (p2 == NULL) {
			if (cflg)
				(void) fprintf(stderr, gettext("%s: %s: format "
				    "error, no tab after %s on line %d\n"),
				    File, magfile, p, lcnt);
			continue;
		}
		*p2++ = NULL;
		p3 = strchr(p, '&');
		if (p3 != NULL) {
			*p3++ = '\0';
			ep->e_mask = strtoull((const char *)p3, (char **)NULL,
			    0);	/* returns 0 or ULLONG_MAX on error */
		} else {
			ep->e_mask = 0ULL;
		}
		switch (*p) {
			case 'd':
				if (*(p+1) == NULL) {
					/* d */
					ep->e_type = LONG;
				} else if (*(p+2) == NULL) {	/* d? */
					switch (*(p+1)) {
						case 'C':
						case '1':
							/* dC, d1 */
							ep->e_type = BYTE;
							break;
						case 'S':
						case '2':
							/* dS, d2 */
							ep->e_type = SHORT;
							break;
						case 'I':
						case 'L':
						case '4':
							/* dI, dL, d4 */
							ep->e_type = LONG;
							break;
						case '8':
							/* d8 */
							ep->e_type = LLONG;
							break;
						default:
							ep->e_type = LONG;
							break;
					}
				}
				break;
			case 'l':
				if (*(p+1) == 'l') {	/* llong */
					ep->e_type = LLONG;
				} else { 		/* long */
					ep->e_type = LONG;
				}
				break;
			case 's':
				if (*(p+1) == 'h') {
					/* short */
					ep->e_type = SHORT;
				} else {
					/* s or string */
					ep->e_type = STR;
				}
				break;
			case 'u':
				if (*(p+1) == NULL) {
					/* u */
					ep->e_type = ULONG;
				} else if (*(p+2) == NULL) {	/* u? */
					switch (*(p+1)) {
						case 'C':
						case '1':
							/* uC, u1 */
							ep->e_type = UBYTE;
							break;
						case 'S':
						case '2':
							/* uS, u2 */
							ep->e_type = USHORT;
							break;
						case 'I':
						case 'L':
						case '4':
							/* uI, uL, u4 */
							ep->e_type = ULONG;
							break;
						case '8':
							/* u8 */
							ep->e_type = ULLONG;
							break;
						default:
							ep->e_type = ULONG;
							break;
					}
				} else { /* u?* */
					switch (*(p+1)) {
					case 'b':	/* ubyte */
						ep->e_type = UBYTE;
						break;
					case 's':	/* ushort */
						ep->e_type = USHORT;
						break;
					case 'l':
						if (*(p+2) == 'l') {
							/* ullong */
							ep->e_type = ULLONG;
						} else {
							/* ulong */
							ep->e_type = ULONG;
						}
						break;
					default:
						/* default, same as "u" */
						ep->e_type = ULONG;
						break;
					}
				}
				break;
			default:
				/* retain (undocumented) default type */
				ep->e_type = BYTE;
				break;
		}
		if (ep->e_type == 0) {
			ep->e_type = BYTE;	/* default */
		}
		while (*p2 == '\t')
			p2++;
			/* OP-VALUE */
		p = p2;
		p2 = strchr(p, '\t');
		if (p2 == NULL) {
			if (cflg)
				(void) fprintf(stderr, gettext("%s: %s: format "
				    "error, no tab after %s on line %d\n"),
				    File, magfile, p, lcnt);
			continue;
		}
		*p2++ = NULL;
		if (ep->e_type != STR) {
			opc = *p++;
			switch (opc) {
			case '=':
				ep->e_opcode = EQ;
				break;

			case '>':
				ep->e_opcode = GT;
				break;

			case '<':
				ep->e_opcode = LT;
				break;

			case 'x':
				ep->e_opcode = ANY;
				break;

			case '&':
				ep->e_opcode = AND;
				break;

			case '^':
				ep->e_opcode = NSET;
				break;
			default:	/* EQ (i.e. 0) is default	*/
				p--;	/* since global ep->e_opcode=0	*/
			}
		}
		if (ep->e_opcode != ANY) {
			if (ep->e_type != STR) {
				ep->e_value.num = strtoull((const char *)p,
				    (char **)NULL, 0);
			} else if ((ep->e_value.str =
			    getstr(p, magfile)) == NULL) {
				return (-1);
			}
		}
		p2 += strspn(p2, "\t");
			/* STRING */
		if ((ep->e_str = strdup(p2)) == NULL) {
			int err = errno;
			(void) fprintf(stderr, gettext("%s: malloc "
			    "failed: %s\n"), File, strerror(err));
			return (-1);
		} else {
			if ((p = strchr(ep->e_str, '\n')) != NULL)
				*p = '\0';
			if (strchr(ep->e_str, '%') != NULL)
				ep->e_opcode |= SUB;
		}
		ep++;
	}	/* end while (fgets) */

	ep->e_off = -1L;	/* mark end of table */
	if (first) {
		mtab1 = mtab;
		mend1 = mend;
		ep1 = ep;
	} else {
		mtab2 = mtab;
		mend2 = mend;
		ep2 = ep;
	}
	if (fclose(fp) != 0) {
		int err = errno;
		(void) fprintf(stderr, gettext("%s: fclose failed: %s\n"),
		    File, strerror(err));
		return (-1);
	}
	return (0);
}

/*
 * Check for Magic Table entries in the file.
 *
 * Since there may be two sets of magic tables, first = 1
 * for the first magic table (mtab1) and 0 for the second magic
 * table (mtab2).
 */
int
f_ckmtab(char *buf, int bufsize, int first)
{
	int 		result;
	Entry		*mtab;
	Entry		*ep;
	char		*p;
	int		lev1 = 0;

	uint16_t	u16_val;
	uint32_t	u32_val;
	uint64_t	u64_val;

	if (first) {
		mtab = mtab1;
	} else {
		mtab = mtab2;
	}

	if (mtab == (Entry *)NULL) {
		return (0);	/* no magic file tests in this table */
	}

	for (ep = mtab; ep->e_off != -1L; ep++) {  /* -1 offset marks end of */
		if (lev1) {			/* valid magic file entries */
			if (ep->e_level != 1)
				break;
		} else if (ep->e_level == 1) {
			continue;
		}
		if (ep->e_off > (off_t)bufsize)
			continue;
		p = &buf[ep->e_off];
		switch (ep->e_type) {
		case STR:
		{
			if (strncmp(p, ep->e_value.str,
			    strlen(ep->e_value.str)))
				continue;
			if (lev1) {
				(void) putchar(' ');
			}
			if (ep->e_opcode & SUB)
				(void) printf(ep->e_str,
				    ep->e_value.str);
			else
				(void) printf(ep->e_str);
			lev1 = 1;
			continue;
			/*
			 * We've matched the string and printed the message;
			 * no STR processing occurs beyond this point.
			 */
		}

		case BYTE:
		case UBYTE:
			u64_val = (uint64_t)(uint8_t)(*p);
			break;

		case SHORT:
		case USHORT:
			(void) memcpy(&u16_val, p, sizeof (uint16_t));
			u64_val = (uint64_t)u16_val;
			break;

		case LONG:
		case ULONG:
			(void) memcpy(&u32_val, p, sizeof (uint32_t));
			u64_val = (uint64_t)u32_val;
			break;

		case LLONG:
		case ULLONG:
			(void) memcpy(&(u64_val), p, sizeof (uint64_t));
			break;

		}

		if (ep->e_mask) {
			u64_val &= ep->e_mask;
		}

		/*
		 * Compare the values according to the size and sign
		 * of the type.  For =, &, and ^ operators, the sign
		 * does not have any effect, so these are always compared
		 * unsigned.  Only for < and > operators is the
		 * sign significant.
		 * If the file value was masked, the compare should
		 * be unsigned.
		 */
		switch (ep->e_opcode & ~SUB) {
		case EQ:
			switch (ep->e_type) {
			case BYTE:
			case UBYTE:
				if ((uint8_t)u64_val !=
				    (uint8_t)(ep->e_value.num))
					continue;
				break;
			case SHORT:
			case USHORT:
				if ((uint16_t)u64_val !=
				    (uint16_t)(ep->e_value.num))
					continue;
				break;
			case LONG:
			case ULONG:
				if ((uint32_t)u64_val !=
				    (uint32_t)(ep->e_value.num))
					continue;
				break;
			case LLONG:
			case ULLONG:
				if (u64_val != ep->e_value.num)
					continue;
				break;
			default:
				continue;
			}
			break;
		case GT:
			switch (ep->e_type) {
			case BYTE:
				if (ep->e_mask == 0) {
					if ((int8_t)u64_val <=
					    (int8_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case UBYTE:
				if ((uint8_t)u64_val <=
				    (uint8_t)(ep->e_value.num))
					continue;
				break;
			case SHORT:
				if (ep->e_mask == 0) {
					if ((int16_t)u64_val <=
					    (int16_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case USHORT:
				if ((uint16_t)u64_val <=
				    (uint16_t)(ep->e_value.num))
					continue;
				break;
			case LONG:
				if (ep->e_mask == 0) {
					if ((int32_t)u64_val <=
					    (int32_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case ULONG:
				if ((uint32_t)u64_val <=
				    (uint32_t)(ep->e_value.num))
					continue;
				break;
			case LLONG:
				if (ep->e_mask == 0) {
					if ((int64_t)u64_val <=
					    (int64_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case ULLONG:
				if (u64_val <= ep->e_value.num)
					continue;
				break;
			default:
				continue;
			}
			break;
		case LT:
			switch (ep->e_type) {
			case BYTE:
				if (ep->e_mask == 0) {
					if ((int8_t)u64_val >=
					    (int8_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case UBYTE:
				if ((uint8_t)u64_val >=
				    (uint8_t)(ep->e_value.num))
					continue;
				break;
			case SHORT:
				if (ep->e_mask == 0) {
					if ((int16_t)u64_val >=
					    (int16_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case USHORT:
				if ((uint16_t)u64_val >=
				    (uint16_t)(ep->e_value.num))
					continue;
				break;
			case LONG:
				if (ep->e_mask == 0) {
					if ((int32_t)u64_val >=
					    (int32_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case ULONG:
				if ((uint32_t)u64_val >=
				    (uint32_t)(ep->e_value.num))
					continue;
				break;
			case LLONG:
				if (ep->e_mask == 0) {
					if ((int64_t)u64_val >=
					    (int64_t)(ep->e_value.num))
						continue;
					break;
				}
				/*FALLTHROUGH*/
			case ULLONG:
				if (u64_val >= ep->e_value.num)
					continue;
				break;
			default:
				continue;
			}
			break;
		case AND:
			switch (ep->e_type) {
			case BYTE:
			case UBYTE:
				if (((uint8_t)u64_val &
				    (uint8_t)(ep->e_value.num)) ==
				    (uint8_t)(ep->e_value.num))
					break;
				continue;
			case SHORT:
			case USHORT:
				if (((uint16_t)u64_val &
				    (uint16_t)(ep->e_value.num)) ==
				    (uint16_t)(ep->e_value.num))
					break;
				continue;
			case LONG:
			case ULONG:
				if (((uint32_t)u64_val &
				    (uint32_t)(ep->e_value.num)) ==
				    (uint32_t)(ep->e_value.num))
					break;
				continue;
			case LLONG:
			case ULLONG:
				if ((u64_val & ep->e_value.num) ==
				    ep->e_value.num)
					break;
				continue;
			default:
				continue;
			}
			break;
		case NSET:
			switch (ep->e_type) {
			case BYTE:
			case UBYTE:
				if (((uint8_t)u64_val &
				    (uint8_t)(ep->e_value.num)) !=
				    (uint8_t)(ep->e_value.num))
					break;
				continue;
			case SHORT:
			case USHORT:
				if (((uint16_t)u64_val &
				    (uint16_t)(ep->e_value.num)) !=
				    (uint16_t)(ep->e_value.num))
					break;
				continue;
			case LONG:
			case ULONG:
				if (((uint32_t)u64_val &
				    (uint32_t)(ep->e_value.num)) !=
				    (uint32_t)(ep->e_value.num))
					break;
				continue;
			case LLONG:
			case ULLONG:
				if ((u64_val & ep->e_value.num) !=
				    ep->e_value.num)
					break;
				continue;
			default:
				continue;
			}
			break;
		case ANY:	/* matches anything */
			break;
		default:	/* shouldn't occur; ignore it */
			continue;
		}
		if (lev1)
			(void) putchar(' ');
		if (ep->e_opcode & SUB) {
			switch (ep->e_type) {
			case LLONG:
#ifdef XPG4
				if (ep->e_mask == 0) {
					(void) printf(ep->e_str,
						(int64_t)u64_val);
					break;
				}
#endif	/* XPG4 */
				/*FALLTHROUGH*/
			case ULLONG:
				(void) printf(ep->e_str, u64_val);
				break;
			case LONG:
#ifdef XPG4
				if (ep->e_mask == 0) {
					(void) printf(ep->e_str,
						(int32_t)u64_val);
					break;
				}
#endif	/* XPG4 */
				/*FALLTHROUGH*/
			case ULONG:
				(void) printf(ep->e_str,
				    (uint32_t)u64_val);
				break;
			case SHORT:
#ifdef XPG4
				if (ep->e_mask == 0) {
					(void) printf(ep->e_str,
						(int16_t)u64_val);
					break;
				}
#endif	/* XPG4 */
				/*FALLTHROUGH*/
			case USHORT:
				(void) printf(ep->e_str,
				    (uint16_t)u64_val);
				break;
			case BYTE:
#ifdef XPG4
				if (ep->e_mask == 0) {
					(void) printf(ep->e_str,
						(int8_t)u64_val);
					break;
				}
#endif	/* XPG4 */
				/*FALLTHROUGH*/
			case UBYTE:
				(void) printf(ep->e_str,
				    (uint8_t)u64_val);
				break;
			case STR:
				/*
				 * Note: Currently can't get type
				 * STR here because we already
				 * did a 'continue' out of the
				 * loop earlier for case STR
				 */
				break;
			}
		} else
			(void) printf(ep->e_str);
		lev1 = 1;
	}
	result = lev1 ? (int)(1 + ep - mtab) : 0;

	return (result);
}

static void
showstr(char *s, int width)
{
	char c;

	while ((c = *s++) != '\0')
		if (c >= 040 && c < 0176) {
			(void) putchar(c);
			width--;
		} else {
			(void) putchar('\\');
			switch (c) {

			case '\n':
				(void) putchar('n');
				width -= 2;
				break;

			case '\r':
				(void) putchar('r');
				width -= 2;
				break;

			case '\a':
				(void) putchar('a');
				width -= 2;
				break;

			case '\b':
				(void) putchar('b');
				width -= 2;
				break;

			case '\t':
				(void) putchar('t');
				width -= 2;
				break;

			case '\f':
				(void) putchar('f');
				width -= 2;
				break;

			case '\v':
				(void) putchar('v');
				width -= 2;
				break;

			default:
				(void) printf("%.3o", c & 0377);
				width -= 4;
				break;
			}
		}
	while (width >= 0) {
		(void) putchar(' ');
		width--;
	};
}

static char *
type_to_name(Entry *ep)
{
	static char buf[20];
	char	*s;

	switch (ep->e_type) {
	case BYTE:
		s = "byte";
		break;
	case SHORT:
		s = "short";
		break;
	case LONG:
		s = "long";
		break;
	case LLONG:
		s = "llong";
		break;
	case UBYTE:
		s = "ubyte";
		break;
	case USHORT:
		s = "ushort";
		break;
	case ULONG:
		s = "ulong";
		break;
	case ULLONG:
		s = "ullong";
		break;
	case STR:
		return ("string");
	default:
		/* more of an emergency measure .. */
		(void) sprintf(buf, "%d", ep->e_type);
		return (buf);
	}
	if (ep->e_mask) {
		(void) snprintf(buf, sizeof (buf), "%s&0x%llx", s, ep->e_mask);
		return (buf);
	} else
		return (s);
}

static char
op_to_name(char op)
{
	char c;

	switch (op & ~SUB) {

	case EQ:
	case STRC:
		c = '=';
		break;

	case GT:
		c = '>';
		break;

	case LT:
		c = '<';
		break;

	case ANY:
		c = 'x';
		break;

	case AND:
		c = '&';
		break;

	case NSET:
		c = '^';
		break;

	default:
		c = '?';
		break;
	}

	return (c);
}

/*
 * f_prtmtab - Prints out a header, then entries from both magic
 *	tables, mtab1 and mtab2, if any exist.
 */
void
f_prtmtab(void)
{
	Entry	*mtab;
	Entry	*ep;
	int	count;

	(void) printf("%-7s %-7s %-10s %-7s %-11s %s\n",
		"level", "off", "type", "opcode", "value", "string");
	for (mtab = mtab1, count = 1; count <= 2; count++, mtab = mtab2) {
		if (mtab == (Entry *)NULL) {
			continue;
		}
		for (ep = mtab; ep->e_off != -1L; ep++) {
			(void) printf("%-7d %-7ld %-10s %-7c ",
			    ep->e_level,
			    ep->e_off, type_to_name(ep),
			    op_to_name(ep->e_opcode));
			if (ep->e_type == STR) {
				showstr(ep->e_value.str, 10);
			} else {	/* numeric */
				(void) printf("%-#11llo", ep->e_value.num);
			}
			(void) printf(" %s", ep->e_str);
			if (ep->e_opcode & SUB)
				(void) printf("\tsubst");
			(void) printf("\n");
		}
	}
}

intmax_t
f_getmaxoffset(int first)
{
	Entry *mtab;
	Entry *ep;
	intmax_t cur;
	intmax_t max = 0;

	if (first) {
		mtab = mtab1;
	} else {
		mtab = mtab2;
	}
	if (mtab == (Entry *)NULL) {
		return (0);
	}
	for (ep = mtab; ep->e_off != -1L; ep++) {
		cur = ep->e_off;
		switch (ep->e_type) {
		case STR:
			cur += strlen(ep->e_value.str);
			break;
		case BYTE:
		case UBYTE:
			cur += sizeof (uchar_t);
			break;
		case SHORT:
		case USHORT:
			cur += sizeof (uint16_t);
			break;
		case LONG:
		case ULONG:
			cur += sizeof (uint32_t);
			break;
		case LLONG:
		case ULLONG:
			cur += sizeof (uint64_t);
			break;
		}
		if (cur <= INT_MAX && cur > max) {
			max = cur;
		}
	}

	return (max);
}