text: /*
   * 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 <ctype.h>
  #include <stdlib.h>
  #include <time.h>
  
  #include "file.h"
  
  
  int debug = 0;
  
  
  static int match	__P((unsigned char *, int));
  static int mget		__P((union VALUETYPE *,
  			     unsigned char *, struct magic *, int));
  static int mcheck	__P((union VALUETYPE *, struct magic *));
  static int mprint	__P((union VALUETYPE *, struct magic *, char *buf));
  static void mdebug	__P((int, char *, int));
  static int mconvert	__P((union VALUETYPE *, struct magic *));
  
  /*
   * 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.
   */
  /*ARGSUSED1*/		/* nbytes passed for regularity, maybe need later */
  int
  softmagic(buf, nbytes)
  	unsigned char *buf;
  	int nbytes;
  {
  	if (match(buf, nbytes))	return 1;
  	else return 0;
  }
  
  /*
   * Go through the whole list, stopping if you find a match.  Process all
   * the continuations of that match before returning.
   *
   * We support multi-level continuations:
   *
   *	At any time when processing a successful top-level match, there is a
   *	current continuation level; it represents the level of the last
   *	successfully matched continuation.
   *
   *	Continuations above that level are skipped as, if we see one, it
   *	means that the continuation that controls them - i.e, the
   *	lower-level continuation preceding them - failed to match.
   *
   *	Continuations below that level are processed as, if we see one,
   *	it means we've finished processing or skipping higher-level
   *	continuations under the control of a successful or unsuccessful
   *	lower-level continuation, and are now seeing the next lower-level
   *	continuation and should process it.  The current continuation
   *	level reverts to the level of the one we're seeing.
   *
   *	Continuations at the current level are processed as, if we see
   *	one, there's no lower-level continuation that may have failed.
   *
   *	If a continuation matches, we bump the current continuation level
   *	so that higher-level continuations are processed.
   */
  static int
  match(s, nbytes)
  	unsigned char	*s;
  	int nbytes;
  {
  	int magindex = 0;
  	int cont_level = 0;
  	int need_separator = 0;
  	union VALUETYPE p;
  	static int *tmpoff = NULL;
  	static size_t tmplen = 0;
  	int oldoff = 0;
  	int returnval = 0; /* if a match is found it is set to 1*/
  	int firstline = 1; /* a flag to print X\n  X\n- X */
  
  	if (tmpoff == NULL)
  		if ((tmpoff = (int *) malloc(tmplen = 20)) == NULL)
  			fprintf(stderr, "out of memory\n");
  
  	for (magindex = 0; magindex < nmagic; magindex++) {
  		/* if main entry matches, print it... */
  		if (!mget(&p, s, &magic[magindex], nbytes) ||
  		    !mcheck(&p, &magic[magindex])) {
  			    /* 
  			     * main entry didn't match,
  			     * flush its continuations
  			     */
  			    while (magindex < nmagic &&
  			    	   magic[magindex + 1].cont_level != 0)
  			    	   magindex++;
  			    continue;
  		}
  
  		if (! firstline) { /* we found another match */
  			/* put a newline and '-' to do some simple formatting*/
  			printf("\n- ");
  		}
  
  		tmpoff[cont_level] = mprint(&p, &magic[magindex], s);
  		/*
  		 * If we printed something, we'll need to print
  		 * a blank before we print something else.
  		 */
  		if (magic[magindex].desc[0])
  			need_separator = 1;
  		/* and any continuations that match */
  		if (++cont_level >= tmplen)
  			if ((tmpoff = (int *) realloc(tmpoff,
  						       tmplen += 20)) == NULL)
  				fprintf(stderr, "out of memory\n");
  		while (magic[magindex+1].cont_level != 0 && 
  		       ++magindex < nmagic) {
  			if (cont_level >= magic[magindex].cont_level) {
  				if (cont_level > magic[magindex].cont_level) {
  					/*
  					 * We're at the end of the level
  					 * "cont_level" continuations.
  					 */
  					cont_level = magic[magindex].cont_level;
  				}
  				if (magic[magindex].flag & ADD) {
  					oldoff=magic[magindex].offset;
  					magic[magindex].offset += tmpoff[cont_level-1];
  				}
  				if (mget(&p, s, &magic[magindex], nbytes) &&
  				    mcheck(&p, &magic[magindex])) {
  					/*
  					 * This continuation matched.
  					 * Print its message, with
  					 * a blank before it if
  					 * the previous item printed
  					 * and this item isn't empty.
  					 */
  					/* space if previous printed */
  					if (need_separator
  					   && (magic[magindex].nospflag == 0)
  					   && (magic[magindex].desc[0] != '\0')
  					   ) {
  						(void) putchar(' ');
  						need_separator = 0;
  					}
  					tmpoff[cont_level] = mprint(&p, &magic[magindex], s);
  					if (magic[magindex].desc[0])
  						need_separator = 1;
  
  					/*
  					 * If we see any continuations
  					 * at a higher level,
  					 * process them.
  					 */
  					if (++cont_level >= tmplen)
  						if ((tmpoff = 
  						    (int *) realloc(tmpoff,
  						    tmplen += 20)) == NULL)
  							fprintf(stderr, "out of memory\n");
  				}
  				if (magic[magindex].flag & ADD) {
  					 magic[magindex].offset = oldoff;
  				}
  			}
  		}
  		firstline = 0;
  		returnval = 1;
                  return 1; /* don't keep searching */
  	}
  	return returnval;  /* This is hit if -k is set or there is no match */
  }
  
  static int
  mprint(p, m, buf)
  	union VALUETYPE *p;
  	struct magic *m;
          char *buf;
  {
  #define printf(a, b)  (sprintf(buf, a, b))
  
  	char *pp, *rt;
  	uint v;
  	time_t curtime;
  	int t=0 ;
  
  
    	switch (m->type) {
    	case BYTE:
  		v = p->b;
  		v = signextend(m, v) & m->mask;
  		(void) printf(m->desc, (unsigned char) v);
  		t = m->offset + sizeof(char);
  		break;
  
    	case SHORT:
    	case BESHORT:
    	case LESHORT:
  		v = p->h;
  		v = signextend(m, v) & m->mask;
  		(void) printf(m->desc, (unsigned short) v);
  		t = m->offset + sizeof(short);
  		break;
  
    	case LONG:
    	case BELONG:
    	case LELONG:
  		v = p->l;
  		v = signextend(m, v) & m->mask;
  		(void) printf(m->desc, (unsigned int) v);
  		t = m->offset + sizeof(int);
    		break;
  
    	case STRING:
  		if (m->reln == '=') {
  			(void) printf(m->desc, m->value.s);
  			t = m->offset + strlen(m->value.s);
  		}
  		else {
  			if (*m->value.s == '\0') {
  				char *cp = strchr(p->s,'\n');
  				if (cp)
  					*cp = '\0';
  			}
  			(void) printf(m->desc, p->s);
  			t = m->offset + strlen(p->s);
  		}
  		break;
  
  	case DATE:
  	case BEDATE:
  	case LEDATE:
  		curtime = p->l;
  		pp = ctime(&curtime);
  		if ((rt = strchr(pp, '\n')) != NULL)
  			*rt = '\0';
  		(void) printf(m->desc, pp);
  		t = m->offset + sizeof(time_t);
  		break;
  
  	default:
  		fprintf(stderr, "invalid m->type (%d) in mprint().\n", m->type);
  		/*NOTREACHED*/
  	}
  	return(t);
  #undef printf
  }
  
  /*
   * Convert the byte order of the data we are looking at
   */
  static int
  mconvert(p, m)
  	union VALUETYPE *p;
  	struct magic *m;
  {
  	switch (m->type) {
  	case BYTE:
  	case SHORT:
  	case LONG:
  	case DATE:
  		return 1;
  	case STRING:
  		{
  			char *ptr;
  
  			/* Null terminate and eat the return */
  			p->s[sizeof(p->s) - 1] = '\0';
  			if ((ptr = strchr(p->s, '\n')) != NULL)
  				*ptr = '\0';
  			return 1;
  		}
  	case BESHORT:
  		p->h = (short)((p->hs[0]<<8)|(p->hs[1]));
  		return 1;
  	case BELONG:
  	case BEDATE:
  		p->l = (int)
  		    ((p->hl[0]<<24)|(p->hl[1]<<16)|(p->hl[2]<<8)|(p->hl[3]));
  		return 1;
  	case LESHORT:
  		p->h = (short)((p->hs[1]<<8)|(p->hs[0]));
  		return 1;
  	case LELONG:
  	case LEDATE:
  		p->l = (int)
  		    ((p->hl[3]<<24)|(p->hl[2]<<16)|(p->hl[1]<<8)|(p->hl[0]));
  		return 1;
  	default:
  		fprintf(stderr, "invalid type %d in mconvert().\n", m->type);
  		return 0;
  	}
  }
  
  
  static void
  mdebug(offset, str, len)
  	int offset;
  	char *str;
  	int len;
  {
  	(void) fprintf(stderr, "mget @%d: ", offset);
  	showstr(stderr, (char *) str, len);
  	(void) fputc('\n', stderr);
  	(void) fputc('\n', stderr);
  }
  
  static int
  mget(p, s, m, nbytes)
  	union VALUETYPE* p;
  	unsigned char	*s;
  	struct magic *m;
  	int nbytes;
  {
  	int offset = m->offset;
  
  	if (offset + sizeof(union VALUETYPE) <= nbytes)
  		memcpy(p, s + offset, sizeof(union VALUETYPE));
  	else {
  		/*
  		 * the usefulness of padding with zeroes eludes me, it
  		 * might even cause problems
  		 */
  		int have = nbytes - offset;
  		memset(p, 0, sizeof(union VALUETYPE));
  		if (have > 0)
  			memcpy(p, s + offset, have);
  	}
  
  
  	if (debug) {
  		mdebug(offset, (char *) p, sizeof(union VALUETYPE));
  		mdump(m);
  	}
  
  	if (m->flag & INDIR) {
  
  		switch (m->in.type) {
  		case BYTE:
  			offset = p->b + m->in.offset;
  			break;
  		case BESHORT:
  		        offset = (short)((p->hs[0]<<8)|(p->hs[1]))+
  			          m->in.offset;
  			break;
  		case LESHORT:
  		        offset = (short)((p->hs[1]<<8)|(p->hs[0]))+
  			         m->in.offset;
  			break;
  		case SHORT:
  			offset = p->h + m->in.offset;
  			break;
  		case BELONG:
  		        offset = (int)((p->hl[0]<<24)|(p->hl[1]<<16)|
  					 (p->hl[2]<<8)|(p->hl[3]))+
  			         m->in.offset;
  			break;
  		case LELONG:
  		        offset = (int)((p->hl[3]<<24)|(p->hl[2]<<16)|
  					 (p->hl[1]<<8)|(p->hl[0]))+
  			         m->in.offset;
  			break;
  		case LONG:
  			offset = p->l + m->in.offset;
  			break;
  		}
  
  		if (offset + sizeof(union VALUETYPE) > nbytes)
  			return 0;
  
  		memcpy(p, s + offset, sizeof(union VALUETYPE));
  
  		if (debug) {
  			mdebug(offset, (char *) p, sizeof(union VALUETYPE));
  			mdump(m);
  		}
  	}
  	if (!mconvert(p, m))
  	  return 0;
  	return 1;
  }
  
  static int
  mcheck(p, m)
  	union VALUETYPE* p;
  	struct magic *m;
  {
  	unsigned int l = m->value.l;
  	unsigned int v;
  	int matched;
  
  	if ( (m->value.s[0] == 'x') && (m->value.s[1] == '\0') ) {
  		fprintf(stderr, "BOINK");
  		return 1;
  	}
  
  
  	switch (m->type) {
  	case BYTE:
  		v = p->b;
  		break;
  
  	case SHORT:
  	case BESHORT:
  	case LESHORT:
  		v = p->h;
  		break;
  
  	case LONG:
  	case BELONG:
  	case LELONG:
  	case DATE:
  	case BEDATE:
  	case LEDATE:
  		v = p->l;
  		break;
  
  	case STRING: {
  		/*
  		 * 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.
  		 */
  		unsigned char *a = (unsigned char*)m->value.s;
  		unsigned char *b = (unsigned char*)p->s;
  		int len = m->vallen;
  		l = 0;
  		v = 0;
  		if (0L == m->mask) { /* normal string: do it fast */
  			while (--len >= 0)
  				if ((v = *b++ - *a++) != '\0')
  					break; 
  		} else { /* combine the others */
  			while (--len >= 0) {
  				if ((m->mask & STRING_IGNORE_LOWERCASE) &&
  				    islower(*a)) {
  					if ((v = tolower(*b++) - *a++) != '\0')
  						break;
  				} else if ((m->mask & STRING_COMPACT_BLANK) && 
  				    isspace(*a)) { 
  					a++;
  					if (isspace(*b++)) {
  						while (isspace(*b))
  							b++;
  					} else {
  						v = 1;
  						break;
  					}
  				} else if (isspace(*a) &&
  				    (m->mask & STRING_COMPACT_OPTIONAL_BLANK)) {
  					a++;
  					while (isspace(*b))
  						b++;
  				} else {
  					if ((v = *b++ - *a++) != '\0')
  						break;
  				}
  			}
  		}
  		break;
  	}
  	default:
  		fprintf(stderr, "invalid type %d in mcheck().\n", m->type);
  		return 0;/*NOTREACHED*/
  	}
  
  	if(m->type != STRING)
  		v = signextend(m, v) & m->mask;
  
  	switch (m->reln) {
  	case 'x':
  		if (debug)
  			(void) fprintf(stderr, "%u == *any* = 1\n", v);
  		matched = 1;
  		break;
  
  	case '!':
  		matched = v != l;
  		if (debug)
  			(void) fprintf(stderr, "%u != %u = %d\n",
  				       v, l, matched);
  		break;
  
  	case '=':
  		matched = v == l;
  		if (debug)
  			(void) fprintf(stderr, "%u == %u = %d\n",
  				       v, l, matched);
  		break;
  
  	case '>':
  		if (m->flag & UNSIGNED) {
  			matched = v > l;
  			if (debug)
  				(void) fprintf(stderr, "%u > %u = %d\n",
  					       v, l, matched);
  		}
  		else {
  			matched = (int) v > (int) l;
  			if (debug)
  				(void) fprintf(stderr, "%d > %d = %d\n",
  					       v, l, matched);
  		}
  		break;
  
  	case '<':
  		if (m->flag & UNSIGNED) {
  			matched = v < l;
  			if (debug)
  				(void) fprintf(stderr, "%u < %u = %d\n",
  					       v, l, matched);
  		}
  		else {
  			matched = (int) v < (int) l;
  			if (debug)
  				(void) fprintf(stderr, "%d < %d = %d\n",
  					       v, l, matched);
  		}
  		break;
  
  	case '&':
  		matched = (v & l) == l;
  		if (debug)
  			(void) fprintf(stderr, "((%x & %x) == %x) = %d\n",
  				       v, l, l, matched);
  		break;
  
  	case '^':
  		matched = (v & l) != l;
  		if (debug)
  			(void) fprintf(stderr, "((%x & %x) != %x) = %d\n",
  				       v, l, l, matched);
  		break;
  
  	default:
  		matched = 0;
  		fprintf(stderr, "mcheck: can't happen: invalid relation %d.\n", m->reln);
  		break;/*NOTREACHED*/
  	}
  
  	return matched;
  }
  