|  | // 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); | 
|  | } |