apprentice.c revision 75937
1/*
2 * apprentice - make one pass through /etc/magic, learning its secrets.
3 *
4 * Copyright (c) Ian F. Darwin, 1987.
5 * Written by Ian F. Darwin.
6 *
7 * This software is not subject to any license of the American Telephone
8 * and Telegraph Company or of the Regents of the University of California.
9 *
10 * Permission is granted to anyone to use this software for any purpose on
11 * any computer system, and to alter it and redistribute it freely, subject
12 * to the following restrictions:
13 *
14 * 1. The author is not responsible for the consequences of use of this
15 *    software, no matter how awful, even if they arise from flaws in it.
16 *
17 * 2. The origin of this software must not be misrepresented, either by
18 *    explicit claim or by omission.  Since few users ever read sources,
19 *    credits must appear in the documentation.
20 *
21 * 3. Altered versions must be plainly marked as such, and must not be
22 *    misrepresented as being the original software.  Since few users
23 *    ever read sources, credits must appear in the documentation.
24 *
25 * 4. This notice may not be removed or altered.
26 */
27
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <ctype.h>
32#include <errno.h>
33#ifdef QUICK
34#include <fcntl.h>
35#include <sys/stat.h>
36#include <sys/mman.h>
37#endif
38#include "file.h"
39
40#ifndef	lint
41FILE_RCSID("@(#)$Id: apprentice.c,v 1.39 2001/04/24 14:40:24 christos Exp $")
42#endif	/* lint */
43
44#define	EATAB {while (isascii((unsigned char) *l) && \
45		      isspace((unsigned char) *l))  ++l;}
46#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
47			tolower((unsigned char) (l)) : (l))
48/*
49 * Work around a bug in headers on Digital Unix.
50 * At least confirmed for: OSF1 V4.0 878
51 */
52#if defined(__osf__) && defined(__DECC)
53#ifdef MAP_FAILED
54#undef MAP_FAILED
55#endif
56#endif
57
58#ifndef MAP_FAILED
59#define MAP_FAILED (void *) -1
60#endif
61
62#ifndef MAP_FILE
63#define MAP_FILE 0
64#endif
65
66#ifdef __EMX__
67  char PATHSEP=';';
68#else
69  char PATHSEP=':';
70#endif
71
72
73static int getvalue	__P((struct magic *, char **));
74static int hextoint	__P((int));
75static char *getstr	__P((char *, char *, int, int *));
76static int parse	__P((struct magic **, uint32 *, char *, int));
77static void eatsize	__P((char **));
78static int apprentice_1	__P((const char *, int));
79static int apprentice_file	__P((struct magic **, uint32 *,
80    const char *, int));
81#ifdef QUICK
82static void byteswap	__P((struct magic *, uint32));
83static void bs1		__P((struct magic *));
84static uint16 swap2	__P((uint16));
85static uint32 swap4	__P((uint32));
86static char * mkdbname	__P((const char *));
87static int apprentice_map	__P((struct magic **, uint32 *,
88    const char *, int));
89static int apprentice_compile	__P((struct magic **, uint32 *,
90    const char *, int));
91#endif
92
93static int maxmagic = 0;
94
95struct mlist mlist;
96
97
98/*
99 * Handle one file.
100 */
101static int
102apprentice_1(fn, action)
103	const char *fn;
104	int action;
105{
106	struct magic *magic = NULL;
107	uint32 nmagic = 0;
108	struct mlist *ml;
109	int rv = -1;
110
111#ifdef QUICK
112	if (action == COMPILE) {
113		rv = apprentice_file(&magic, &nmagic, fn, action);
114		if (rv == 0)
115			return apprentice_compile(&magic, &nmagic, fn, action);
116		else
117			return rv;
118	}
119	if ((rv = apprentice_map(&magic, &nmagic, fn, action)) != 0)
120		(void)fprintf(stderr, "%s: Using regular magic file `%s'\n",
121		    progname, fn);
122#endif
123
124	if (rv != 0)
125		rv = apprentice_file(&magic, &nmagic, fn, action);
126
127	if (rv != 0)
128		return rv;
129
130	if ((ml = malloc(sizeof(*ml))) == NULL) {
131		(void) fprintf(stderr, "%s: Out of memory.\n", progname);
132		if (action == CHECK)
133			return -1;
134	}
135
136	if (magic == NULL || nmagic == 0)
137		return rv;
138
139	ml->magic = magic;
140	ml->nmagic = nmagic;
141
142	mlist.prev->next = ml;
143	ml->prev = mlist.prev;
144	ml->next = &mlist;
145	mlist.prev = ml;
146
147	return rv;
148}
149
150
151int
152apprentice(fn, action)
153	const char *fn;			/* list of magic files */
154	int action;
155{
156	char *p, *mfn;
157	int file_err, errs = -1;
158
159	mlist.next = mlist.prev = &mlist;
160	mfn = malloc(strlen(fn)+1);
161	if (mfn == NULL) {
162		(void) fprintf(stderr, "%s: Out of memory.\n", progname);
163		if (action == CHECK)
164			return -1;
165		else
166			exit(1);
167	}
168	fn = strcpy(mfn, fn);
169
170	while (fn) {
171		p = strchr(fn, PATHSEP);
172		if (p)
173			*p++ = '\0';
174		file_err = apprentice_1(fn, action);
175		if (file_err > errs)
176			errs = file_err;
177		fn = p;
178	}
179	if (errs == -1)
180		(void) fprintf(stderr, "%s: couldn't find any magic files!\n",
181		    progname);
182	if (action == CHECK && errs)
183		exit(1);
184
185	free(mfn);
186	return errs;
187}
188
189/*
190 * parse from a file
191 */
192static int
193apprentice_file(magicp, nmagicp, fn, action)
194	struct magic **magicp;
195	uint32 *nmagicp;
196	const char *fn;			/* name of magic file */
197	int action;
198{
199	static const char hdr[] =
200		"cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
201	FILE *f;
202	char line[BUFSIZ+1];
203	int errs = 0;
204
205	f = fopen(fn, "r");
206	if (f == NULL) {
207		if (errno != ENOENT)
208			(void) fprintf(stderr,
209			    "%s: can't read magic file %s (%s)\n",
210			    progname, fn, strerror(errno));
211		return -1;
212	}
213
214        maxmagic = MAXMAGIS;
215	*magicp = (struct magic *) calloc(sizeof(struct magic), maxmagic);
216	if (*magicp == NULL) {
217		(void) fprintf(stderr, "%s: Out of memory.\n", progname);
218		if (action == CHECK)
219			return -1;
220	}
221
222	/* parse it */
223	if (action == CHECK)	/* print silly verbose header for USG compat. */
224		(void) printf("%s\n", hdr);
225
226	for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) {
227		if (line[0]=='#')	/* comment, do not parse */
228			continue;
229		if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */
230			continue;
231		line[strlen(line)-1] = '\0'; /* delete newline */
232		if (parse(magicp, nmagicp, line, action) != 0)
233			errs = 1;
234	}
235
236	(void) fclose(f);
237	if (errs) {
238		free(*magicp);
239		*magicp = NULL;
240		*nmagicp = 0;
241	}
242	return errs;
243}
244
245/*
246 * extend the sign bit if the comparison is to be signed
247 */
248uint32
249signextend(m, v)
250	struct magic *m;
251	uint32 v;
252{
253	if (!(m->flag & UNSIGNED))
254		switch(m->type) {
255		/*
256		 * Do not remove the casts below.  They are
257		 * vital.  When later compared with the data,
258		 * the sign extension must have happened.
259		 */
260		case BYTE:
261			v = (char) v;
262			break;
263		case SHORT:
264		case BESHORT:
265		case LESHORT:
266			v = (short) v;
267			break;
268		case DATE:
269		case BEDATE:
270		case LEDATE:
271		case LONG:
272		case BELONG:
273		case LELONG:
274			v = (int32) v;
275			break;
276		case STRING:
277			break;
278		default:
279			magwarn("can't happen: m->type=%d\n",
280				m->type);
281			return -1;
282		}
283	return v;
284}
285
286/*
287 * parse one line from magic file, put into magic[index++] if valid
288 */
289static int
290parse(magicp, nmagicp, l, action)
291	struct magic **magicp;
292	uint32 *nmagicp;
293	char *l;
294	int action;
295{
296	int i = 0;
297	struct magic *m;
298	char *t, *s;
299
300#define ALLOC_INCR	200
301	if (*nmagicp + 1 >= maxmagic){
302		maxmagic += ALLOC_INCR;
303		if ((m = (struct magic *) realloc(*magicp,
304		    sizeof(struct magic) * maxmagic)) == NULL) {
305			(void) fprintf(stderr, "%s: Out of memory.\n",
306			    progname);
307			if (*magicp)
308				free(*magicp);
309			if (action == CHECK)
310				return -1;
311			else
312				exit(1);
313		}
314		*magicp = m;
315		memset(&(*magicp)[*nmagicp], 0, sizeof(struct magic)
316		    * ALLOC_INCR);
317	}
318	m = &(*magicp)[*nmagicp];
319	m->flag = 0;
320	m->cont_level = 0;
321
322	while (*l == '>') {
323		++l;		/* step over */
324		m->cont_level++;
325	}
326
327	if (m->cont_level != 0 && *l == '(') {
328		++l;		/* step over */
329		m->flag |= INDIR;
330	}
331	if (m->cont_level != 0 && *l == '&') {
332                ++l;            /* step over */
333                m->flag |= ADD;
334        }
335
336	/* get offset, then skip over it */
337	m->offset = (int) strtoul(l,&t,0);
338        if (l == t)
339		magwarn("offset %s invalid", l);
340        l = t;
341
342	if (m->flag & INDIR) {
343		m->in_type = LONG;
344		m->in_offset = 0;
345		/*
346		 * read [.lbs][+-]nnnnn)
347		 */
348		if (*l == '.') {
349			l++;
350			switch (*l) {
351			case 'l':
352				m->in_type = LELONG;
353				break;
354			case 'L':
355				m->in_type = BELONG;
356				break;
357			case 'h':
358			case 's':
359				m->in_type = LESHORT;
360				break;
361			case 'H':
362			case 'S':
363				m->in_type = BESHORT;
364				break;
365			case 'c':
366			case 'b':
367			case 'C':
368			case 'B':
369				m->in_type = BYTE;
370				break;
371			default:
372				magwarn("indirect offset type %c invalid", *l);
373				break;
374			}
375			l++;
376		}
377		s = l;
378		if (*l == '+' || *l == '-') l++;
379		if (isdigit((unsigned char)*l)) {
380			m->in_offset = strtoul(l, &t, 0);
381			if (*s == '-') m->in_offset = - m->in_offset;
382		}
383		else
384			t = l;
385		if (*t++ != ')')
386			magwarn("missing ')' in indirect offset");
387		l = t;
388	}
389
390
391	while (isascii((unsigned char)*l) && isdigit((unsigned char)*l))
392		++l;
393	EATAB;
394
395#define NBYTE		4
396#define NSHORT		5
397#define NLONG		4
398#define NSTRING 	6
399#define NDATE		4
400#define NBESHORT	7
401#define NBELONG		6
402#define NBEDATE		6
403#define NLESHORT	7
404#define NLELONG		6
405#define NLEDATE		6
406
407	if (*l == 'u') {
408		++l;
409		m->flag |= UNSIGNED;
410	}
411
412	/* get type, skip it */
413	if (strncmp(l, "char", NBYTE)==0) {	/* HP/UX compat */
414		m->type = BYTE;
415		l += NBYTE;
416	} else if (strncmp(l, "byte", NBYTE)==0) {
417		m->type = BYTE;
418		l += NBYTE;
419	} else if (strncmp(l, "short", NSHORT)==0) {
420		m->type = SHORT;
421		l += NSHORT;
422	} else if (strncmp(l, "long", NLONG)==0) {
423		m->type = LONG;
424		l += NLONG;
425	} else if (strncmp(l, "string", NSTRING)==0) {
426		m->type = STRING;
427		l += NSTRING;
428	} else if (strncmp(l, "date", NDATE)==0) {
429		m->type = DATE;
430		l += NDATE;
431	} else if (strncmp(l, "beshort", NBESHORT)==0) {
432		m->type = BESHORT;
433		l += NBESHORT;
434	} else if (strncmp(l, "belong", NBELONG)==0) {
435		m->type = BELONG;
436		l += NBELONG;
437	} else if (strncmp(l, "bedate", NBEDATE)==0) {
438		m->type = BEDATE;
439		l += NBEDATE;
440	} else if (strncmp(l, "leshort", NLESHORT)==0) {
441		m->type = LESHORT;
442		l += NLESHORT;
443	} else if (strncmp(l, "lelong", NLELONG)==0) {
444		m->type = LELONG;
445		l += NLELONG;
446	} else if (strncmp(l, "ledate", NLEDATE)==0) {
447		m->type = LEDATE;
448		l += NLEDATE;
449	} else {
450		magwarn("type %s invalid", l);
451		return -1;
452	}
453	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
454	if (*l == '&') {
455		++l;
456		m->mask = signextend(m, strtoul(l, &l, 0));
457		eatsize(&l);
458	} else if (STRING == m->type) {
459		m->mask = 0L;
460		if (*l == '/') {
461			while (!isspace(*++l)) {
462				switch (*l) {
463				case CHAR_IGNORE_LOWERCASE:
464					m->mask |= STRING_IGNORE_LOWERCASE;
465					break;
466				case CHAR_COMPACT_BLANK:
467					m->mask |= STRING_COMPACT_BLANK;
468					break;
469				case CHAR_COMPACT_OPTIONAL_BLANK:
470					m->mask |=
471					    STRING_COMPACT_OPTIONAL_BLANK;
472					break;
473				default:
474					magwarn("string extension %c invalid",
475					    *l);
476					return -1;
477				}
478			}
479		}
480	} else
481		m->mask = ~0L;
482	EATAB;
483
484	switch (*l) {
485	case '>':
486	case '<':
487	/* Old-style anding: "0 byte &0x80 dynamically linked" */
488	case '&':
489	case '^':
490	case '=':
491  		m->reln = *l;
492  		++l;
493		if (*l == '=') {
494		   /* HP compat: ignore &= etc. */
495		   ++l;
496		}
497		break;
498	case '!':
499		if (m->type != STRING) {
500			m->reln = *l;
501			++l;
502			break;
503		}
504		/* FALL THROUGH */
505	default:
506		if (*l == 'x' && isascii((unsigned char)l[1]) &&
507		    isspace((unsigned char)l[1])) {
508			m->reln = *l;
509			++l;
510			goto GetDesc;	/* Bill The Cat */
511		}
512  		m->reln = '=';
513		break;
514	}
515  	EATAB;
516
517	if (getvalue(m, &l))
518		return -1;
519	/*
520	 * TODO finish this macro and start using it!
521	 * #define offsetcheck {if (offset > HOWMANY-1)
522	 *	magwarn("offset too big"); }
523	 */
524
525	/*
526	 * now get last part - the description
527	 */
528GetDesc:
529	EATAB;
530	if (l[0] == '\b') {
531		++l;
532		m->nospflag = 1;
533	} else if ((l[0] == '\\') && (l[1] == 'b')) {
534		++l;
535		++l;
536		m->nospflag = 1;
537	} else
538		m->nospflag = 0;
539	while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
540		/* NULLBODY */;
541
542	if (action == CHECK) {
543		mdump(m);
544	}
545	++(*nmagicp);		/* make room for next */
546	return 0;
547}
548
549/*
550 * Read a numeric value from a pointer, into the value union of a magic
551 * pointer, according to the magic type.  Update the string pointer to point
552 * just after the number read.  Return 0 for success, non-zero for failure.
553 */
554static int
555getvalue(m, p)
556	struct magic *m;
557	char **p;
558{
559	int slen;
560
561	if (m->type == STRING) {
562		*p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
563		m->vallen = slen;
564	} else
565		if (m->reln != 'x') {
566			m->value.l = signextend(m, strtoul(*p, p, 0));
567			eatsize(p);
568		}
569	return 0;
570}
571
572/*
573 * Convert a string containing C character escapes.  Stop at an unescaped
574 * space or tab.
575 * Copy the converted version to "p", returning its length in *slen.
576 * Return updated scan pointer as function result.
577 */
578static char *
579getstr(s, p, plen, slen)
580	char	*s;
581	char	*p;
582	int	plen, *slen;
583{
584	char	*origs = s, *origp = p;
585	char	*pmax = p + plen - 1;
586	int	c;
587	int	val;
588
589	while ((c = *s++) != '\0') {
590		if (isspace((unsigned char) c))
591			break;
592		if (p >= pmax) {
593			fprintf(stderr, "String too long: %s\n", origs);
594			break;
595		}
596		if(c == '\\') {
597			switch(c = *s++) {
598
599			case '\0':
600				goto out;
601
602			default:
603				*p++ = (char) c;
604				break;
605
606			case 'n':
607				*p++ = '\n';
608				break;
609
610			case 'r':
611				*p++ = '\r';
612				break;
613
614			case 'b':
615				*p++ = '\b';
616				break;
617
618			case 't':
619				*p++ = '\t';
620				break;
621
622			case 'f':
623				*p++ = '\f';
624				break;
625
626			case 'v':
627				*p++ = '\v';
628				break;
629
630			/* \ and up to 3 octal digits */
631			case '0':
632			case '1':
633			case '2':
634			case '3':
635			case '4':
636			case '5':
637			case '6':
638			case '7':
639				val = c - '0';
640				c = *s++;  /* try for 2 */
641				if(c >= '0' && c <= '7') {
642					val = (val<<3) | (c - '0');
643					c = *s++;  /* try for 3 */
644					if(c >= '0' && c <= '7')
645						val = (val<<3) | (c-'0');
646					else
647						--s;
648				}
649				else
650					--s;
651				*p++ = (char)val;
652				break;
653
654			/* \x and up to 2 hex digits */
655			case 'x':
656				val = 'x';	/* Default if no digits */
657				c = hextoint(*s++);	/* Get next char */
658				if (c >= 0) {
659					val = c;
660					c = hextoint(*s++);
661					if (c >= 0)
662						val = (val << 4) + c;
663					else
664						--s;
665				} else
666					--s;
667				*p++ = (char)val;
668				break;
669			}
670		} else
671			*p++ = (char)c;
672	}
673out:
674	*p = '\0';
675	*slen = p - origp;
676	return s;
677}
678
679
680/* Single hex char to int; -1 if not a hex char. */
681static int
682hextoint(c)
683	int c;
684{
685	if (!isascii((unsigned char) c))
686		return -1;
687	if (isdigit((unsigned char) c))
688		return c - '0';
689	if ((c >= 'a')&&(c <= 'f'))
690		return c + 10 - 'a';
691	if (( c>= 'A')&&(c <= 'F'))
692		return c + 10 - 'A';
693	return -1;
694}
695
696
697/*
698 * Print a string containing C character escapes.
699 */
700void
701showstr(fp, s, len)
702	FILE *fp;
703	const char *s;
704	int len;
705{
706	char	c;
707
708	for (;;) {
709		c = *s++;
710		if (len == -1) {
711			if (c == '\0')
712				break;
713		}
714		else  {
715			if (len-- == 0)
716				break;
717		}
718		if(c >= 040 && c <= 0176)	/* TODO isprint && !iscntrl */
719			(void) fputc(c, fp);
720		else {
721			(void) fputc('\\', fp);
722			switch (c) {
723
724			case '\n':
725				(void) fputc('n', fp);
726				break;
727
728			case '\r':
729				(void) fputc('r', fp);
730				break;
731
732			case '\b':
733				(void) fputc('b', fp);
734				break;
735
736			case '\t':
737				(void) fputc('t', fp);
738				break;
739
740			case '\f':
741				(void) fputc('f', fp);
742				break;
743
744			case '\v':
745				(void) fputc('v', fp);
746				break;
747
748			default:
749				(void) fprintf(fp, "%.3o", c & 0377);
750				break;
751			}
752		}
753	}
754}
755
756/*
757 * eatsize(): Eat the size spec from a number [eg. 10UL]
758 */
759static void
760eatsize(p)
761	char **p;
762{
763	char *l = *p;
764
765	if (LOWCASE(*l) == 'u')
766		l++;
767
768	switch (LOWCASE(*l)) {
769	case 'l':    /* long */
770	case 's':    /* short */
771	case 'h':    /* short */
772	case 'b':    /* char/byte */
773	case 'c':    /* char/byte */
774		l++;
775		/*FALLTHROUGH*/
776	default:
777		break;
778	}
779
780	*p = l;
781}
782
783#ifdef QUICK
784/*
785 * handle an mmaped file.
786 */
787static int
788apprentice_map(magicp, nmagicp, fn, action)
789	struct magic **magicp;
790	uint32 *nmagicp;
791	const char *fn;
792	int action;
793{
794	int fd;
795	struct stat st;
796	uint32 *ptr;
797	uint32 version;
798	int needsbyteswap;
799	char *dbname = mkdbname(fn);
800
801	if ((fd = open(dbname, O_RDONLY)) == -1)
802		return -1;
803
804	if (fstat(fd, &st) == -1) {
805		(void)fprintf(stderr, "%s: Cannot stat `%s' (%s)\n",
806		    progname, dbname, strerror(errno));
807		goto error;
808	}
809
810	if ((*magicp = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
811	    MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
812		(void)fprintf(stderr, "%s: Cannot map `%s' (%s)\n",
813		    progname, dbname, strerror(errno));
814		goto error;
815	}
816	(void)close(fd);
817	fd = -1;
818	ptr = (uint32 *) *magicp;
819	if (*ptr != MAGICNO) {
820		if (swap4(*ptr) != MAGICNO) {
821			(void)fprintf(stderr, "%s: Bad magic in `%s'\n",
822			    progname, dbname);
823			goto error;
824		}
825		needsbyteswap = 1;
826	} else
827		needsbyteswap = 0;
828	if (needsbyteswap)
829		version = swap4(ptr[1]);
830	else
831		version = ptr[1];
832	if (version != VERSIONNO) {
833		(void)fprintf(stderr,
834		    "%s: version mismatch (%d != %d) in `%s'\n",
835		    progname, version, VERSIONNO, dbname);
836		goto error;
837	}
838	*nmagicp = (st.st_size / sizeof(struct magic)) - 1;
839	(*magicp)++;
840	if (needsbyteswap)
841		byteswap(*magicp, *nmagicp);
842	return 0;
843
844error:
845	if (fd != -1)
846		(void)close(fd);
847	if (*magicp)
848		(void)munmap(*magicp, (size_t)st.st_size);
849	else {
850		*magicp = NULL;
851		*nmagicp = 0;
852	}
853	return -1;
854}
855
856/*
857 * handle an mmaped file.
858 */
859static int
860apprentice_compile(magicp, nmagicp, fn, action)
861	struct magic **magicp;
862	uint32 *nmagicp;
863	const char *fn;
864	int action;
865{
866	int fd;
867	char *dbname = mkdbname(fn);
868	static const uint32 ar[] = {
869	    MAGICNO, VERSIONNO
870	};
871
872	if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
873		(void)fprintf(stderr, "%s: Cannot open `%s' (%s)\n",
874		    progname, dbname, strerror(errno));
875		return -1;
876	}
877
878	if (write(fd, ar, sizeof(ar)) != sizeof(ar)) {
879		(void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
880		    progname, dbname, strerror(errno));
881		return -1;
882	}
883
884	if (lseek(fd, sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) {
885		(void)fprintf(stderr, "%s: error seeking `%s' (%s)\n",
886		    progname, dbname, strerror(errno));
887		return -1;
888	}
889
890	if (write(fd, *magicp,  sizeof(struct magic) * *nmagicp)
891	    != sizeof(struct magic) * *nmagicp) {
892		(void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
893		    progname, dbname, strerror(errno));
894		return -1;
895	}
896
897	(void)close(fd);
898	return 0;
899}
900
901/*
902 * make a dbname
903 */
904char *
905mkdbname(fn)
906	const char *fn;
907{
908	static const char ext[] = ".mgc";
909	static char *buf = NULL;
910	size_t len = strlen(fn) + sizeof(ext) + 1;
911	if (buf == NULL)
912		buf = malloc(len);
913	else
914		buf = realloc(buf, len);
915	(void)strcpy(buf, fn);
916	(void)strcat(buf, ext);
917	return buf;
918}
919
920/*
921 * Byteswap an mmap'ed file if needed
922 */
923static void
924byteswap(magic, nmagic)
925	struct magic *magic;
926	uint32 nmagic;
927{
928	uint32 i;
929	for (i = 0; i < nmagic; i++)
930		bs1(&magic[i]);
931}
932
933/*
934 * swap a short
935 */
936static uint16
937swap2(sv)
938	uint16 sv;
939{
940	uint16 rv;
941	uint8 *s = (uint8 *) &sv;
942	uint8 *d = (uint8 *) &rv;
943	d[0] = s[1];
944	d[1] = s[0];
945	return rv;
946}
947
948/*
949 * swap an int
950 */
951static uint32
952swap4(sv)
953	uint32 sv;
954{
955	uint32 rv;
956	uint8 *s = (uint8 *) &sv;
957	uint8 *d = (uint8 *) &rv;
958	d[0] = s[3];
959	d[1] = s[2];
960	d[2] = s[1];
961	d[3] = s[0];
962	return rv;
963}
964
965/*
966 * byteswap a single magic entry
967 */
968static
969void bs1(m)
970	struct magic *m;
971{
972	m->cont_level = swap2(m->cont_level);
973	m->offset = swap4(m->offset);
974	m->in_offset = swap4(m->in_offset);
975	if (m->type != STRING)
976		m->value.l = swap4(m->value.l);
977	m->mask = swap4(m->mask);
978}
979#endif
980