
/*
 * softmagic - interpret variable magic from /etc/magic
 *
 * Copyright (c) Ian F. Darwin, 1987.
 * Written by Ian F. Darwin.
 *
 * This software is not subject to any license of the American Telephone
 * and Telegraph Company or of the Regents of the University of California.
 *
 * Permission is granted to anyone to use this software for any purpose on
 * any computer system, and to alter it and redistribute it freely, subject
 * to the following restrictions:
 *
 * 1. The author is not responsible for the consequences of use of this
 *    software, no matter how awful, even if they arise from flaws in it.
 *
 * 2. The origin of this software must not be misrepresented, either by
 *    explicit claim or by omission.  Since few users ever read sources,
 *    credits must appear in the documentation.
 *
 * 3. Altered versions must be plainly marked as such, and must not be
 *    misrepresented as being the original software.  Since few users
 *    ever read sources, credits must appear in the documentation.
 *
 * 4. This notice may not be removed or altered.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "file.h"

#ifndef	lint
static char *moduleid =
"@(#)/projects/agile/cvs/harvest/src/gatherer/essence/file/softmagic.c,v 1.3 1996/01/05 00:47:59 duane Exp";
#endif /* lint */

extern char *progname;
extern char *magicfile;		/* name of current /etc/magic or clone */
extern int debug, nmagic;
extern FILE *efopen();
extern struct magic magic[];
static int magindex;
#ifdef MAKE_LIBRARY_ONLY
static char typebuf[BUFSIZ];
#endif

/*
 * softmagic - lookup one file in database 
 * (already read from /etc/magic by apprentice.c).
 * Passed the name and FILE * of one file to be typed.
 */
#ifdef MAKE_LIBRARY_ONLY
char *
     softmagic(buf)
     char *buf;
{
	magindex = 0;
	typebuf[0] = '\0';
	if (match(buf))
		return (typebuf);

	return (NULL);
}
#else
softmagic(buf)
     char *buf;
{
	magindex = 0;
	if (match(buf))
		return 1;

	return 0;
}
#endif

/*
 * go through the whole list, stopping if you find a match.
 * Be sure to process every continuation of this match.
 */
match(s)
     char *s;
{
	while (magindex < nmagic) {
		/* if main entry matches, print it... */
		if (mcheck(s, &magic[magindex])) {
			mprint(&magic[magindex], s);
			/* and any continuations that match */
			while (magic[magindex + 1].contflag &&
			    magindex < nmagic) {
				++magindex;
				if (mcheck(s, &magic[magindex])) {
#ifndef MAKE_LIBRARY_ONLY
					(void) putchar(' ');
#endif
					mprint(&magic[magindex], s);
				}
			}
			return 1;	/* all through */
		} else {
			/* main entry didn't match, flush its continuations */
			while (magic[magindex + 1].contflag &&
			    magindex < nmagic) {
				++magindex;
			}
		}
		++magindex;	/* on to the next */
	}
	return 0;		/* no match at all */
}


#ifdef MAKE_LIBRARY_ONLY
static char typebuf2[BUFSIZ];
#define printf(a,b)					\
	if (typebuf[0] == '\0') {			\
		sprintf(typebuf, (a), (b));		\
	} else {					\
		sprintf(typebuf2, (a), (b));		\
		strcat(typebuf, " ");			\
		strcat(typebuf, typebuf2);		\
	}
#endif
mprint(m, s)
     struct magic *m;
     char *s;
{
	register union VALUETYPE *p = (union VALUETYPE *) (s + m->offset);
	char *pp, *strchr();

	switch (m->type) {
	case BYTE:
		printf(m->desc, p->b);
		break;
	case SHORT:
		printf(m->desc, p->h);
		break;
	case LONG:
		printf(m->desc, p->l);
		break;
	case STRING:
		if ((pp = strchr(p->s, '\n')) != NULL)
			*pp = '\0';
		printf(m->desc, p->s);
		break;
	default:
		warning("invalid m->type (%d) in mprint()", m->type);
	}
}
#ifdef MAKE_LIBRARY_ONLY
#undef printf
#endif

int mcheck(s, m)
     char *s;
     struct magic *m;
{
	register union VALUETYPE *p = (union VALUETYPE *) (s + m->offset);
	register long l = m->value.l;
	register long v;

	if (debug) {
		(void) printf("mcheck: %10.10s ", s);
		mdump(m);
	}
	switch (m->type) {
	case BYTE:
		v = p->b;
		break;
	case SHORT:
		v = p->h;
		break;
	case LONG:
		v = p->l;
		break;
	case STRING:
		l = 0;
		/* What we want here is:
		 * v = strncmp(m->value.s, p->s, m->vallen);
		 * but ignoring any nulls.  bcmp doesn't give -/+/0
		 * and isn't universally available anyway.
		 */
		{
			register unsigned char *a = (unsigned char *) m->value.s;
			register unsigned char *b = (unsigned char *) p->s;
			register int len = m->vallen;

			while (--len >= 0)
				if ((v = *b++ - *a++) != 0)
					break;
		}
		break;
	default:
		warning("invalid type %d in mcheck()", m->type);
		return 0;
	}

	switch (m->reln) {
	case '=':
		return v == l;
	case '>':
		return v > l;
	case '<':
		return v < l;
	case '&':
		return v & l;
	default:
		warning("mcheck: can't happen: invalid relation %d", m->reln);
		return 0;
	}
}
