version 1.25, 2008/05/08 01:40:56 |
version 1.26, 2009/04/24 18:54:34 |
|
|
|
|
#include "file.h" |
#include "file.h" |
#include "magic.h" |
#include "magic.h" |
|
#include "patchlevel.h" |
#include <stdlib.h> |
#include <stdlib.h> |
#ifdef HAVE_UNISTD_H |
#ifdef HAVE_UNISTD_H |
#include <unistd.h> |
#include <unistd.h> |
|
|
#ifdef QUICK |
#ifdef QUICK |
#include <sys/mman.h> |
#include <sys/mman.h> |
#endif |
#endif |
|
#include <sys/types.h> |
|
#include <dirent.h> |
|
|
#ifndef lint |
#ifndef lint |
FILE_RCSID("@(#)$Id$") |
FILE_RCSID("@(#)$Id$") |
|
|
int *, int); |
int *, int); |
private int parse(struct magic_set *, struct magic_entry **, uint32_t *, |
private int parse(struct magic_set *, struct magic_entry **, uint32_t *, |
const char *, size_t, int); |
const char *, size_t, int); |
|
private int parse_mime(struct magic_set *, struct magic_entry **, uint32_t *, |
|
const char *); |
private void eatsize(const char **); |
private void eatsize(const char **); |
private int apprentice_1(struct magic_set *, const char *, int, struct mlist *); |
private int apprentice_1(struct magic_set *, const char *, int, struct mlist *); |
private size_t apprentice_magic_strength(const struct magic *); |
private size_t apprentice_magic_strength(const struct magic *); |
private int apprentice_sort(const void *, const void *); |
private int apprentice_sort(const void *, const void *); |
private int apprentice_file(struct magic_set *, struct magic **, uint32_t *, |
private int apprentice_load(struct magic_set *, struct magic **, uint32_t *, |
const char *, int); |
const char *, int); |
private void byteswap(struct magic *, uint32_t); |
private void byteswap(struct magic *, uint32_t); |
private void bs1(struct magic *); |
private void bs1(struct magic *); |
private uint16_t swap2(uint16_t); |
private uint16_t swap2(uint16_t); |
private uint32_t swap4(uint32_t); |
private uint32_t swap4(uint32_t); |
private uint64_t swap8(uint64_t); |
private uint64_t swap8(uint64_t); |
private char *mkdbname(const char *, char *, size_t, int); |
private void mkdbname(const char *, char **, int); |
private int apprentice_map(struct magic_set *, struct magic **, uint32_t *, |
private int apprentice_map(struct magic_set *, struct magic **, uint32_t *, |
const char *); |
const char *); |
private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *, |
private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *, |
const char *); |
const char *); |
private int check_format_type(const char *, int); |
private int check_format_type(const char *, int); |
private int check_format(struct magic_set *, struct magic *); |
private int check_format(struct magic_set *, struct magic *); |
|
private int get_op(char); |
|
|
private size_t maxmagic = 0; |
private size_t maxmagic = 0; |
private size_t magicsize = sizeof(struct magic); |
private size_t magicsize = sizeof(struct magic); |
|
|
|
private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc"; |
|
private const char mime_marker[] = "!:mime"; |
|
private const size_t mime_marker_len = sizeof(mime_marker) - 1; |
|
|
#ifdef COMPILE_ONLY |
#ifdef COMPILE_ONLY |
|
|
|
|
#endif /* COMPILE_ONLY */ |
#endif /* COMPILE_ONLY */ |
|
|
static const struct type_tbl_s { |
static const struct type_tbl_s { |
const char *name; |
const char name[16]; |
const size_t len; |
const size_t len; |
const int type; |
const int type; |
const int format; |
const int format; |
} type_tbl[] = { |
} type_tbl[] = { |
# define XX(s) s, (sizeof(s) - 1) |
# define XX(s) s, (sizeof(s) - 1) |
# define XX_NULL NULL, 0 |
# define XX_NULL "", 0 |
{ XX("byte"), FILE_BYTE, FILE_FMT_NUM }, |
{ XX("byte"), FILE_BYTE, FILE_FMT_NUM }, |
{ XX("short"), FILE_SHORT, FILE_FMT_NUM }, |
{ XX("short"), FILE_SHORT, FILE_FMT_NUM }, |
{ XX("default"), FILE_DEFAULT, FILE_FMT_STR }, |
{ XX("default"), FILE_DEFAULT, FILE_FMT_STR }, |
|
|
{ XX("qldate"), FILE_QLDATE, FILE_FMT_STR }, |
{ XX("qldate"), FILE_QLDATE, FILE_FMT_STR }, |
{ XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR }, |
{ XX("leqldate"), FILE_LEQLDATE, FILE_FMT_STR }, |
{ XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR }, |
{ XX("beqldate"), FILE_BEQLDATE, FILE_FMT_STR }, |
|
{ XX("float"), FILE_FLOAT, FILE_FMT_FLOAT }, |
|
{ XX("befloat"), FILE_BEFLOAT, FILE_FMT_FLOAT }, |
|
{ XX("lefloat"), FILE_LEFLOAT, FILE_FMT_FLOAT }, |
|
{ XX("double"), FILE_DOUBLE, FILE_FMT_DOUBLE }, |
|
{ XX("bedouble"), FILE_BEDOUBLE, FILE_FMT_DOUBLE }, |
|
{ XX("ledouble"), FILE_LEDOUBLE, FILE_FMT_DOUBLE }, |
{ XX_NULL, FILE_INVALID, FILE_FMT_NONE }, |
{ XX_NULL, FILE_INVALID, FILE_FMT_NONE }, |
# undef XX |
# undef XX |
# undef XX_NULL |
# undef XX_NULL |
|
|
{ |
{ |
const struct type_tbl_s *p; |
const struct type_tbl_s *p; |
|
|
for (p = type_tbl; p->name; p++) { |
for (p = type_tbl; p->len; p++) { |
if (strncmp(l, p->name, p->len) == 0) { |
if (strncmp(l, p->name, p->len) == 0) { |
if (t) |
if (t) |
*t = l + p->len; |
*t = l + p->len; |
|
|
return; |
return; |
done++; |
done++; |
|
|
for (p = type_tbl; p->name; p++) { |
for (p = type_tbl; p->len; p++) { |
assert(p->type < FILE_NAMES_SIZE); |
assert(p->type < FILE_NAMES_SIZE); |
file_names[p->type] = p->name; |
file_names[p->type] = p->name; |
file_formats[p->type] = p->format; |
file_formats[p->type] = p->format; |
|
|
} |
} |
|
|
/* |
/* |
* Handle one file. |
* Handle one file or directory. |
*/ |
*/ |
private int |
private int |
apprentice_1(struct magic_set *ms, const char *fn, int action, |
apprentice_1(struct magic_set *ms, const char *fn, int action, |
|
|
} |
} |
|
|
if (action == FILE_COMPILE) { |
if (action == FILE_COMPILE) { |
rv = apprentice_file(ms, &magic, &nmagic, fn, action); |
rv = apprentice_load(ms, &magic, &nmagic, fn, action); |
if (rv != 0) |
if (rv != 0) |
return -1; |
return -1; |
rv = apprentice_compile(ms, &magic, &nmagic, fn); |
rv = apprentice_compile(ms, &magic, &nmagic, fn); |
|
|
if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) { |
if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) { |
if (ms->flags & MAGIC_CHECK) |
if (ms->flags & MAGIC_CHECK) |
file_magwarn(ms, "using regular magic file `%s'", fn); |
file_magwarn(ms, "using regular magic file `%s'", fn); |
rv = apprentice_file(ms, &magic, &nmagic, fn, action); |
rv = apprentice_load(ms, &magic, &nmagic, fn, action); |
if (rv != 0) |
if (rv != 0) |
return -1; |
return -1; |
} |
} |
|
|
mapped = rv; |
mapped = rv; |
|
|
if (magic == NULL || nmagic == 0) { |
if (magic == NULL) { |
file_delmagic(magic, mapped, nmagic); |
file_delmagic(magic, mapped, nmagic); |
return -1; |
return -1; |
} |
} |
|
|
} |
} |
} |
} |
|
|
/* const char *fn: list of magic files */ |
/* const char *fn: list of magic files and directories */ |
protected struct mlist * |
protected struct mlist * |
file_apprentice(struct magic_set *ms, const char *fn, int action) |
file_apprentice(struct magic_set *ms, const char *fn, int action) |
{ |
{ |
char *p, *mfn, *afn = NULL; |
char *p, *mfn; |
int file_err, errs = -1; |
int file_err, errs = -1; |
struct mlist *mlist; |
struct mlist *mlist; |
static const char mime[] = ".mime"; |
|
|
|
init_file_tables(); |
init_file_tables(); |
|
|
|
|
*p++ = '\0'; |
*p++ = '\0'; |
if (*fn == '\0') |
if (*fn == '\0') |
break; |
break; |
if (ms->flags & MAGIC_MIME) { |
|
size_t len = strlen(fn) + sizeof(mime); |
|
if ((afn = malloc(len)) == NULL) { |
|
free(mfn); |
|
free(mlist); |
|
file_oomem(ms, len); |
|
return NULL; |
|
} |
|
(void)strlcpy(afn, fn, len); |
|
(void)strlcat(afn, ".mime", len); |
|
fn = afn; |
|
} |
|
file_err = apprentice_1(ms, fn, action, mlist); |
file_err = apprentice_1(ms, fn, action, mlist); |
if (file_err > errs) |
errs = MAX(errs, file_err); |
errs = file_err; |
|
if (afn) { |
|
free(afn); |
|
afn = NULL; |
|
} |
|
fn = p; |
fn = p; |
} |
} |
if (errs == -1) { |
if (errs == -1) { |
|
|
|
|
case FILE_SEARCH: |
case FILE_SEARCH: |
case FILE_REGEX: |
case FILE_REGEX: |
val += m->vallen; |
val += m->vallen * MAX(MULT / m->vallen, 1); |
break; |
break; |
|
|
case FILE_DATE: |
case FILE_DATE: |
|
|
case FILE_LELDATE: |
case FILE_LELDATE: |
case FILE_BELDATE: |
case FILE_BELDATE: |
case FILE_MELDATE: |
case FILE_MELDATE: |
|
case FILE_FLOAT: |
|
case FILE_BEFLOAT: |
|
case FILE_LEFLOAT: |
val += 4 * MULT; |
val += 4 * MULT; |
break; |
break; |
|
|
|
|
case FILE_QLDATE: |
case FILE_QLDATE: |
case FILE_LEQLDATE: |
case FILE_LEQLDATE: |
case FILE_BEQLDATE: |
case FILE_BEQLDATE: |
|
case FILE_DOUBLE: |
|
case FILE_BEDOUBLE: |
|
case FILE_LEDOUBLE: |
val += 8 * MULT; |
val += 8 * MULT; |
break; |
break; |
|
|
|
|
|
|
switch (m->reln) { |
switch (m->reln) { |
case 'x': /* matches anything penalize */ |
case 'x': /* matches anything penalize */ |
|
case '!': /* matches almost anything penalize */ |
val = 0; |
val = 0; |
break; |
break; |
|
|
case '!': |
|
case '=': /* Exact match, prefer */ |
case '=': /* Exact match, prefer */ |
val += MULT; |
val += MULT; |
break; |
break; |
|
|
return 1; |
return 1; |
} |
} |
|
|
|
private void |
|
set_test_type(struct magic *mstart, struct magic *m) |
|
{ |
|
switch (m->type) { |
|
case FILE_BYTE: |
|
case FILE_SHORT: |
|
case FILE_LONG: |
|
case FILE_DATE: |
|
case FILE_BESHORT: |
|
case FILE_BELONG: |
|
case FILE_BEDATE: |
|
case FILE_LESHORT: |
|
case FILE_LELONG: |
|
case FILE_LEDATE: |
|
case FILE_LDATE: |
|
case FILE_BELDATE: |
|
case FILE_LELDATE: |
|
case FILE_MEDATE: |
|
case FILE_MELDATE: |
|
case FILE_MELONG: |
|
case FILE_QUAD: |
|
case FILE_LEQUAD: |
|
case FILE_BEQUAD: |
|
case FILE_QDATE: |
|
case FILE_LEQDATE: |
|
case FILE_BEQDATE: |
|
case FILE_QLDATE: |
|
case FILE_LEQLDATE: |
|
case FILE_BEQLDATE: |
|
case FILE_FLOAT: |
|
case FILE_BEFLOAT: |
|
case FILE_LEFLOAT: |
|
case FILE_DOUBLE: |
|
case FILE_BEDOUBLE: |
|
case FILE_LEDOUBLE: |
|
case FILE_STRING: |
|
case FILE_PSTRING: |
|
case FILE_BESTRING16: |
|
case FILE_LESTRING16: |
|
/* binary test, set flag */ |
|
mstart->flag |= BINTEST; |
|
break; |
|
case FILE_REGEX: |
|
case FILE_SEARCH: |
|
/* binary test if pattern is not text */ |
|
if (file_looks_utf8(m->value.s, m->vallen, NULL, NULL) == 0) |
|
mstart->flag |= BINTEST; |
|
break; |
|
case FILE_DEFAULT: |
|
/* can't deduce anything; we shouldn't see this at the |
|
top level anyway */ |
|
break; |
|
case FILE_INVALID: |
|
default: |
|
/* invalid search type, but no need to complain here */ |
|
break; |
|
} |
|
} |
|
|
/* |
/* |
* parse from a file |
* Load and parse one file. |
* const char *fn: name of magic file |
|
*/ |
*/ |
private int |
private void |
apprentice_file(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, |
load_1(struct magic_set *ms, int action, const char *fn, int *errs, |
const char *fn, int action) |
struct magic_entry **marray, uint32_t *marraycount) |
{ |
{ |
private const char hdr[] = |
|
"cont\toffset\ttype\topcode\tmask\tvalue\tdesc"; |
|
FILE *f; |
|
char line[BUFSIZ]; |
char line[BUFSIZ]; |
int errs = 0; |
|
struct magic_entry *marray; |
|
uint32_t marraycount, i, mentrycount = 0; |
|
size_t lineno = 0; |
size_t lineno = 0; |
|
FILE *f = fopen(ms->file = fn, "r"); |
ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ |
|
|
|
f = fopen(ms->file = fn, "r"); |
|
if (f == NULL) { |
if (f == NULL) { |
if (errno != ENOENT) |
if (errno != ENOENT) |
file_error(ms, errno, "cannot read magic file `%s'", |
file_error(ms, errno, "cannot read magic file `%s'", |
fn); |
fn); |
return -1; |
(*errs)++; |
|
} else { |
|
/* read and parse this file */ |
|
for (ms->line = 1; fgets(line, sizeof(line), f) != NULL; ms->line++) { |
|
size_t len; |
|
len = strlen(line); |
|
if (len == 0) /* null line, garbage, etc */ |
|
continue; |
|
if (line[len - 1] == '\n') { |
|
lineno++; |
|
line[len - 1] = '\0'; /* delete newline */ |
|
} |
|
if (line[0] == '\0') /* empty, do not parse */ |
|
continue; |
|
if (line[0] == '#') /* comment, do not parse */ |
|
continue; |
|
if (len > mime_marker_len && |
|
memcmp(line, mime_marker, mime_marker_len) == 0) { |
|
/* MIME type */ |
|
if (parse_mime(ms, marray, marraycount, |
|
line + mime_marker_len) != 0) |
|
(*errs)++; |
|
continue; |
|
} |
|
if (parse(ms, marray, marraycount, line, lineno, action) != 0) |
|
(*errs)++; |
|
} |
|
|
|
(void)fclose(f); |
} |
} |
|
} |
|
|
|
/* |
|
* parse a file or directory of files |
|
* const char *fn: name of magic file or directory |
|
*/ |
|
private int |
|
apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp, |
|
const char *fn, int action) |
|
{ |
|
int errs = 0; |
|
struct magic_entry *marray; |
|
uint32_t marraycount, i, mentrycount = 0, starttest; |
|
char subfn[MAXPATHLEN]; |
|
struct stat st; |
|
DIR *dir; |
|
struct dirent *d; |
|
|
|
ms->flags |= MAGIC_CHECK; /* Enable checks for parsed files */ |
|
|
maxmagic = MAXMAGIS; |
maxmagic = MAXMAGIS; |
if ((marray = calloc(maxmagic, sizeof(*marray))) == NULL) { |
if ((marray = calloc(maxmagic, sizeof(*marray))) == NULL) { |
(void)fclose(f); |
|
file_oomem(ms, maxmagic * sizeof(*marray)); |
file_oomem(ms, maxmagic * sizeof(*marray)); |
return -1; |
return -1; |
} |
} |
|
|
|
|
/* print silly verbose header for USG compat. */ |
/* print silly verbose header for USG compat. */ |
if (action == FILE_CHECK) |
if (action == FILE_CHECK) |
(void)fprintf(stderr, "%s\n", hdr); |
(void)fprintf(stderr, "%s\n", usg_hdr); |
|
|
/* read and parse this file */ |
/* load directory or file */ |
for (ms->line = 1; fgets(line, sizeof(line), f) != NULL; ms->line++) { |
if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) { |
size_t len; |
dir = opendir(fn); |
len = strlen(line); |
if (dir) { |
if (len == 0) /* null line, garbage, etc */ |
while (d = readdir(dir)) { |
continue; |
snprintf(subfn, sizeof(subfn), "%s/%s", |
if (line[len - 1] == '\n') { |
fn, d->d_name); |
lineno++; |
if (stat(subfn, &st) == 0 && S_ISREG(st.st_mode)) { |
line[len - 1] = '\0'; /* delete newline */ |
load_1(ms, action, subfn, &errs, |
} |
&marray, &marraycount); |
if (line[0] == '\0') /* empty, do not parse */ |
} |
continue; |
} |
if (line[0] == '#') /* comment, do not parse */ |
closedir(dir); |
continue; |
} else |
if (parse(ms, &marray, &marraycount, line, lineno, action) != 0) |
|
errs++; |
errs++; |
} |
} else |
|
load_1(ms, action, fn, &errs, &marray, &marraycount); |
(void)fclose(f); |
|
if (errs) |
if (errs) |
goto out; |
goto out; |
|
|
#ifndef NOORDER |
/* Set types of tests */ |
|
for (i = 0; i < marraycount; ) { |
|
if (marray[i].mp->cont_level != 0) { |
|
i++; |
|
continue; |
|
} |
|
|
|
starttest = i; |
|
do { |
|
set_test_type(marray[starttest].mp, marray[i].mp); |
|
if (ms->flags & MAGIC_DEBUG) { |
|
(void)fprintf(stderr, "%s%s%s: %s\n", |
|
marray[i].mp->mimetype, |
|
marray[i].mp->mimetype[0] == '\0' ? "" : "; ", |
|
marray[i].mp->desc[0] ? marray[i].mp->desc : "(no description)", |
|
marray[i].mp->flag & BINTEST ? "binary" : "text"); |
|
if (marray[i].mp->flag & BINTEST) { |
|
#define SYMBOL "text" |
|
#define SYMLEN sizeof(SYMBOL) |
|
char *p = strstr(marray[i].mp->desc, "text"); |
|
if (p && (p == marray[i].mp->desc || isspace(p[-1])) && |
|
(p + SYMLEN - marray[i].mp->desc == MAXstring || |
|
(p[SYMLEN] == '\0' || isspace(p[SYMLEN])))) { |
|
(void)fprintf(stderr, |
|
"*** Possible binary test for text type\n"); |
|
} |
|
#undef SYMBOL |
|
#undef SYMLEN |
|
} |
|
} |
|
} while (++i < marraycount && marray[i].mp->cont_level != 0); |
|
} |
|
|
qsort(marray, marraycount, sizeof(*marray), apprentice_sort); |
qsort(marray, marraycount, sizeof(*marray), apprentice_sort); |
|
|
/* |
/* |
* Make sure that any level 0 "default" line is last (if one exists). |
* Make sure that any level 0 "default" line is last (if one exists). |
*/ |
*/ |
|
|
break; |
break; |
} |
} |
} |
} |
#endif |
|
|
|
for (i = 0; i < marraycount; i++) |
for (i = 0; i < marraycount; i++) |
mentrycount += marray[i].cont_count; |
mentrycount += marray[i].cont_count; |
|
|
case FILE_BELONG: |
case FILE_BELONG: |
case FILE_LELONG: |
case FILE_LELONG: |
case FILE_MELONG: |
case FILE_MELONG: |
|
case FILE_FLOAT: |
|
case FILE_BEFLOAT: |
|
case FILE_LEFLOAT: |
v = (int32_t) v; |
v = (int32_t) v; |
break; |
break; |
case FILE_QUAD: |
case FILE_QUAD: |
|
|
case FILE_BEQLDATE: |
case FILE_BEQLDATE: |
case FILE_LEQDATE: |
case FILE_LEQDATE: |
case FILE_LEQLDATE: |
case FILE_LEQLDATE: |
|
case FILE_DOUBLE: |
|
case FILE_BEDOUBLE: |
|
case FILE_LEDOUBLE: |
v = (int64_t) v; |
v = (int64_t) v; |
break; |
break; |
case FILE_STRING: |
case FILE_STRING: |
|
|
} |
} |
|
|
private int |
private int |
string_modifier_check(struct magic_set *ms, struct magic const *m) |
string_modifier_check(struct magic_set *ms, struct magic *m) |
{ |
{ |
if ((ms->flags & MAGIC_CHECK) == 0) |
if ((ms->flags & MAGIC_CHECK) == 0) |
return 0; |
return 0; |
|
|
case FILE_BESTRING16: |
case FILE_BESTRING16: |
case FILE_LESTRING16: |
case FILE_LESTRING16: |
if (m->str_flags != 0) { |
if (m->str_flags != 0) { |
file_magwarn(ms, "no modifiers allowed for 16-bit strings\n"); |
file_magwarn(ms, |
|
"no modifiers allowed for 16-bit strings\n"); |
return -1; |
return -1; |
} |
} |
break; |
break; |
case FILE_STRING: |
case FILE_STRING: |
case FILE_PSTRING: |
case FILE_PSTRING: |
if ((m->str_flags & REGEX_OFFSET_START) != 0) { |
if ((m->str_flags & REGEX_OFFSET_START) != 0) { |
file_magwarn(ms, "'/%c' only allowed on regex and search\n", |
file_magwarn(ms, |
|
"'/%c' only allowed on regex and search\n", |
CHAR_REGEX_OFFSET_START); |
CHAR_REGEX_OFFSET_START); |
return -1; |
return -1; |
} |
} |
break; |
break; |
case FILE_SEARCH: |
case FILE_SEARCH: |
|
if (m->str_range == 0) { |
|
file_magwarn(ms, |
|
"missing range; defaulting to %d\n", |
|
STRING_DEFAULT_RANGE); |
|
m->str_range = STRING_DEFAULT_RANGE; |
|
return -1; |
|
} |
break; |
break; |
case FILE_REGEX: |
case FILE_REGEX: |
if ((m->str_flags & STRING_COMPACT_BLANK) != 0) { |
if ((m->str_flags & STRING_COMPACT_BLANK) != 0) { |
|
|
private int |
private int |
get_cond(const char *l, const char **t) |
get_cond(const char *l, const char **t) |
{ |
{ |
static struct cond_tbl_s { |
static const struct cond_tbl_s { |
const char *name; |
char name[8]; |
const size_t len; |
size_t len; |
const int cond; |
int cond; |
} cond_tbl[] = { |
} cond_tbl[] = { |
{ "if", 2, COND_IF }, |
{ "if", 2, COND_IF }, |
{ "elif", 4, COND_ELIF }, |
{ "elif", 4, COND_ELIF }, |
{ "else", 4, COND_ELSE }, |
{ "else", 4, COND_ELSE }, |
{ NULL, 0, COND_NONE }, |
{ "", 0, COND_NONE }, |
}; |
}; |
struct cond_tbl_s *p; |
const struct cond_tbl_s *p; |
|
|
for (p = cond_tbl; p->name; p++) { |
for (p = cond_tbl; p->len; p++) { |
if (strncmp(l, p->name, p->len) == 0 && |
if (strncmp(l, p->name, p->len) == 0 && |
isspace((unsigned char)l[p->len])) { |
isspace((unsigned char)l[p->len])) { |
if (t) |
if (t) |
|
|
case 'B': |
case 'B': |
m->in_type = FILE_BYTE; |
m->in_type = FILE_BYTE; |
break; |
break; |
|
case 'e': |
|
case 'f': |
|
case 'g': |
|
m->in_type = FILE_LEDOUBLE; |
|
break; |
|
case 'E': |
|
case 'F': |
|
case 'G': |
|
m->in_type = FILE_BEDOUBLE; |
|
break; |
default: |
default: |
if (ms->flags & MAGIC_CHECK) |
if (ms->flags & MAGIC_CHECK) |
file_magwarn(ms, |
file_magwarn(ms, |
|
|
file_magwarn(ms, "'~' invalid for string types"); |
file_magwarn(ms, "'~' invalid for string types"); |
++l; |
++l; |
} |
} |
m->str_count = 0; |
m->str_range = 0; |
m->str_flags = 0; |
m->str_flags = 0; |
m->num_mask = 0; |
m->num_mask = 0; |
if ((op = get_op(*l)) != -1) { |
if ((op = get_op(*l)) != -1) { |
|
|
eatsize(&l); |
eatsize(&l); |
} |
} |
else if (op == FILE_OPDIVIDE) { |
else if (op == FILE_OPDIVIDE) { |
int have_count = 0; |
int have_range = 0; |
while (!isspace((unsigned char)*++l)) { |
while (!isspace((unsigned char)*++l)) { |
switch (*l) { |
switch (*l) { |
/* for portability avoid "case '0' ... '9':" */ |
|
case '0': case '1': case '2': |
case '0': case '1': case '2': |
case '3': case '4': case '5': |
case '3': case '4': case '5': |
case '6': case '7': case '8': |
case '6': case '7': case '8': |
case '9': { |
case '9': |
if (have_count && ms->flags & MAGIC_CHECK) |
if (have_range && |
|
(ms->flags & MAGIC_CHECK)) |
file_magwarn(ms, |
file_magwarn(ms, |
"multiple counts"); |
"multiple ranges"); |
have_count = 1; |
have_range = 1; |
m->str_count = strtoul(l, &t, 0); |
m->str_range = strtoul(l, &t, 0); |
|
if (m->str_range == 0) |
|
file_magwarn(ms, |
|
"zero range"); |
l = t - 1; |
l = t - 1; |
break; |
break; |
} |
|
case CHAR_COMPACT_BLANK: |
case CHAR_COMPACT_BLANK: |
m->str_flags |= STRING_COMPACT_BLANK; |
m->str_flags |= STRING_COMPACT_BLANK; |
break; |
break; |
|
|
return -1; |
return -1; |
} |
} |
/* allow multiple '/' for readability */ |
/* allow multiple '/' for readability */ |
if (l[1] == '/' && !isspace((unsigned char)l[2])) |
if (l[1] == '/' && |
|
!isspace((unsigned char)l[2])) |
l++; |
l++; |
} |
} |
if (string_modifier_check(ms, m) == -1) |
if (string_modifier_check(ms, m) == -1) |
|
|
EATAB; |
EATAB; |
if (l[0] == '\b') { |
if (l[0] == '\b') { |
++l; |
++l; |
m->nospflag = 1; |
m->flag |= NOSPACE; |
} else if ((l[0] == '\\') && (l[1] == 'b')) { |
} else if ((l[0] == '\\') && (l[1] == 'b')) { |
++l; |
++l; |
++l; |
++l; |
m->nospflag = 1; |
m->flag |= NOSPACE; |
} else |
} |
m->nospflag = 0; |
|
for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); ) |
for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); ) |
continue; |
continue; |
if (i == sizeof(m->desc)) { |
if (i == sizeof(m->desc)) { |
|
|
file_mdump(m); |
file_mdump(m); |
} |
} |
#endif |
#endif |
|
m->mimetype[0] = '\0'; /* initialise MIME type to none */ |
if (m->cont_level == 0) |
if (m->cont_level == 0) |
++(*nmentryp); /* make room for next */ |
++(*nmentryp); /* make room for next */ |
return 0; |
return 0; |
} |
} |
|
|
|
/* |
|
* parse a MIME annotation line from magic file, put into magic[index - 1] |
|
* if valid |
|
*/ |
private int |
private int |
|
parse_mime(struct magic_set *ms, struct magic_entry **mentryp, |
|
uint32_t *nmentryp, const char *line) |
|
{ |
|
size_t i; |
|
const char *l = line; |
|
struct magic *m; |
|
struct magic_entry *me; |
|
|
|
if (*nmentryp == 0) { |
|
file_error(ms, 0, "No current entry for MIME type"); |
|
return -1; |
|
} |
|
|
|
me = &(*mentryp)[*nmentryp - 1]; |
|
m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1]; |
|
|
|
if (m->mimetype[0] != '\0') { |
|
file_error(ms, 0, "Current entry already has a MIME type: %s\n" |
|
"Description: %s\nNew type: %s", m->mimetype, m->desc, l); |
|
return -1; |
|
} |
|
|
|
EATAB; |
|
for (i = 0; |
|
*l && ((isascii((unsigned char)*l) && isalnum((unsigned char)*l)) |
|
|| strchr("-+/.", *l)) && i < sizeof(m->mimetype); |
|
m->mimetype[i++] = *l++) |
|
continue; |
|
if (i == sizeof(m->mimetype)) { |
|
m->desc[sizeof(m->mimetype) - 1] = '\0'; |
|
if (ms->flags & MAGIC_CHECK) |
|
file_magwarn(ms, "MIME type `%s' truncated %zu", |
|
m->mimetype, i); |
|
} else |
|
m->mimetype[i] = '\0'; |
|
|
|
if (i > 0) |
|
return 0; |
|
else |
|
return -1; |
|
} |
|
|
|
private int |
check_format_type(const char *ptr, int type) |
check_format_type(const char *ptr, int type) |
{ |
{ |
int quad = 0; |
int quad = 0; |
|
|
return -1; |
return -1; |
} |
} |
|
|
|
case FILE_FMT_FLOAT: |
|
case FILE_FMT_DOUBLE: |
|
if (*ptr == '-') |
|
ptr++; |
|
if (*ptr == '.') |
|
ptr++; |
|
while (isdigit((unsigned char)*ptr)) ptr++; |
|
if (*ptr == '.') |
|
ptr++; |
|
while (isdigit((unsigned char)*ptr)) ptr++; |
|
|
|
switch (*ptr++) { |
|
case 'e': |
|
case 'E': |
|
case 'f': |
|
case 'F': |
|
case 'g': |
|
case 'G': |
|
return 0; |
|
|
|
default: |
|
return -1; |
|
} |
|
|
|
|
case FILE_FMT_STR: |
case FILE_FMT_STR: |
if (*ptr == '-') |
if (*ptr == '-') |
ptr++; |
ptr++; |
|
|
assert(file_nformats == file_nnames); |
assert(file_nformats == file_nnames); |
|
|
if (m->type >= file_nformats) { |
if (m->type >= file_nformats) { |
file_error(ms, 0, "Internal error inconsistency between " |
file_magwarn(ms, "Internal error inconsistency between " |
"m->type and format strings"); |
"m->type and format strings"); |
return -1; |
return -1; |
} |
} |
if (file_formats[m->type] == FILE_FMT_NONE) { |
if (file_formats[m->type] == FILE_FMT_NONE) { |
file_error(ms, 0, "No format string for `%s' with description " |
file_magwarn(ms, "No format string for `%s' with description " |
"`%s'", m->desc, file_names[m->type]); |
"`%s'", m->desc, file_names[m->type]); |
return -1; |
return -1; |
} |
} |
|
|
* TODO: this error message is unhelpful if the format |
* TODO: this error message is unhelpful if the format |
* string is not one character long |
* string is not one character long |
*/ |
*/ |
file_error(ms, 0, "Printf format `%c' is not valid for type " |
file_magwarn(ms, "Printf format `%c' is not valid for type " |
" `%s' in description `%s'", |
"`%s' in description `%s'", |
ptr && *ptr ? *ptr : '?', |
ptr && *ptr ? *ptr : '?', |
file_names[m->type], m->desc); |
file_names[m->type], m->desc); |
return -1; |
return -1; |
|
|
|
|
for (; *ptr; ptr++) { |
for (; *ptr; ptr++) { |
if (*ptr == '%') { |
if (*ptr == '%') { |
file_error(ms, 0, |
file_magwarn(ms, |
"Too many format strings (should have at most one) " |
"Too many format strings (should have at most one) " |
"for `%s' with description `%s'", |
"for `%s' with description `%s'", |
file_names[m->type], m->desc); |
file_names[m->type], m->desc); |
|
|
return -1; |
return -1; |
} |
} |
m->vallen = slen; |
m->vallen = slen; |
|
if (m->type == FILE_PSTRING) |
|
m->vallen++; |
return 0; |
return 0; |
|
case FILE_FLOAT: |
|
case FILE_BEFLOAT: |
|
case FILE_LEFLOAT: |
|
if (m->reln != 'x') { |
|
char *ep; |
|
#ifdef HAVE_STRTOF |
|
m->value.f = strtof(*p, &ep); |
|
#else |
|
m->value.f = (float)strtod(*p, &ep); |
|
#endif |
|
*p = ep; |
|
} |
|
return 0; |
|
case FILE_DOUBLE: |
|
case FILE_BEDOUBLE: |
|
case FILE_LEDOUBLE: |
|
if (m->reln != 'x') { |
|
char *ep; |
|
m->value.d = strtod(*p, &ep); |
|
*p = ep; |
|
} |
|
return 0; |
default: |
default: |
if (m->reln != 'x') { |
if (m->reln != 'x') { |
char *ep; |
char *ep; |
|
|
uint32_t *ptr; |
uint32_t *ptr; |
uint32_t version; |
uint32_t version; |
int needsbyteswap; |
int needsbyteswap; |
char buf[MAXPATHLEN]; |
char *dbname = NULL; |
char *dbname = mkdbname(fn, buf, sizeof(buf), 0); |
|
void *mm = NULL; |
void *mm = NULL; |
|
|
|
mkdbname(fn, &dbname, 0); |
if (dbname == NULL) |
if (dbname == NULL) |
return -1; |
goto error2; |
|
|
if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) |
if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1) |
return -1; |
goto error2; |
|
|
if (fstat(fd, &st) == -1) { |
if (fstat(fd, &st) == -1) { |
file_error(ms, errno, "cannot stat `%s'", dbname); |
file_error(ms, errno, "cannot stat `%s'", dbname); |
goto error; |
goto error1; |
} |
} |
if (st.st_size < 16) { |
if (st.st_size < 8) { |
file_error(ms, 0, "file `%s' is too small", dbname); |
file_error(ms, 0, "file `%s' is too small", dbname); |
goto error; |
goto error1; |
} |
} |
|
|
#ifdef QUICK |
#ifdef QUICK |
if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, |
if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE, |
MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { |
MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) { |
file_error(ms, errno, "cannot map `%s'", dbname); |
file_error(ms, errno, "cannot map `%s'", dbname); |
goto error; |
goto error1; |
} |
} |
#define RET 2 |
#define RET 2 |
#else |
#else |
if ((mm = malloc((size_t)st.st_size)) == NULL) { |
if ((mm = malloc((size_t)st.st_size)) == NULL) { |
file_oomem(ms, (size_t)st.st_size); |
file_oomem(ms, (size_t)st.st_size); |
goto error; |
goto error1; |
} |
} |
if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) { |
if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) { |
file_badread(ms); |
file_badread(ms); |
goto error; |
goto error1; |
} |
} |
#define RET 1 |
#define RET 1 |
#endif |
#endif |
|
|
if (*ptr != MAGICNO) { |
if (*ptr != MAGICNO) { |
if (swap4(*ptr) != MAGICNO) { |
if (swap4(*ptr) != MAGICNO) { |
file_error(ms, 0, "bad magic in `%s'"); |
file_error(ms, 0, "bad magic in `%s'"); |
goto error; |
goto error1; |
} |
} |
needsbyteswap = 1; |
needsbyteswap = 1; |
} else |
} else |
|
|
else |
else |
version = ptr[1]; |
version = ptr[1]; |
if (version != VERSIONNO) { |
if (version != VERSIONNO) { |
file_error(ms, 0, "version mismatch (%d != %d) in `%s'", |
file_error(ms, 0, "File %d.%d supports only %d version magic " |
version, VERSIONNO, dbname); |
"files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel, |
goto error; |
VERSIONNO, dbname, version); |
|
goto error1; |
} |
} |
*nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)) - 1; |
*nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)); |
|
if (*nmagicp > 0) |
|
(*nmagicp)--; |
(*magicp)++; |
(*magicp)++; |
if (needsbyteswap) |
if (needsbyteswap) |
byteswap(*magicp, *nmagicp); |
byteswap(*magicp, *nmagicp); |
|
free(dbname); |
return RET; |
return RET; |
|
|
error: |
error1: |
if (fd != -1) |
if (fd != -1) |
(void)close(fd); |
(void)close(fd); |
if (mm) { |
if (mm) { |
|
|
*magicp = NULL; |
*magicp = NULL; |
*nmagicp = 0; |
*nmagicp = 0; |
} |
} |
|
error2: |
|
free(dbname); |
return -1; |
return -1; |
} |
} |
|
|
|
|
uint32_t *nmagicp, const char *fn) |
uint32_t *nmagicp, const char *fn) |
{ |
{ |
int fd; |
int fd; |
char buf[MAXPATHLEN]; |
char *dbname; |
char *dbname = mkdbname(fn, buf, sizeof(buf), 1); |
int rv = -1; |
|
|
|
mkdbname(fn, &dbname, 1); |
|
|
if (dbname == NULL) |
if (dbname == NULL) |
return -1; |
goto out; |
|
|
if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) { |
if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) { |
file_error(ms, errno, "cannot open `%s'", dbname); |
file_error(ms, errno, "cannot open `%s'", dbname); |
return -1; |
goto out; |
} |
} |
|
|
if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { |
if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) { |
file_error(ms, errno, "error writing `%s'", dbname); |
file_error(ms, errno, "error writing `%s'", dbname); |
return -1; |
goto out; |
} |
} |
|
|
if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET) |
if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET) |
!= sizeof(struct magic)) { |
!= sizeof(struct magic)) { |
file_error(ms, errno, "error seeking `%s'", dbname); |
file_error(ms, errno, "error seeking `%s'", dbname); |
return -1; |
goto out; |
} |
} |
|
|
if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) |
if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) |
!= (ssize_t)(sizeof(struct magic) * *nmagicp)) { |
!= (ssize_t)(sizeof(struct magic) * *nmagicp)) { |
file_error(ms, errno, "error writing `%s'", dbname); |
file_error(ms, errno, "error writing `%s'", dbname); |
return -1; |
goto out; |
} |
} |
|
|
(void)close(fd); |
(void)close(fd); |
return 0; |
rv = 0; |
|
out: |
|
free(dbname); |
|
return rv; |
} |
} |
|
|
private const char ext[] = ".mgc"; |
private const char ext[] = ".mgc"; |
/* |
/* |
* make a dbname |
* make a dbname |
*/ |
*/ |
private char * |
private void |
mkdbname(const char *fn, char *buf, size_t bufsiz, int strip) |
mkdbname(const char *fn, char **buf, int strip) |
{ |
{ |
if (strip) { |
if (strip) { |
const char *p; |
const char *p; |
|
|
fn = ++p; |
fn = ++p; |
} |
} |
|
|
(void)snprintf(buf, bufsiz, "%s%s", fn, ext); |
(void)asprintf(buf, "%s%s", fn, ext); |
return buf; |
if (*buf && strlen(*buf) > MAXPATHLEN) { |
|
free(*buf); |
|
*buf = NULL; |
|
} |
} |
} |
|
|
/* |
/* |
|
|
uint32_t rv; |
uint32_t rv; |
uint8_t *s = (uint8_t *)(void *)&sv; |
uint8_t *s = (uint8_t *)(void *)&sv; |
uint8_t *d = (uint8_t *)(void *)&rv; |
uint8_t *d = (uint8_t *)(void *)&rv; |
|
#if 0 |
d[0] = s[3]; |
d[0] = s[3]; |
d[1] = s[2]; |
d[1] = s[2]; |
d[2] = s[1]; |
d[2] = s[1]; |
|
|
d[5] = s[6]; |
d[5] = s[6]; |
d[6] = s[5]; |
d[6] = s[5]; |
d[7] = s[4]; |
d[7] = s[4]; |
|
#else |
|
d[0] = s[7]; |
|
d[1] = s[6]; |
|
d[2] = s[5]; |
|
d[3] = s[4]; |
|
d[4] = s[3]; |
|
d[5] = s[2]; |
|
d[6] = s[1]; |
|
d[7] = s[0]; |
|
#endif |
return rv; |
return rv; |
} |
} |
|
|
|
|
m->in_offset = swap4((uint32_t)m->in_offset); |
m->in_offset = swap4((uint32_t)m->in_offset); |
m->lineno = swap4((uint32_t)m->lineno); |
m->lineno = swap4((uint32_t)m->lineno); |
if (IS_STRING(m->type)) { |
if (IS_STRING(m->type)) { |
m->str_count = swap4(m->str_count); |
m->str_range = swap4(m->str_range); |
m->str_flags = swap4(m->str_flags); |
m->str_flags = swap4(m->str_flags); |
} |
} |
else { |
else { |