1N/A/***********************************************************************
1N/A* *
1N/A* This software is part of the ast package *
1N/A* Copyright (c) 1985-2011 AT&T Intellectual Property *
1N/A* and is licensed under the *
1N/A* Common Public License, Version 1.0 *
1N/A* by AT&T Intellectual Property *
1N/A* *
1N/A* A copy of the License is available at *
1N/A* http://www.opensource.org/licenses/cpl1.0.txt *
1N/A* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
1N/A* *
1N/A* Information and Software Systems Research *
1N/A* AT&T Research *
1N/A* Florham Park NJ *
1N/A* *
1N/A* Glenn Fowler <gsf@research.att.com> *
1N/A* David Korn <dgk@research.att.com> *
1N/A* Phong Vo <kpv@research.att.com> *
1N/A* *
1N/A***********************************************************************/
1N/A#pragma prototyped
1N/A/*
1N/A * Glenn Fowler
1N/A * AT&T Research
1N/A *
1N/A * library interface to file
1N/A *
1N/A * the sum of the hacks {s5,v10,planix} is _____ than the parts
1N/A */
1N/A
1N/Astatic const char id[] = "\n@(#)$Id: magic library (AT&T Research) 2011-01-28 $\0\n";
1N/A
1N/Astatic const char lib[] = "libast:magic";
1N/A
1N/A#include <ast.h>
1N/A#include <ctype.h>
1N/A#include <ccode.h>
1N/A#include <dt.h>
1N/A#include <modex.h>
1N/A#include <error.h>
1N/A#include <regex.h>
1N/A#include <swap.h>
1N/A
1N/A#define T(m) (*m?ERROR_translate(NiL,NiL,lib,m):m)
1N/A
1N/A#define match(s,p) strgrpmatch(s,p,NiL,0,STR_LEFT|STR_RIGHT|STR_ICASE)
1N/A
1N/A#define MAXNEST 10 /* { ... } nesting limit */
1N/A#define MINITEM 4 /* magic buffer rounding */
1N/A
1N/Atypedef struct /* identifier dictionary entry */
1N/A{
1N/A const char name[16]; /* identifier name */
1N/A int value; /* identifier value */
1N/A Dtlink_t link; /* dictionary link */
1N/A} Info_t;
1N/A
1N/Atypedef struct Edit /* edit substitution */
1N/A{
1N/A struct Edit* next; /* next in list */
1N/A regex_t* from; /* from pattern */
1N/A} Edit_t;
1N/A
1N/Astruct Entry;
1N/A
1N/Atypedef struct /* loop info */
1N/A{
1N/A struct Entry* lab; /* call this function */
1N/A int start; /* start here */
1N/A int size; /* increment by this amount */
1N/A int count; /* dynamic loop count */
1N/A int offset; /* dynamic offset */
1N/A} Loop_t;
1N/A
1N/Atypedef struct Entry /* magic file entry */
1N/A{
1N/A struct Entry* next; /* next in list */
1N/A char* expr; /* offset expression */
1N/A union
1N/A {
1N/A unsigned long num;
1N/A char* str;
1N/A struct Entry* lab;
1N/A regex_t* sub;
1N/A Loop_t* loop;
1N/A } value; /* comparison value */
1N/A char* desc; /* file description */
1N/A char* mime; /* file mime type */
1N/A unsigned long offset; /* offset in bytes */
1N/A unsigned long mask; /* mask before compare */
1N/A char cont; /* continuation operation */
1N/A char type; /* datum type */
1N/A char op; /* comparison operation */
1N/A char nest; /* { or } nesting operation */
1N/A char swap; /* forced swap order */
1N/A} Entry_t;
1N/A
1N/A#define CC_BIT 5
1N/A
1N/A#if (CC_MAPS*CC_BIT) <= (CHAR_BIT*2)
1N/Atypedef unsigned short Cctype_t;
1N/A#else
1N/Atypedef unsigned long Cctype_t;
1N/A#endif
1N/A
1N/A#define CC_text 0x01
1N/A#define CC_control 0x02
1N/A#define CC_latin 0x04
1N/A#define CC_binary 0x08
1N/A#define CC_utf_8 0x10
1N/A
1N/A#define CC_notext CC_text /* CC_text is flipped before checking */
1N/A
1N/A#define CC_MASK (CC_binary|CC_latin|CC_control|CC_text)
1N/A
1N/A#define CCTYPE(c) (((c)>0240)?CC_binary:((c)>=0200)?CC_latin:((c)<040&&(c)!=007&&(c)!=011&&(c)!=012&&(c)!=013&&(c)!=015)?CC_control:CC_text)
1N/A
1N/A#define ID_NONE 0
1N/A#define ID_ASM 1
1N/A#define ID_C 2
1N/A#define ID_COBOL 3
1N/A#define ID_COPYBOOK 4
1N/A#define ID_CPLUSPLUS 5
1N/A#define ID_FORTRAN 6
1N/A#define ID_HTML 7
1N/A#define ID_INCL1 8
1N/A#define ID_INCL2 9
1N/A#define ID_INCL3 10
1N/A#define ID_MAM1 11
1N/A#define ID_MAM2 12
1N/A#define ID_MAM3 13
1N/A#define ID_NOTEXT 14
1N/A#define ID_PL1 15
1N/A#define ID_YACC 16
1N/A
1N/A#define ID_MAX ID_YACC
1N/A
1N/A#define INFO_atime 1
1N/A#define INFO_blocks 2
1N/A#define INFO_ctime 3
1N/A#define INFO_fstype 4
1N/A#define INFO_gid 5
1N/A#define INFO_mode 6
1N/A#define INFO_mtime 7
1N/A#define INFO_name 8
1N/A#define INFO_nlink 9
1N/A#define INFO_size 10
1N/A#define INFO_uid 11
1N/A
1N/A#define _MAGIC_PRIVATE_ \
1N/A Magicdisc_t* disc; /* discipline */ \
1N/A Vmalloc_t* vm; /* vmalloc region */ \
1N/A Entry_t* magic; /* parsed magic table */ \
1N/A Entry_t* magiclast; /* last entry in magic */ \
1N/A char* mime; /* MIME type */ \
1N/A unsigned char* x2n; /* CC_ALIEN=>CC_NATIVE */ \
1N/A char fbuf[SF_BUFSIZE + 1]; /* file data */ \
1N/A char xbuf[SF_BUFSIZE + 1]; /* indirect file data */ \
1N/A char nbuf[256]; /* !CC_NATIVE data */ \
1N/A char mbuf[64]; /* mime string */ \
1N/A char sbuf[64]; /* type suffix string */ \
1N/A char tbuf[2 * PATH_MAX]; /* type string */ \
1N/A Cctype_t cctype[UCHAR_MAX + 1]; /* char code types */ \
1N/A unsigned int count[UCHAR_MAX + 1]; /* char frequency count */ \
1N/A unsigned int multi[UCHAR_MAX + 1]; /* muti char count */ \
1N/A int keep[MAXNEST]; /* ckmagic nest stack */ \
1N/A char* cap[MAXNEST]; /* ckmagic mime stack */ \
1N/A char* msg[MAXNEST]; /* ckmagic text stack */ \
1N/A Entry_t* ret[MAXNEST]; /* ckmagic return stack */ \
1N/A int fbsz; /* fbuf size */ \
1N/A int fbmx; /* fbuf max size */ \
1N/A int xbsz; /* xbuf size */ \
1N/A int swap; /* swap() operation */ \
1N/A unsigned long flags; /* disc+open flags */ \
1N/A long xoff; /* xbuf offset */ \
1N/A int identifier[ID_MAX + 1]; /* Info_t identifier */ \
1N/A Sfio_t* fp; /* fbuf fp */ \
1N/A Sfio_t* tmp; /* tmp string */ \
1N/A regdisc_t redisc; /* regex discipline */ \
1N/A Dtdisc_t dtdisc; /* dict discipline */ \
1N/A Dt_t* idtab; /* identifier dict */ \
1N/A Dt_t* infotab; /* info keyword dict */
1N/A
1N/A#include <magic.h>
1N/A
1N/Astatic Info_t dict[] = /* keyword dictionary */
1N/A{
1N/A { "COMMON", ID_FORTRAN },
1N/A { "COMPUTE", ID_COBOL },
1N/A { "COMP", ID_COPYBOOK },
1N/A { "COMPUTATIONAL",ID_COPYBOOK },
1N/A { "DCL", ID_PL1 },
1N/A { "DEFINED", ID_PL1 },
1N/A { "DIMENSION", ID_FORTRAN },
1N/A { "DIVISION", ID_COBOL },
1N/A { "FILLER", ID_COPYBOOK },
1N/A { "FIXED", ID_PL1 },
1N/A { "FUNCTION", ID_FORTRAN },
1N/A { "HTML", ID_HTML },
1N/A { "INTEGER", ID_FORTRAN },
1N/A { "MAIN", ID_PL1 },
1N/A { "OPTIONS", ID_PL1 },
1N/A { "PERFORM", ID_COBOL },
1N/A { "PIC", ID_COPYBOOK },
1N/A { "REAL", ID_FORTRAN },
1N/A { "REDEFINES", ID_COPYBOOK },
1N/A { "S9", ID_COPYBOOK },
1N/A { "SECTION", ID_COBOL },
1N/A { "SELECT", ID_COBOL },
1N/A { "SUBROUTINE", ID_FORTRAN },
1N/A { "TEXT", ID_ASM },
1N/A { "VALUE", ID_COPYBOOK },
1N/A { "attr", ID_MAM3 },
1N/A { "binary", ID_YACC },
1N/A { "block", ID_FORTRAN },
1N/A { "bss", ID_ASM },
1N/A { "byte", ID_ASM },
1N/A { "char", ID_C },
1N/A { "class", ID_CPLUSPLUS },
1N/A { "clr", ID_NOTEXT },
1N/A { "comm", ID_ASM },
1N/A { "common", ID_FORTRAN },
1N/A { "data", ID_ASM },
1N/A { "dimension", ID_FORTRAN },
1N/A { "done", ID_MAM2 },
1N/A { "double", ID_C },
1N/A { "even", ID_ASM },
1N/A { "exec", ID_MAM3 },
1N/A { "extern", ID_C },
1N/A { "float", ID_C },
1N/A { "function", ID_FORTRAN },
1N/A { "globl", ID_ASM },
1N/A { "h", ID_INCL3 },
1N/A { "html", ID_HTML },
1N/A { "include", ID_INCL1 },
1N/A { "int", ID_C },
1N/A { "integer", ID_FORTRAN },
1N/A { "jmp", ID_NOTEXT },
1N/A { "left", ID_YACC },
1N/A { "libc", ID_INCL2 },
1N/A { "long", ID_C },
1N/A { "make", ID_MAM1 },
1N/A { "mov", ID_NOTEXT },
1N/A { "private", ID_CPLUSPLUS },
1N/A { "public", ID_CPLUSPLUS },
1N/A { "real", ID_FORTRAN },
1N/A { "register", ID_C },
1N/A { "right", ID_YACC },
1N/A { "sfio", ID_INCL2 },
1N/A { "static", ID_C },
1N/A { "stdio", ID_INCL2 },
1N/A { "struct", ID_C },
1N/A { "subroutine", ID_FORTRAN },
1N/A { "sys", ID_NOTEXT },
1N/A { "term", ID_YACC },
1N/A { "text", ID_ASM },
1N/A { "tst", ID_NOTEXT },
1N/A { "type", ID_YACC },
1N/A { "typedef", ID_C },
1N/A { "u", ID_INCL2 },
1N/A { "union", ID_YACC },
1N/A { "void", ID_C },
1N/A};
1N/A
1N/Astatic Info_t info[] =
1N/A{
1N/A { "atime", INFO_atime },
1N/A { "blocks", INFO_blocks },
1N/A { "ctime", INFO_ctime },
1N/A { "fstype", INFO_fstype },
1N/A { "gid", INFO_gid },
1N/A { "mode", INFO_mode },
1N/A { "mtime", INFO_mtime },
1N/A { "name", INFO_name },
1N/A { "nlink", INFO_nlink },
1N/A { "size", INFO_size },
1N/A { "uid", INFO_uid },
1N/A};
1N/A
1N/A/*
1N/A * return pointer to data at offset off and size siz
1N/A */
1N/A
1N/Astatic char*
1N/Agetdata(register Magic_t* mp, register long off, register int siz)
1N/A{
1N/A register long n;
1N/A
1N/A if (off < 0)
1N/A return 0;
1N/A if (off + siz <= mp->fbsz)
1N/A return mp->fbuf + off;
1N/A if (off < mp->xoff || off + siz > mp->xoff + mp->xbsz)
1N/A {
1N/A if (off + siz > mp->fbmx)
1N/A return 0;
1N/A n = (off / (SF_BUFSIZE / 2)) * (SF_BUFSIZE / 2);
1N/A if (sfseek(mp->fp, n, SEEK_SET) != n)
1N/A return 0;
1N/A if ((mp->xbsz = sfread(mp->fp, mp->xbuf, sizeof(mp->xbuf) - 1)) < 0)
1N/A {
1N/A mp->xoff = 0;
1N/A mp->xbsz = 0;
1N/A return 0;
1N/A }
1N/A mp->xbuf[mp->xbsz] = 0;
1N/A mp->xoff = n;
1N/A if (off + siz > mp->xoff + mp->xbsz)
1N/A return 0;
1N/A }
1N/A return mp->xbuf + off - mp->xoff;
1N/A}
1N/A
1N/A/*
1N/A * @... evaluator for strexpr()
1N/A */
1N/A
1N/Astatic long
1N/Aindirect(const char* cs, char** e, void* handle)
1N/A{
1N/A register char* s = (char*)cs;
1N/A register Magic_t* mp = (Magic_t*)handle;
1N/A register long n = 0;
1N/A register char* p;
1N/A
1N/A if (s)
1N/A {
1N/A if (*s == '@')
1N/A {
1N/A n = *++s == '(' ? strexpr(s, e, indirect, mp) : strtol(s, e, 0);
1N/A switch (*(s = *e))
1N/A {
1N/A case 'b':
1N/A case 'B':
1N/A s++;
1N/A if (p = getdata(mp, n, 1))
1N/A n = *(unsigned char*)p;
1N/A else
1N/A s = (char*)cs;
1N/A break;
1N/A case 'h':
1N/A case 'H':
1N/A s++;
1N/A if (p = getdata(mp, n, 2))
1N/A n = swapget(mp->swap, p, 2);
1N/A else
1N/A s = (char*)cs;
1N/A break;
1N/A case 'q':
1N/A case 'Q':
1N/A s++;
1N/A if (p = getdata(mp, n, 8))
1N/A n = swapget(mp->swap, p, 8);
1N/A else
1N/A s = (char*)cs;
1N/A break;
1N/A default:
1N/A if (isalnum(*s))
1N/A s++;
1N/A if (p = getdata(mp, n, 4))
1N/A n = swapget(mp->swap, p, 4);
1N/A else
1N/A s = (char*)cs;
1N/A break;
1N/A }
1N/A }
1N/A *e = s;
1N/A }
1N/A else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%s in indirect expression", *e);
1N/A return n;
1N/A}
1N/A
1N/A/*
1N/A * emit regex error message
1N/A */
1N/A
1N/Astatic void
1N/Aregmessage(Magic_t* mp, regex_t* re, int code)
1N/A{
1N/A char buf[128];
1N/A
1N/A if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A {
1N/A regerror(code, re, buf, sizeof(buf));
1N/A (*mp->disc->errorf)(mp, mp->disc, 3, "regex: %s", buf);
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * decompose vcodex(3) method composition
1N/A */
1N/A
1N/Astatic char*
1N/Avcdecomp(char* b, char* e, unsigned char* m, unsigned char* x)
1N/A{
1N/A unsigned char* map;
1N/A const char* o;
1N/A int c;
1N/A int n;
1N/A int i;
1N/A int a;
1N/A
1N/A map = CCMAP(CC_ASCII, CC_NATIVE);
1N/A a = 0;
1N/A i = 1;
1N/A for (;;)
1N/A {
1N/A if (i)
1N/A i = 0;
1N/A else
1N/A *b++ = '^';
1N/A if (m < (x - 1) && !*(m + 1))
1N/A {
1N/A /*
1N/A * obsolete indices
1N/A */
1N/A
1N/A if (!a)
1N/A {
1N/A a = 1;
1N/A o = "old, ";
1N/A while (b < e && (c = *o++))
1N/A *b++ = c;
1N/A }
1N/A switch (*m)
1N/A {
1N/A case 0: o = "delta"; break;
1N/A case 1: o = "huffman"; break;
1N/A case 2: o = "huffgroup"; break;
1N/A case 3: o = "arith"; break;
1N/A case 4: o = "bwt"; break;
1N/A case 5: o = "rle"; break;
1N/A case 6: o = "mtf"; break;
1N/A case 7: o = "transpose"; break;
1N/A case 8: o = "table"; break;
1N/A case 9: o = "huffpart"; break;
1N/A case 50: o = "map"; break;
1N/A case 100: o = "recfm"; break;
1N/A case 101: o = "ss7"; break;
1N/A default: o = "UNKNOWN"; break;
1N/A }
1N/A m += 2;
1N/A while (b < e && (c = *o++))
1N/A *b++ = c;
1N/A }
1N/A else
1N/A while (b < e && m < x && (c = *m++))
1N/A {
1N/A if (map)
1N/A c = map[c];
1N/A *b++ = c;
1N/A }
1N/A if (b >= e)
1N/A break;
1N/A n = 0;
1N/A while (m < x)
1N/A {
1N/A n = (n<<7) | (*m & 0x7f);
1N/A if (!(*m++ & 0x80))
1N/A break;
1N/A }
1N/A if (n >= (x - m))
1N/A break;
1N/A m += n;
1N/A }
1N/A return b;
1N/A}
1N/A
1N/A/*
1N/A * check for magic table match in buf
1N/A */
1N/A
1N/Astatic char*
1N/Ackmagic(register Magic_t* mp, const char* file, char* buf, char* end, struct stat* st, unsigned long off)
1N/A{
1N/A register Entry_t* ep;
1N/A register char* p;
1N/A register char* b;
1N/A register int level = 0;
1N/A int call = -1;
1N/A int all = 0;
1N/A int c;
1N/A int str;
1N/A char* q;
1N/A char* t;
1N/A char* cur;
1N/A char* base = 0;
1N/A unsigned long num;
1N/A unsigned long mask;
1N/A regmatch_t matches[10];
1N/A
1N/A mp->swap = 0;
1N/A b = mp->msg[0] = cur = buf;
1N/A mp->mime = mp->cap[0] = 0;
1N/A mp->keep[0] = 0;
1N/A for (ep = mp->magic; ep; ep = ep->next)
1N/A {
1N/A fun:
1N/A if (ep->nest == '{')
1N/A {
1N/A if (++level >= MAXNEST)
1N/A {
1N/A call = -1;
1N/A level = 0;
1N/A mp->keep[0] = 0;
1N/A b = mp->msg[0];
1N/A mp->mime = mp->cap[0];
1N/A continue;
1N/A }
1N/A mp->keep[level] = mp->keep[level - 1] != 0;
1N/A mp->msg[level] = b;
1N/A mp->cap[level] = mp->mime;
1N/A }
1N/A switch (ep->cont)
1N/A {
1N/A case '#':
1N/A if (mp->keep[level] && b > cur)
1N/A {
1N/A if ((mp->flags & MAGIC_ALL) && b < (end - 3))
1N/A {
1N/A all = 1;
1N/A *b++ = '\n';
1N/A cur = b;
1N/A continue;
1N/A }
1N/A *b = 0;
1N/A return buf;
1N/A }
1N/A mp->swap = 0;
1N/A b = mp->msg[0] = cur;
1N/A mp->mime = mp->cap[0] = 0;
1N/A if (ep->type == ' ')
1N/A continue;
1N/A break;
1N/A case '$':
1N/A if (mp->keep[level] && call < (MAXNEST - 1))
1N/A {
1N/A mp->ret[++call] = ep;
1N/A ep = ep->value.lab;
1N/A goto fun;
1N/A }
1N/A continue;
1N/A case ':':
1N/A ep = mp->ret[call--];
1N/A if (ep->op == 'l')
1N/A goto fun;
1N/A continue;
1N/A case '|':
1N/A if (mp->keep[level] > 1)
1N/A goto checknest;
1N/A /*FALLTHROUGH*/
1N/A default:
1N/A if (!mp->keep[level])
1N/A {
1N/A b = mp->msg[level];
1N/A mp->mime = mp->cap[level];
1N/A goto checknest;
1N/A }
1N/A break;
1N/A }
1N/A p = "";
1N/A num = 0;
1N/A if (!ep->expr)
1N/A num = ep->offset + off;
1N/A else
1N/A switch (ep->offset)
1N/A {
1N/A case 0:
1N/A num = strexpr(ep->expr, NiL, indirect, mp) + off;
1N/A break;
1N/A case INFO_atime:
1N/A num = st->st_atime;
1N/A ep->type = 'D';
1N/A break;
1N/A case INFO_blocks:
1N/A num = iblocks(st);
1N/A ep->type = 'N';
1N/A break;
1N/A case INFO_ctime:
1N/A num = st->st_ctime;
1N/A ep->type = 'D';
1N/A break;
1N/A case INFO_fstype:
1N/A p = fmtfs(st);
1N/A ep->type = toupper(ep->type);
1N/A break;
1N/A case INFO_gid:
1N/A if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
1N/A {
1N/A p = fmtgid(st->st_gid);
1N/A ep->type = toupper(ep->type);
1N/A }
1N/A else
1N/A {
1N/A num = st->st_gid;
1N/A ep->type = 'N';
1N/A }
1N/A break;
1N/A case INFO_mode:
1N/A if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
1N/A {
1N/A p = fmtmode(st->st_mode, 0);
1N/A ep->type = toupper(ep->type);
1N/A }
1N/A else
1N/A {
1N/A num = modex(st->st_mode);
1N/A ep->type = 'N';
1N/A }
1N/A break;
1N/A case INFO_mtime:
1N/A num = st->st_ctime;
1N/A ep->type = 'D';
1N/A break;
1N/A case INFO_name:
1N/A if (!base)
1N/A {
1N/A if (base = strrchr(file, '/'))
1N/A base++;
1N/A else
1N/A base = (char*)file;
1N/A }
1N/A p = base;
1N/A ep->type = toupper(ep->type);
1N/A break;
1N/A case INFO_nlink:
1N/A num = st->st_nlink;
1N/A ep->type = 'N';
1N/A break;
1N/A case INFO_size:
1N/A num = st->st_size;
1N/A ep->type = 'N';
1N/A break;
1N/A case INFO_uid:
1N/A if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
1N/A {
1N/A p = fmtuid(st->st_uid);
1N/A ep->type = toupper(ep->type);
1N/A }
1N/A else
1N/A {
1N/A num = st->st_uid;
1N/A ep->type = 'N';
1N/A }
1N/A break;
1N/A }
1N/A switch (ep->type)
1N/A {
1N/A
1N/A case 'b':
1N/A if (!(p = getdata(mp, num, 1)))
1N/A goto next;
1N/A num = *(unsigned char*)p;
1N/A break;
1N/A
1N/A case 'h':
1N/A if (!(p = getdata(mp, num, 2)))
1N/A goto next;
1N/A num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 2);
1N/A break;
1N/A
1N/A case 'd':
1N/A case 'l':
1N/A case 'v':
1N/A if (!(p = getdata(mp, num, 4)))
1N/A goto next;
1N/A num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 4);
1N/A break;
1N/A
1N/A case 'q':
1N/A if (!(p = getdata(mp, num, 8)))
1N/A goto next;
1N/A num = swapget(ep->swap ? (~ep->swap ^ mp->swap) : mp->swap, p, 8);
1N/A break;
1N/A
1N/A case 'e':
1N/A if (!(p = getdata(mp, num, 0)))
1N/A goto next;
1N/A /*FALLTHROUGH*/
1N/A case 'E':
1N/A if (!ep->value.sub)
1N/A goto next;
1N/A if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
1N/A {
1N/A c = mp->fbsz;
1N/A if (c >= sizeof(mp->nbuf))
1N/A c = sizeof(mp->nbuf) - 1;
1N/A p = (char*)memcpy(mp->nbuf, p, c);
1N/A p[c] = 0;
1N/A ccmapstr(mp->x2n, p, c);
1N/A if ((c = regexec(ep->value.sub, p, elementsof(matches), matches, 0)) || (c = regsubexec(ep->value.sub, p, elementsof(matches), matches)))
1N/A {
1N/A if (c != REG_NOMATCH)
1N/A regmessage(mp, ep->value.sub, c);
1N/A goto next;
1N/A }
1N/A }
1N/A p = ep->value.sub->re_sub->re_buf;
1N/A q = T(ep->desc);
1N/A t = *q ? q : p;
1N/A if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *t && *t != ',' && *t != '.' && *t != '\b')
1N/A *b++ = ' ';
1N/A b += sfsprintf(b, end - b, *q ? q : "%s", p + (*p == '\b'));
1N/A if (ep->mime)
1N/A mp->mime = ep->mime;
1N/A goto checknest;
1N/A
1N/A case 's':
1N/A if (!(p = getdata(mp, num, ep->mask)))
1N/A goto next;
1N/A goto checkstr;
1N/A case 'm':
1N/A if (!(p = getdata(mp, num, 0)))
1N/A goto next;
1N/A /*FALLTHROUGH*/
1N/A case 'M':
1N/A case 'S':
1N/A checkstr:
1N/A for (;;)
1N/A {
1N/A if (*ep->value.str == '*' && !*(ep->value.str + 1) && isprint(*p))
1N/A break;
1N/A if ((ep->type == 'm' || ep->type == 'M') ? strmatch(p, ep->value.str) : !memcmp(p, ep->value.str, ep->mask))
1N/A break;
1N/A if (p == mp->nbuf || ep->mask >= sizeof(mp->nbuf))
1N/A goto next;
1N/A p = (char*)memcpy(mp->nbuf, p, ep->mask);
1N/A p[ep->mask] = 0;
1N/A ccmapstr(mp->x2n, p, ep->mask);
1N/A }
1N/A q = T(ep->desc);
1N/A if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
1N/A *b++ = ' ';
1N/A for (t = p; (c = *t) >= 0 && c <= 0177 && isprint(c) && c != '\n'; t++);
1N/A *t = 0;
1N/A b += sfsprintf(b, end - b, q + (*q == '\b'), p);
1N/A *t = c;
1N/A if (ep->mime)
1N/A mp->mime = ep->mime;
1N/A goto checknest;
1N/A
1N/A }
1N/A if (mask = ep->mask)
1N/A num &= mask;
1N/A switch (ep->op)
1N/A {
1N/A
1N/A case '=':
1N/A case '@':
1N/A if (num == ep->value.num)
1N/A break;
1N/A if (ep->cont != '#')
1N/A goto next;
1N/A if (!mask)
1N/A mask = ~mask;
1N/A if (ep->type == 'h')
1N/A {
1N/A if ((num = swapget(mp->swap = 1, p, 2) & mask) == ep->value.num)
1N/A {
1N/A if (!(mp->swap & (mp->swap + 1)))
1N/A mp->swap = 7;
1N/A goto swapped;
1N/A }
1N/A }
1N/A else if (ep->type == 'l')
1N/A {
1N/A for (c = 1; c < 4; c++)
1N/A if ((num = swapget(mp->swap = c, p, 4) & mask) == ep->value.num)
1N/A {
1N/A if (!(mp->swap & (mp->swap + 1)))
1N/A mp->swap = 7;
1N/A goto swapped;
1N/A }
1N/A }
1N/A else if (ep->type == 'q')
1N/A {
1N/A for (c = 1; c < 8; c++)
1N/A if ((num = swapget(mp->swap = c, p, 8) & mask) == ep->value.num)
1N/A goto swapped;
1N/A }
1N/A goto next;
1N/A
1N/A case '!':
1N/A if (num != ep->value.num)
1N/A break;
1N/A goto next;
1N/A
1N/A case '^':
1N/A if (num ^ ep->value.num)
1N/A break;
1N/A goto next;
1N/A
1N/A case '>':
1N/A if (num > ep->value.num)
1N/A break;
1N/A goto next;
1N/A
1N/A case '<':
1N/A if (num < ep->value.num)
1N/A break;
1N/A goto next;
1N/A
1N/A case 'l':
1N/A if (num > 0 && mp->keep[level] && call < (MAXNEST - 1))
1N/A {
1N/A if (!ep->value.loop->count)
1N/A {
1N/A ep->value.loop->count = num;
1N/A ep->value.loop->offset = off;
1N/A off = ep->value.loop->start;
1N/A }
1N/A else if (!--ep->value.loop->count)
1N/A {
1N/A off = ep->value.loop->offset;
1N/A goto next;
1N/A }
1N/A else
1N/A off += ep->value.loop->size;
1N/A mp->ret[++call] = ep;
1N/A ep = ep->value.loop->lab;
1N/A goto fun;
1N/A }
1N/A goto next;
1N/A
1N/A case 'm':
1N/A c = mp->swap;
1N/A t = ckmagic(mp, file, b + (b > cur), end, st, num);
1N/A mp->swap = c;
1N/A if (t)
1N/A {
1N/A if (b > cur && b < end)
1N/A *b = ' ';
1N/A b += strlen(b);
1N/A }
1N/A else if (ep->cont == '&')
1N/A goto next;
1N/A break;
1N/A
1N/A case 'r':
1N/A#if _UWIN
1N/A {
1N/A char* e;
1N/A Sfio_t* rp;
1N/A Sfio_t* gp;
1N/A
1N/A if (!(t = strrchr(file, '.')))
1N/A goto next;
1N/A sfprintf(mp->tmp, "/reg/classes_root/%s", t);
1N/A if (!(t = sfstruse(mp->tmp)) || !(rp = sfopen(NiL, t, "r")))
1N/A goto next;
1N/A *ep->desc = 0;
1N/A *ep->mime = 0;
1N/A gp = 0;
1N/A while (t = sfgetr(rp, '\n', 1))
1N/A {
1N/A if (strneq(t, "Content Type=", 13))
1N/A {
1N/A ep->mime = vmnewof(mp->vm, ep->mime, char, sfvalue(rp), 0);
1N/A strcpy(ep->mime, t + 13);
1N/A if (gp)
1N/A break;
1N/A }
1N/A else
1N/A {
1N/A sfprintf(mp->tmp, "/reg/classes_root/%s", t);
1N/A if ((e = sfstruse(mp->tmp)) && (gp = sfopen(NiL, e, "r")))
1N/A {
1N/A ep->desc = vmnewof(mp->vm, ep->desc, char, strlen(t), 1);
1N/A strcpy(ep->desc, t);
1N/A if (*ep->mime)
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A sfclose(rp);
1N/A if (!gp)
1N/A goto next;
1N/A if (!*ep->mime)
1N/A {
1N/A t = T(ep->desc);
1N/A if (!strncasecmp(t, "microsoft", 9))
1N/A t += 9;
1N/A while (isspace(*t))
1N/A t++;
1N/A e = "application/x-ms-";
1N/A ep->mime = vmnewof(mp->vm, ep->mime, char, strlen(t), strlen(e));
1N/A e = strcopy(ep->mime, e);
1N/A while ((c = *t++) && c != '.' && c != ' ')
1N/A *e++ = isupper(c) ? tolower(c) : c;
1N/A *e = 0;
1N/A }
1N/A while (t = sfgetr(gp, '\n', 1))
1N/A if (*t && !streq(t, "\"\""))
1N/A {
1N/A ep->desc = vmnewof(mp->vm, ep->desc, char, sfvalue(gp), 0);
1N/A strcpy(ep->desc, t);
1N/A break;
1N/A }
1N/A sfclose(gp);
1N/A if (!*ep->desc)
1N/A goto next;
1N/A if (!t)
1N/A for (t = T(ep->desc); *t; t++)
1N/A if (*t == '.')
1N/A *t = ' ';
1N/A if (!mp->keep[level])
1N/A mp->keep[level] = 2;
1N/A mp->mime = ep->mime;
1N/A break;
1N/A }
1N/A#else
1N/A if (ep->cont == '#' && !mp->keep[level])
1N/A mp->keep[level] = 1;
1N/A goto next;
1N/A#endif
1N/A
1N/A case 'v':
1N/A if (!(p = getdata(mp, num, 4)))
1N/A goto next;
1N/A c = 0;
1N/A do
1N/A {
1N/A num++;
1N/A c = (c<<7) | (*p & 0x7f);
1N/A } while (*p++ & 0x80);
1N/A if (!(p = getdata(mp, num, c)))
1N/A goto next;
1N/A if (mp->keep[level]++ && b > cur && b < (end - 1) && *(b - 1) != ' ')
1N/A {
1N/A *b++ = ',';
1N/A *b++ = ' ';
1N/A }
1N/A b = vcdecomp(b, cur + PATH_MAX, (unsigned char*)p, (unsigned char*)p + c);
1N/A goto checknest;
1N/A
1N/A }
1N/A swapped:
1N/A q = T(ep->desc);
1N/A if (mp->keep[level]++ && b > cur && b < end && *(b - 1) != ' ' && *q && *q != ',' && *q != '.' && *q != '\b')
1N/A *b++ = ' ';
1N/A if (*q == '\b')
1N/A q++;
1N/A str = 0;
1N/A for (t = q; *t; t++)
1N/A if (*t == '%' && (c = *(t + 1)))
1N/A {
1N/A if (c == '%')
1N/A t++;
1N/A else
1N/A while (c && c != '%')
1N/A {
1N/A if (c == 's')
1N/A {
1N/A str = 1;
1N/A break;
1N/A }
1N/A t++;
1N/A c = *(t + 1);
1N/A }
1N/A }
1N/A if (!str)
1N/A b += sfsprintf(b, end - b, q, num, 0, 0, 0, 0, 0, 0, 0);
1N/A else if (ep->type == 'd' || ep->type == 'D')
1N/A b += sfsprintf(b, end - b, q, fmttime("%?%QL", (time_t)num), 0, 0, 0, 0, 0, 0, 0);
1N/A else if (ep->type == 'v')
1N/A b += sfsprintf(b, end - b, q, fmtversion(num), 0, 0, 0, 0, 0, 0, 0);
1N/A else
1N/A b += sfsprintf(b, end - b, q, fmtnum(num, 0), 0, 0, 0, 0, 0, 0, 0);
1N/A if (ep->mime && *ep->mime)
1N/A mp->mime = ep->mime;
1N/A checknest:
1N/A if (ep->nest == '}')
1N/A {
1N/A if (!mp->keep[level])
1N/A {
1N/A b = mp->msg[level];
1N/A mp->mime = mp->cap[level];
1N/A }
1N/A else if (level > 0)
1N/A mp->keep[level - 1] = mp->keep[level];
1N/A if (--level < 0)
1N/A {
1N/A level = 0;
1N/A mp->keep[0] = 0;
1N/A }
1N/A }
1N/A continue;
1N/A next:
1N/A if (ep->cont == '&')
1N/A mp->keep[level] = 0;
1N/A goto checknest;
1N/A }
1N/A if (all && b-- || mp->keep[level] && b > cur)
1N/A {
1N/A *b = 0;
1N/A return buf;
1N/A }
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * check english language stats
1N/A */
1N/A
1N/Astatic int
1N/Ackenglish(register Magic_t* mp, int pun, int badpun)
1N/A{
1N/A register char* s;
1N/A register int vowl = 0;
1N/A register int freq = 0;
1N/A register int rare = 0;
1N/A
1N/A if (5 * badpun > pun)
1N/A return 0;
1N/A if (2 * mp->count[';'] > mp->count['E'] + mp->count['e'])
1N/A return 0;
1N/A if ((mp->count['>'] + mp->count['<'] + mp->count['/']) > mp->count['E'] + mp->count['e'])
1N/A return 0;
1N/A for (s = "aeiou"; *s; s++)
1N/A vowl += mp->count[toupper(*s)] + mp->count[*s];
1N/A for (s = "etaion"; *s; s++)
1N/A freq += mp->count[toupper(*s)] + mp->count[*s];
1N/A for (s = "vjkqxz"; *s; s++)
1N/A rare += mp->count[toupper(*s)] + mp->count[*s];
1N/A return 5 * vowl >= mp->fbsz - mp->count[' '] && freq >= 10 * rare;
1N/A}
1N/A
1N/A/*
1N/A * check programming language stats
1N/A */
1N/A
1N/Astatic char*
1N/Acklang(register Magic_t* mp, const char* file, char* buf, char* end, struct stat* st)
1N/A{
1N/A register int c;
1N/A register unsigned char* b;
1N/A register unsigned char* e;
1N/A register int q;
1N/A register char* s;
1N/A char* t;
1N/A char* base;
1N/A char* suff;
1N/A char* t1;
1N/A char* t2;
1N/A char* t3;
1N/A int n;
1N/A int badpun;
1N/A int code;
1N/A int pun;
1N/A Cctype_t flags;
1N/A Info_t* ip;
1N/A
1N/A b = (unsigned char*)mp->fbuf;
1N/A e = b + mp->fbsz;
1N/A memzero(mp->count, sizeof(mp->count));
1N/A memzero(mp->multi, sizeof(mp->multi));
1N/A memzero(mp->identifier, sizeof(mp->identifier));
1N/A
1N/A /*
1N/A * check character coding
1N/A */
1N/A
1N/A flags = 0;
1N/A while (b < e)
1N/A flags |= mp->cctype[*b++];
1N/A b = (unsigned char*)mp->fbuf;
1N/A code = 0;
1N/A q = CC_ASCII;
1N/A n = CC_MASK;
1N/A for (c = 0; c < CC_MAPS; c++)
1N/A {
1N/A flags ^= CC_text;
1N/A if ((flags & CC_MASK) < n)
1N/A {
1N/A n = flags & CC_MASK;
1N/A q = c;
1N/A }
1N/A flags >>= CC_BIT;
1N/A }
1N/A flags = n;
1N/A if (!(flags & (CC_binary|CC_notext)))
1N/A {
1N/A if (q != CC_NATIVE)
1N/A {
1N/A code = q;
1N/A ccmaps(mp->fbuf, mp->fbsz, q, CC_NATIVE);
1N/A }
1N/A if (b[0] == '#' && b[1] == '!')
1N/A {
1N/A for (b += 2; b < e && isspace(*b); b++);
1N/A for (s = (char*)b; b < e && isprint(*b); b++);
1N/A c = *b;
1N/A *b = 0;
1N/A if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) || match(s, "/*bin*/*") || !access(s, F_OK))
1N/A {
1N/A if (t = strrchr(s, '/'))
1N/A s = t + 1;
1N/A for (t = s; *t; t++)
1N/A if (isspace(*t))
1N/A {
1N/A *t = 0;
1N/A break;
1N/A }
1N/A sfsprintf(mp->mbuf, sizeof(mp->mbuf), "application/x-%s", *s ? s : "sh");
1N/A mp->mime = mp->mbuf;
1N/A if (match(s, "*sh"))
1N/A {
1N/A t1 = T("command");
1N/A if (streq(s, "sh"))
1N/A *s = 0;
1N/A else
1N/A {
1N/A *b++ = ' ';
1N/A *b = 0;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A t1 = T("interpreter");
1N/A *b++ = ' ';
1N/A *b = 0;
1N/A }
1N/A sfsprintf(mp->sbuf, sizeof(mp->sbuf), T("%s%s script"), s, t1);
1N/A s = mp->sbuf;
1N/A goto qualify;
1N/A }
1N/A *b = c;
1N/A b = (unsigned char*)mp->fbuf;
1N/A }
1N/A badpun = 0;
1N/A pun = 0;
1N/A q = 0;
1N/A s = 0;
1N/A t = 0;
1N/A while (b < e)
1N/A {
1N/A c = *b++;
1N/A mp->count[c]++;
1N/A if (c == q && (q != '*' || *b == '/' && b++))
1N/A {
1N/A mp->multi[q]++;
1N/A q = 0;
1N/A }
1N/A else if (c == '\\')
1N/A {
1N/A s = 0;
1N/A b++;
1N/A }
1N/A else if (!q)
1N/A {
1N/A if (isalpha(c) || c == '_')
1N/A {
1N/A if (!s)
1N/A s = (char*)b - 1;
1N/A }
1N/A else if (!isdigit(c))
1N/A {
1N/A if (s)
1N/A {
1N/A if (s > mp->fbuf)
1N/A switch (*(s - 1))
1N/A {
1N/A case ':':
1N/A if (*b == ':')
1N/A mp->multi[':']++;
1N/A break;
1N/A case '.':
1N/A if (((char*)b - s) == 3 && (s == (mp->fbuf + 1) || *(s - 2) == '\n'))
1N/A mp->multi['.']++;
1N/A break;
1N/A case '\n':
1N/A case '\\':
1N/A if (*b == '{')
1N/A t = (char*)b + 1;
1N/A break;
1N/A case '{':
1N/A if (s == t && *b == '}')
1N/A mp->multi['X']++;
1N/A break;
1N/A }
1N/A if (!mp->idtab)
1N/A {
1N/A if (mp->idtab = dtnew(mp->vm, &mp->dtdisc, Dthash))
1N/A for (q = 0; q < elementsof(dict); q++)
1N/A dtinsert(mp->idtab, &dict[q]);
1N/A else if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
1N/A q = 0;
1N/A }
1N/A if (mp->idtab)
1N/A {
1N/A *(b - 1) = 0;
1N/A if (ip = (Info_t*)dtmatch(mp->idtab, s))
1N/A mp->identifier[ip->value]++;
1N/A *(b - 1) = c;
1N/A }
1N/A s = 0;
1N/A }
1N/A switch (c)
1N/A {
1N/A case '\t':
1N/A if (b == (unsigned char*)(mp->fbuf + 1) || *(b - 2) == '\n')
1N/A mp->multi['\t']++;
1N/A break;
1N/A case '"':
1N/A case '\'':
1N/A q = c;
1N/A break;
1N/A case '/':
1N/A if (*b == '*')
1N/A q = *b++;
1N/A else if (*b == '/')
1N/A q = '\n';
1N/A break;
1N/A case '$':
1N/A if (*b == '(' && *(b + 1) != ' ')
1N/A mp->multi['$']++;
1N/A break;
1N/A case '{':
1N/A case '}':
1N/A case '[':
1N/A case ']':
1N/A case '(':
1N/A mp->multi[c]++;
1N/A break;
1N/A case ')':
1N/A mp->multi[c]++;
1N/A goto punctuation;
1N/A case ':':
1N/A if (*b == ':' && isspace(*(b + 1)) && b > (unsigned char*)(mp->fbuf + 1) && isspace(*(b - 2)))
1N/A mp->multi[':']++;
1N/A goto punctuation;
1N/A case '.':
1N/A case ',':
1N/A case '%':
1N/A case ';':
1N/A case '?':
1N/A punctuation:
1N/A pun++;
1N/A if (*b != ' ' && *b != '\n')
1N/A badpun++;
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A }
1N/A }
1N/A else
1N/A while (b < e)
1N/A mp->count[*b++]++;
1N/A base = (t1 = strrchr(file, '/')) ? t1 + 1 : (char*)file;
1N/A suff = (t1 = strrchr(base, '.')) ? t1 + 1 : "";
1N/A if (!flags)
1N/A {
1N/A if (match(suff, "*sh|bat|cmd"))
1N/A goto id_sh;
1N/A if (match(base, "*@(mkfile)"))
1N/A goto id_mk;
1N/A if (match(base, "*@(makefile|.mk)"))
1N/A goto id_make;
1N/A if (match(base, "*@(mamfile|.mam)"))
1N/A goto id_mam;
1N/A if (match(suff, "[cly]?(pp|xx|++)|cc|ll|yy"))
1N/A goto id_c;
1N/A if (match(suff, "f"))
1N/A goto id_fortran;
1N/A if (match(suff, "htm+(l)"))
1N/A goto id_html;
1N/A if (match(suff, "cpy"))
1N/A goto id_copybook;
1N/A if (match(suff, "cob|cbl|cb2"))
1N/A goto id_cobol;
1N/A if (match(suff, "pl[1i]"))
1N/A goto id_pl1;
1N/A if (match(suff, "tex"))
1N/A goto id_tex;
1N/A if (match(suff, "asm|s"))
1N/A goto id_asm;
1N/A if ((st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) && (!suff || suff != strchr(suff, '.')))
1N/A {
1N/A id_sh:
1N/A s = T("command script");
1N/A mp->mime = "application/sh";
1N/A goto qualify;
1N/A }
1N/A if (strmatch(mp->fbuf, "From * [0-9][0-9]:[0-9][0-9]:[0-9][0-9] *"))
1N/A {
1N/A s = T("mail message");
1N/A mp->mime = "message/rfc822";
1N/A goto qualify;
1N/A }
1N/A if (match(base, "*@(mkfile)"))
1N/A {
1N/A id_mk:
1N/A s = "mkfile";
1N/A mp->mime = "application/mk";
1N/A goto qualify;
1N/A }
1N/A if (match(base, "*@(makefile|.mk)") || mp->multi['\t'] >= mp->count[':'] && (mp->multi['$'] > 0 || mp->multi[':'] > 0))
1N/A {
1N/A id_make:
1N/A s = "makefile";
1N/A mp->mime = "application/make";
1N/A goto qualify;
1N/A }
1N/A if (mp->multi['.'] >= 3)
1N/A {
1N/A s = T("nroff input");
1N/A mp->mime = "application/x-troff";
1N/A goto qualify;
1N/A }
1N/A if (mp->multi['X'] >= 3)
1N/A {
1N/A s = T("TeX input");
1N/A mp->mime = "application/x-tex";
1N/A goto qualify;
1N/A }
1N/A if (mp->fbsz < SF_BUFSIZE &&
1N/A (mp->multi['('] == mp->multi[')'] &&
1N/A mp->multi['{'] == mp->multi['}'] &&
1N/A mp->multi['['] == mp->multi[']']) ||
1N/A mp->fbsz >= SF_BUFSIZE &&
1N/A (mp->multi['('] >= mp->multi[')'] &&
1N/A mp->multi['{'] >= mp->multi['}'] &&
1N/A mp->multi['['] >= mp->multi[']']))
1N/A {
1N/A c = mp->identifier[ID_INCL1];
1N/A if (c >= 2 && mp->identifier[ID_INCL2] >= c && mp->identifier[ID_INCL3] >= c && mp->count['.'] >= c ||
1N/A mp->identifier[ID_C] >= 5 && mp->count[';'] >= 5 ||
1N/A mp->count['='] >= 20 && mp->count[';'] >= 20)
1N/A {
1N/A id_c:
1N/A t1 = "";
1N/A t2 = "c ";
1N/A t3 = T("program");
1N/A switch (*suff)
1N/A {
1N/A case 'c':
1N/A case 'C':
1N/A mp->mime = "application/x-cc";
1N/A break;
1N/A case 'l':
1N/A case 'L':
1N/A t1 = "lex ";
1N/A mp->mime = "application/x-lex";
1N/A break;
1N/A default:
1N/A t3 = T("header");
1N/A if (mp->identifier[ID_YACC] < 5 || mp->count['%'] < 5)
1N/A {
1N/A mp->mime = "application/x-cc";
1N/A break;
1N/A }
1N/A /*FALLTHROUGH*/
1N/A case 'y':
1N/A case 'Y':
1N/A t1 = "yacc ";
1N/A mp->mime = "application/x-yacc";
1N/A break;
1N/A }
1N/A if (mp->identifier[ID_CPLUSPLUS] >= 3)
1N/A {
1N/A t2 = "c++ ";
1N/A mp->mime = "application/x-c++";
1N/A }
1N/A sfsprintf(mp->sbuf, sizeof(mp->sbuf), "%s%s%s", t1, t2, t3);
1N/A s = mp->sbuf;
1N/A goto qualify;
1N/A }
1N/A }
1N/A if (mp->identifier[ID_MAM1] >= 2 && mp->identifier[ID_MAM3] >= 2 &&
1N/A (mp->fbsz < SF_BUFSIZE && mp->identifier[ID_MAM1] == mp->identifier[ID_MAM2] ||
1N/A mp->fbsz >= SF_BUFSIZE && mp->identifier[ID_MAM1] >= mp->identifier[ID_MAM2]))
1N/A {
1N/A id_mam:
1N/A s = T("mam program");
1N/A mp->mime = "application/x-mam";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_FORTRAN] >= 8)
1N/A {
1N/A id_fortran:
1N/A s = T("fortran program");
1N/A mp->mime = "application/x-fortran";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_HTML] > 0 && mp->count['<'] >= 8 && (c = mp->count['<'] - mp->count['>']) >= -2 && c <= 2)
1N/A {
1N/A id_html:
1N/A s = T("html input");
1N/A mp->mime = "text/html";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_COPYBOOK] > 0 && mp->identifier[ID_COBOL] == 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1N/A {
1N/A id_copybook:
1N/A s = T("cobol copybook");
1N/A mp->mime = "application/x-cobol";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_COBOL] > 0 && mp->identifier[ID_COPYBOOK] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1N/A {
1N/A id_cobol:
1N/A s = T("cobol program");
1N/A mp->mime = "application/x-cobol";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_PL1] > 0 && (c = mp->count['('] - mp->count[')']) >= -2 && c <= 2)
1N/A {
1N/A id_pl1:
1N/A s = T("pl1 program");
1N/A mp->mime = "application/x-pl1";
1N/A goto qualify;
1N/A }
1N/A if (mp->count['{'] >= 6 && (c = mp->count['{'] - mp->count['}']) >= -2 && c <= 2 && mp->count['\\'] >= mp->count['{'])
1N/A {
1N/A id_tex:
1N/A s = T("TeX input");
1N/A mp->mime = "text/tex";
1N/A goto qualify;
1N/A }
1N/A if (mp->identifier[ID_ASM] >= 4)
1N/A {
1N/A id_asm:
1N/A s = T("as program");
1N/A mp->mime = "application/x-as";
1N/A goto qualify;
1N/A }
1N/A if (ckenglish(mp, pun, badpun))
1N/A {
1N/A s = T("english text");
1N/A mp->mime = "text/plain";
1N/A goto qualify;
1N/A }
1N/A }
1N/A else if (streq(base, "core"))
1N/A {
1N/A mp->mime = "x-system/core";
1N/A return T("core dump");
1N/A }
1N/A if (flags & (CC_binary|CC_notext))
1N/A {
1N/A b = (unsigned char*)mp->fbuf;
1N/A e = b + mp->fbsz;
1N/A n = 0;
1N/A for (;;)
1N/A {
1N/A c = *b++;
1N/A q = 0;
1N/A while (c & 0x80)
1N/A {
1N/A c <<= 1;
1N/A q++;
1N/A }
1N/A switch (q)
1N/A {
1N/A case 4:
1N/A if (b < e && (*b++ & 0xc0) != 0x80)
1N/A break;
1N/A case 3:
1N/A if (b < e && (*b++ & 0xc0) != 0x80)
1N/A break;
1N/A case 2:
1N/A if (b < e && (*b++ & 0xc0) != 0x80)
1N/A break;
1N/A n = 1;
1N/A case 0:
1N/A if (b >= e)
1N/A {
1N/A if (n)
1N/A {
1N/A flags &= ~(CC_binary|CC_notext);
1N/A flags |= CC_utf_8;
1N/A }
1N/A break;
1N/A }
1N/A continue;
1N/A }
1N/A break;
1N/A }
1N/A }
1N/A if (flags & (CC_binary|CC_notext))
1N/A {
1N/A unsigned long d = 0;
1N/A
1N/A if ((q = mp->fbsz / UCHAR_MAX) >= 2)
1N/A {
1N/A /*
1N/A * compression/encryption via standard deviation
1N/A */
1N/A
1N/A
1N/A for (c = 0; c < UCHAR_MAX; c++)
1N/A {
1N/A pun = mp->count[c] - q;
1N/A d += pun * pun;
1N/A }
1N/A d /= mp->fbsz;
1N/A }
1N/A if (d <= 0)
1N/A s = T("binary");
1N/A else if (d < 4)
1N/A s = T("encrypted");
1N/A else if (d < 16)
1N/A s = T("packed");
1N/A else if (d < 64)
1N/A s = T("compressed");
1N/A else if (d < 256)
1N/A s = T("delta");
1N/A else
1N/A s = T("data");
1N/A mp->mime = "application/octet-stream";
1N/A return s;
1N/A }
1N/A mp->mime = "text/plain";
1N/A if (flags & CC_utf_8)
1N/A s = (flags & CC_control) ? T("utf-8 text with control characters") : T("utf-8 text");
1N/A else if (flags & CC_latin)
1N/A s = (flags & CC_control) ? T("latin text with control characters") : T("latin text");
1N/A else
1N/A s = (flags & CC_control) ? T("text with control characters") : T("text");
1N/A qualify:
1N/A if (!flags && mp->count['\n'] >= mp->count['\r'] && mp->count['\n'] <= (mp->count['\r'] + 1) && mp->count['\r'])
1N/A {
1N/A t = "dos ";
1N/A mp->mime = "text/dos";
1N/A }
1N/A else
1N/A t = "";
1N/A if (code)
1N/A {
1N/A if (code == CC_ASCII)
1N/A sfsprintf(buf, end - buf, "ascii %s%s", t, s);
1N/A else
1N/A {
1N/A sfsprintf(buf, end - buf, "ebcdic%d %s%s", code - 1, t, s);
1N/A mp->mime = "text/ebcdic";
1N/A }
1N/A s = buf;
1N/A }
1N/A else if (*t)
1N/A {
1N/A sfsprintf(buf, end - buf, "%s%s", t, s);
1N/A s = buf;
1N/A }
1N/A return s;
1N/A}
1N/A
1N/A/*
1N/A * return the basic magic string for file,st in buf,size
1N/A */
1N/A
1N/Astatic char*
1N/Atype(register Magic_t* mp, const char* file, struct stat* st, char* buf, char* end)
1N/A{
1N/A register char* s;
1N/A register char* t;
1N/A
1N/A mp->mime = 0;
1N/A if (!S_ISREG(st->st_mode))
1N/A {
1N/A if (S_ISDIR(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/dir";
1N/A return T("directory");
1N/A }
1N/A if (S_ISLNK(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/lnk";
1N/A s = buf;
1N/A s += sfsprintf(s, end - s, T("symbolic link to "));
1N/A if (pathgetlink(file, s, end - s) < 0)
1N/A return T("cannot read symbolic link text");
1N/A return buf;
1N/A }
1N/A if (S_ISBLK(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/blk";
1N/A sfsprintf(buf, PATH_MAX, T("block special (%s)"), fmtdev(st));
1N/A return buf;
1N/A }
1N/A if (S_ISCHR(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/chr";
1N/A sfsprintf(buf, end - buf, T("character special (%s)"), fmtdev(st));
1N/A return buf;
1N/A }
1N/A if (S_ISFIFO(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/fifo";
1N/A return "fifo";
1N/A }
1N/A#ifdef S_ISSOCK
1N/A if (S_ISSOCK(st->st_mode))
1N/A {
1N/A mp->mime = "x-system/sock";
1N/A return "socket";
1N/A }
1N/A#endif
1N/A }
1N/A if (!(mp->fbmx = st->st_size))
1N/A s = T("empty");
1N/A else if (!mp->fp)
1N/A s = T("cannot read");
1N/A else
1N/A {
1N/A mp->fbsz = sfread(mp->fp, mp->fbuf, sizeof(mp->fbuf) - 1);
1N/A if (mp->fbsz < 0)
1N/A s = fmterror(errno);
1N/A else if (mp->fbsz == 0)
1N/A s = T("empty");
1N/A else
1N/A {
1N/A mp->fbuf[mp->fbsz] = 0;
1N/A mp->xoff = 0;
1N/A mp->xbsz = 0;
1N/A if (!(s = ckmagic(mp, file, buf, end, st, 0)))
1N/A s = cklang(mp, file, buf, end, st);
1N/A }
1N/A }
1N/A if (!mp->mime)
1N/A mp->mime = "application/unknown";
1N/A else if ((t = strchr(mp->mime, '%')) && *(t + 1) == 's' && !*(t + 2))
1N/A {
1N/A register char* b;
1N/A register char* be;
1N/A register char* m;
1N/A register char* me;
1N/A
1N/A b = mp->mime;
1N/A me = (m = mp->mime = mp->fbuf) + sizeof(mp->fbuf) - 1;
1N/A while (m < me && b < t)
1N/A *m++ = *b++;
1N/A b = t = s;
1N/A for (;;)
1N/A {
1N/A if (!(be = strchr(t, ' ')))
1N/A {
1N/A be = b + strlen(b);
1N/A break;
1N/A }
1N/A if (*(be - 1) == ',' || strneq(be + 1, "data", 4) || strneq(be + 1, "file", 4))
1N/A break;
1N/A b = t;
1N/A t = be + 1;
1N/A }
1N/A while (m < me && b < be)
1N/A if ((*m++ = *b++) == ' ')
1N/A *(m - 1) = '-';
1N/A *m = 0;
1N/A }
1N/A return s;
1N/A}
1N/A
1N/A/*
1N/A * low level for magicload()
1N/A */
1N/A
1N/Astatic int
1N/Aload(register Magic_t* mp, char* file, register Sfio_t* fp)
1N/A{
1N/A register Entry_t* ep;
1N/A register char* p;
1N/A register char* p2;
1N/A char* p3;
1N/A char* next;
1N/A int n;
1N/A int lge;
1N/A int lev;
1N/A int ent;
1N/A int old;
1N/A int cont;
1N/A Info_t* ip;
1N/A Entry_t* ret;
1N/A Entry_t* first;
1N/A Entry_t* last = 0;
1N/A Entry_t* fun['z' - 'a' + 1];
1N/A
1N/A memzero(fun, sizeof(fun));
1N/A cont = '$';
1N/A ent = 0;
1N/A lev = 0;
1N/A old = 0;
1N/A ret = 0;
1N/A error_info.file = file;
1N/A error_info.line = 0;
1N/A first = ep = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1N/A while (p = sfgetr(fp, '\n', 1))
1N/A {
1N/A error_info.line++;
1N/A for (; isspace(*p); p++);
1N/A
1N/A /*
1N/A * nesting
1N/A */
1N/A
1N/A switch (*p)
1N/A {
1N/A case 0:
1N/A case '#':
1N/A cont = '#';
1N/A continue;
1N/A case '{':
1N/A if (++lev < MAXNEST)
1N/A ep->nest = *p;
1N/A else if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "{ ... } operator nesting too deep -- %d max", MAXNEST);
1N/A continue;
1N/A case '}':
1N/A if (!last || lev <= 0)
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "`%c': invalid nesting", *p);
1N/A }
1N/A else if (lev-- == ent)
1N/A {
1N/A ent = 0;
1N/A ep->cont = ':';
1N/A ep->offset = ret->offset;
1N/A ep->nest = ' ';
1N/A ep->type = ' ';
1N/A ep->op = ' ';
1N/A ep->desc = "[RETURN]";
1N/A last = ep;
1N/A ep = ret->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1N/A ret = 0;
1N/A }
1N/A else
1N/A last->nest = *p;
1N/A continue;
1N/A default:
1N/A if (*(p + 1) == '{' || *(p + 1) == '(' && *p != '+' && *p != '>' && *p != '&' && *p != '|')
1N/A {
1N/A n = *p++;
1N/A if (n >= 'a' && n <= 'z')
1N/A n -= 'a';
1N/A else
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
1N/A n = 0;
1N/A }
1N/A if (ret && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
1N/A if (*p == '{')
1N/A {
1N/A ent = ++lev;
1N/A ret = ep;
1N/A ep->desc = "[FUNCTION]";
1N/A }
1N/A else
1N/A {
1N/A if (*(p + 1) != ')' && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function call argument list", n + 'a');
1N/A ep->desc = "[CALL]";
1N/A }
1N/A ep->cont = cont;
1N/A ep->offset = n;
1N/A ep->nest = ' ';
1N/A ep->type = ' ';
1N/A ep->op = ' ';
1N/A last = ep;
1N/A ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1N/A if (ret)
1N/A fun[n] = last->value.lab = ep;
1N/A else if (!(last->value.lab = fun[n]) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
1N/A continue;
1N/A }
1N/A if (!ep->nest)
1N/A ep->nest = (lev > 0 && lev != ent) ? ('0' + lev - !!ent) : ' ';
1N/A break;
1N/A }
1N/A
1N/A /*
1N/A * continuation
1N/A */
1N/A
1N/A cont = '$';
1N/A switch (*p)
1N/A {
1N/A case '>':
1N/A old = 1;
1N/A if (*(p + 1) == *p)
1N/A {
1N/A /*
1N/A * old style nesting push
1N/A */
1N/A
1N/A p++;
1N/A old = 2;
1N/A if (!lev && last)
1N/A {
1N/A lev = 1;
1N/A last->nest = '{';
1N/A if (last->cont == '>')
1N/A last->cont = '&';
1N/A ep->nest = '1';
1N/A }
1N/A }
1N/A /*FALLTHROUGH*/
1N/A case '+':
1N/A case '&':
1N/A case '|':
1N/A ep->cont = *p++;
1N/A break;
1N/A default:
1N/A if ((mp->flags & MAGIC_VERBOSE) && !isalpha(*p) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "`%c': invalid line continuation operator", *p);
1N/A /*FALLTHROUGH*/
1N/A case '*':
1N/A case '0': case '1': case '2': case '3': case '4':
1N/A case '5': case '6': case '7': case '8': case '9':
1N/A ep->cont = (lev > 0) ? '&' : '#';
1N/A break;
1N/A }
1N/A switch (old)
1N/A {
1N/A case 1:
1N/A old = 0;
1N/A if (lev)
1N/A {
1N/A /*
1N/A * old style nesting pop
1N/A */
1N/A
1N/A lev = 0;
1N/A if (last)
1N/A last->nest = '}';
1N/A ep->nest = ' ';
1N/A if (ep->cont == '&')
1N/A ep->cont = '#';
1N/A }
1N/A break;
1N/A case 2:
1N/A old = 1;
1N/A break;
1N/A }
1N/A if (isdigit(*p))
1N/A {
1N/A /*
1N/A * absolute offset
1N/A */
1N/A
1N/A ep->offset = strton(p, &next, NiL, 0);
1N/A p2 = next;
1N/A }
1N/A else
1N/A {
1N/A for (p2 = p; *p2 && !isspace(*p2); p2++);
1N/A if (!*p2)
1N/A {
1N/A if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
1N/A continue;
1N/A }
1N/A
1N/A /*
1N/A * offset expression
1N/A */
1N/A
1N/A *p2++ = 0;
1N/A ep->expr = vmstrdup(mp->vm, p);
1N/A if (isalpha(*p))
1N/A ep->offset = (ip = (Info_t*)dtmatch(mp->infotab, p)) ? ip->value : 0;
1N/A else if (*p == '(' && ep->cont == '>')
1N/A {
1N/A /*
1N/A * convert old style indirection to @
1N/A */
1N/A
1N/A p = ep->expr + 1;
1N/A for (;;)
1N/A {
1N/A switch (*p++)
1N/A {
1N/A case 0:
1N/A case '@':
1N/A case '(':
1N/A break;
1N/A case ')':
1N/A break;
1N/A default:
1N/A continue;
1N/A }
1N/A break;
1N/A }
1N/A if (*--p == ')')
1N/A {
1N/A *p = 0;
1N/A *ep->expr = '@';
1N/A }
1N/A }
1N/A }
1N/A for (; isspace(*p2); p2++);
1N/A for (p = p2; *p2 && !isspace(*p2); p2++);
1N/A if (!*p2)
1N/A {
1N/A if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "not enough fields: `%s'", p);
1N/A continue;
1N/A }
1N/A *p2++ = 0;
1N/A
1N/A /*
1N/A * type
1N/A */
1N/A
1N/A if ((*p == 'b' || *p == 'l') && *(p + 1) == 'e')
1N/A {
1N/A ep->swap = ~(*p == 'l' ? 7 : 0);
1N/A p += 2;
1N/A }
1N/A if (*p == 's')
1N/A {
1N/A if (*(p + 1) == 'h')
1N/A ep->type = 'h';
1N/A else
1N/A ep->type = 's';
1N/A }
1N/A else if (*p == 'a')
1N/A ep->type = 's';
1N/A else
1N/A ep->type = *p;
1N/A if (p = strchr(p, '&'))
1N/A {
1N/A /*
1N/A * old style mask
1N/A */
1N/A
1N/A ep->mask = strton(++p, NiL, NiL, 0);
1N/A }
1N/A for (; isspace(*p2); p2++);
1N/A if (ep->mask)
1N/A *--p2 = '=';
1N/A
1N/A /*
1N/A * comparison operation
1N/A */
1N/A
1N/A p = p2;
1N/A if (p2 = strchr(p, '\t'))
1N/A *p2++ = 0;
1N/A else
1N/A {
1N/A int qe = 0;
1N/A int qn = 0;
1N/A
1N/A /*
1N/A * assume balanced {}[]()\\""'' field
1N/A */
1N/A
1N/A for (p2 = p;;)
1N/A {
1N/A switch (n = *p2++)
1N/A {
1N/A case 0:
1N/A break;
1N/A case '{':
1N/A if (!qe)
1N/A qe = '}';
1N/A if (qe == '}')
1N/A qn++;
1N/A continue;
1N/A case '(':
1N/A if (!qe)
1N/A qe = ')';
1N/A if (qe == ')')
1N/A qn++;
1N/A continue;
1N/A case '[':
1N/A if (!qe)
1N/A qe = ']';
1N/A if (qe == ']')
1N/A qn++;
1N/A continue;
1N/A case '}':
1N/A case ')':
1N/A case ']':
1N/A if (qe == n && qn > 0)
1N/A qn--;
1N/A continue;
1N/A case '"':
1N/A case '\'':
1N/A if (!qe)
1N/A qe = n;
1N/A else if (qe == n)
1N/A qe = 0;
1N/A continue;
1N/A case '\\':
1N/A if (*p2)
1N/A p2++;
1N/A continue;
1N/A default:
1N/A if (!qe && isspace(n))
1N/A break;
1N/A continue;
1N/A }
1N/A if (n)
1N/A *(p2 - 1) = 0;
1N/A else
1N/A p2--;
1N/A break;
1N/A }
1N/A }
1N/A lge = 0;
1N/A if (ep->type == 'e' || ep->type == 'm' || ep->type == 's')
1N/A ep->op = '=';
1N/A else
1N/A {
1N/A if (*p == '&')
1N/A {
1N/A ep->mask = strton(++p, &next, NiL, 0);
1N/A p = next;
1N/A }
1N/A switch (*p)
1N/A {
1N/A case '=':
1N/A case '>':
1N/A case '<':
1N/A case '*':
1N/A ep->op = *p++;
1N/A if (*p == '=')
1N/A {
1N/A p++;
1N/A switch (ep->op)
1N/A {
1N/A case '>':
1N/A lge = -1;
1N/A break;
1N/A case '<':
1N/A lge = 1;
1N/A break;
1N/A }
1N/A }
1N/A break;
1N/A case '!':
1N/A case '@':
1N/A ep->op = *p++;
1N/A if (*p == '=')
1N/A p++;
1N/A break;
1N/A case 'x':
1N/A p++;
1N/A ep->op = '*';
1N/A break;
1N/A default:
1N/A ep->op = '=';
1N/A if (ep->mask)
1N/A ep->value.num = ep->mask;
1N/A break;
1N/A }
1N/A }
1N/A if (ep->op != '*' && !ep->value.num)
1N/A {
1N/A if (ep->type == 'e')
1N/A {
1N/A if (ep->value.sub = vmnewof(mp->vm, 0, regex_t, 1, 0))
1N/A {
1N/A ep->value.sub->re_disc = &mp->redisc;
1N/A if (!(n = regcomp(ep->value.sub, p, REG_DELIMITED|REG_LENIENT|REG_NULL|REG_DISCIPLINE)))
1N/A {
1N/A p += ep->value.sub->re_npat;
1N/A if (!(n = regsubcomp(ep->value.sub, p, NiL, 0, 0)))
1N/A p += ep->value.sub->re_npat;
1N/A }
1N/A if (n)
1N/A {
1N/A regmessage(mp, ep->value.sub, n);
1N/A ep->value.sub = 0;
1N/A }
1N/A else if (*p && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "invalid characters after substitution: %s", p);
1N/A }
1N/A }
1N/A else if (ep->type == 'm')
1N/A {
1N/A ep->mask = stresc(p) + 1;
1N/A ep->value.str = vmnewof(mp->vm, 0, char, ep->mask + 1, 0);
1N/A memcpy(ep->value.str, p, ep->mask);
1N/A if ((!ep->expr || !ep->offset) && !strmatch(ep->value.str, "\\!\\(*\\)"))
1N/A ep->value.str[ep->mask - 1] = '*';
1N/A }
1N/A else if (ep->type == 's')
1N/A {
1N/A ep->mask = stresc(p);
1N/A ep->value.str = vmnewof(mp->vm, 0, char, ep->mask, 0);
1N/A memcpy(ep->value.str, p, ep->mask);
1N/A }
1N/A else if (*p == '\'')
1N/A {
1N/A stresc(p);
1N/A ep->value.num = *(unsigned char*)(p + 1) + lge;
1N/A }
1N/A else if (strmatch(p, "+([a-z])\\(*\\)"))
1N/A {
1N/A char* t;
1N/A
1N/A t = p;
1N/A ep->type = 'V';
1N/A ep->op = *p;
1N/A while (*p && *p++ != '(');
1N/A switch (ep->op)
1N/A {
1N/A case 'l':
1N/A n = *p++;
1N/A if (n < 'a' || n > 'z')
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: invalid function name", n);
1N/A }
1N/A else if (!fun[n -= 'a'])
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function not defined", n + 'a');
1N/A }
1N/A else
1N/A {
1N/A ep->value.loop = vmnewof(mp->vm, 0, Loop_t, 1, 0);
1N/A ep->value.loop->lab = fun[n];
1N/A while (*p && *p++ != ',');
1N/A ep->value.loop->start = strton(p, &t, NiL, 0);
1N/A while (*t && *t++ != ',');
1N/A ep->value.loop->size = strton(t, &t, NiL, 0);
1N/A }
1N/A break;
1N/A case 'm':
1N/A case 'r':
1N/A ep->desc = vmnewof(mp->vm, 0, char, 32, 0);
1N/A ep->mime = vmnewof(mp->vm, 0, char, 32, 0);
1N/A break;
1N/A case 'v':
1N/A break;
1N/A default:
1N/A if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "%-.*s: unknown function", p - t, t);
1N/A break;
1N/A }
1N/A }
1N/A else
1N/A {
1N/A ep->value.num = strton(p, NiL, NiL, 0) + lge;
1N/A if (ep->op == '@')
1N/A ep->value.num = swapget(0, (char*)&ep->value.num, sizeof(ep->value.num));
1N/A }
1N/A }
1N/A
1N/A /*
1N/A * file description
1N/A */
1N/A
1N/A if (p2)
1N/A {
1N/A for (; isspace(*p2); p2++);
1N/A if (p = strchr(p2, '\t'))
1N/A {
1N/A /*
1N/A * check for message catalog index
1N/A */
1N/A
1N/A *p++ = 0;
1N/A if (isalpha(*p2))
1N/A {
1N/A for (p3 = p2; isalnum(*p3); p3++);
1N/A if (*p3++ == ':')
1N/A {
1N/A for (; isdigit(*p3); p3++);
1N/A if (!*p3)
1N/A {
1N/A for (p2 = p; isspace(*p2); p2++);
1N/A if (p = strchr(p2, '\t'))
1N/A *p++ = 0;
1N/A }
1N/A }
1N/A }
1N/A }
1N/A stresc(p2);
1N/A ep->desc = vmstrdup(mp->vm, p2);
1N/A if (p)
1N/A {
1N/A for (; isspace(*p); p++);
1N/A if (*p)
1N/A ep->mime = vmstrdup(mp->vm, p);
1N/A }
1N/A }
1N/A else
1N/A ep->desc = "";
1N/A
1N/A /*
1N/A * get next entry
1N/A */
1N/A
1N/A last = ep;
1N/A ep = ep->next = vmnewof(mp->vm, 0, Entry_t, 1, 0);
1N/A }
1N/A if (last)
1N/A {
1N/A last->next = 0;
1N/A if (mp->magiclast)
1N/A mp->magiclast->next = first;
1N/A else
1N/A mp->magic = first;
1N/A mp->magiclast = last;
1N/A }
1N/A vmfree(mp->vm, ep);
1N/A if ((mp->flags & MAGIC_VERBOSE) && mp->disc->errorf)
1N/A {
1N/A if (lev < 0)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "too many } operators");
1N/A else if (lev > 0)
1N/A (*mp->disc->errorf)(mp, mp->disc, 1, "not enough } operators");
1N/A if (ret)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "%c: function has no return", ret->offset + 'a');
1N/A }
1N/A error_info.file = 0;
1N/A error_info.line = 0;
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * load a magic file into mp
1N/A */
1N/A
1N/Aint
1N/Amagicload(register Magic_t* mp, const char* file, unsigned long flags)
1N/A{
1N/A register char* s;
1N/A register char* e;
1N/A register char* t;
1N/A int n;
1N/A int found;
1N/A int list;
1N/A Sfio_t* fp;
1N/A
1N/A mp->flags = mp->disc->flags | flags;
1N/A found = 0;
1N/A if (list = !(s = (char*)file) || !*s || (*s == '-' || *s == '.') && !*(s + 1))
1N/A {
1N/A if (!(s = getenv(MAGIC_FILE_ENV)) || !*s)
1N/A s = MAGIC_FILE;
1N/A }
1N/A for (;;)
1N/A {
1N/A if (!list)
1N/A e = 0;
1N/A else if (e = strchr(s, ':'))
1N/A {
1N/A /*
1N/A * ok, so ~ won't work for the last list element
1N/A * we do it for MAGIC_FILES_ENV anyway
1N/A */
1N/A
1N/A if ((strneq(s, "~/", n = 2) || strneq(s, "$HOME/", n = 6) || strneq(s, "${HOME}/", n = 8)) && (t = getenv("HOME")))
1N/A {
1N/A sfputr(mp->tmp, t, -1);
1N/A s += n - 1;
1N/A }
1N/A sfwrite(mp->tmp, s, e - s);
1N/A if (!(s = sfstruse(mp->tmp)))
1N/A goto nospace;
1N/A }
1N/A if (!*s || streq(s, "-"))
1N/A s = MAGIC_FILE;
1N/A if (!(fp = sfopen(NiL, s, "r")))
1N/A {
1N/A if (list)
1N/A {
1N/A if (!(t = pathpath(s, "", PATH_REGULAR|PATH_READ, mp->fbuf, sizeof(mp->fbuf))) && !strchr(s, '/'))
1N/A {
1N/A strcpy(mp->fbuf, s);
1N/A sfprintf(mp->tmp, "%s/%s", MAGIC_DIR, mp->fbuf);
1N/A if (!(s = sfstruse(mp->tmp)))
1N/A goto nospace;
1N/A if (!(t = pathpath(s, "", PATH_REGULAR|PATH_READ, mp->fbuf, sizeof(mp->fbuf))))
1N/A goto next;
1N/A }
1N/A if (!(fp = sfopen(NiL, t, "r")))
1N/A goto next;
1N/A }
1N/A else
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 3, "%s: cannot open magic file", s);
1N/A return -1;
1N/A }
1N/A }
1N/A found = 1;
1N/A n = load(mp, s, fp);
1N/A sfclose(fp);
1N/A if (n && !list)
1N/A return -1;
1N/A next:
1N/A if (!e)
1N/A break;
1N/A s = e + 1;
1N/A }
1N/A if (!found)
1N/A {
1N/A if (mp->flags & MAGIC_VERBOSE)
1N/A {
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 2, "cannot find magic file");
1N/A }
1N/A return -1;
1N/A }
1N/A return 0;
1N/A nospace:
1N/A if (mp->disc->errorf)
1N/A (*mp->disc->errorf)(mp, mp->disc, 3, "out of space");
1N/A return -1;
1N/A}
1N/A
1N/A/*
1N/A * open a magic session
1N/A */
1N/A
1N/AMagic_t*
1N/Amagicopen(Magicdisc_t* disc)
1N/A{
1N/A register Magic_t* mp;
1N/A register int i;
1N/A register int n;
1N/A register int f;
1N/A register int c;
1N/A register Vmalloc_t* vm;
1N/A unsigned char* map[CC_MAPS + 1];
1N/A
1N/A if (!(vm = vmopen(Vmdcheap, Vmbest, 0)))
1N/A return 0;
1N/A if (!(mp = vmnewof(vm, 0, Magic_t, 1, 0)))
1N/A {
1N/A vmclose(vm);
1N/A return 0;
1N/A }
1N/A mp->id = lib;
1N/A mp->disc = disc;
1N/A mp->vm = vm;
1N/A mp->flags = disc->flags;
1N/A mp->redisc.re_version = REG_VERSION;
1N/A mp->redisc.re_flags = REG_NOFREE;
1N/A mp->redisc.re_errorf = (regerror_t)disc->errorf;
1N/A mp->redisc.re_resizef = (regresize_t)vmgetmem;
1N/A mp->redisc.re_resizehandle = (void*)mp->vm;
1N/A mp->dtdisc.key = offsetof(Info_t, name);
1N/A mp->dtdisc.link = offsetof(Info_t, link);
1N/A if (!(mp->tmp = sfstropen()) || !(mp->infotab = dtnew(mp->vm, &mp->dtdisc, Dthash)))
1N/A goto bad;
1N/A for (n = 0; n < elementsof(info); n++)
1N/A dtinsert(mp->infotab, &info[n]);
1N/A for (i = 0; i < CC_MAPS; i++)
1N/A map[i] = ccmap(i, CC_ASCII);
1N/A mp->x2n = ccmap(CC_ALIEN, CC_NATIVE);
1N/A for (n = 0; n <= UCHAR_MAX; n++)
1N/A {
1N/A f = 0;
1N/A i = CC_MAPS;
1N/A while (--i >= 0)
1N/A {
1N/A c = ccmapchr(map[i], n);
1N/A f = (f << CC_BIT) | CCTYPE(c);
1N/A }
1N/A mp->cctype[n] = f;
1N/A }
1N/A return mp;
1N/A bad:
1N/A magicclose(mp);
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * close a magicopen() session
1N/A */
1N/A
1N/Aint
1N/Amagicclose(register Magic_t* mp)
1N/A{
1N/A if (!mp)
1N/A return -1;
1N/A if (mp->tmp)
1N/A sfstrclose(mp->tmp);
1N/A if (mp->vm)
1N/A vmclose(mp->vm);
1N/A return 0;
1N/A}
1N/A
1N/A/*
1N/A * return the magic string for file with optional stat info st
1N/A */
1N/A
1N/Achar*
1N/Amagictype(register Magic_t* mp, Sfio_t* fp, const char* file, register struct stat* st)
1N/A{
1N/A off_t off;
1N/A char* s;
1N/A
1N/A mp->flags = mp->disc->flags;
1N/A mp->mime = 0;
1N/A if (!st)
1N/A s = T("cannot stat");
1N/A else
1N/A {
1N/A if (mp->fp = fp)
1N/A off = sfseek(mp->fp, (off_t)0, SEEK_CUR);
1N/A s = type(mp, file, st, mp->tbuf, &mp->tbuf[sizeof(mp->tbuf)-1]);
1N/A if (mp->fp)
1N/A sfseek(mp->fp, off, SEEK_SET);
1N/A if (!(mp->flags & (MAGIC_MIME|MAGIC_ALL)))
1N/A {
1N/A if (S_ISREG(st->st_mode) && (st->st_size > 0) && (st->st_size < 128))
1N/A sfprintf(mp->tmp, "%s ", T("short"));
1N/A sfprintf(mp->tmp, "%s", s);
1N/A if (!mp->fp && (st->st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
1N/A sfprintf(mp->tmp, ", %s", S_ISDIR(st->st_mode) ? T("searchable") : T("executable"));
1N/A if (st->st_mode & S_ISUID)
1N/A sfprintf(mp->tmp, ", setuid=%s", fmtuid(st->st_uid));
1N/A if (st->st_mode & S_ISGID)
1N/A sfprintf(mp->tmp, ", setgid=%s", fmtgid(st->st_gid));
1N/A if (st->st_mode & S_ISVTX)
1N/A sfprintf(mp->tmp, ", sticky");
1N/A if (!(s = sfstruse(mp->tmp)))
1N/A s = T("out of space");
1N/A }
1N/A }
1N/A if (mp->flags & MAGIC_MIME)
1N/A s = mp->mime;
1N/A if (!s)
1N/A s = T("error");
1N/A return s;
1N/A}
1N/A
1N/A/*
1N/A * list the magic table in mp on sp
1N/A */
1N/A
1N/Aint
1N/Amagiclist(register Magic_t* mp, register Sfio_t* sp)
1N/A{
1N/A register Entry_t* ep = mp->magic;
1N/A register Entry_t* rp = 0;
1N/A
1N/A mp->flags = mp->disc->flags;
1N/A sfprintf(sp, "cont\toffset\ttype\top\tmask\tvalue\tmime\tdesc\n");
1N/A while (ep)
1N/A {
1N/A sfprintf(sp, "%c %c\t", ep->cont, ep->nest);
1N/A if (ep->expr)
1N/A sfprintf(sp, "%s", ep->expr);
1N/A else
1N/A sfprintf(sp, "%ld", ep->offset);
1N/A sfprintf(sp, "\t%s%c\t%c\t%lo\t", ep->swap == (char)~3 ? "L" : ep->swap == (char)~0 ? "B" : "", ep->type, ep->op, ep->mask);
1N/A switch (ep->type)
1N/A {
1N/A case 'm':
1N/A case 's':
1N/A sfputr(sp, fmtesc(ep->value.str), -1);
1N/A break;
1N/A case 'V':
1N/A switch (ep->op)
1N/A {
1N/A case 'l':
1N/A sfprintf(sp, "loop(%d,%d,%d,%d)", ep->value.loop->start, ep->value.loop->size, ep->value.loop->count, ep->value.loop->offset);
1N/A break;
1N/A case 'v':
1N/A sfprintf(sp, "vcodex()");
1N/A break;
1N/A default:
1N/A sfprintf(sp, "%p", ep->value.str);
1N/A break;
1N/A }
1N/A break;
1N/A default:
1N/A sfprintf(sp, "%lo", ep->value.num);
1N/A break;
1N/A }
1N/A sfprintf(sp, "\t%s\t%s\n", ep->mime ? ep->mime : "", fmtesc(ep->desc));
1N/A if (ep->cont == '$' && !ep->value.lab->mask)
1N/A {
1N/A rp = ep;
1N/A ep = ep->value.lab;
1N/A }
1N/A else
1N/A {
1N/A if (ep->cont == ':')
1N/A {
1N/A ep = rp;
1N/A ep->value.lab->mask = 1;
1N/A }
1N/A ep = ep->next;
1N/A }
1N/A }
1N/A return 0;
1N/A}