| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2000-2001,2005 Silicon Graphics, Inc. |
| * All Rights Reserved. |
| */ |
| |
| #include "libxfs.h" |
| #include "type.h" |
| #include "faddr.h" |
| #include "fprint.h" |
| #include "field.h" |
| #include "flist.h" |
| #include "debug.h" |
| #include "output.h" |
| #include "malloc.h" |
| |
| static void flist_expand_arrays(flist_t *fl); |
| static void flist_expand_structs(flist_t *fl, void *obj); |
| static flist_t *flist_replicate(flist_t *fl); |
| static ftok_t *flist_split(char *s); |
| static void ftok_free(ftok_t *ft); |
| |
| static void |
| flist_expand_arrays( |
| flist_t *fl) |
| { |
| const field_t *f; |
| #ifdef DEBUG |
| const ftattr_t *fa; |
| #endif |
| int high; |
| int idx; |
| int low; |
| flist_t *new; |
| flist_t *prev; |
| flist_t *sib; |
| |
| f = fl->fld; |
| #ifdef DEBUG |
| fa = &ftattrtab[f->ftyp]; |
| #endif |
| ASSERT(fa->ftyp == f->ftyp); |
| ASSERT(f->flags & FLD_ARRAY); |
| low = fl->low; |
| high = fl->high; |
| fl->high = fl->low; |
| sib = fl->sibling; |
| for (idx = low + 1, prev = fl; idx <= high; idx++) { |
| new = flist_make(f->name); |
| new->fld = f; |
| new->low = new->high = idx; |
| new->flags |= FL_OKLOW | FL_OKHIGH; |
| new->child = flist_replicate(fl->child); |
| prev->sibling = new; |
| prev = new; |
| } |
| prev->sibling = sib; |
| } |
| |
| static void |
| flist_expand_structs( |
| flist_t *fl, |
| void *obj) |
| { |
| const field_t *cf; |
| const field_t *f; |
| const ftattr_t *fa; |
| flist_t *new; |
| flist_t *prev; |
| |
| f = fl->fld; |
| fa = &ftattrtab[f->ftyp]; |
| ASSERT(fa->ftyp == f->ftyp); |
| ASSERT(fa->subfld != NULL); |
| ASSERT(fl->child == NULL); |
| for (cf = fa->subfld, prev = NULL; cf->name != NULL; cf++) { |
| if (fcount(cf, obj, fl->offset) == 0) |
| continue; |
| if (cf->flags & FLD_SKIPALL) |
| continue; |
| new = flist_make(cf->name); |
| new->fld = cf; |
| if (prev) |
| prev->sibling = new; |
| else |
| fl->child = new; |
| prev = new; |
| } |
| } |
| |
| void |
| flist_free( |
| flist_t *fl) |
| { |
| if (fl->child) |
| flist_free(fl->child); |
| if (fl->sibling) |
| flist_free(fl->sibling); |
| if (fl->name) |
| xfree(fl->name); |
| xfree(fl); |
| } |
| |
| flist_t * |
| flist_make( |
| char *name) |
| { |
| flist_t *fl; |
| |
| fl = xmalloc(sizeof(*fl)); |
| fl->name = xstrdup(name); |
| fl->fld = NULL; |
| fl->child = NULL; |
| fl->sibling = NULL; |
| fl->low = 0; |
| fl->high = 0; |
| fl->flags = 0; |
| fl->offset = 0; |
| return fl; |
| } |
| |
| int |
| flist_parse( |
| const field_t *fields, |
| flist_t *fl, |
| void *obj, |
| int startoff) |
| { |
| const field_t *f; |
| const ftattr_t *fa; |
| int high; |
| int low; |
| |
| while (fl) { |
| f = findfield(fl->name, fields, obj, startoff); |
| if (f == NULL) { |
| dbprintf(_("field %s not found\n"), fl->name); |
| return 0; |
| } |
| fl->fld = f; |
| fa = &ftattrtab[f->ftyp]; |
| ASSERT(fa->ftyp == f->ftyp); |
| if (f->flags & FLD_ARRAY) { |
| low = (f->flags & FLD_ABASE1) != 0; |
| high = fcount(f, obj, startoff) + low - 1; |
| if (low > high) { |
| dbprintf(_("no elements in %s\n"), fl->name); |
| return 0; |
| } |
| if (fl->flags & FL_OKHIGH) { |
| if (fl->low < low || fl->low > high || |
| fl->high < low || fl->high > high) { |
| dbprintf(_("indices %d-%d for field %s " |
| "out of range %d-%d\n"), |
| fl->low, fl->high, fl->name, |
| low, high); |
| return 0; |
| } |
| } else if (fl->flags & FL_OKLOW) { |
| if (fl->low < low || fl->low > high) { |
| dbprintf(_("index %d for field %s out of " |
| "range %d-%d\n"), |
| fl->low, fl->name, low, high); |
| return 0; |
| } |
| fl->high = fl->low; |
| fl->flags |= FL_OKHIGH; |
| } else { |
| fl->low = low; |
| fl->high = high; |
| fl->flags |= FL_OKLOW | FL_OKHIGH; |
| } |
| } else { |
| if (fl->flags & FL_OKLOW) { |
| dbprintf(_("field %s is not an array\n"), |
| fl->name); |
| return 0; |
| } |
| } |
| fl->offset = startoff + bitoffset(f, obj, startoff, fl->low); |
| if ((fl->child != NULL || fa->prfunc == NULL) && |
| (f->flags & FLD_ARRAY) && fl->low != fl->high) |
| flist_expand_arrays(fl); |
| if (fa->prfunc == NULL && fl->child == NULL) |
| flist_expand_structs(fl, obj); |
| if (fl->child) { |
| if (fa->subfld == NULL) { |
| dbprintf(_("field %s has no subfields\n"), |
| fl->name); |
| return 0; |
| } |
| if (!flist_parse(fa->subfld, fl->child, obj, |
| fl->offset)) |
| return 0; |
| } |
| fl = fl->sibling; |
| } |
| return 1; |
| } |
| |
| void |
| flist_print( |
| flist_t *fl) |
| { |
| if (!(debug_state & DEBUG_FLIST)) |
| return; |
| while (fl) { |
| dbprintf(_("fl@%p:\n"), fl); |
| dbprintf(_("\tname=%s, fld=%p, child=%p, sibling=%p\n"), |
| fl->name, fl->fld, fl->child, fl->sibling); |
| dbprintf(_("\tlow=%d, high=%d, flags=%d (%s%s), offset=%d\n"), |
| fl->low, fl->high, fl->flags, |
| fl->flags & FL_OKLOW ? _("oklow ") : "", |
| fl->flags & FL_OKHIGH ? _("okhigh") : "", fl->offset); |
| dbprintf(_("\tfld->name=%s, fld->ftyp=%d (%s)\n"), |
| fl->fld->name, fl->fld->ftyp, |
| ftattrtab[fl->fld->ftyp].name); |
| dbprintf(_("\tfld->flags=%d (%s%s%s%s%s)\n"), fl->fld->flags, |
| fl->fld->flags & FLD_ABASE1 ? "abase1 " : "", |
| fl->fld->flags & FLD_SKIPALL ? "skipall " : "", |
| fl->fld->flags & FLD_ARRAY ? "array " : "", |
| fl->fld->flags & FLD_OFFSET ? "offset " : "", |
| fl->fld->flags & FLD_COUNT ? "count " : ""); |
| if (fl->child) |
| flist_print(fl->child); |
| fl = fl->sibling; |
| } |
| } |
| |
| static flist_t * |
| flist_replicate( |
| flist_t *f) |
| { |
| flist_t *new; |
| |
| if (f == NULL) |
| return NULL; |
| new = flist_make(f->name); |
| new->fld = f->fld; |
| new->child = flist_replicate(f->child); |
| new->sibling = flist_replicate(f->sibling); |
| new->low = f->low; |
| new->high = f->high; |
| new->flags = f->flags; |
| new->offset = f->offset; |
| return new; |
| } |
| |
| flist_t * |
| flist_scan( |
| char *name) |
| { |
| flist_t *fl; |
| flist_t *lfl; |
| flist_t *nfl; |
| int num; |
| ftok_t *p; |
| ftok_t *v; |
| char *x; |
| |
| v = flist_split(name); |
| if (!v) |
| return NULL; |
| p = v; |
| fl = lfl = NULL; |
| while (p->tokty != TT_END) { |
| if (p->tokty != TT_NAME) |
| goto bad; |
| nfl = flist_make(p->tok); |
| if (lfl) |
| lfl->child = nfl; |
| else |
| fl = nfl; |
| lfl = nfl; |
| p++; |
| if (p->tokty == TT_LB) { |
| p++; |
| if (p->tokty != TT_NUM) |
| goto bad; |
| num = (int)strtoul(p->tok, &x, 0); |
| if (*x != '\0') |
| goto bad; |
| nfl->flags |= FL_OKLOW; |
| nfl->low = num; |
| p++; |
| if (p->tokty == TT_DASH) { |
| p++; |
| if (p->tokty != TT_NUM) |
| goto bad; |
| num = (int)strtoul(p->tok, &x, 0); |
| if (*x != '\0') |
| goto bad; |
| nfl->flags |= FL_OKHIGH; |
| nfl->high = num; |
| p++; |
| } |
| if (p->tokty != TT_RB) |
| goto bad; |
| p++; |
| } |
| if (p->tokty == TT_DOT) { |
| p++; |
| if (p->tokty == TT_END) |
| goto bad; |
| } |
| } |
| ftok_free(v); |
| return fl; |
| bad: |
| dbprintf(_("bad syntax in field name %s\n"), name); |
| ftok_free(v); |
| if (fl) |
| flist_free(fl); |
| return NULL; |
| } |
| |
| static ftok_t * |
| flist_split( |
| char *s) |
| { |
| char *a; |
| int i; |
| static char *idchars; |
| static char *initidchar; |
| int l; |
| int tailskip = 0; |
| static char *numchars; |
| static char *xnumchars; /* extended for hex conversion */ |
| int nv; |
| static char punctchars[] = "[-]."; |
| static tokty_t puncttypes[] = { TT_LB, TT_DASH, TT_RB, TT_DOT }; |
| tokty_t t; |
| ftok_t *v; |
| |
| if (idchars == NULL) { |
| idchars = xmalloc(26 + 10 + 1 + 1); |
| initidchar = xmalloc(26 + 1); |
| numchars = xmalloc(10 + 1); |
| xnumchars = xmalloc(12 + 1); |
| for (i = 'a'; i <= 'z'; i++) { |
| idchars[i - 'a'] = i; |
| initidchar[i - 'a'] = i; |
| } |
| |
| for (i = '0'; i <= '9'; i++) { |
| idchars[26 + (i - '0')] = i; |
| numchars[i - '0'] = i; |
| xnumchars[i - '0'] = i; |
| } |
| idchars[26 + 10] = '_'; |
| idchars[26 + 10 + 1] = '\0'; |
| initidchar[26] = '\0'; |
| numchars[10] = '\0'; |
| xnumchars[10] = 'x'; |
| xnumchars[11] = 'X'; |
| xnumchars[12] = '\0'; |
| } |
| nv = 0; |
| v = xmalloc(sizeof(*v)); |
| v->tok = NULL; |
| while (*s) { |
| /* need to add string handling */ |
| if (*s == '\"') { |
| s++; /* skip first quote */ |
| if ((a = strrchr(s, '\"')) == NULL) { |
| dbprintf(_("missing closing quote %s\n"), s); |
| ftok_free(v); |
| return NULL; |
| } |
| tailskip = 1; /* skip remaing quote */ |
| l = (int)(a - s); |
| t = TT_STRING; |
| } else if (strchr(initidchar, *s)) { |
| l = (int)strspn(s, idchars); |
| t = TT_NAME; |
| } else if (strchr(numchars, *s)) { |
| l = (int)strspn(s, xnumchars); |
| t = TT_NUM; |
| } else if ((a = strchr(punctchars, *s))) { |
| l = 1; |
| t = puncttypes[a - punctchars]; |
| } else { |
| dbprintf(_("bad character in field %s\n"), s); |
| ftok_free(v); |
| return NULL; |
| } |
| a = xmalloc(l + 1); |
| strncpy(a, s, l); |
| a[l] = '\0'; |
| v = xrealloc(v, (nv + 2) * sizeof(*v)); |
| v[nv + 1].tok = NULL; |
| v[nv].tok = a; |
| v[nv].tokty = t; |
| nv++; |
| s += l + tailskip; |
| tailskip = 0; |
| } |
| v[nv].tok = NULL; |
| v[nv].tokty = TT_END; |
| return v; |
| } |
| |
| static flist_t * |
| flist_field_match( |
| const field_t *field, |
| fldt_t type, |
| void *obj, |
| int startoff) |
| { |
| flist_t *fl; |
| int count; |
| const ftattr_t *fa; |
| flist_t *nfl; |
| |
| fl = flist_make(field->name); |
| fl->fld = field; |
| if (field->ftyp == type) |
| return fl; |
| count = fcount(field, obj, startoff); |
| if (!count) |
| goto out; |
| fa = &ftattrtab[field->ftyp]; |
| if (!fa->subfld) |
| goto out; |
| |
| nfl = flist_find_ftyp(fa->subfld, type, obj, startoff); |
| if (nfl) { |
| fl->child = nfl; |
| return fl; |
| } |
| |
| out: |
| flist_free(fl); |
| return NULL; |
| } |
| |
| /* |
| * Given a set of fields, scan for a field of the given type. |
| * Return an flist leading to the first found field |
| * of that type. |
| * Return NULL if no field of the given type is found. |
| */ |
| flist_t * |
| flist_find_ftyp( |
| const field_t *fields, |
| fldt_t type, |
| void *obj, |
| int startoff) |
| { |
| const field_t *f; |
| flist_t *fl; |
| |
| for (f = fields; f->name; f++) { |
| fl = flist_field_match(f, type, obj, startoff); |
| if (fl) |
| return fl; |
| } |
| |
| return NULL; |
| } |
| |
| static void |
| ftok_free( |
| ftok_t *ft) |
| { |
| ftok_t *p; |
| |
| for (p = ft; p->tok; p++) |
| xfree(p->tok); |
| xfree(ft); |
| } |