-modunconnomods # 12 ctype.h
-mustmod # 1 mman.h
-noparams # 3 getopt_long
-#-nullderef # 4
+-nullderef # 4
-nullpass # 27
-nullptrarith # 1
-redef # 4
/*@unchecked@*/
struct mlist mlist;
-
-static int getvalue(struct magic *m, /*@out@*/ char **p)
- /*@globals fileSystem @*/
- /*@modifies m, *p, fileSystem @*/;
-static int hextoint(int c)
- /*@*/;
-static char *getstr(/*@returned@*/ char *s, char *p, int plen,
- /*@out@*/ int * slen)
- /*@globals fileSystem @*/
- /*@modifies *p, *slen, fileSystem @*/;
-static int parse(/*@out@*/ struct magic **magicp,
- /*@out@*/ uint32_t *nmagicp, char *l, int action)
- /*@globals maxmagic, fileSystem @*/
- /*@modifies *magicp, *nmagicp, maxmagic, fileSystem @*/;
-static void eatsize(/*@out@*/ char **p)
- /*@modifies *p @*/;
-static int apprentice_1(const char *fn, int action)
- /*@globals lineno, mlist, fileSystem, internalState @*/
- /*@modifies lineno, mlist, fileSystem, internalState @*/;
-static int apprentice_file(/*@out@*/ struct magic **magicp,
- /*@out@*/ uint32_t *nmagicp, const char *fn, int action)
- /*@globals lineno, maxmagic, fileSystem @*/
- /*@modifies *magicp, *nmagicp, lineno, maxmagic, fileSystem @*/;
-static void byteswap(struct magic *m, uint32_t nmagic)
- /*@modifies m @*/;
-static void bs1(struct magic *m)
- /*@modifies m @*/;
-static uint16_t swap2(uint16_t sv)
- /*@*/;
-static uint32_t swap4(uint32_t sv)
- /*@*/;
-/*@null@*/
-static char *mkdbname(const char *fn)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-static int apprentice_map(/*@out@*/ struct magic **magicp,
- /*@out@*/ uint32_t *nmagicp, const char *fn, int action)
- /*@globals fileSystem, internalState @*/
- /*@modifies *magicp, *nmagicp, fileSystem, internalState @*/;
-static int apprentice_compile(/*@out@*/ struct magic **magicp,
- /*@out@*/ uint32_t *nmagicp, const char *fn, int action)
- /*@globals fileSystem, internalState @*/
- /*@modifies fileSystem, internalState @*/;
-
-#ifdef COMPILE_ONLY
-const char *magicfile;
-char *progname;
-int lineno;
-
-int main(int argc, char *argv[])
- /*@*/;
-
-int
-main(int argc, char *argv[])
+/*
+ * extend the sign bit if the comparison is to be signed
+ */
+uint32_t
+signextend(struct magic *m, uint32_t v)
{
- int ret;
-
- if ((progname = strrchr(argv[0], '/')) != NULL)
- progname++;
- else
- progname = argv[0];
-
- if (argc != 2) {
- (void)fprintf(stderr, "usage: %s file\n", progname);
- exit(1);
- }
- magicfile = argv[1];
-
- exit(apprentice(magicfile, COMPILE));
+ if (!(m->flag & UNSIGNED))
+ switch(m->type) {
+ /*
+ * Do not remove the casts below. They are
+ * vital. When later compared with the data,
+ * the sign extension must have happened.
+ */
+ case BYTE:
+ v = (char) v;
+ break;
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = (short) v;
+ break;
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ case LDATE:
+ case BELDATE:
+ case LELDATE:
+ case LONG:
+ case BELONG:
+ case LELONG:
+ v = (int32_t) v;
+ break;
+ case STRING:
+ case PSTRING:
+ break;
+ case REGEX:
+ break;
+ default:
+ magwarn("can't happen: m->type=%d\n", m->type);
+ return -1;
+ }
+ return v;
}
-#endif /* COMPILE_ONLY */
-
/*
- * Handle one file.
+ * eatsize(): Eat the size spec from a number [eg. 10UL]
*/
-static int
-apprentice_1(const char *fn, int action)
+static void
+eatsize(/*@out@*/ char **p)
+ /*@modifies *p @*/
{
-/*@-shadow@*/
- struct magic *magic = NULL;
- uint32_t nmagic = 0;
-/*@=shadow@*/
- struct mlist *ml;
- int rv = -1;
+ char *l = *p;
- if (action == COMPILE) {
- rv = apprentice_file(&magic, &nmagic, fn, action);
- if (rv == 0)
- return apprentice_compile(&magic, &nmagic, fn, action);
- else
- return rv;
- }
-#ifndef COMPILE_ONLY
- if ((rv = apprentice_map(&magic, &nmagic, fn, action)) != 0)
- (void)fprintf(stderr, "%s: Using regular magic file `%s'\n",
- progname, fn);
-
- if (rv != 0)
- rv = apprentice_file(&magic, &nmagic, fn, action);
+ if (LOWCASE(*l) == 'u')
+ l++;
- if (rv != 0)
- return rv;
-
- if ((ml = malloc(sizeof(*ml))) == NULL) {
- (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
- strerror(errno));
- if (action == CHECK)
- return -1;
+ switch (LOWCASE(*l)) {
+ case 'l': /* long */
+ case 's': /* short */
+ case 'h': /* short */
+ case 'b': /* char/byte */
+ case 'c': /* char/byte */
+ l++;
+ /*@fallthrough@*/
+ default:
+ break;
}
- if (magic == NULL || nmagic == 0)
- return rv;
-
- ml->magic = magic;
- ml->nmagic = nmagic;
-
- mlist.prev->next = ml;
- ml->prev = mlist.prev;
-/*@-immediatetrans@*/
- ml->next = &mlist;
-/*@=immediatetrans@*/
-/*@-kepttrans@*/
- mlist.prev = ml;
-/*@=kepttrans@*/
-
-/*@-compdef@*/
- return rv;
-/*@=compdef@*/
-#endif /* COMPILE_ONLY */
+ *p = l;
}
-
-/* const char *fn: list of magic files */
-int
-apprentice(const char *fn, int action)
+/* Single hex char to int; -1 if not a hex char. */
+static int
+hextoint(int c)
+ /*@*/
{
- char *p, *mfn;
- int file_err, errs = -1;
-
-/*@-immediatetrans@*/
- mlist.next = &mlist;
- mlist.prev = &mlist;
-/*@=immediatetrans@*/
- mfn = malloc(strlen(fn)+1);
- if (mfn == NULL) {
- (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
- strerror(errno));
-/*@-compmempass@*/
- if (action == CHECK)
- return -1;
- else
- exit(EXIT_FAILURE);
-/*@=compmempass@*/
- }
- fn = strcpy(mfn, fn);
-
-/*@-branchstate@*/
- while (fn != NULL) {
- p = strchr(fn, PATHSEP);
- if (p != NULL)
- *p++ = '\0';
- file_err = apprentice_1(fn, action);
- if (file_err > errs)
- errs = file_err;
- fn = p;
- }
-/*@=branchstate@*/
- if (errs == -1)
- (void) fprintf(stderr, "%s: couldn't find any magic files!\n",
- progname);
- if (action == CHECK && errs)
- exit(EXIT_FAILURE);
-
- free(mfn);
-/*@-compdef -compmempass@*/
- return errs;
-/*@=compdef =compmempass@*/
+ if (!isascii((unsigned char) c))
+ return -1;
+ if (isdigit((unsigned char) c))
+ return c - '0';
+ if ((c >= 'a')&&(c <= 'f'))
+ return c + 10 - 'a';
+ if (( c>= 'A')&&(c <= 'F'))
+ return c + 10 - 'A';
+ return -1;
}
/*
- * parse from a file
- * const char *fn: name of magic file
+ * Convert a string containing C character escapes. Stop at an unescaped
+ * space or tab.
+ * Copy the converted version to "p", returning its length in *slen.
+ * Return updated scan pointer as function result.
*/
-static int
-apprentice_file(struct magic **magicp, uint32_t *nmagicp, const char *fn,
- int action)
+static char *
+getstr(/*@returned@*/ char *s, char *p, int plen, /*@out@*/ int *slen)
+ /*@globals fileSystem @*/
+ /*@modifies *p, *slen, fileSystem @*/
{
- static const char hdr[] =
- "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
- FILE *f;
- char line[BUFSIZ+1];
- int errs = 0;
+ char *origs = s, *origp = p;
+ char *pmax = p + plen - 1;
+ int c;
+ int val;
- f = fopen(fn, "r");
- if (f == NULL) {
- if (errno != ENOENT)
- (void) fprintf(stderr,
- "%s: can't read magic file %s (%s)\n",
- progname, fn, strerror(errno));
- return -1;
- }
+ while ((c = *s++) != '\0') {
+ if (isspace((unsigned char) c))
+ break;
+ if (p >= pmax) {
+ fprintf(stderr, "String too long: %s\n", origs);
+ break;
+ }
+ if(c == '\\') {
+ switch(c = *s++) {
- maxmagic = MAXMAGIS;
- *magicp = (struct magic *) calloc(sizeof(struct magic), maxmagic);
- if (*magicp == NULL) {
- (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
- strerror(errno));
- if (action == CHECK)
- return -1;
- }
+ case '\0':
+ goto out;
- /* parse it */
- if (action == CHECK) /* print silly verbose header for USG compat. */
- (void) printf("%s\n", hdr);
+ default:
+ *p++ = (char) c;
+ /*@switchbreak@*/ break;
- for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) {
- if (line[0]=='#') /* comment, do not parse */
- continue;
- if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */
- continue;
- line[strlen(line)-1] = '\0'; /* delete newline */
- if (parse(magicp, nmagicp, line, action) != 0)
- errs = 1;
- }
+ case 'n':
+ *p++ = '\n';
+ /*@switchbreak@*/ break;
- (void) fclose(f);
- if (errs) {
- free(*magicp);
- *magicp = NULL;
- *nmagicp = 0;
+ case 'r':
+ *p++ = '\r';
+ /*@switchbreak@*/ break;
+
+ case 'b':
+ *p++ = '\b';
+ /*@switchbreak@*/ break;
+
+ case 't':
+ *p++ = '\t';
+ /*@switchbreak@*/ break;
+
+ case 'f':
+ *p++ = '\f';
+ /*@switchbreak@*/ break;
+
+ case 'v':
+ *p++ = '\v';
+ /*@switchbreak@*/ break;
+
+ /* \ and up to 3 octal digits */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = c - '0';
+ c = *s++; /* try for 2 */
+ if(c >= '0' && c <= '7') {
+ val = (val<<3) | (c - '0');
+ c = *s++; /* try for 3 */
+ if(c >= '0' && c <= '7')
+ val = (val<<3) | (c-'0');
+ else
+ --s;
+ }
+ else
+ --s;
+ *p++ = (char)val;
+ /*@switchbreak@*/ break;
+
+ /* \x and up to 2 hex digits */
+ case 'x':
+ val = 'x'; /* Default if no digits */
+ c = hextoint(*s++); /* Get next char */
+ if (c >= 0) {
+ val = c;
+ c = hextoint(*s++);
+ if (c >= 0)
+ val = (val << 4) + c;
+ else
+ --s;
+ } else
+ --s;
+ *p++ = (char)val;
+ /*@switchbreak@*/ break;
+ }
+ } else
+ *p++ = (char)c;
}
- return errs;
+out:
+ *p = '\0';
+ *slen = p - origp;
+ return s;
}
-/*
- * extend the sign bit if the comparison is to be signed
+/*
+ * Read a numeric value from a pointer, into the value union of a magic
+ * pointer, according to the magic type. Update the string pointer to point
+ * just after the number read. Return 0 for success, non-zero for failure.
*/
-uint32_t
-signextend(struct magic *m, uint32_t v)
+static int
+getvalue(struct magic *m, /*@out@*/ char **p)
+ /*@globals fileSystem @*/
+ /*@modifies m, *p, fileSystem @*/
{
- if (!(m->flag & UNSIGNED))
- switch(m->type) {
- /*
- * Do not remove the casts below. They are
- * vital. When later compared with the data,
- * the sign extension must have happened.
- */
- case BYTE:
- v = (char) v;
- break;
- case SHORT:
- case BESHORT:
- case LESHORT:
- v = (short) v;
- break;
- case DATE:
- case BEDATE:
- case LEDATE:
- case LDATE:
- case BELDATE:
- case LELDATE:
- case LONG:
- case BELONG:
- case LELONG:
- v = (int32_t) v;
- break;
- case STRING:
- case PSTRING:
- break;
- case REGEX:
- break;
- default:
- magwarn("can't happen: m->type=%d\n",
- m->type);
- return -1;
+ int slen;
+
+ if (m->type == STRING || m->type == PSTRING || m->type == REGEX) {
+ *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
+ m->vallen = slen;
+ } else
+ if (m->reln != 'x') {
+ m->value.l = signextend(m, strtoul(*p, p, 0));
+ eatsize(p);
}
- return v;
+ return 0;
}
/*
* parse one line from magic file, put into magic[index++] if valid
*/
static int
-parse(struct magic **magicp, uint32_t *nmagicp, char *l, int action)
+parse(/*@out@*/ struct magic **magicp, /*@out@*/ uint32_t *nmagicp,
+ char *l, int action)
+ /*@globals maxmagic, fileSystem @*/
+ /*@modifies *magicp, *nmagicp, maxmagic, fileSystem @*/
{
int i = 0;
struct magic *m;
case CHAR_IGNORE_LOWERCASE:
m->mask |= STRING_IGNORE_LOWERCASE;
/*@switchbreak@*/ break;
- case CHAR_COMPACT_BLANK:
- m->mask |= STRING_COMPACT_BLANK;
- /*@switchbreak@*/ break;
- case CHAR_COMPACT_OPTIONAL_BLANK:
- m->mask |=
- STRING_COMPACT_OPTIONAL_BLANK;
- /*@switchbreak@*/ break;
- default:
- magwarn("string extension %c invalid",
- *l);
- return -1;
- }
- }
- }
- break;
- }
- /* We used to set mask to all 1's here, instead let's just not do anything
- if mask = 0 (unless you have a better idea) */
- EATAB;
-
- switch (*l) {
- case '>':
- case '<':
- /* Old-style anding: "0 byte &0x80 dynamically linked" */
- case '&':
- case '^':
- case '=':
- m->reln = *l;
- ++l;
- if (*l == '=') {
- /* HP compat: ignore &= etc. */
- ++l;
- }
- break;
- case '!':
- if (m->type != STRING && m->type != PSTRING) {
- m->reln = *l;
- ++l;
- break;
- }
- /*@fallthrough@*/
- default:
- if (*l == 'x' && isascii((unsigned char)l[1]) &&
- isspace((unsigned char)l[1])) {
- m->reln = *l;
- ++l;
- goto GetDesc; /* Bill The Cat */
- }
- m->reln = '=';
- break;
- }
- EATAB;
-
- if (getvalue(m, &l))
- return -1;
- /*
- * TODO finish this macro and start using it!
- * #define offsetcheck {if (offset > HOWMANY-1)
- * magwarn("offset too big"); }
- */
-
- /*
- * now get last part - the description
- */
-GetDesc:
- EATAB;
- if (l[0] == '\b') {
- ++l;
- m->nospflag = 1;
- } else if ((l[0] == '\\') && (l[1] == 'b')) {
- ++l;
- ++l;
- m->nospflag = 1;
- } else
- m->nospflag = 0;
- while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
- {};
-
-#ifndef COMPILE_ONLY
- if (action == CHECK) {
- mdump(m);
- }
-#endif
- ++(*nmagicp); /* make room for next */
- return 0;
-}
-
-/*
- * Read a numeric value from a pointer, into the value union of a magic
- * pointer, according to the magic type. Update the string pointer to point
- * just after the number read. Return 0 for success, non-zero for failure.
- */
-static int
-getvalue(struct magic *m, char **p)
-{
- int slen;
-
- if (m->type == STRING || m->type == PSTRING || m->type == REGEX) {
- *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
- m->vallen = slen;
- } else
- if (m->reln != 'x') {
- m->value.l = signextend(m, strtoul(*p, p, 0));
- eatsize(p);
- }
- return 0;
-}
-
-/*
- * Convert a string containing C character escapes. Stop at an unescaped
- * space or tab.
- * Copy the converted version to "p", returning its length in *slen.
- * Return updated scan pointer as function result.
- */
-static char *
-getstr(char *s, char *p, int plen, int *slen)
-{
- char *origs = s, *origp = p;
- char *pmax = p + plen - 1;
- int c;
- int val;
-
- while ((c = *s++) != '\0') {
- if (isspace((unsigned char) c))
- break;
- if (p >= pmax) {
- fprintf(stderr, "String too long: %s\n", origs);
- break;
- }
- if(c == '\\') {
- switch(c = *s++) {
-
- case '\0':
- goto out;
-
- default:
- *p++ = (char) c;
- /*@switchbreak@*/ break;
-
- case 'n':
- *p++ = '\n';
- /*@switchbreak@*/ break;
-
- case 'r':
- *p++ = '\r';
- /*@switchbreak@*/ break;
-
- case 'b':
- *p++ = '\b';
- /*@switchbreak@*/ break;
-
- case 't':
- *p++ = '\t';
- /*@switchbreak@*/ break;
-
- case 'f':
- *p++ = '\f';
- /*@switchbreak@*/ break;
-
- case 'v':
- *p++ = '\v';
- /*@switchbreak@*/ break;
-
- /* \ and up to 3 octal digits */
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- val = c - '0';
- c = *s++; /* try for 2 */
- if(c >= '0' && c <= '7') {
- val = (val<<3) | (c - '0');
- c = *s++; /* try for 3 */
- if(c >= '0' && c <= '7')
- val = (val<<3) | (c-'0');
- else
- --s;
+ case CHAR_COMPACT_BLANK:
+ m->mask |= STRING_COMPACT_BLANK;
+ /*@switchbreak@*/ break;
+ case CHAR_COMPACT_OPTIONAL_BLANK:
+ m->mask |=
+ STRING_COMPACT_OPTIONAL_BLANK;
+ /*@switchbreak@*/ break;
+ default:
+ magwarn("string extension %c invalid",
+ *l);
+ return -1;
}
- else
- --s;
- *p++ = (char)val;
- /*@switchbreak@*/ break;
-
- /* \x and up to 2 hex digits */
- case 'x':
- val = 'x'; /* Default if no digits */
- c = hextoint(*s++); /* Get next char */
- if (c >= 0) {
- val = c;
- c = hextoint(*s++);
- if (c >= 0)
- val = (val << 4) + c;
- else
- --s;
- } else
- --s;
- *p++ = (char)val;
- /*@switchbreak@*/ break;
}
- } else
- *p++ = (char)c;
+ }
+ break;
}
-out:
- *p = '\0';
- *slen = p - origp;
- return s;
-}
+ /* We used to set mask to all 1's here, instead let's just not do anything
+ if mask = 0 (unless you have a better idea) */
+ EATAB;
+
+ switch (*l) {
+ case '>':
+ case '<':
+ /* Old-style anding: "0 byte &0x80 dynamically linked" */
+ case '&':
+ case '^':
+ case '=':
+ m->reln = *l;
+ ++l;
+ if (*l == '=') {
+ /* HP compat: ignore &= etc. */
+ ++l;
+ }
+ break;
+ case '!':
+ if (m->type != STRING && m->type != PSTRING) {
+ m->reln = *l;
+ ++l;
+ break;
+ }
+ /*@fallthrough@*/
+ default:
+ if (*l == 'x' && isascii((unsigned char)l[1]) &&
+ isspace((unsigned char)l[1])) {
+ m->reln = *l;
+ ++l;
+ goto GetDesc; /* Bill The Cat */
+ }
+ m->reln = '=';
+ break;
+ }
+ EATAB;
+
+ if (getvalue(m, &l))
+ return -1;
+ /*
+ * TODO finish this macro and start using it!
+ * #define offsetcheck {if (offset > HOWMANY-1)
+ * magwarn("offset too big"); }
+ */
+ /*
+ * now get last part - the description
+ */
+GetDesc:
+ EATAB;
+ if (l[0] == '\b') {
+ ++l;
+ m->nospflag = 1;
+ } else if ((l[0] == '\\') && (l[1] == 'b')) {
+ ++l;
+ ++l;
+ m->nospflag = 1;
+ } else
+ m->nospflag = 0;
+ while ((m->desc[i++] = *l++) != '\0' && i<MAXDESC)
+ {};
-/* Single hex char to int; -1 if not a hex char. */
-static int
-hextoint(int c)
-{
- if (!isascii((unsigned char) c))
- return -1;
- if (isdigit((unsigned char) c))
- return c - '0';
- if ((c >= 'a')&&(c <= 'f'))
- return c + 10 - 'a';
- if (( c>= 'A')&&(c <= 'F'))
- return c + 10 - 'A';
- return -1;
+#ifndef COMPILE_ONLY
+ if (action == CHECK) {
+ mdump(m);
+ }
+#endif
+ ++(*nmagicp); /* make room for next */
+ return 0;
}
-
/*
* Print a string containing C character escapes.
*/
}
/*
- * eatsize(): Eat the size spec from a number [eg. 10UL]
+ * swap a short
+ */
+static uint16_t
+swap2(uint16_t sv)
+ /*@*/
+{
+ uint16_t rv;
+ uint8_t *s = (uint8_t *) &sv;
+ uint8_t *d = (uint8_t *) &rv;
+ d[0] = s[1];
+ d[1] = s[0];
+ return rv;
+}
+
+/*
+ * swap an int
+ */
+static uint32_t
+swap4(uint32_t sv)
+ /*@*/
+{
+ uint32_t rv;
+ uint8_t *s = (uint8_t *) &sv;
+ uint8_t *d = (uint8_t *) &rv;
+ d[0] = s[3];
+ d[1] = s[2];
+ d[2] = s[1];
+ d[3] = s[0];
+ return rv;
+}
+
+/*
+ * byteswap a single magic entry
+ */
+static
+void bs1(struct magic *m)
+ /*@modifies m @*/
+{
+ m->cont_level = swap2(m->cont_level);
+ m->offset = swap4(m->offset);
+ m->in_offset = swap4(m->in_offset);
+ if (m->type != STRING)
+ m->value.l = swap4(m->value.l);
+ m->mask = swap4(m->mask);
+}
+
+/*
+ * Byteswap an mmap'ed file if needed
+ */
+static void
+byteswap(struct magic *m, uint32_t nmagic)
+ /*@modifies m @*/
+{
+ uint32_t i;
+ for (i = 0; i < nmagic; i++)
+ bs1(&m[i]);
+}
+
+/*
+ * make a dbname
+ */
+/*@null@*/
+static char *
+mkdbname(const char *fn)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
+{
+ static const char ext[] = ".mgc";
+ /*@only@*/
+ static char *buf = NULL;
+ size_t len = strlen(fn) + sizeof(ext) + 1;
+ if (buf == NULL)
+ buf = malloc(len);
+ else
+ buf = realloc(buf, len);
+ if (buf == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
+ strerror(errno));
+ return NULL;
+ }
+ (void)strcpy(buf, fn);
+ (void)strcat(buf, ext);
+ return buf;
+}
+
+/*
+ * parse from a file
+ * const char *fn: name of magic file
+ */
+static int
+apprentice_file(/*@out@*/ struct magic **magicp, /*@out@*/ uint32_t *nmagicp,
+ const char *fn, int action)
+ /*@globals lineno, maxmagic, fileSystem @*/
+ /*@modifies *magicp, *nmagicp, lineno, maxmagic, fileSystem @*/
+{
+ /*@observer@*/
+ static const char hdr[] =
+ "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
+ FILE *f;
+ char line[BUFSIZ+1];
+ int errs = 0;
+
+ f = fopen(fn, "r");
+ if (f == NULL) {
+ if (errno != ENOENT)
+ (void) fprintf(stderr,
+ "%s: can't read magic file %s (%s)\n",
+ progname, fn, strerror(errno));
+ return -1;
+ }
+
+ maxmagic = MAXMAGIS;
+ *magicp = (struct magic *) calloc(sizeof(struct magic), maxmagic);
+ if (*magicp == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
+ strerror(errno));
+ if (action == CHECK)
+ return -1;
+ }
+
+ /* parse it */
+ if (action == CHECK) /* print silly verbose header for USG compat. */
+ (void) printf("%s\n", hdr);
+
+ for (lineno = 1;fgets(line, BUFSIZ, f) != NULL; lineno++) {
+ if (line[0]=='#') /* comment, do not parse */
+ continue;
+ if (strlen(line) <= (unsigned)1) /* null line, garbage, etc */
+ continue;
+ line[strlen(line)-1] = '\0'; /* delete newline */
+ if (parse(magicp, nmagicp, line, action) != 0)
+ errs = 1;
+ }
+
+ (void) fclose(f);
+ if (errs) {
+ free(*magicp);
+ *magicp = NULL;
+ *nmagicp = 0;
+ }
+ return errs;
+}
+
+/*
+ * handle an mmaped file.
*/
-static void
-eatsize(char **p)
+static int
+apprentice_compile(/*@out@*/ struct magic **magicp, /*@out@*/ uint32_t *nmagicp,
+ const char *fn, /*@unused@*/ int action)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies fileSystem, internalState @*/
{
- char *l = *p;
+ int fd;
+ char *dbname = mkdbname(fn);
+ static const uint32_t ar[] = {
+ MAGICNO, VERSIONNO
+ };
- if (LOWCASE(*l) == 'u')
- l++;
+ if (dbname == NULL)
+ return -1;
- switch (LOWCASE(*l)) {
- case 'l': /* long */
- case 's': /* short */
- case 'h': /* short */
- case 'b': /* char/byte */
- case 'c': /* char/byte */
- l++;
- /*@fallthrough@*/
- default:
- break;
+ if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
+ (void)fprintf(stderr, "%s: Cannot open `%s' (%s)\n",
+ progname, dbname, strerror(errno));
+ return -1;
}
- *p = l;
+ if (write(fd, ar, sizeof(ar)) != sizeof(ar)) {
+ (void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
+ progname, dbname, strerror(errno));
+ return -1;
+ }
+
+ if (lseek(fd, sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) {
+ (void)fprintf(stderr, "%s: error seeking `%s' (%s)\n",
+ progname, dbname, strerror(errno));
+ return -1;
+ }
+
+ if (write(fd, *magicp, sizeof(struct magic) * *nmagicp)
+ != sizeof(struct magic) * *nmagicp) {
+ (void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
+ progname, dbname, strerror(errno));
+ return -1;
+ }
+
+ (void)close(fd);
+ return 0;
}
/*
* handle a compiled file.
*/
static int
-apprentice_map(struct magic **magicp, uint32_t *nmagicp, const char *fn,
- /*@unused@*/ int action)
+apprentice_map(/*@out@*/ struct magic **magicp, /*@out@*/ uint32_t *nmagicp,
+ const char *fn, /*@unused@*/ int action)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies *magicp, *nmagicp, fileSystem, internalState @*/
{
int fd;
struct stat st;
}
/*
- * handle an mmaped file.
+ * Handle one file.
*/
static int
-apprentice_compile(struct magic **magicp, uint32_t *nmagicp, const char *fn,
- /*@unused@*/ int action)
+apprentice_1(const char *fn, int action)
+ /*@globals lineno, mlist, fileSystem, internalState @*/
+ /*@modifies lineno, mlist, fileSystem, internalState @*/
{
- int fd;
- char *dbname = mkdbname(fn);
- static const uint32_t ar[] = {
- MAGICNO, VERSIONNO
- };
-
- if (dbname == NULL)
- return -1;
+/*@-shadow@*/
+ struct magic *magic = NULL;
+ uint32_t nmagic = 0;
+/*@=shadow@*/
+ struct mlist *ml;
+ int rv = -1;
- if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
- (void)fprintf(stderr, "%s: Cannot open `%s' (%s)\n",
- progname, dbname, strerror(errno));
- return -1;
+ if (action == COMPILE) {
+ rv = apprentice_file(&magic, &nmagic, fn, action);
+ if (rv == 0)
+ return apprentice_compile(&magic, &nmagic, fn, action);
+ else
+ return rv;
}
+#ifndef COMPILE_ONLY
+ if ((rv = apprentice_map(&magic, &nmagic, fn, action)) != 0)
+ (void)fprintf(stderr, "%s: Using regular magic file `%s'\n",
+ progname, fn);
+
+ if (rv != 0)
+ rv = apprentice_file(&magic, &nmagic, fn, action);
- if (write(fd, ar, sizeof(ar)) != sizeof(ar)) {
- (void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
- progname, dbname, strerror(errno));
- return -1;
+ if (rv != 0)
+ return rv;
+
+ if ((ml = malloc(sizeof(*ml))) == NULL) {
+ (void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
+ strerror(errno));
+ if (action == CHECK)
+ return -1;
}
- if (lseek(fd, sizeof(struct magic), SEEK_SET) != sizeof(struct magic)) {
- (void)fprintf(stderr, "%s: error seeking `%s' (%s)\n",
- progname, dbname, strerror(errno));
- return -1;
- }
+ if (magic == NULL || nmagic == 0)
+ return rv;
- if (write(fd, *magicp, sizeof(struct magic) * *nmagicp)
- != sizeof(struct magic) * *nmagicp) {
- (void)fprintf(stderr, "%s: error writing `%s' (%s)\n",
- progname, dbname, strerror(errno));
- return -1;
- }
+ ml->magic = magic;
+ ml->nmagic = nmagic;
- (void)close(fd);
- return 0;
+ mlist.prev->next = ml;
+ ml->prev = mlist.prev;
+/*@-immediatetrans@*/
+ ml->next = &mlist;
+/*@=immediatetrans@*/
+/*@-kepttrans@*/
+ mlist.prev = ml;
+/*@=kepttrans@*/
+
+/*@-compdef@*/
+ return rv;
+/*@=compdef@*/
+#endif /* COMPILE_ONLY */
}
-/*
- * make a dbname
- */
-static char *
-mkdbname(const char *fn)
+/* const char *fn: list of magic files */
+int
+apprentice(const char *fn, int action)
{
- static const char ext[] = ".mgc";
- /*@only@*/
- static char *buf = NULL;
- size_t len = strlen(fn) + sizeof(ext) + 1;
- if (buf == NULL)
- buf = malloc(len);
- else
- buf = realloc(buf, len);
- if (buf == NULL) {
+ char *p, *mfn;
+ int file_err, errs = -1;
+
+/*@-immediatetrans@*/
+ mlist.next = &mlist;
+ mlist.prev = &mlist;
+/*@=immediatetrans@*/
+ mfn = malloc(strlen(fn)+1);
+ if (mfn == NULL) {
(void) fprintf(stderr, "%s: Out of memory (%s).\n", progname,
strerror(errno));
- return NULL;
+/*@-compmempass@*/
+ if (action == CHECK)
+ return -1;
+ else
+ exit(EXIT_FAILURE);
+/*@=compmempass@*/
}
- (void)strcpy(buf, fn);
- (void)strcat(buf, ext);
- return buf;
-}
+ fn = strcpy(mfn, fn);
+
+/*@-branchstate@*/
+ while (fn != NULL) {
+ p = strchr(fn, PATHSEP);
+ if (p != NULL)
+ *p++ = '\0';
+ file_err = apprentice_1(fn, action);
+ if (file_err > errs)
+ errs = file_err;
+ fn = p;
+ }
+/*@=branchstate@*/
+ if (errs == -1)
+ (void) fprintf(stderr, "%s: couldn't find any magic files!\n",
+ progname);
+ if (action == CHECK && errs)
+ exit(EXIT_FAILURE);
-/*
- * Byteswap an mmap'ed file if needed
- */
-static void
-byteswap(struct magic *m, uint32_t nmagic)
-{
- uint32_t i;
- for (i = 0; i < nmagic; i++)
- bs1(&m[i]);
+ free(mfn);
+/*@-compdef -compmempass@*/
+ return errs;
+/*@=compdef =compmempass@*/
}
-/*
- * swap a short
- */
-static uint16_t
-swap2(uint16_t sv)
-{
- uint16_t rv;
- uint8_t *s = (uint8_t *) &sv;
- uint8_t *d = (uint8_t *) &rv;
- d[0] = s[1];
- d[1] = s[0];
- return rv;
-}
+#ifdef COMPILE_ONLY
+const char *magicfile;
+char *progname;
+int lineno;
-/*
- * swap an int
- */
-static uint32_t
-swap4(uint32_t sv)
+int
+main(int argc, char *argv[])
+ /*@*/
{
- uint32_t rv;
- uint8_t *s = (uint8_t *) &sv;
- uint8_t *d = (uint8_t *) &rv;
- d[0] = s[3];
- d[1] = s[2];
- d[2] = s[1];
- d[3] = s[0];
- return rv;
-}
+ int ret;
-/*
- * byteswap a single magic entry
- */
-static
-void bs1(struct magic *m)
-{
- m->cont_level = swap2(m->cont_level);
- m->offset = swap4(m->offset);
- m->in_offset = swap4(m->in_offset);
- if (m->type != STRING)
- m->value.l = swap4(m->value.l);
- m->mask = swap4(m->mask);
+ if ((progname = strrchr(argv[0], '/')) != NULL)
+ progname++;
+ else
+ progname = argv[0];
+
+ if (argc != 2) {
+ (void)fprintf(stderr, "usage: %s file\n", progname);
+ exit(1);
+ }
+ magicfile = argv[1];
+
+ exit(apprentice(magicfile, COMPILE));
}
+#endif /* COMPILE_ONLY */
#define ISSPC(x) ((x) == ' ' || (x) == '\t' || (x) == '\r' || (x) == '\n' \
|| (x) == 0x85 || (x) == '\f')
-static int looks_ascii(const unsigned char *buf, int nbytes,
- /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
- /*@modifies *ubuf, *ulen @*/;
-static int looks_utf8(const unsigned char *buf, int nbytes,
- /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
- /*@modifies *ubuf, *ulen @*/;
-static int looks_unicode(const unsigned char *buf, int nbytes,
- /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
- /*@modifies *ubuf, *ulen @*/;
-static int looks_latin1(const unsigned char *buf, int nbytes,
- /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
- /*@modifies *ubuf, *ulen @*/;
-static int looks_extended(const unsigned char *buf, int nbytes,
- /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
- /*@modifies *ubuf, *ulen @*/;
-static void from_ebcdic(const unsigned char *buf, int nbytes,
- /*@out@*/ unsigned char *otp)
- /*@modifies *otp @*/;
-static int ascmatch(const unsigned char *s, const unichar *us, int ulen)
- /*@*/;
+/*
+ * This table reflects a particular philosophy about what constitutes
+ * "text," and there is room for disagreement about it.
+ *
+ * Version 3.31 of the file command considered a file to be ASCII if
+ * each of its characters was approved by either the isascii() or
+ * isalpha() function. On most systems, this would mean that any
+ * file consisting only of characters in the range 0x00 ... 0x7F
+ * would be called ASCII text, but many systems might reasonably
+ * consider some characters outside this range to be alphabetic,
+ * so the file command would call such characters ASCII. It might
+ * have been more accurate to call this "considered textual on the
+ * local system" than "ASCII."
+ *
+ * It considered a file to be "International language text" if each
+ * of its characters was either an ASCII printing character (according
+ * to the real ASCII standard, not the above test), a character in
+ * the range 0x80 ... 0xFF, or one of the following control characters:
+ * backspace, tab, line feed, vertical tab, form feed, carriage return,
+ * escape. No attempt was made to determine the language in which files
+ * of this type were written.
+ *
+ *
+ * The table below considers a file to be ASCII if all of its characters
+ * are either ASCII printing characters (again, according to the X3.4
+ * standard, not isascii()) or any of the following controls: bell,
+ * backspace, tab, line feed, form feed, carriage return, esc, nextline.
+ *
+ * I include bell because some programs (particularly shell scripts)
+ * use it literally, even though it is rare in normal text. I exclude
+ * vertical tab because it never seems to be used in real text. I also
+ * include, with hesitation, the X3.64/ECMA-43 control nextline (0x85),
+ * because that's what the dd EBCDIC->ASCII table maps the EBCDIC newline
+ * character to. It might be more appropriate to include it in the 8859
+ * set instead of the ASCII set, but it's got to be included in *something*
+ * we recognize or EBCDIC files aren't going to be considered textual.
+ * Some old Unix source files use SO/SI (^N/^O) to shift between Greek
+ * and Latin characters, so these should possibly be allowed. But they
+ * make a real mess on VT100-style displays if they're not paired properly,
+ * so we are probably better off not calling them text.
+ *
+ * A file is considered to be ISO-8859 text if its characters are all
+ * either ASCII, according to the above definition, or printing characters
+ * from the ISO-8859 8-bit extension, characters 0xA0 ... 0xFF.
+ *
+ * Finally, a file is considered to be international text from some other
+ * character code if its characters are all either ISO-8859 (according to
+ * the above definition) or characters in the range 0x80 ... 0x9F, which
+ * ISO-8859 considers to be control characters but the IBM PC and Macintosh
+ * consider to be printing characters.
+ */
-/* int nbytes: size actually read */
-int
-ascmagic(unsigned char *buf, int nbytes)
+#define F 0 /* character never appears in text */
+#define T 1 /* character appears in plain ASCII text */
+#define I 2 /* character appears in ISO-8859 text */
+#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */
+
+/*@unchecked@*/ /*@observer@*/
+static char text_chars[256] = {
+ /* BEL BS HT LF FF CR */
+ F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */
+ /* ESC */
+ F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */
+ T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */
+ /* NEL */
+ X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */
+ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */
+ I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */
+};
+
+static int
+looks_ascii(const unsigned char *buf, int nbytes,
+ /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
+ /*@modifies *ubuf, *ulen @*/
{
int i;
- char nbuf[HOWMANY+1]; /* one extra for terminating '\0' */
- unichar ubuf[HOWMANY+1]; /* one extra for terminating '\0' */
- int ulen;
- struct names *p;
-
- char *code = NULL;
- char *code_mime = NULL;
- char *type = NULL;
- char *subtype = NULL;
- char *subtype_mime = NULL;
- int has_escapes = 0;
- int has_backspace = 0;
+ *ulen = 0;
- int n_crlf = 0;
- int n_lf = 0;
- int n_cr = 0;
- int n_nel = 0;
+ for (i = 0; i < nbytes; i++) {
+ int t = text_chars[buf[i]];
- int last_line_end = -1;
- int has_long_lines = 0;
+ if (t != T)
+ return 0;
- /*
- * Do the tar test first, because if the first file in the tar
- * archive starts with a dot, we can confuse it with an nroff file.
- */
- switch (is_tar(buf, nbytes)) {
- case 1:
- ckfputs(iflag ? "application/x-tar" : "tar archive", stdout);
- return 1;
- case 2:
- ckfputs(iflag ? "application/x-tar, POSIX"
- : "POSIX tar archive", stdout);
- return 1;
+ ubuf[(*ulen)++] = buf[i];
}
- /*
- * Undo the NUL-termination kindly provided by process()
- * but leave at least one byte to look at
- */
-
- while (nbytes > 1 && buf[nbytes - 1] == '\0')
- nbytes--;
-
- /*
- * Then try to determine whether it's any character code we can
- * identify. Each of these tests, if it succeeds, will leave
- * the text converted into one-unichar-per-character Unicode in
- * ubuf, and the number of characters converted in ulen.
- */
- if (looks_ascii(buf, nbytes, ubuf, &ulen)) {
- code = "ASCII";
- code_mime = "us-ascii";
- type = "text";
- } else if (looks_utf8(buf, nbytes, ubuf, &ulen)) {
- code = "UTF-8 Unicode";
- code_mime = "utf-8";
- type = "text";
- } else if ((i = looks_unicode(buf, nbytes, ubuf, &ulen))) {
- if (i == 1)
- code = "Little-endian UTF-16 Unicode";
- else
- code = "Big-endian UTF-16 Unicode";
+ return 1;
+}
- type = "character data";
- code_mime = "utf-16"; /* is this defined? */
- } else if (looks_latin1(buf, nbytes, ubuf, &ulen)) {
- code = "ISO-8859";
- type = "text";
- code_mime = "iso-8859-1";
- } else if (looks_extended(buf, nbytes, ubuf, &ulen)) {
- code = "Non-ISO extended-ASCII";
- type = "text";
- code_mime = "unknown";
- } else {
- from_ebcdic(buf, nbytes, nbuf);
+static int
+looks_latin1(const unsigned char *buf, int nbytes,
+ /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
+ /*@modifies *ubuf, *ulen @*/
+{
+ int i;
- if (looks_ascii(nbuf, nbytes, ubuf, &ulen)) {
- code = "EBCDIC";
- type = "character data";
- code_mime = "ebcdic";
- } else if (looks_latin1(nbuf, nbytes, ubuf, &ulen)) {
- code = "International EBCDIC";
- type = "character data";
- code_mime = "ebcdic";
- } else {
- return 0; /* doesn't look like text at all */
- }
- }
+ *ulen = 0;
- /*
- * for troff, look for . + letter + letter or .\";
- * this must be done to disambiguate tar archives' ./file
- * and other trash from real troff input.
- *
- * I believe Plan 9 troff allows non-ASCII characters in the names
- * of macros, so this test might possibly fail on such a file.
- */
- if (*ubuf == '.') {
- unichar *tp = ubuf + 1;
+ for (i = 0; i < nbytes; i++) {
+ int t = text_chars[buf[i]];
- while (ISSPC(*tp))
- ++tp; /* skip leading whitespace */
- if ((tp[0] == '\\' && tp[1] == '\"') ||
- (isascii(tp[0]) && isalnum(tp[0]) &&
- isascii(tp[1]) && isalnum(tp[1]) &&
- ISSPC(tp[2]))) {
- subtype_mime = "text/troff";
- subtype = "troff or preprocessor input";
- goto subtype_identified;
- }
- }
+ if (t != T && t != I)
+ return 0;
- if ((*buf == 'c' || *buf == 'C') && ISSPC(buf[1])) {
- subtype_mime = "text/fortran";
- subtype = "fortran program";
- goto subtype_identified;
+ ubuf[(*ulen)++] = buf[i];
}
- /* look for tokens from names.h - this is expensive! */
+ return 1;
+}
- i = 0;
- while (i < ulen) {
- int end;
+static int
+looks_extended(const unsigned char *buf, int nbytes,
+ /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
+ /*@modifies *ubuf, *ulen @*/
+{
+ int i;
- /*
- * skip past any leading space
- */
- while (i < ulen && ISSPC(ubuf[i]))
- i++;
- if (i >= ulen)
- break;
+ *ulen = 0;
- /*
- * find the next whitespace
- */
- for (end = i + 1; end < nbytes; end++)
- if (ISSPC(ubuf[end]))
- /*@innerbreak@*/ break;
+ for (i = 0; i < nbytes; i++) {
+ int t = text_chars[buf[i]];
- /*
- * compare the word thus isolated against the token list
- */
- for (p = names; p < names + NNAMES; p++) {
- if (ascmatch(p->name, ubuf + i, end - i)) {
- subtype = types[p->type].human;
- subtype_mime = types[p->type].mime;
- goto subtype_identified;
- }
- }
+ if (t != T && t != I && t != X)
+ return 0;
- i = end;
+ ubuf[(*ulen)++] = buf[i];
}
-subtype_identified:
-
- /*
- * Now try to discover other details about the file.
- */
- for (i = 0; i < ulen; i++) {
- if (i > last_line_end + MAXLINELEN)
- has_long_lines = 1;
-
- if (ubuf[i] == '\033')
- has_escapes = 1;
- if (ubuf[i] == '\b')
- has_backspace = 1;
-
- if (ubuf[i] == '\r' && (i + 1 < ulen && ubuf[i + 1] == '\n')) {
- n_crlf++;
- last_line_end = i;
- }
- if (ubuf[i] == '\r' && (i + 1 >= ulen || ubuf[i + 1] != '\n')) {
- n_cr++;
- last_line_end = i;
- }
- if (ubuf[i] == '\n' && (i - 1 < 0 || ubuf[i - 1] != '\r')) {
- n_lf++;
- last_line_end = i;
- }
- if (ubuf[i] == 0x85) { /* X3.64/ECMA-43 "next line" character */
- n_nel++;
- last_line_end = i;
- }
- }
-
- if (iflag) {
- if (subtype_mime != NULL)
- ckfputs(subtype_mime, stdout);
- else
- ckfputs("text/plain", stdout);
-
- if (code_mime != NULL) {
- ckfputs("; charset=", stdout);
- ckfputs(code_mime, stdout);
- }
- } else {
- ckfputs(code, stdout);
-
- if (subtype != NULL) {
- ckfputs(" ", stdout);
- ckfputs(subtype, stdout);
- }
-
- ckfputs(" ", stdout);
- ckfputs(type, stdout);
-
- if (has_long_lines)
- ckfputs(", with very long lines", stdout);
-
- /*
- * Only report line terminators if we find one other than LF,
- * or if we find none at all.
- */
- if ((n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0) ||
- (n_crlf != 0 || n_cr != 0 || n_nel != 0)) {
- ckfputs(", with", stdout);
-
- if (n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0)
- ckfputs(" no", stdout);
- else {
- if (n_crlf) {
- ckfputs(" CRLF", stdout);
- if (n_cr || n_lf || n_nel)
- ckfputs(",", stdout);
- }
- if (n_cr) {
- ckfputs(" CR", stdout);
- if (n_lf || n_nel)
- ckfputs(",", stdout);
- }
- if (n_lf) {
- ckfputs(" LF", stdout);
- if (n_nel)
- ckfputs(",", stdout);
- }
- if (n_nel)
- ckfputs(" NEL", stdout);
- }
-
- ckfputs(" line terminators", stdout);
- }
-
- if (has_escapes)
- ckfputs(", with escape sequences", stdout);
- if (has_backspace)
- ckfputs(", with overstriking", stdout);
- }
-
- return 1;
-}
-
-static int
-ascmatch(const unsigned char *s, const unichar *us, int ulen)
-{
- size_t i;
-
- for (i = 0; i < ulen; i++) {
- if (s[i] != us[i])
- return 0;
- }
-
- if (s[i])
- return 0;
- else
- return 1;
-}
-
-/*
- * This table reflects a particular philosophy about what constitutes
- * "text," and there is room for disagreement about it.
- *
- * Version 3.31 of the file command considered a file to be ASCII if
- * each of its characters was approved by either the isascii() or
- * isalpha() function. On most systems, this would mean that any
- * file consisting only of characters in the range 0x00 ... 0x7F
- * would be called ASCII text, but many systems might reasonably
- * consider some characters outside this range to be alphabetic,
- * so the file command would call such characters ASCII. It might
- * have been more accurate to call this "considered textual on the
- * local system" than "ASCII."
- *
- * It considered a file to be "International language text" if each
- * of its characters was either an ASCII printing character (according
- * to the real ASCII standard, not the above test), a character in
- * the range 0x80 ... 0xFF, or one of the following control characters:
- * backspace, tab, line feed, vertical tab, form feed, carriage return,
- * escape. No attempt was made to determine the language in which files
- * of this type were written.
- *
- *
- * The table below considers a file to be ASCII if all of its characters
- * are either ASCII printing characters (again, according to the X3.4
- * standard, not isascii()) or any of the following controls: bell,
- * backspace, tab, line feed, form feed, carriage return, esc, nextline.
- *
- * I include bell because some programs (particularly shell scripts)
- * use it literally, even though it is rare in normal text. I exclude
- * vertical tab because it never seems to be used in real text. I also
- * include, with hesitation, the X3.64/ECMA-43 control nextline (0x85),
- * because that's what the dd EBCDIC->ASCII table maps the EBCDIC newline
- * character to. It might be more appropriate to include it in the 8859
- * set instead of the ASCII set, but it's got to be included in *something*
- * we recognize or EBCDIC files aren't going to be considered textual.
- * Some old Unix source files use SO/SI (^N/^O) to shift between Greek
- * and Latin characters, so these should possibly be allowed. But they
- * make a real mess on VT100-style displays if they're not paired properly,
- * so we are probably better off not calling them text.
- *
- * A file is considered to be ISO-8859 text if its characters are all
- * either ASCII, according to the above definition, or printing characters
- * from the ISO-8859 8-bit extension, characters 0xA0 ... 0xFF.
- *
- * Finally, a file is considered to be international text from some other
- * character code if its characters are all either ISO-8859 (according to
- * the above definition) or characters in the range 0x80 ... 0x9F, which
- * ISO-8859 considers to be control characters but the IBM PC and Macintosh
- * consider to be printing characters.
- */
-
-#define F 0 /* character never appears in text */
-#define T 1 /* character appears in plain ASCII text */
-#define I 2 /* character appears in ISO-8859 text */
-#define X 3 /* character appears in non-ISO extended ASCII (Mac, IBM PC) */
-
-/*@unchecked@*/ /*@observer@*/
-static char text_chars[256] = {
- /* BEL BS HT LF FF CR */
- F, F, F, F, F, F, F, T, T, T, T, F, T, T, F, F, /* 0x0X */
- /* ESC */
- F, F, F, F, F, F, F, F, F, F, F, T, F, F, F, F, /* 0x1X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x2X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x3X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x4X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x5X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, /* 0x6X */
- T, T, T, T, T, T, T, T, T, T, T, T, T, T, T, F, /* 0x7X */
- /* NEL */
- X, X, X, X, X, T, X, X, X, X, X, X, X, X, X, X, /* 0x8X */
- X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 0x9X */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xaX */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xbX */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xcX */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xdX */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, /* 0xeX */
- I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I /* 0xfX */
-};
-
-static int
-looks_ascii(const unsigned char *buf, int nbytes, unichar *ubuf, int *ulen)
-{
- int i;
-
- *ulen = 0;
-
- for (i = 0; i < nbytes; i++) {
- int t = text_chars[buf[i]];
-
- if (t != T)
- return 0;
-
- ubuf[(*ulen)++] = buf[i];
- }
-
- return 1;
-}
-
-static int
-looks_latin1(const unsigned char *buf, int nbytes, unichar *ubuf, int *ulen)
-{
- int i;
-
- *ulen = 0;
-
- for (i = 0; i < nbytes; i++) {
- int t = text_chars[buf[i]];
-
- if (t != T && t != I)
- return 0;
-
- ubuf[(*ulen)++] = buf[i];
- }
-
- return 1;
-}
-
-static int
-looks_extended(const unsigned char *buf, int nbytes, unichar *ubuf, int *ulen)
-{
- int i;
-
- *ulen = 0;
-
- for (i = 0; i < nbytes; i++) {
- int t = text_chars[buf[i]];
-
- if (t != T && t != I && t != X)
- return 0;
-
- ubuf[(*ulen)++] = buf[i];
- }
-
- return 1;
-}
+ return 1;
+}
int
-looks_utf8(const unsigned char *buf, int nbytes, unichar *ubuf, int *ulen)
+looks_utf8(const unsigned char *buf, int nbytes,
+ /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
+ /*@modifies *ubuf, *ulen @*/
{
int i, n;
unichar c;
}
static int
-looks_unicode(const unsigned char *buf, int nbytes, unichar *ubuf, int *ulen)
+looks_unicode(const unsigned char *buf, int nbytes,
+ /*@out@*/ unichar *ubuf, /*@out@*/ int *ulen)
+ /*@modifies *ubuf, *ulen @*/
{
int bigend;
int i;
* Copy buf[0 ... nbytes-1] into out[], translating EBCDIC to ASCII.
*/
static void
-from_ebcdic(const unsigned char *buf, int nbytes, unsigned char *otp)
+from_ebcdic(const unsigned char *buf, int nbytes, /*@out@*/ unsigned char *otp)
+ /*@modifies *otp @*/
{
int i;
otp[i] = ebcdic_to_ascii[buf[i]];
}
}
+
+static int
+ascmatch(const unsigned char *s, const unichar *us, int ulen)
+ /*@*/
+{
+ size_t i;
+
+ for (i = 0; i < ulen; i++) {
+ if (s[i] != us[i])
+ return 0;
+ }
+
+ if (s[i])
+ return 0;
+ else
+ return 1;
+}
+
+/* int nbytes: size actually read */
+int
+ascmagic(unsigned char *buf, int nbytes)
+{
+ int i;
+ char nbuf[HOWMANY+1]; /* one extra for terminating '\0' */
+ unichar ubuf[HOWMANY+1]; /* one extra for terminating '\0' */
+ int ulen;
+ struct names *p;
+
+ char *code = NULL;
+ char *code_mime = NULL;
+ char *type = NULL;
+ char *subtype = NULL;
+ char *subtype_mime = NULL;
+
+ int has_escapes = 0;
+ int has_backspace = 0;
+
+ int n_crlf = 0;
+ int n_lf = 0;
+ int n_cr = 0;
+ int n_nel = 0;
+
+ int last_line_end = -1;
+ int has_long_lines = 0;
+
+ /*
+ * Do the tar test first, because if the first file in the tar
+ * archive starts with a dot, we can confuse it with an nroff file.
+ */
+ switch (is_tar(buf, nbytes)) {
+ case 1:
+ ckfputs(iflag ? "application/x-tar" : "tar archive", stdout);
+ return 1;
+ case 2:
+ ckfputs(iflag ? "application/x-tar, POSIX"
+ : "POSIX tar archive", stdout);
+ return 1;
+ }
+
+ /*
+ * Undo the NUL-termination kindly provided by process()
+ * but leave at least one byte to look at
+ */
+
+ while (nbytes > 1 && buf[nbytes - 1] == '\0')
+ nbytes--;
+
+ /*
+ * Then try to determine whether it's any character code we can
+ * identify. Each of these tests, if it succeeds, will leave
+ * the text converted into one-unichar-per-character Unicode in
+ * ubuf, and the number of characters converted in ulen.
+ */
+ if (looks_ascii(buf, nbytes, ubuf, &ulen)) {
+ code = "ASCII";
+ code_mime = "us-ascii";
+ type = "text";
+ } else if (looks_utf8(buf, nbytes, ubuf, &ulen)) {
+ code = "UTF-8 Unicode";
+ code_mime = "utf-8";
+ type = "text";
+ } else if ((i = looks_unicode(buf, nbytes, ubuf, &ulen))) {
+ if (i == 1)
+ code = "Little-endian UTF-16 Unicode";
+ else
+ code = "Big-endian UTF-16 Unicode";
+
+ type = "character data";
+ code_mime = "utf-16"; /* is this defined? */
+ } else if (looks_latin1(buf, nbytes, ubuf, &ulen)) {
+ code = "ISO-8859";
+ type = "text";
+ code_mime = "iso-8859-1";
+ } else if (looks_extended(buf, nbytes, ubuf, &ulen)) {
+ code = "Non-ISO extended-ASCII";
+ type = "text";
+ code_mime = "unknown";
+ } else {
+ from_ebcdic(buf, nbytes, nbuf);
+
+ if (looks_ascii(nbuf, nbytes, ubuf, &ulen)) {
+ code = "EBCDIC";
+ type = "character data";
+ code_mime = "ebcdic";
+ } else if (looks_latin1(nbuf, nbytes, ubuf, &ulen)) {
+ code = "International EBCDIC";
+ type = "character data";
+ code_mime = "ebcdic";
+ } else {
+ return 0; /* doesn't look like text at all */
+ }
+ }
+
+ /*
+ * for troff, look for . + letter + letter or .\";
+ * this must be done to disambiguate tar archives' ./file
+ * and other trash from real troff input.
+ *
+ * I believe Plan 9 troff allows non-ASCII characters in the names
+ * of macros, so this test might possibly fail on such a file.
+ */
+ if (*ubuf == '.') {
+ unichar *tp = ubuf + 1;
+
+ while (ISSPC(*tp))
+ ++tp; /* skip leading whitespace */
+ if ((tp[0] == '\\' && tp[1] == '\"') ||
+ (isascii(tp[0]) && isalnum(tp[0]) &&
+ isascii(tp[1]) && isalnum(tp[1]) &&
+ ISSPC(tp[2]))) {
+ subtype_mime = "text/troff";
+ subtype = "troff or preprocessor input";
+ goto subtype_identified;
+ }
+ }
+
+ if ((*buf == 'c' || *buf == 'C') && ISSPC(buf[1])) {
+ subtype_mime = "text/fortran";
+ subtype = "fortran program";
+ goto subtype_identified;
+ }
+
+ /* look for tokens from names.h - this is expensive! */
+
+ i = 0;
+ while (i < ulen) {
+ int end;
+
+ /*
+ * skip past any leading space
+ */
+ while (i < ulen && ISSPC(ubuf[i]))
+ i++;
+ if (i >= ulen)
+ break;
+
+ /*
+ * find the next whitespace
+ */
+ for (end = i + 1; end < nbytes; end++)
+ if (ISSPC(ubuf[end]))
+ /*@innerbreak@*/ break;
+
+ /*
+ * compare the word thus isolated against the token list
+ */
+ for (p = names; p < names + NNAMES; p++) {
+ if (ascmatch(p->name, ubuf + i, end - i)) {
+ subtype = types[p->type].human;
+ subtype_mime = types[p->type].mime;
+ goto subtype_identified;
+ }
+ }
+
+ i = end;
+ }
+
+subtype_identified:
+
+ /*
+ * Now try to discover other details about the file.
+ */
+ for (i = 0; i < ulen; i++) {
+ if (i > last_line_end + MAXLINELEN)
+ has_long_lines = 1;
+
+ if (ubuf[i] == '\033')
+ has_escapes = 1;
+ if (ubuf[i] == '\b')
+ has_backspace = 1;
+
+ if (ubuf[i] == '\r' && (i + 1 < ulen && ubuf[i + 1] == '\n')) {
+ n_crlf++;
+ last_line_end = i;
+ }
+ if (ubuf[i] == '\r' && (i + 1 >= ulen || ubuf[i + 1] != '\n')) {
+ n_cr++;
+ last_line_end = i;
+ }
+ if (ubuf[i] == '\n' && (i - 1 < 0 || ubuf[i - 1] != '\r')) {
+ n_lf++;
+ last_line_end = i;
+ }
+ if (ubuf[i] == 0x85) { /* X3.64/ECMA-43 "next line" character */
+ n_nel++;
+ last_line_end = i;
+ }
+ }
+
+ if (iflag) {
+ if (subtype_mime != NULL)
+ ckfputs(subtype_mime, stdout);
+ else
+ ckfputs("text/plain", stdout);
+
+ if (code_mime != NULL) {
+ ckfputs("; charset=", stdout);
+ ckfputs(code_mime, stdout);
+ }
+ } else {
+ ckfputs(code, stdout);
+
+ if (subtype != NULL) {
+ ckfputs(" ", stdout);
+ ckfputs(subtype, stdout);
+ }
+
+ ckfputs(" ", stdout);
+ ckfputs(type, stdout);
+
+ if (has_long_lines)
+ ckfputs(", with very long lines", stdout);
+
+ /*
+ * Only report line terminators if we find one other than LF,
+ * or if we find none at all.
+ */
+ if ((n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0) ||
+ (n_crlf != 0 || n_cr != 0 || n_nel != 0)) {
+ ckfputs(", with", stdout);
+
+ if (n_crlf == 0 && n_cr == 0 && n_nel == 0 && n_lf == 0)
+ ckfputs(" no", stdout);
+ else {
+ if (n_crlf) {
+ ckfputs(" CRLF", stdout);
+ if (n_cr || n_lf || n_nel)
+ ckfputs(",", stdout);
+ }
+ if (n_cr) {
+ ckfputs(" CR", stdout);
+ if (n_lf || n_nel)
+ ckfputs(",", stdout);
+ }
+ if (n_lf) {
+ ckfputs(" LF", stdout);
+ if (n_nel)
+ ckfputs(",", stdout);
+ }
+ if (n_nel)
+ ckfputs(" NEL", stdout);
+ }
+
+ ckfputs(" line terminators", stdout);
+ }
+
+ if (has_escapes)
+ ckfputs(", with escape sequences", stdout);
+ if (has_backspace)
+ ckfputs(", with overstriking", stdout);
+ }
+
+ return 1;
+}
-/*
- * compress routines:
- * zmagic() - returns 0 if not recognized, uncompresses and prints
- * information if recognized
- * uncompress(method, old, n, newch) - uncompress old into new,
- * using method, return sizeof new
- */
+
#include "file.h"
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
FILE_RCSID("@(#)Id: compress.c,v 1.25 2002/07/03 18:26:37 christos Exp ")
#endif
-
/*@-nullassign@*/
/*@unchecked@*/
static struct {
/*@unchecked@*/
static int ncompr = sizeof(compr) / sizeof(compr[0]);
-
-static int swrite(int fd, const void *buf, size_t n)
- /*@*/;
-static int sread(int fd, /*@out@*/ void *buf, size_t n)
- /*@modifies *buf @*/;
-static int uncompressbuf(int method, const unsigned char *old,
- /*@out@*/ unsigned char **newch, int n)
- /*@globals fileSystem, internalState @*/
- /*@modifies *newch, fileSystem, internalState @*/;
-#ifdef HAVE_LIBZ
-static int uncompressgzipped(const unsigned char *old,
- /*@out@*/ unsigned char **newch, int n)
- /*@globals fileSystem @*/
- /*@modifies *newch, fileSystem @*/;
-#endif
-
-int
-zmagic(const char *fname, unsigned char *buf, int nbytes)
-{
- unsigned char *newbuf;
- int newsize;
- int i;
-
- for (i = 0; i < ncompr; i++) {
- if (nbytes < compr[i].maglen)
- continue;
- if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
- (newsize = uncompressbuf(i, buf, &newbuf, nbytes)) != 0) {
- (void) tryit(fname, newbuf, newsize, 1);
- free(newbuf);
- printf(" (");
- (void) tryit(fname, buf, nbytes, 0);
- printf(")");
- return 1;
- }
- }
-
- if (i == ncompr)
- return 0;
-
- return 1;
-}
-
/*
* `safe' write for sockets and pipes.
*/
static int
swrite(int fd, const void *buf, size_t n)
+ /*@*/
{
int rv;
size_t rn = n;
- do
+ do {
switch (rv = write(fd, buf, n)) {
case -1:
if (errno == EINTR)
buf = ((const char *)buf) + rv;
/*@switchbreak@*/ break;
}
- while (n > 0);
+ } while (n > 0);
return rn;
}
* `safe' read for sockets and pipes.
*/
static int
-sread(int fd, void *buf, size_t n)
+sread(int fd, /*@out@*/ void *buf, size_t n)
+ /*@modifies *buf @*/
{
int rv;
size_t rn = n;
- do
+ do {
switch (rv = read(fd, buf, n)) {
case -1:
if (errno == EINTR)
buf = ((char *)buf) + rv;
/*@switchbreak@*/ break;
}
- while (n > 0);
+ } while (n > 0);
return rn;
}
#define FCOMMENT (1 << 4)
static int
-uncompressgzipped(const unsigned char *old, unsigned char **newch, int n)
+uncompressgzipped(const unsigned char *old,
+ /*@out@*/ unsigned char **newch, int n)
+ /*@globals fileSystem @*/
+ /*@modifies *newch, fileSystem @*/
{
unsigned char flg = old[3];
int data_start = 10;
#endif
static int
-uncompressbuf(int method, const unsigned char *old, unsigned char **newch,
- int n)
+uncompressbuf(int method, const unsigned char *old,
+ /*@out@*/ unsigned char **newch, int n)
+ /*@globals fileSystem, internalState @*/
+ /*@modifies *newch, fileSystem, internalState @*/
{
int fdin[2], fdout[2];
}
/*@notreached@*/
}
+
+/*
+ * compress routines:
+ * zmagic() - returns 0 if not recognized, uncompresses and prints
+ * information if recognized
+ */
+int
+zmagic(const char *fname, unsigned char *buf, int nbytes)
+{
+ unsigned char *newbuf;
+ int newsize;
+ int i;
+
+ for (i = 0; i < ncompr; i++) {
+ if (nbytes < compr[i].maglen)
+ continue;
+ if (memcmp(buf, compr[i].magic, compr[i].maglen) == 0 &&
+ (newsize = uncompressbuf(i, buf, &newbuf, nbytes)) != 0) {
+ (void) tryit(fname, newbuf, newsize, 1);
+ free(newbuf);
+ printf(" (");
+ (void) tryit(fname, buf, nbytes, 0);
+ printf(")");
+ return 1;
+ }
+ }
+
+ if (i == ncompr)
+ return 0;
+
+ return 1;
+}
#include <getopt.h> /* for long options (is this portable?)*/
#endif
-#include <netinet/in.h> /* for byte swapping */
-
#include "patchlevel.h"
#ifndef lint
nobuffer = 0, /* Do not buffer stdout */
kflag = 0; /* Keep going after the first match */
-/*@unchecked@*/ /*@unused@*/
-int /* Misc globals */
- nmagic = 0; /* number of valid magic[]s */
-
-/*@unchecked@*/ /*@unused@*/
-struct magic *magic; /* array of magic entries */
-
/*@unchecked@*/ /*@null@*/
const char *magicfile = 0; /* where the magic is */
/*@unchecked@*/ /*@observer@*/
/*@unchecked@*/
int lineno; /* line number in the magic file */
+int
+tryit(const char *fn, unsigned char *buf, int nb, int zfl)
+{
+
+ /*
+ * The main work is done here!
+ * We have the file name and/or the data buffer to be identified.
+ */
+
+#ifdef __EMX__
+ /*
+ * Ok, here's the right place to add a call to some os-specific
+ * routine, e.g.
+ */
+ if (os2_apptype(fn, buf, nb) == 1)
+ return 'o';
+#endif
+ /* try compression stuff */
+ if (zfl && zmagic(fn, buf, nb))
+ return 'z';
+
+ /* try tests in /etc/magic (or surrogate magic file) */
+ if (softmagic(buf, nb))
+ return 's';
+
+ /* try known keywords, check whether it is ASCII */
+ if (ascmagic(buf, nb))
+ return 'a';
+
+ /* abandon hope, all ye who remain here */
+ ckfputs(iflag ? "application/octet-stream" : "data", stdout);
+ return '\0';
+}
+
+/*
+ * process - process input file
+ */
+void
+process(const char *inname, int wid)
+{
+ int fd = 0;
+ static const char stdname[] = "standard input";
+ unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */
+ struct stat sb;
+ int nbytes = 0; /* number of bytes read from a datafile */
+ char match = '\0';
+
+ if (strcmp("-", inname) == 0) {
+ if (fstat(0, &sb)<0) {
+ error("cannot fstat `%s' (%s).\n", stdname,
+ strerror(errno));
+ /*@notreached@*/
+ }
+ inname = stdname;
+ }
+
+ if (wid > 0 && !bflag)
+ (void) printf("%s:%*s ", inname,
+ (int) (wid - strlen(inname)), "");
+
+ if (inname != stdname) {
+ /*
+ * first try judging the file based on its filesystem status
+ */
+ if (fsmagic(inname, &sb) != 0) {
+ (void) putchar('\n');
+ return;
+ }
+
+ if ((fd = open(inname, O_RDONLY)) < 0) {
+ /* We can't open it, but we were able to stat it. */
+ if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
+ if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
+ ckfprintf(stdout, "can't read `%s' (%s).\n",
+ inname, strerror(errno));
+ return;
+ }
+ }
+
+
+ /*
+ * try looking at the first HOWMANY bytes
+ */
+ if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
+ error("read failed (%s).\n", strerror(errno));
+ /*@notreached@*/
+ }
+
+ if (nbytes == 0)
+ ckfputs(iflag ? "application/x-empty" : "empty", stdout);
+ else {
+ buf[nbytes++] = '\0'; /* null-terminate it */
+ match = tryit(inname, buf, nbytes, zflag);
+ }
-static void unwrap(char *fn)
+#ifdef BUILTIN_ELF
+ if (match == 's' && nbytes > 5) {
+ /*
+ * We matched something in the file, so this *might*
+ * be an ELF file, and the file is at least 5 bytes long,
+ * so if it's an ELF file it has at least one byte
+ * past the ELF magic number - try extracting information
+ * from the ELF headers that can't easily be extracted
+ * with rules in the magic file.
+ */
+ tryelf(fd, buf, nbytes);
+ }
+#endif
+
+ if (inname != stdname) {
+#ifdef RESTORE_TIME
+ /*
+ * Try to restore access, modification times if read it.
+ * This is really *bad* because it will modify the status
+ * time of the file... And of course this will affect
+ * backup programs
+ */
+# ifdef USE_UTIMES
+ struct timeval utsbuf[2];
+ utsbuf[0].tv_sec = sb.st_atime;
+ utsbuf[1].tv_sec = sb.st_mtime;
+
+ (void) utimes(inname, utsbuf); /* don't care if loses */
+# else
+ struct utimbuf utbuf;
+
+ utbuf.actime = sb.st_atime;
+ utbuf.modtime = sb.st_mtime;
+ (void) utime(inname, &utbuf); /* don't care if loses */
+# endif
+#endif
+ (void) close(fd);
+ }
+ (void) putchar('\n');
+}
+
+/*
+ * unwrap -- read a file of filenames, do each one.
+ */
+static void
+unwrap(char *fn)
/*@globals fileSystem, internalState @*/
- /*@modifies fileSystem, internalState @*/;
+ /*@modifies fileSystem, internalState @*/
+{
+ char buf[MAXPATHLEN];
+ FILE *f;
+ int wid = 0, cwid;
+
+ if (strcmp("-", fn) == 0) {
+ f = stdin;
+ wid = 1;
+ } else {
+ if ((f = fopen(fn, "r")) == NULL) {
+ error("Cannot open `%s' (%s).\n", fn, strerror(errno));
+ /*@notreached@*/
+ }
+
+ while (fgets(buf, MAXPATHLEN, f) != NULL) {
+ cwid = strlen(buf) - 1;
+ if (cwid > wid)
+ wid = cwid;
+ }
+
+ rewind(f);
+ }
+
+ while (fgets(buf, MAXPATHLEN, f) != NULL) {
+ buf[strlen(buf)-1] = '\0';
+ process(buf, wid);
+ if(nobuffer)
+ (void) fflush(stdout);
+ }
+
+ (void) fclose(f);
+}
+
/*@exits@*/
-static void usage(void)
+static void
+usage(void)
/*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
+ /*@modifies fileSystem @*/
+{
+ (void)fprintf(stderr, USAGE, progname);
+ (void)fprintf(stderr, "Usage: %s -C [-m magic]\n", progname);
+#ifdef HAVE_GETOPT_H
+ (void)fputs("Try `file --help' for more information.\n", stderr);
+#endif
+ exit(EXIT_FAILURE);
+}
+
#ifdef HAVE_GETOPT_H
/*@exits@*/
-static void help(void)
+static void
+help(void)
/*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
+ /*@modifies fileSystem @*/
+{
+ (void) puts(
+"Usage: file [OPTION]... [FILE]...\n"
+"Determine file type of FILEs.\n"
+"\n"
+" -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
+" number files\n"
+" -z, --uncompress try to look inside compressed files\n"
+" -b, --brief do not prepend filenames to output lines\n"
+" -c, --checking-printout print the parsed form of the magic file, use in\n"
+" conjunction with -m to debug a new magic file\n"
+" before installing it\n"
+" -f, --files-from FILE read the filenames to be examined from FILE\n"
+" -i, --mime output mime type strings\n"
+" -k, --keep-going don't stop at the first match\n"
+" -L, --dereference causes symlinks to be followed\n"
+" -n, --no-buffer do not buffer output\n"
+" -s, --special-files treat special (block/char devices) files as\n"
+" ordinary ones\n"
+" --help display this help and exit\n"
+" --version output version information and exit\n"
+);
+ exit(0);
+}
#endif
-int main(int argc, char *argv[])
- /*@globals debug, bflag, zflag, sflag, iflag, nobuffer, kflag,
- default_magicfile, lineno, magicfile, mlist, optind, progname,
- fileSystem, internalState @*/
- /*@modifies argv, debug, bflag, zflag, sflag, iflag, nobuffer, kflag,
- default_magicfile, lineno, magicfile, mlist, optind, progname,
- fileSystem, internalState @*/;
-
/*
* main - parse arguments and handle options
*/
int
main(int argc, char **argv)
+ /*@globals debug, bflag, zflag, sflag, iflag, nobuffer, kflag,
+ default_magicfile, lineno, magicfile, mlist, optind, progname,
+ fileSystem, internalState @*/
+ /*@modifies argv, debug, bflag, zflag, sflag, iflag, nobuffer, kflag,
+ default_magicfile, lineno, magicfile, mlist, optind, progname,
+ fileSystem, internalState @*/
{
int c;
int action = 0, didsomefiles = 0, errflg = 0, ret = 0, app = 0;
magicfile = default_magicfile;
if ((usermagic = getenv("MAGIC")) != NULL)
magicfile = usermagic;
- else
+ else {
if ((home = getenv("HOME")) != NULL) {
if ((usermagic = malloc(strlen(home) + 8)) != NULL) {
(void)strcpy(usermagic, home);
magicfile = usermagic;
}
}
+ }
#ifndef HAVE_GETOPT_H
while ((c = getopt(argc, argv, OPTSTRING)) != -1)
while ((c = getopt_long(argc, argv, OPTSTRING, long_options,
&longindex)) != -1)
#endif
+ {
switch (c) {
#ifdef HAVE_GETOPT_H
case 0 :
errflg++;
/*@switchbreak@*/ break;
}
+ }
if (errflg) {
usage();
return 0;
}
-
-
-/*
- * unwrap -- read a file of filenames, do each one.
- */
-static void
-unwrap(char *fn)
-{
- char buf[MAXPATHLEN];
- FILE *f;
- int wid = 0, cwid;
-
- if (strcmp("-", fn) == 0) {
- f = stdin;
- wid = 1;
- } else {
- if ((f = fopen(fn, "r")) == NULL) {
- error("Cannot open `%s' (%s).\n", fn, strerror(errno));
- /*@notreached@*/
- }
-
- while (fgets(buf, MAXPATHLEN, f) != NULL) {
- cwid = strlen(buf) - 1;
- if (cwid > wid)
- wid = cwid;
- }
-
- rewind(f);
- }
-
- while (fgets(buf, MAXPATHLEN, f) != NULL) {
- buf[strlen(buf)-1] = '\0';
- process(buf, wid);
- if(nobuffer)
- (void) fflush(stdout);
- }
-
- (void) fclose(f);
-}
-
-
-#if 0
-/*
- * byteconv4
- * Input:
- * from 4 byte quantity to convert
- * same whether to perform byte swapping
- * big_endian whether we are a big endian host
- */
-static int
-byteconv4(int from, int same, int big_endian)
- /*@*/
-{
- if (same)
- return from;
- else if (big_endian) { /* lsb -> msb conversion on msb */
- union {
- int i;
- char c[4];
- } retval, tmpval;
-
- tmpval.i = from;
- retval.c[0] = tmpval.c[3];
- retval.c[1] = tmpval.c[2];
- retval.c[2] = tmpval.c[1];
- retval.c[3] = tmpval.c[0];
-
- return retval.i;
- }
- else
- return ntohl(from); /* msb -> lsb conversion on lsb */
-}
-
-/*
- * byteconv2
- * Same as byteconv4, but for shorts
- */
-static short
-byteconv2(int from, int same, int big_endian)
- /*@*/
-{
- if (same)
- return from;
- else if (big_endian) { /* lsb -> msb conversion on msb */
- union {
- short s;
- char c[2];
- } retval, tmpval;
-
- tmpval.s = (short) from;
- retval.c[0] = tmpval.c[1];
- retval.c[1] = tmpval.c[0];
-
- return retval.s;
- }
- else
- return ntohs(from); /* msb -> lsb conversion on lsb */
-}
-#endif
-
-/*
- * process - process input file
- */
-void
-process(const char *inname, int wid)
-{
- int fd = 0;
- static const char stdname[] = "standard input";
- unsigned char buf[HOWMANY+1]; /* one extra for terminating '\0' */
- struct stat sb;
- int nbytes = 0; /* number of bytes read from a datafile */
- char match = '\0';
-
- if (strcmp("-", inname) == 0) {
- if (fstat(0, &sb)<0) {
- error("cannot fstat `%s' (%s).\n", stdname,
- strerror(errno));
- /*@notreached@*/
- }
- inname = stdname;
- }
-
- if (wid > 0 && !bflag)
- (void) printf("%s:%*s ", inname,
- (int) (wid - strlen(inname)), "");
-
- if (inname != stdname) {
- /*
- * first try judging the file based on its filesystem status
- */
- if (fsmagic(inname, &sb) != 0) {
- (void) putchar('\n');
- return;
- }
-
- if ((fd = open(inname, O_RDONLY)) < 0) {
- /* We can't open it, but we were able to stat it. */
- if (sb.st_mode & 0002) ckfputs("writeable, ", stdout);
- if (sb.st_mode & 0111) ckfputs("executable, ", stdout);
- ckfprintf(stdout, "can't read `%s' (%s).\n",
- inname, strerror(errno));
- return;
- }
- }
-
-
- /*
- * try looking at the first HOWMANY bytes
- */
- if ((nbytes = read(fd, (char *)buf, HOWMANY)) == -1) {
- error("read failed (%s).\n", strerror(errno));
- /*@notreached@*/
- }
-
- if (nbytes == 0)
- ckfputs(iflag ? "application/x-empty" : "empty", stdout);
- else {
- buf[nbytes++] = '\0'; /* null-terminate it */
- match = tryit(inname, buf, nbytes, zflag);
- }
-
-#ifdef BUILTIN_ELF
- if (match == 's' && nbytes > 5) {
- /*
- * We matched something in the file, so this *might*
- * be an ELF file, and the file is at least 5 bytes long,
- * so if it's an ELF file it has at least one byte
- * past the ELF magic number - try extracting information
- * from the ELF headers that can't easily be extracted
- * with rules in the magic file.
- */
- tryelf(fd, buf, nbytes);
- }
-#endif
-
- if (inname != stdname) {
-#ifdef RESTORE_TIME
- /*
- * Try to restore access, modification times if read it.
- * This is really *bad* because it will modify the status
- * time of the file... And of course this will affect
- * backup programs
- */
-# ifdef USE_UTIMES
- struct timeval utsbuf[2];
- utsbuf[0].tv_sec = sb.st_atime;
- utsbuf[1].tv_sec = sb.st_mtime;
-
- (void) utimes(inname, utsbuf); /* don't care if loses */
-# else
- struct utimbuf utbuf;
-
- utbuf.actime = sb.st_atime;
- utbuf.modtime = sb.st_mtime;
- (void) utime(inname, &utbuf); /* don't care if loses */
-# endif
-#endif
- (void) close(fd);
- }
- (void) putchar('\n');
-}
-
-
-int
-tryit(const char *fn, unsigned char *buf, int nb, int zfl)
-{
-
- /*
- * The main work is done here!
- * We have the file name and/or the data buffer to be identified.
- */
-
-#ifdef __EMX__
- /*
- * Ok, here's the right place to add a call to some os-specific
- * routine, e.g.
- */
- if (os2_apptype(fn, buf, nb) == 1)
- return 'o';
-#endif
- /* try compression stuff */
- if (zfl && zmagic(fn, buf, nb))
- return 'z';
-
- /* try tests in /etc/magic (or surrogate magic file) */
- if (softmagic(buf, nb))
- return 's';
-
- /* try known keywords, check whether it is ASCII */
- if (ascmagic(buf, nb))
- return 'a';
-
- /* abandon hope, all ye who remain here */
- ckfputs(iflag ? "application/octet-stream" : "data", stdout);
- return '\0';
-}
-
-static void
-usage(void)
-{
- (void)fprintf(stderr, USAGE, progname);
- (void)fprintf(stderr, "Usage: %s -C [-m magic]\n", progname);
-#ifdef HAVE_GETOPT_H
- (void)fputs("Try `file --help' for more information.\n", stderr);
-#endif
- exit(EXIT_FAILURE);
-}
-
-#ifdef HAVE_GETOPT_H
-static void
-help(void)
-{
- (void) puts(
-"Usage: file [OPTION]... [FILE]...\n"
-"Determine file type of FILEs.\n"
-"\n"
-" -m, --magic-file LIST use LIST as a colon-separated list of magic\n"
-" number files\n"
-" -z, --uncompress try to look inside compressed files\n"
-" -b, --brief do not prepend filenames to output lines\n"
-" -c, --checking-printout print the parsed form of the magic file, use in\n"
-" conjunction with -m to debug a new magic file\n"
-" before installing it\n"
-" -f, --files-from FILE read the filenames to be examined from FILE\n"
-" -i, --mime output mime type strings\n"
-" -k, --keep-going don't stop at the first match\n"
-" -L, --dereference causes symlinks to be followed\n"
-" -n, --no-buffer do not buffer output\n"
-" -s, --special-files treat special (block/char devices) files as\n"
-" ordinary ones\n"
-" --help display this help and exit\n"
-" --version output version information and exit\n"
-);
- exit(0);
-}
-#endif
#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
-/* Decode octal number */
-static int from_oct(int digs, char *where)
- /*@*/;
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or nonoctal).
+ */
+static int
+from_oct(int digs, char *where)
+ /*@*/
+{
+ int value;
+
+ while (isspace((unsigned char)*where)) { /* Skip spaces */
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where && !isspace((unsigned char)*where))
+ return -1; /* Ended on non-space/nul */
+
+ return value;
+}
/*
* Return
return 1; /* Old fashioned tar archive */
}
-
-
-/*
- * Quick and dirty octal conversion.
- *
- * Result is -1 if the field is invalid (all blank, or nonoctal).
- */
-static int
-from_oct(int digs, char *where)
-{
- int value;
-
- while (isspace((unsigned char)*where)) { /* Skip spaces */
- where++;
- if (--digs <= 0)
- return -1; /* All blank field */
- }
- value = 0;
- while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
- value = (value << 3) | (*where++ - '0');
- --digs;
- }
-
- if (digs > 0 && *where && !isspace((unsigned char)*where))
- return -1; /* Ended on non-space/nul */
-
- return value;
-}
FILE_RCSID("@(#)Id: readelf.c,v 1.22 2002/07/03 18:26:38 christos Exp ")
#endif
-#ifdef ELFCORE
-static void dophn_core(int class, int swap, int fd, off_t off, int num,
- size_t size)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-#endif
-static void dophn_exec(int class, int swap, int fd, off_t off, int num,
- size_t size)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-static void doshn(int class, int swap, int fd, off_t off, int num, size_t size)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-
-static uint16_t getu16(int swap, uint16_t value)
- /*@*/;
-static uint32_t getu32(int swap, uint32_t value)
- /*@*/;
-static uint64_t getu64(int swap, uint64_t value)
- /*@*/;
-
static uint16_t
getu16(int swap, uint16_t value)
+ /*@*/
{
union {
uint16_t ui;
static uint32_t
getu32(int swap, uint32_t value)
+ /*@*/
{
union {
uint32_t ui;
static uint64_t
getu64(int swap, uint64_t value)
+ /*@*/
{
union {
uint64_t ui;
return value;
}
-#define sh_addr (class == ELFCLASS32 \
+#define sh_addr (cls == ELFCLASS32 \
? (void *) &sh32 \
: (void *) &sh64)
-#define shs_type (class == ELFCLASS32 \
+#define shs_type (cls == ELFCLASS32 \
? getu32(swap, sh32.sh_type) \
: getu32(swap, sh64.sh_type))
-#define ph_addr (class == ELFCLASS32 \
+#define ph_addr (cls == ELFCLASS32 \
? (void *) &ph32 \
: (void *) &ph64)
-#define ph_type (class == ELFCLASS32 \
+#define ph_type (cls == ELFCLASS32 \
? getu32(swap, ph32.p_type) \
: getu32(swap, ph64.p_type))
-#define ph_offset (class == ELFCLASS32 \
+#define ph_offset (cls == ELFCLASS32 \
? getu32(swap, ph32.p_offset) \
: getu64(swap, ph64.p_offset))
-#define nh_size (class == ELFCLASS32 \
+#define nh_size (cls == ELFCLASS32 \
? sizeof *nh32 \
: sizeof *nh64)
-#define nh_type (class == ELFCLASS32 \
+#define nh_type (cls == ELFCLASS32 \
? getu32(swap, nh32->n_type) \
: getu32(swap, nh64->n_type))
-#define nh_namesz (class == ELFCLASS32 \
+#define nh_namesz (cls == ELFCLASS32 \
? getu32(swap, nh32->n_namesz) \
: getu32(swap, nh64->n_namesz))
-#define nh_descsz (class == ELFCLASS32 \
+#define nh_descsz (cls == ELFCLASS32 \
? getu32(swap, nh32->n_descsz) \
: getu32(swap, nh64->n_descsz))
-#define prpsoffsets(i) (class == ELFCLASS32 \
+#define prpsoffsets(i) (cls == ELFCLASS32 \
? prpsoffsets32[i] \
: prpsoffsets64[i])
static void
-doshn(int class, int swap, int fd, off_t off, int num, size_t size)
+doshn(int cls, int swap, int fd, off_t off, int num, size_t size)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
{
Elf32_Shdr sh32;
Elf64_Shdr sh64;
* otherwise it's statically linked.
*/
static void
-dophn_exec(int class, int swap, int fd, off_t off, int num, size_t size)
+dophn_exec(int cls, int swap, int fd, off_t off, int num, size_t size)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
{
Elf32_Phdr ph32;
Elf32_Nhdr *nh32 = NULL;
for (;;) {
if (offset >= bufsize)
/*@innerbreak@*/ break;
- if (class == ELFCLASS32)
+ if (cls == ELFCLASS32)
nh32 = (Elf32_Nhdr *)&nbuf[offset];
else
nh64 = (Elf64_Nhdr *)&nbuf[offset];
}
#ifdef ELFCORE
-/*@unchecked@*/
-size_t prpsoffsets32[] = {
+/*@unchecked@*/ /*@observer@*/
+static size_t prpsoffsets32[] = {
8, /* FreeBSD */
28, /* Linux 2.0.36 */
32, /* Linux (I forget which kernel version) */
- 84, /* SunOS 5.x */
+ 84 /* SunOS 5.x */
};
-/*@unchecked@*/
-size_t prpsoffsets64[] = {
- 120, /* SunOS 5.x, 64-bit */
+/*@unchecked@*/ /*@observer@*/
+static size_t prpsoffsets64[] = {
+ 120 /* SunOS 5.x, 64-bit */
};
#define NOFFSETS32 (sizeof prpsoffsets32 / sizeof prpsoffsets32[0])
#define NOFFSETS64 (sizeof prpsoffsets64 / sizeof prpsoffsets64[0])
-#define NOFFSETS (class == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
+#define NOFFSETS (cls == ELFCLASS32 ? NOFFSETS32 : NOFFSETS64)
/*
* Look through the program headers of an executable image, searching
};
static void
-dophn_core(int class, int swap, int fd, off_t off, int num, size_t size)
+dophn_core(int cls, int swap, int fd, off_t off, int num, size_t size)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
{
Elf32_Phdr ph32;
Elf32_Nhdr *nh32 = NULL;
for (;;) {
if (offset >= bufsize)
/*@innerbreak@*/ break;
- if (class == ELFCLASS32)
+ if (cls == ELFCLASS32)
nh32 = (Elf32_Nhdr *)&nbuf[offset];
else
nh64 = (Elf64_Nhdr *)&nbuf[offset];
int32_t l;
char c[sizeof (int32_t)];
} u;
- int class;
+ int cls;
int swap;
/*
return;
- class = buf[4];
+ cls = buf[4];
- if (class == ELFCLASS32) {
+ if (cls == ELFCLASS32) {
Elf32_Ehdr elfhdr;
if (nbytes <= sizeof (Elf32_Ehdr))
return;
if (getu16(swap, elfhdr.e_type) == ET_CORE)
#ifdef ELFCORE
- dophn_core(class, swap,
+ dophn_core(cls, swap,
fd,
getu32(swap, elfhdr.e_phoff),
getu16(swap, elfhdr.e_phnum),
#endif
else {
if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
- dophn_exec(class, swap,
+ dophn_exec(cls, swap,
fd,
getu32(swap, elfhdr.e_phoff),
getu16(swap, elfhdr.e_phnum),
getu16(swap, elfhdr.e_phentsize));
}
- doshn(class, swap,
+ doshn(cls, swap,
fd,
getu32(swap, elfhdr.e_shoff),
getu16(swap, elfhdr.e_shnum),
return;
}
- if (class == ELFCLASS64) {
+ if (cls == ELFCLASS64) {
Elf64_Ehdr elfhdr;
if (nbytes <= sizeof (Elf64_Ehdr))
return;
if (getu16(swap, elfhdr.e_type) == ET_CORE)
#ifdef ELFCORE
- dophn_core(class, swap,
+ dophn_core(cls, swap,
fd,
#ifdef USE_ARRAY_FOR_64BIT_TYPES
getu32(swap, elfhdr.e_phoff[1]),
else
{
if (getu16(swap, elfhdr.e_type) == ET_EXEC) {
- dophn_exec(class, swap,
+ dophn_exec(cls, swap,
fd,
#ifdef USE_ARRAY_FOR_64BIT_TYPES
getu32(swap, elfhdr.e_phoff[1]),
getu16(swap, elfhdr.e_phnum),
getu16(swap, elfhdr.e_phentsize));
}
- doshn(class, swap,
+ doshn(cls, swap,
fd,
#ifdef USE_ARRAY_FOR_64BIT_TYPES
getu32(swap, elfhdr.e_shoff[1]),
#include <time.h>
#include <regex.h>
-
#ifndef lint
FILE_RCSID("@(#)Id: softmagic.c,v 1.51 2002/07/03 18:26:38 christos Exp ")
#endif /* lint */
extern int kflag;
/*@=redecl@*/
-static int match(struct magic *m, uint32_t nmagic,
- unsigned char *s, int nbytes)
- /*@globals fileSystem @*/
- /*@modifies m, s, fileSystem @*/;
-static int mget(union VALUETYPE *p, unsigned char *s,
- struct magic *m, int nbytes)
- /*@globals fileSystem @*/
- /*@modifies p, s, fileSystem @*/;
-static int mcheck(union VALUETYPE *p, struct magic *m)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-static int32_t mprint(union VALUETYPE *p, struct magic *m)
- /*@globals fileSystem @*/
- /*@modifies p, fileSystem @*/;
-static void mdebug(int32_t offset, char *str, int len)
- /*@globals fileSystem @*/
- /*@modifies fileSystem @*/;
-static int mconvert(union VALUETYPE *p, struct magic *m)
- /*@globals fileSystem @*/
- /*@modifies p, fileSystem @*/;
-
-/*
- * softmagic - lookup one file in database
- * (already read from 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(unsigned char *buf, int nbytes)
-{
- struct mlist *ml;
-
- for (ml = mlist.next; ml != &mlist; ml = ml->next)
- if (match(ml->magic, ml->nmagic, buf, nbytes))
- return 1;
-
- 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(struct magic *m, uint32_t nmagic, unsigned char *s, int nbytes)
-{
- int magindex = 0;
- int cont_level = 0;
- int need_separator = 0;
- union VALUETYPE p;
- /*@only@*/
- static int32_t *tmpoff = NULL;
- static size_t tmplen = 0;
- int32_t 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 = (int32_t *) malloc(tmplen = 20)) == NULL)
- error("out of memory\n");
-
- for (magindex = 0; magindex < nmagic; magindex++) {
- /* if main entry matches, print it... */
- if (!mget(&p, s, &m[magindex], nbytes) ||
- !mcheck(&p, &m[magindex])) {
- /*
- * main entry didn't match,
- * flush its continuations
- */
- while (magindex < nmagic &&
- m[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, &m[magindex]);
- /*
- * If we printed something, we'll need to print
- * a blank before we print something else.
- */
- if (m[magindex].desc[0])
- need_separator = 1;
- /* and any continuations that match */
- if (++cont_level >= tmplen)
- if ((tmpoff = (int32_t *) realloc(tmpoff,
- tmplen += 20)) == NULL)
- error("out of memory\n");
- while (m[magindex+1].cont_level != 0 &&
- ++magindex < nmagic) {
- if (cont_level >= m[magindex].cont_level) {
- if (cont_level > m[magindex].cont_level) {
- /*
- * We're at the end of the level
- * "cont_level" continuations.
- */
- cont_level = m[magindex].cont_level;
- }
- if (m[magindex].flag & OFFADD) {
- oldoff=m[magindex].offset;
- m[magindex].offset +=
- tmpoff[cont_level-1];
- }
- if (mget(&p, s, &m[magindex], nbytes) &&
- mcheck(&p, &m[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
- && (m[magindex].nospflag == 0)
- && (m[magindex].desc[0] != '\0')
- ) {
- (void) putchar(' ');
- need_separator = 0;
- }
- tmpoff[cont_level] =
- mprint(&p, &m[magindex]);
- if (m[magindex].desc[0])
- need_separator = 1;
-
- /*
- * If we see any continuations
- * at a higher level,
- * process them.
- */
- if (++cont_level >= tmplen)
- if ((tmpoff =
- (int32_t *) realloc(tmpoff,
- tmplen += 20)) == NULL)
- error("out of memory\n");
- }
- if (m[magindex].flag & OFFADD) {
- m[magindex].offset = oldoff;
- }
- }
- }
- firstline = 0;
- returnval = 1;
- if (!kflag) {
- return 1; /* don't keep searching */
- }
- }
- return returnval; /* This is hit if -k is set or there is no match */
-}
-
static int32_t
mprint(union VALUETYPE *p, struct magic *m)
+ /*@globals fileSystem @*/
+ /*@modifies p, fileSystem @*/
{
uint32_t v;
int32_t t=0 ;
*/
static int
mconvert(union VALUETYPE *p, struct magic *m)
+ /*@globals fileSystem @*/
+ /*@modifies p, fileSystem @*/
{
switch (m->type) {
case BYTE:
static void
mdebug(int32_t offset, char *str, int len)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
{
(void) fprintf(stderr, "mget @%d: ", offset);
showstr(stderr, (char *) str, len);
static int
mget(union VALUETYPE *p, unsigned char *s, struct magic *m, int nbytes)
+ /*@globals fileSystem @*/
+ /*@modifies p, s, fileSystem @*/
{
int32_t offset = m->offset;
static int
mcheck(union VALUETYPE *p, struct magic *m)
+ /*@globals fileSystem @*/
+ /*@modifies fileSystem @*/
{
uint32_t l = m->value.l;
uint32_t v;
return matched;
}
+
+/*
+ * 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(struct magic *m, uint32_t nmagic, unsigned char *s, int nbytes)
+ /*@globals fileSystem @*/
+ /*@modifies m, s, fileSystem @*/
+{
+ int magindex = 0;
+ int cont_level = 0;
+ int need_separator = 0;
+ union VALUETYPE p;
+ /*@only@*/
+ static int32_t *tmpoff = NULL;
+ static size_t tmplen = 0;
+ int32_t 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 = (int32_t *) malloc(tmplen = 20)) == NULL)
+ error("out of memory\n");
+
+ for (magindex = 0; magindex < nmagic; magindex++) {
+ /* if main entry matches, print it... */
+ if (!mget(&p, s, &m[magindex], nbytes) ||
+ !mcheck(&p, &m[magindex])) {
+ /*
+ * main entry didn't match,
+ * flush its continuations
+ */
+ while (magindex < nmagic &&
+ m[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, &m[magindex]);
+ /*
+ * If we printed something, we'll need to print
+ * a blank before we print something else.
+ */
+ if (m[magindex].desc[0])
+ need_separator = 1;
+ /* and any continuations that match */
+ if (++cont_level >= tmplen)
+ if ((tmpoff = (int32_t *) realloc(tmpoff,
+ tmplen += 20)) == NULL)
+ error("out of memory\n");
+ while (m[magindex+1].cont_level != 0 &&
+ ++magindex < nmagic) {
+ if (cont_level >= m[magindex].cont_level) {
+ if (cont_level > m[magindex].cont_level) {
+ /*
+ * We're at the end of the level
+ * "cont_level" continuations.
+ */
+ cont_level = m[magindex].cont_level;
+ }
+ if (m[magindex].flag & OFFADD) {
+ oldoff=m[magindex].offset;
+ m[magindex].offset +=
+ tmpoff[cont_level-1];
+ }
+ if (mget(&p, s, &m[magindex], nbytes) &&
+ mcheck(&p, &m[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
+ && (m[magindex].nospflag == 0)
+ && (m[magindex].desc[0] != '\0')
+ ) {
+ (void) putchar(' ');
+ need_separator = 0;
+ }
+ tmpoff[cont_level] =
+ mprint(&p, &m[magindex]);
+ if (m[magindex].desc[0])
+ need_separator = 1;
+
+ /*
+ * If we see any continuations
+ * at a higher level,
+ * process them.
+ */
+ if (++cont_level >= tmplen)
+ if ((tmpoff =
+ (int32_t *) realloc(tmpoff,
+ tmplen += 20)) == NULL)
+ error("out of memory\n");
+ }
+ if (m[magindex].flag & OFFADD) {
+ m[magindex].offset = oldoff;
+ }
+ }
+ }
+ firstline = 0;
+ returnval = 1;
+ if (!kflag) {
+ return 1; /* don't keep searching */
+ }
+ }
+ return returnval; /* This is hit if -k is set or there is no match */
+}
+
+/*
+ * softmagic - lookup one file in database
+ * (already read from MAGIC by apprentice.c).
+ * Passed the name and FILE * of one file to be typed.
+ */
+int
+softmagic(unsigned char *buf, int nbytes)
+{
+ struct mlist *ml;
+
+ for (ml = mlist.next; ml != &mlist; ml = ml->next)
+ if (match(ml->magic, ml->nmagic, buf, nbytes))
+ return 1;
+
+ return 0;
+}