/* * parse one line from magic file, put into magic[index++] if valid */ static int parse(RMagic *ms, struct r_magic_entry **mentryp, ut32 *nmentryp, const char *line, size_t lineno, int action) { static ut32 last_cont_level = 0; size_t i; struct r_magic_entry *me; struct r_magic *m; const char *l = line; char *t; int op; ut32 cont_level = 0; for (; *l == '>'; l++, cont_level++); if (cont_level == 0 || cont_level > last_cont_level) if (file_check_mem (ms, cont_level) == -1) return -1; last_cont_level = cont_level; #define ALLOC_CHUNK (size_t)10 #define ALLOC_INCR (size_t)200 if (cont_level != 0) { if (*nmentryp == 0) { file_error(ms, 0, "No current entry for continuation"); return -1; } me = &(*mentryp)[*nmentryp - 1]; if (me->cont_count == me->max_count) { struct r_magic *nm; size_t cnt = me->max_count + ALLOC_CHUNK; if (!(nm = realloc(me->mp, sizeof (*nm) * cnt))) { file_oomem(ms, sizeof (*nm) * cnt); return -1; } me->mp = nm; me->max_count = cnt; } m = &me->mp[me->cont_count++]; (void)memset(m, 0, sizeof (*m)); m->cont_level = cont_level; } else { if (*nmentryp == maxmagic) { struct r_magic_entry *mp; maxmagic += ALLOC_INCR; if (!(mp = realloc (*mentryp, sizeof (*mp) * maxmagic))) { file_oomem (ms, sizeof (*mp) * maxmagic); return -1; } (void)memset(&mp[*nmentryp], 0, sizeof (*mp) * ALLOC_INCR); *mentryp = mp; } me = &(*mentryp)[*nmentryp]; if (!me->mp) { if (!(m = malloc (sizeof (*m) * ALLOC_CHUNK))) { file_oomem (ms, sizeof (*m) * ALLOC_CHUNK); return -1; } me->mp = m; me->max_count = ALLOC_CHUNK; } else m = me->mp; (void)memset(m, 0, sizeof (*m)); m->cont_level = 0; me->cont_count = 1; } m->lineno = lineno; if (*l == '&') { /* m->cont_level == 0 checked below. */ ++l; /* step over */ m->flag |= OFFADD; } if (*l == '(') { ++l; /* step over */ m->flag |= INDIR; if (m->flag & OFFADD) m->flag = (m->flag & ~OFFADD) | INDIROFFADD; if (*l == '&') { /* m->cont_level == 0 checked below */ ++l; /* step over */ m->flag |= OFFADD; } } /* Indirect offsets are not valid at level 0. */ if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "relative offset at level 0"); /* get offset, then skip over it */ m->offset = (ut32)strtoul(l, &t, 0); if ((l == t) && (ms->flags & R_MAGIC_CHECK)) file_magwarn(ms, "offset `%s' invalid", l); l = t; if (m->flag & INDIR) { m->in_type = FILE_LONG; m->in_offset = 0; /* * read [.lbs][+-]nnnnn) */ if (*l == '.') { l++; switch (*l) { case 'l': m->in_type = FILE_LELONG; break; case 'L': m->in_type = FILE_BELONG; break; case 'm': m->in_type = FILE_MELONG; break; case 'h': case 's': m->in_type = FILE_LESHORT; break; case 'H': case 'S': m->in_type = FILE_BESHORT; break; case 'c': case 'b': case 'C': case 'B': m->in_type = FILE_BYTE; 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: if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "indirect offset type `%c' invalid", *l); break; } l++; } m->in_op = 0; if (*l == '~') { m->in_op |= FILE_OPINVERSE; l++; } if ((op = get_op(*l)) != -1) { m->in_op |= op; l++; } if (*l == '(') { m->in_op |= FILE_OPINDIRECT; l++; } if (isdigit((ut8)*l) || *l == '-') { m->in_offset = (int32_t)strtol(l, &t, 0); if (l == t) if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "in_offset `%s' invalid", l); l = t; } if (*l++ != ')' || ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "missing ')' in indirect offset"); } EATAB; m->cond = get_cond(l, &l); if (check_cond(ms, m->cond, cont_level) == -1) return -1; EATAB; if (*l == 'u') { ++l; m->flag |= UNSIGNED; } m->type = get_type(l, &l); if (m->type == FILE_INVALID) { if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "type `%s' invalid", l); return -1; } /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */ /* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */ m->mask_op = 0; if (*l == '~') { if (!MAGIC_IS_STRING (m->type)) m->mask_op |= FILE_OPINVERSE; else if (ms->flags & R_MAGIC_CHECK) file_magwarn (ms, "'~' invalid for string types"); ++l; } m->str_range = 0; m->str_flags = 0; m->num_mask = 0; if ((op = get_op (*l)) != -1) { if (!MAGIC_IS_STRING (m->type)) { ut64 val; ++l; m->mask_op |= op; val = (ut64)strtoull (l, &t, 0); l = t; m->num_mask = file_signextend (ms, m, val); eatsize (&l); } else if (op == FILE_OPDIVIDE) { int have_range = 0; while (!isspace ((ut8)*++l)) { switch (*l) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (have_range && (ms->flags & R_MAGIC_CHECK)) file_magwarn(ms, "multiple ranges"); have_range = 1; m->str_range = strtoul(l, &t, 0); if (m->str_range == 0) file_magwarn(ms, "zero range"); l = t - 1; break; case CHAR_COMPACT_BLANK: m->str_flags |= STRING_COMPACT_BLANK; break; case CHAR_COMPACT_OPTIONAL_BLANK: m->str_flags |= STRING_COMPACT_OPTIONAL_BLANK; break; case CHAR_IGNORE_LOWERCASE: m->str_flags |= STRING_IGNORE_LOWERCASE; break; case CHAR_IGNORE_UPPERCASE: m->str_flags |= STRING_IGNORE_UPPERCASE; break; case CHAR_REGEX_OFFSET_START: m->str_flags |= REGEX_OFFSET_START; break; default: if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "string extension `%c' invalid", *l); return -1; } /* allow multiple '/' for readability */ if (l[1] == '/' && !isspace ((ut8)l[2])) l++; } if (string_modifier_check(ms, m) == -1) return -1; } else { if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "invalid string op: %c", *t); return -1; } } /* * 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 '!': m->reln = *l; ++l; break; default: m->reln = '='; /* the default relation */ if (*l == 'x' && ((isascii((ut8)l[1]) && isspace ((ut8)l[1])) || !l[1])) { m->reln = *l; ++l; } break; } /* * Grab the value part, except for an 'x' reln. */ if (m->reln != 'x' && getvalue (ms, m, &l, action)) 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 */ EATAB; if (l[0] == '\b') { ++l; m->flag |= NOSPACE; } else if ((l[0] == '\\') && (l[1] == 'b')) { ++l; ++l; m->flag |= NOSPACE; } for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof (m->desc); ) continue; if (i == sizeof (m->desc)) { m->desc[sizeof (m->desc) - 1] = '\0'; if (ms->flags & R_MAGIC_CHECK) file_magwarn(ms, "description `%s' truncated", m->desc); } /* * We only do this check while compiling, or if any of the magic * files were not compiled. */ if (ms->flags & R_MAGIC_CHECK) if (check_format (ms, m) == -1) return -1; if (action == FILE_CHECK) file_mdump (m); m->mimetype[0] = '\0'; /* initialise MIME type to none */ if (m->cont_level == 0) ++(*nmentryp); /* make room for next */ 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(RMagic *ms, struct r_magic *magic, ut32 nmagic, const ut8 *s, size_t nbytes, int mode) { ut32 magindex = 0; unsigned int cont_level = 0; int need_separator = 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 */ int printed_something = 0; if (file_check_mem (ms, cont_level) == -1) return -1; for (magindex = 0; magindex < nmagic; magindex++) { int flush; struct r_magic *m = &magic[magindex]; if ((m->flag & BINTEST) != mode) { /* Skip sub-tests */ while (magic[magindex + 1].cont_level != 0 && ++magindex < nmagic - 1) continue; continue; /* Skip to next top-level test*/ } ms->offset = m->offset; ms->line = m->lineno; /* if main entry matches, print it... */ flush = !mget(ms, s, m, nbytes, cont_level); if (flush) { if (m->reln == '!') flush = 0; } else { int ret = magiccheck (ms, m); if (ret == -1) return -1; if (!ret) flush++; } if (flush) { /* * main entry didn't match, * flush its continuations */ while (magindex < nmagic - 1 && magic[magindex + 1].cont_level) magindex++; continue; } /* * If we are going to print something, we'll need to print * a blank before we print something else. */ if (*R_MAGIC_DESC) { need_separator = 1; printed_something = 1; if (print_sep(ms, firstline) == -1) return -1; } if ((ms->c.li[cont_level].off = mprint(ms, m)) == -1) return -1; /* and any continuations that match */ if (file_check_mem(ms, ++cont_level) == -1) return -1; while (++magindex < nmagic - 1 && magic[magindex].cont_level != 0) { m = &magic[magindex]; ms->line = m->lineno; /* for messages */ if (cont_level < m->cont_level) continue; if (cont_level > m->cont_level) { /* * We're at the end of the level * "cont_level" continuations. */ cont_level = m->cont_level; } ms->offset = m->offset; if (m->flag & OFFADD) ms->offset += ms->c.li[cont_level - 1].off; if (m->cond == COND_ELSE || m->cond == COND_ELIF) { if (ms->c.li[cont_level].last_match == 1) continue; } flush = !mget(ms, s, m, nbytes, cont_level); if (flush && m->reln != '!') continue; switch (flush ? 1 : magiccheck(ms, m)) { case -1: return -1; case 0: ms->c.li[cont_level].last_match = 0; break; default: ms->c.li[cont_level].last_match = 1; if (m->type != FILE_DEFAULT) ms->c.li[cont_level].got_match = 1; else if (ms->c.li[cont_level].got_match) { ms->c.li[cont_level].got_match = 0; break; } /* * If we are going to print something, * make sure that we have a separator first. */ if (*R_MAGIC_DESC) { printed_something = 1; if (print_sep(ms, firstline) == -1) return -1; } /* * 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->flag & NOSPACE) == 0) && *R_MAGIC_DESC) { if (file_printf (ms, " ") == -1) return -1; need_separator = 0; } if ((ms->c.li[cont_level].off = mprint(ms, m)) == -1) return -1; if (*R_MAGIC_DESC) need_separator = 1; /* * If we see any continuations * at a higher level, * process them. */ if (file_check_mem(ms, ++cont_level) == -1) return -1; break; } } if (printed_something) { firstline = 0; returnval = 1; } if ((ms->flags & R_MAGIC_CONTINUE) == 0 && printed_something) return 1; /* don't keep searching */ } return returnval; /* This is hit if -k is set or there is no match */ }