tadd acidtypes - plan9port - [fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port
Log
Files
Refs
README
LICENSE
---
commit 98cd2746cff82ab359de6d6ce2c3f87b2c4166a8
parent 70e24710a84797be0c26fec6b22897ddccbf679c
Author: rsc 
Date:   Mon, 19 Apr 2004 22:41:57 +0000

add acidtypes

Diffstat:
  A src/cmd/acidtypes/dat.h             |      72 +++++++++++++++++++++++++++++++
  A src/cmd/acidtypes/dwarf.c           |     193 +++++++++++++++++++++++++++++++
  A src/cmd/acidtypes/main.c            |      66 +++++++++++++++++++++++++++++++
  A src/cmd/acidtypes/mkfile            |      23 +++++++++++++++++++++++
  A src/cmd/acidtypes/stabs.c           |     775 +++++++++++++++++++++++++++++++
  A src/cmd/acidtypes/type.c            |     571 +++++++++++++++++++++++++++++++
  A src/cmd/acidtypes/util.c            |      63 +++++++++++++++++++++++++++++++

7 files changed, 1763 insertions(+), 0 deletions(-)
---
diff --git a/src/cmd/acidtypes/dat.h b/src/cmd/acidtypes/dat.h
t@@ -0,0 +1,72 @@
+typedef struct Type Type;
+typedef struct Typeref Typeref;
+typedef struct TypeList TypeList;
+
+enum
+{
+        None,
+        Base,
+        Enum,
+        Aggr,
+        Function,        
+        Pointer,
+        Array,
+        Range,
+        Defer,
+        Typedef,
+};
+
+struct Type
+{        /* Font Tab 4 */
+        uint        ty;                        /* None, Struct, ... */
+        vlong lo;                        /* for range */
+        char sue;
+        vlong hi;
+        uint        gen;
+        uint        n1;                /* type number (impl dependent) */
+        uint        n2;                /* another type number */
+        char        *name;                /* name of type */
+        char        *suename;        /* name of struct, union, enumeration */
+        uint        isunion;        /* is this Struct a union? */
+        uint        printfmt;        /* describes base type */
+        uint        xsizeof;        /* size of type */
+        Type        *sub;                /* subtype */
+        uint        n;                        /* count for t, tname, val */
+        Type **t;                        /* members of sue, params of function */
+        char        **tname;        /* associated names */
+        long        *val;                /* associated offsets or values */
+        uint        didtypedef;        /* internal flag */
+        uint        didrange;                /* internal flag */
+        uint         printed;                /* internal flag */
+        Type *equiv;                /* internal */
+};
+
+struct TypeList
+{
+        Type *hd;
+        TypeList *tl;
+};
+
+void *erealloc(void*, uint);
+void *emalloc(uint);
+char *estrdup(char*);
+void warn(char*, ...);
+
+Type *typebynum(uint n1, uint n2);
+Type *typebysue(char, char*);
+void printtypes(Biobuf*);
+void renumber(TypeList*, uint);
+void denumber(void);
+TypeList *mktl(Type*, TypeList*);
+
+struct Dwarf;
+struct Stab;
+int dwarf2acid(struct Dwarf*, Biobuf*);
+int stabs2acid(struct Stab*, Biobuf*);
+
+Type *newtype(void);
+char *nameof(Type*, int);
+void freetypes(void);
+
+extern char *prefix;
+char *fixname(char*);
diff --git a/src/cmd/acidtypes/dwarf.c b/src/cmd/acidtypes/dwarf.c
t@@ -0,0 +1,193 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "dat.h"
+
+static ulong
+valof(uint ty, DwarfVal *v)
+{
+        switch(ty){
+        default:
+fmtinstall('H', encodefmt);
+fprint(2, "valof %d %.*H\n", ty, v->b.len, v->b.data);
+                return 0;
+        case TConstant:
+                return v->c;
+        }
+}
+
+static Type*
+xnewtype(uint ty, DwarfSym *s)
+{
+        Type *t;
+
+        t = typebynum(s->unit+s->uoff, 0);
+        t->ty = ty;
+        return t;
+}
+
+int
+dwarf2acid(Dwarf *d, Biobuf *b)
+{
+        char *fn;
+        DwarfSym s;
+        Type *t;
+
+        /* pass over dwarf section pulling out type info */
+
+        if(dwarfenum(d, &s) < 0)
+                return -1;
+
+        while(dwarfnextsym(d, &s, s.depth!=1) == 1){
+        top:
+                switch(s.attrs.tag){
+                case TagSubprogram:
+                case TagLexDwarfBlock:
+                        dwarfnextsym(d, &s, 1);
+                        goto top;
+
+                case TagTypedef:
+                        t = xnewtype(Typedef, &s);
+                        t->name = s.attrs.name;
+                        t->sub = typebynum(s.attrs.type, 0);
+                        break;
+                case TagBaseType:
+                        t = xnewtype(Base, &s);
+                        t->xsizeof = s.attrs.bytesize;
+                        switch(s.attrs.encoding){
+                        default:
+                        case TypeAddress:
+                                t->printfmt = 'x';
+                                break;
+                        case TypeBoolean:
+                        case TypeUnsigned:
+                        case TypeSigned:
+                        case TypeSignedChar:
+                        case TypeUnsignedChar:
+                                t->printfmt = 'd';
+                                break;
+                        case TypeFloat:
+                                t->printfmt = 'f';
+                                break;
+                        case TypeComplexFloat:
+                                t->printfmt = 'F';
+                                break;
+                        case TypeImaginaryFloat:
+                                t->printfmt = 'i';
+                                break;
+                        }
+                        break;
+                case TagPointerType:
+                        t = xnewtype(Pointer, &s);
+                        t->sub = typebynum(s.attrs.type, 0);
+                        break;
+                case TagStructType:
+                case TagUnionType:
+                        t = xnewtype(Aggr, &s);
+                        t->sue = s.attrs.tag==TagStructType ? 's' : 'u';
+                        t->xsizeof = s.attrs.bytesize;
+                        t->suename = s.attrs.name;
+                        t->isunion = s.attrs.tag==TagUnionType;
+                        dwarfnextsym(d, &s, 1);
+                        if(s.depth != 2)
+                                goto top;
+                        do{
+                                if(!s.attrs.have.name || !s.attrs.have.type || s.attrs.tag != TagMember)
+                                        continue;
+                                if(t->n%32 == 0){
+                                        t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0]));
+                                        t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0]));
+                                        t->t = erealloc(t->t, (t->n+32)*sizeof(t->t[0]));
+                                }
+                                t->tname[t->n] = s.attrs.name;
+                                if(t->isunion)
+                                        t->val[t->n] = 0;
+                                else
+                                        t->val[t->n] = valof(s.attrs.have.datamemberloc, &s.attrs.datamemberloc);
+                                t->t[t->n] = typebynum(s.attrs.type, 0);
+                                t->n++;
+                        }while(dwarfnextsym(d, &s, 1) == 1 && s.depth==2);
+                        goto top;
+                        break;
+                case TagSubroutineType:
+                        t = xnewtype(Function, &s);
+                        break;
+                case TagConstType:
+                case TagVolatileType:
+                        t = xnewtype(Defer, &s);
+                        t->sub = typebynum(s.attrs.type, 0);
+                        break;
+                case TagArrayType:
+                        t = xnewtype(Array, &s);
+                        t->sub = typebynum(s.attrs.type, 0);
+                        break;
+                case TagEnumerationType:
+                        t = xnewtype(Enum, &s);
+                        t->sue = 'e';
+                        t->suename = s.attrs.name;
+                        t->xsizeof = s.attrs.bytesize;
+                        dwarfnextsym(d, &s, 1);
+                        if(s.depth != 2)
+                                goto top;
+                        do{
+                                if(!s.attrs.have.name || !s.attrs.have.constvalue || s.attrs.tag != TagEnumerator)
+                                        continue;
+                                if(t->n%32 == 0){
+                                        t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0]));
+                                        t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0]));
+                                }
+                                t->tname[t->n] = s.attrs.name;
+                                t->val[t->n] = valof(s.attrs.have.constvalue, &s.attrs.constvalue);
+                                t->n++;
+                        }while(dwarfnextsym(d, &s, 1) == 1 && s.depth==2);
+                        goto top;
+                        break;
+                }
+        }
+
+        printtypes(b);
+
+        /* pass over dwarf section pulling out type definitions */
+
+        if(dwarfenum(d, &s) < 0)
+                goto out;
+
+        fn = nil;
+        while(dwarfnextsym(d, &s, 1) == 1){
+                if(s.depth == 1)
+                        fn = nil;
+                switch(s.attrs.tag){
+                case TagSubprogram:
+                        fn = s.attrs.name;
+                        break;
+                case TagFormalParameter:
+                        if(s.depth != 2)
+                                break;
+                        /* fall through */
+                case TagVariable:
+                        if(s.attrs.name == nil || s.attrs.type == 0)
+                                continue;
+                        t = typebynum(s.attrs.type, 0);
+                        if(t->ty == Pointer){
+                                t = t->sub;
+                                if(t && t->equiv)
+                                        t = t->equiv;
+                        }
+                        if(t == nil)
+                                break;
+                        if(t->ty != Aggr)
+                                break;
+                        Bprint(b, "complex %s %s%s%s;\n", nameof(t, 1),
+                                fn ? fixname(fn) : "", fn ? ":" : "", fixname(s.attrs.name));
+                        break;
+                }
+        }
+
+out:
+        freetypes();
+        return 0;
+}
+
diff --git a/src/cmd/acidtypes/main.c b/src/cmd/acidtypes/main.c
t@@ -0,0 +1,66 @@
+#include 
+#include 
+#include 
+#include 
+#include "dat.h"
+
+void
+usage(void)
+{
+        fprint(2, "usage: acidtypes [-p prefix] executable...\n");
+        exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+        int i;
+        Fhdr *fp;
+        Biobuf b;
+        char err[ERRMAX];
+
+        quotefmtinstall();
+
+        ARGBEGIN{
+        case 'p':
+                prefix = EARGF(usage());
+                break;
+        default:
+                usage();
+        }ARGEND
+
+        if(argc < 1)
+                usage();
+
+        Binit(&b, 1, OWRITE);
+        for(i=0; idwarf){
+                        if(dwarf2acid(fp->dwarf, &b) < 0){
+                                rerrstr(err, sizeof err);
+                                Bprint(&b, "// dwarf2acid %s: %s\n\n", argv[i], err);
+                                fprint(2, "dwarf2acid %s: %s\n", argv[i], err);
+                        }
+                }else if(fp->stabs.stabbase){
+                        if(stabs2acid(&fp->stabs, &b) < 0){
+                                rerrstr(err, sizeof err);
+                                Bprint(&b, "// dwarf2acid %s: %s\n\n", argv[i], err);
+                                fprint(2, "dwarf2acid %s: %s\n", argv[i], err);
+                        }
+                }else{
+                        Bprint(&b, "// no debugging symbols in %s\n\n", argv[i]);
+                //        fprint(2, "no debugging symbols in %s\n", argv[i]);
+                }
+                uncrackhdr(fp);
+        }
+        Bflush(&b);
+        Bterm(&b);
+        exits(0);
+}
+
diff --git a/src/cmd/acidtypes/mkfile b/src/cmd/acidtypes/mkfile
t@@ -0,0 +1,23 @@
+<$PLAN9/src/mkhdr
+
+TARG=acidtypes
+
+OFILES=\
+        dwarf.$O\
+        main.$O\
+        stabs.$O\
+        type.$O\
+        util.$O\
+
+HFILES=\
+        dat.h\
+        ../../libmach/mach.h\
+        ../../libmach/elf.h\
+        ../../libmach/dwarf.h\
+        ../../libmach/stabs.h\
+
+CFLAGS=-I../../libmach $CFLAGS
+SHORTLIB=mach bio 9
+
+<$PLAN9/src/mkone
+
diff --git a/src/cmd/acidtypes/stabs.c b/src/cmd/acidtypes/stabs.c
t@@ -0,0 +1,775 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "dat.h"
+
+static jmp_buf kaboom;
+
+static Type *parsename(char*, char**);
+static Type *parseinfo(char*, char**);
+static int parsenum(char*, int*, int*, char**);
+static int parseattr(char*, char**, char**);
+static Type *parsedefn(char *p, Type *t, char **pp);
+static int parsebound(char**);
+static vlong parsebigint(char**);
+
+typedef struct Sym Sym;
+struct Sym
+{
+        char *fn;
+        char *name;
+        Type *type;
+        Sym *next;
+};
+
+typedef struct Ftypes Ftypes;
+struct Ftypes
+{
+        Ftypes *down;
+        Ftypes *next;
+        char *file;
+        TypeList *list;
+};
+
+Ftypes *fstack;
+Ftypes *allftypes;
+
+static Sym*
+mksym(char *fn, char *name, Type *type)
+{
+        Sym *s;
+
+        s = emalloc(sizeof *s);
+        s->fn = fn;
+        s->name = name;
+        s->type = type;
+        return s;
+}
+
+static char*
+estrndup(char *s, int n)
+{
+        char *t;
+
+        t = emalloc(n+1);
+        memmove(t, s, n);
+        return t;
+}
+
+static char*
+mkpath(char *dir, char *name)
+{
+        char *s;
+        if(name[0] == '/')
+                return estrdup(name);
+        else{
+                s = emalloc(strlen(dir)+strlen(name)+1);
+                strcpy(s, dir);
+                strcat(s, name);
+                return s;
+        }
+}
+
+static Ftypes*
+mkftypes(char *dir, char *name)
+{
+        Ftypes *f;
+
+        f = emalloc(sizeof(*f));
+        f->file = mkpath(dir, name);
+        f->next = allftypes;
+        allftypes = f;
+        return f;
+}
+
+static Ftypes*
+findftypes(char *dir, char *name)
+{
+        char *s;
+        Ftypes *f, *found;
+
+        found = nil;
+        s = mkpath(dir, name);
+        for(f=allftypes; f; f=f->next)
+                if(strcmp(f->file, s) == 0)
+                        found = f;
+        return found;
+}
+
+static void
+oops(void)
+{
+        longjmp(kaboom, 1);
+}
+
+/* find a : but skip over :: */
+static char*
+findcolon(char *p)
+{
+        while((p = strchr(p, ':')) != nil && *(p+1) == ':')
+                p += 2;
+        if(p == nil)
+                oops();
+        return p;
+}
+
+static void
+semi(char **p)
+{
+        if(**p != ';')
+                oops();
+        (*p)++;
+}
+
+static void
+comma(char **p)
+{
+        if(**p != ',')
+                oops();
+        (*p)++;
+}
+
+static int
+parseint(char **pp)
+{
+        if(!isdigit(**pp))
+                oops();
+        return strtol(*pp, pp, 10);
+}
+
+/*
+        name ::= symbol_opt info
+ */
+static Type*
+parsename(char *desc, char **pp)
+{
+        if(*desc == 'c')
+                return nil;
+
+        if(isdigit(*desc) || *desc=='-' || *desc=='(')
+                return parseinfo(desc, pp);
+        if(*desc == 0)
+                oops();
+        return parseinfo(desc+1, pp);
+}
+
+/*
+        info ::= num | num '=' attr* defn
+ */
+static Type*
+parseinfo(char *desc, char **pp)
+{
+        int n1, n2;
+        Type *t;
+        char *attr;
+
+        parsenum(desc, &n1, &n2, &desc);
+        t = typebynum(n1, n2);
+        if(*desc != '='){
+                *pp = desc;
+                return t;
+        }
+        desc++;
+        if(fstack)
+                fstack->list = mktl(t, fstack->list);
+        while(parseattr(desc, &attr, &desc) >= 0){
+                if(*attr == 's')
+                        t->xsizeof = atoi(attr+1)/8;
+        }
+        return parsedefn(desc, t, pp);
+}
+
+/*
+        num ::= integer | '(' integer ',' integer ')'
+*/
+static int
+parsenum(char *p, int *n1, int *n2, char **pp)
+{
+        if(isdigit(*p)){
+                *n1 = strtol(p, &p, 10);
+                *n2 = 0;
+                *pp = p;
+                return 0;
+        }
+        if(*p == '('){
+                *n1 = strtol(p+1, &p, 10);
+                if(*p != ',')
+                        oops();
+                *n2 = strtol(p+1, &p, 10);
+                if(*p != ')')
+                        oops();
+                *pp = p+1;
+                return 0;
+        }
+        oops();
+        return -1;
+}
+
+/*
+        attr ::= '@' text ';'
+
+        text is 
+                'a' integer (alignment)
+                'p' integer (pointer class)
+                'P' (packed type)
+                's' integer (size of type in bits)
+                'S' (string instead of array of chars)
+*/
+static int
+parseattr(char *p, char **text, char **pp)
+{
+        if(*p != '@')
+                return -1;
+        *text = p+1;
+        if((p = strchr(p, ';')) == nil)
+                oops();
+        *pp = p+1;
+        return 0;
+}
+
+
+typedef struct Basic Basic;
+struct Basic
+{
+        int size;
+        int fmt;
+};
+
+static Basic baseints[] =
+{
+        0, 0,
+/*1*/        4, 'd',        /* int32 */
+/*2*/        1, 'd',        /* char8 */
+/*3*/        2, 'd',        /* int16 */
+/*4*/        4, 'd',        /* long int32 */
+/*5*/        1, 'x',        /* uchar8 */
+/*6*/        1, 'd',        /* schar8 */
+/*7*/        2, 'x',        /* uint16 */
+/*8*/        4, 'x',        /* uint32 */
+/*9*/        4, 'x',        /* uint32 */
+/*10*/        4, 'x',        /* ulong32 */
+/*11*/        0, 0,                /* void */
+/*12*/        4, 'f',                /* float */
+/*13*/        8, 'f',                /* double */
+/*14*/        10, 'f',        /* long double */
+/*15*/        4, 'd',        /* int32 */
+/*16*/        4, 'd',        /* bool32 */        
+/*17*/        2, 'f',                /* short real */
+/*18*/        4, 'f',                /* real */
+/*19*/        4, 'x',        /* stringptr */
+/*20*/        1, 'd',        /* character8 */
+/*21*/        1, 'x',        /* logical*1 */
+/*22*/        2, 'x',        /* logical*2 */
+/*23*/        4, 'X',        /* logical*4 */
+/*24*/        4, 'X',        /* logical32 */
+/*25*/        8, 'F',                /* complex (two single) */
+/*26*/        16, 'F',        /* complex (two double) */
+/*27*/        1, 'd',        /* integer*1 */
+/*28*/        2, 'd',        /* integer*2 */
+/*29*/        4, 'd',        /* integer*4 */
+/*30*/        2, 'x',        /* wide char */
+/*31*/        8, 'd',        /* int64 */
+/*32*/        8, 'x',        /* uint64 */
+/*33*/        8, 'x',        /* logical*8 */
+/*34*/        8, 'd',        /* integer*8 */
+};
+
+static Basic basefloats[] =
+{
+        0,0,
+/*1*/        4, 'f',        /* 32-bit */
+/*2*/        8, 'f',        /* 64-bit */
+/*3*/        8, 'F',        /* complex */
+/*4*/        4, 'F',        /* complex16 */
+/*5*/        8, 'F', /* complex32 */
+/*6*/        10, 'f',        /* long double */
+};
+
+/*
+        defn ::= info
+                | 'b' ('u' | 's') 'c'? width; offset; nbits;                 (builtin, signed/unsigned, char/not, width in bytes, offset & nbits of type)
+                | 'w'                (aix wide char type, not used)
+                | 'R' fptype; bytes;                        (fptype 1=32-bit, 2=64-bit, 3=complex, 4=complex16, 5=complex32, 6=long double)
+                | 'g' typeinfo ';' nbits                 (aix floating, not used)
+                | 'c' typeinfo ';' nbits                (aix complex, not used)
+                | 'b' typeinfo ';' bytes                (ibm, no idea)
+                | 'B' typeinfo                (volatile typref)
+                | 'd' typeinfo                (file of typeref)
+                | 'k' typeinfo                (const typeref)
+                | 'M' typeinfo ';' length                (multiple instance type, fortran)
+                | 'S' typeinfo                (set, typeref must have small number of values)
+                | '*' typeinfo                (pointer to typeref)
+                | 'x' ('s'|'u'|'e') name ':'        (struct, union, enum reference.  name can have '::' in it)
+                | 'r' typeinfo ';' low ';' high ';'                (subrange.  typeref can be type being defined for base types!)
+                                low and high are bounds
+                                if bound is octal power of two, it's a big negative number
+                | ('a'|'P') indextypedef arraytypeinfo         (array, index should be range type)
+                                        indextype is type definition not typeinfo (need not say typenum=)
+                                        P means packed array
+                | 'A' arraytypeinfo                (open array (no index bounds))
+                | 'D' dims ';' typeinfo                (dims-dimensional dynamic array)
+                | 'E' dims ';' typeinfo                (subarray of N-dimensional array)
+                | 'n' typeinfo ';' bytes                (max length string)
+                | 'z' typeinfo ';' bytes                (no idea what difference is from 'n')
+                | 'N'                                        (pascal stringptr)
+                | 'e' (name ':' bigint ',')* ';'        (enum listing)
+                | ('s'|'u') bytes (name ':' type ',' bitoffset ',' bitsize ';')* ';'                (struct/union defn)
+                                                        tag is given as name in stabs entry, with 'T' symbol
+                | 'f' typeinfo ';'                         (function returning type)
+                | 'f' rettypeinfo ',' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+                | 'p' paramcount ';' (typeinfo ',' (0|1) ';')* ';'
+                | 'F' rettypeinfo ',' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+                | 'R' paramcount ';' (name ':' typeinfo ',' (0|1) ';')* ';'
+                                                        (the 0 or 1 is pass-by-reference vs pass-by-value)
+                                                        (the 0 or 1 is pass-by-reference vs pass-by-value)
+*/
+
+static Type*
+parsedefn(char *p, Type *t, char **pp)
+{
+        char c, *name;
+        int ischar, namelen, n, wid, offset, bits, sign;
+        long val;
+        Type *tt;
+
+        if(*p == '(' || isdigit(*p)){
+                t->ty = Defer;
+                t->sub = parseinfo(p, pp);
+                return t;
+        }
+
+        switch(c = *p){
+        case '-':        /* builtin */
+                n = strtol(p+1, &p, 10);
+                if(n >= nelem(baseints) || n < 0)
+                        n = 0;
+                t->ty = Base;
+                t->xsizeof = baseints[n].size;
+                t->printfmt = baseints[n].fmt;
+                break;
+        case 'b':        /* builtin */
+                p++;
+                if(*p != 'u' && *p != 's')
+                        oops();
+                sign = (*p == 's');
+                p++;
+                if(*p == 'c'){
+                        ischar = 1;
+                        p++;
+                }
+                wid = parseint(&p);
+                semi(&p);
+                offset = parseint(&p);
+                semi(&p);
+                bits = parseint(&p);
+                semi(&p);
+                t->ty = Base;
+                t->xsizeof = wid;
+                if(sign == 1)
+                        t->printfmt = 'd';
+                else
+                        t->printfmt = 'x';
+                break;
+        case 'R':        /* fp type */
+                n = parseint(&p);
+                semi(&p);
+                wid = parseint(&p);
+                semi(&p);
+                t->ty = Base;
+                t->xsizeof = wid;
+                if(n < 0 || n >= nelem(basefloats))
+                        n = 0;
+                t->xsizeof = basefloats[n].size;
+                t->printfmt = basefloats[n].fmt;
+                break;
+        case 'r':        /* subrange */
+                t->ty = Range;
+                t->sub = parseinfo(p+1, &p);
+                if(*(p-1) == ';' && *p != ';')
+                        p--;
+                semi(&p);
+                t->lo = parsebound(&p);
+                semi(&p);
+                t->hi = parsebound(&p);
+                semi(&p);
+                break;
+        case 'B':        /* volatile */
+        case 'k':        /* const */
+                t->ty = Defer;
+                t->sub = parseinfo(p+1, &p);
+                break;
+        case '*':        /* pointer */
+        case 'A':        /* open array */
+                t->ty = Pointer;
+                t->sub = parseinfo(p+1, &p);
+                break;
+        case 'a':        /* array */                
+        case 'P':        /* packed array */
+                t->ty = Pointer;
+                tt = newtype();
+                parsedefn(p+1, tt, &p);        /* index type */
+                if(*p == ';')
+                        p++;
+                tt = newtype();
+                t->sub = tt;
+                parsedefn(p, tt, &p);        /* element type */
+                break;
+        case 'e':        /* enum listing */
+                p++;
+                t->sue = 'e';
+                t->ty = Enum;
+                while(*p != ';'){
+                        name = p;
+                        p = findcolon(p)+1;
+                        namelen = (p-name)-1;
+                        val = parsebigint(&p);
+                        comma(&p);
+                        if(t->n%32 == 0){
+                                t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0]));
+                                t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0]));
+                        }
+                        t->tname[t->n] = estrndup(name, namelen);
+                        t->val[t->n] = val;
+                        t->n++;
+                }
+                semi(&p);
+                break;
+                
+        case 's':        /* struct */
+        case 'u':        /* union */
+                p++;
+                t->sue = c;
+                t->ty = Aggr;
+                n = parseint(&p);
+                while(*p != ';'){
+                        name = p;
+                        p = findcolon(p)+1;
+                        namelen = (p-name)-1;
+                        tt = parseinfo(p, &p);
+                        comma(&p);
+                        offset = parseint(&p);
+                        comma(&p);
+                        bits = parseint(&p);
+                        semi(&p);
+                        if(t->n%32 == 0){
+                                t->tname = erealloc(t->tname, (t->n+32)*sizeof(t->tname[0]));
+                                t->val = erealloc(t->val, (t->n+32)*sizeof(t->val[0]));
+                                t->t = erealloc(t->t, (t->n+32)*sizeof(t->t[0]));
+                        }
+                        t->tname[t->n] = estrndup(name, namelen);
+                        t->val[t->n] = offset;
+                        t->t[t->n] = tt;
+                        t->n++;
+                }
+                semi(&p);
+                break;
+
+        case 'x':        /* struct, union, enum reference */
+                p++;
+                t->ty = Defer;
+                if(*p != 's' && *p != 'u' && *p != 'e')
+                        oops();
+                c = *p;
+                name = p+1;
+                p = findcolon(p+1);
+                name = estrndup(name, p-name);
+                t->sub = typebysue(c, name);
+                p++;
+                break;
+
+#if 0        /* AIX */
+        case 'f':        /* function */
+        case 'p':        /* procedure */
+        case 'F':        /* Pascal function */
+        /* case 'R':        /* Pascal procedure */
+                /*
+                 * Even though we don't use the info, we have
+                 * to parse it in case it is embedded in other descriptions.
+                 */
+                t->ty = Function;
+                p++;
+                if(c == 'f' || c == 'F'){
+                        t->sub = parseinfo(p, &p);
+                        if(*p != ','){
+                                if(*p == ';')
+                                        p++;
+                                break;
+                        }
+                        comma(&p);
+                }
+                n = parseint(&p);        /* number of params */
+                semi(&p);
+                while(*p != ';'){
+                        if(c == 'F' || c == 'R'){
+                                name = p;                /* parameter name */
+                                p = findcolon(p)+1;
+                        }
+                        parseinfo(p, &p);        /* param type */
+                        comma(&p);
+                        parseint(&p);        /* bool: passed by value? */
+                        semi(&p);
+                }
+                semi(&p);
+                break;
+#endif
+
+        case 'f':        /* static function */
+        case 'F':        /* global function */
+                t->ty = Function;
+                p++;
+                t->sub = parseinfo(p, &p);
+                break;
+
+        /*
+         * We'll never see any of this stuff.
+         * When we do, we can worry about it.
+         */
+        case 'D':        /* n-dimensional array */
+        case 'E':        /* subarray of n-dimensional array */
+        case 'M':        /* fortran multiple instance type */
+        case 'N':        /* pascal string ptr */
+        case 'S':        /* set */
+        case 'c':        /* aix complex */
+        case 'd':        /* file of */
+        case 'g':        /* aix float */
+        case 'n':        /* max length string */
+        case 'w':        /* aix wide char */
+        case 'z':        /* another max length string */
+        default:
+                fprint(2, "unsupported type char %c (%d)\n", *p, *p);
+                oops();
+        }
+        *pp = p;
+        return t;
+}
+
+/*
+        bound ::= 
+                'A' offset                        (bound is on stack by ref at offset offset from arg list)
+                | 'T' offset                        (bound is on stack by val at offset offset from arg list)
+                | 'a' regnum                (bound passed by reference in register)
+                | 't' regnum                (bound passed by value in register)
+                | 'J'                                (no bound)
+                | bigint
+*/
+static int
+parsebound(char **pp)
+{
+        char *p;
+        int n;
+
+        n = 0;
+        p = *pp;
+        switch(*p){
+        case 'A':        /* bound is on stack by reference at offset n from arg list */
+        case 'T':        /* bound is on stack by value at offset n from arg list */
+        case 'a':        /* bound is passed by reference in register n */
+        case 't':        /* bound is passed by value in register n */
+                p++;
+                parseint(&p);
+                break;
+        case 'J':        /* no bound */
+                p++;
+                break;
+        default:
+                n = parsebigint(&p);
+                break;
+        }
+        *pp = p;
+        return n;
+}
+
+/*
+        bigint ::= '-'? decimal
+                | 0 octal
+                | -1
+ */
+static vlong
+parsebigint(char **pp)
+{
+        char *p;
+        int n, neg;
+
+        p = *pp;
+        if(*p == '0'){
+                errno = 0;
+                n = strtoll(p, &p, 8);
+                if(errno)
+                        n = 0;
+                goto out;
+        }
+        neg = 0;
+        if(*p == '-'){
+                neg = 1;
+                p++;
+        }
+        if(!isdigit(*p))
+                oops();
+        n = strtol(p, &p, 10);
+        if(neg)
+                n = -n;
+
+out:
+        *pp = p;
+        return n;
+}
+
+int
+stabs2acid(Stab *stabs, Biobuf *b)
+{
+        int fno, i;
+        char c, *dir, *fn, *file, *name, *desc, *p;
+        Ftypes *f;
+        Type *t, *tt;
+        StabSym sym;
+        Sym *symbols, *s;
+        Sym **lsym;
+
+        dir = nil;
+        file = nil;
+        fno = 0;
+        fn = nil;
+        symbols = nil;
+        lsym = &symbols;
+        for(i=0; stabsym(stabs, i, &sym)>=0; i++){
+                switch(sym.type){
+                case N_SO:
+                        if(sym.name){
+                                if(sym.name[0] && sym.name[strlen(sym.name)-1] == '/')
+                                        dir = sym.name;
+                                else
+                                        file = sym.name;
+                        }
+                        denumber();
+                        fstack = nil;
+                        fno = 0;
+                        break;
+                case N_BINCL:
+                        fno++;
+                        f = mkftypes(dir, sym.name);
+                        f->down = fstack;
+                        fstack = f;
+                        break;
+                case N_EINCL:
+                        if(fstack)
+                                fstack = fstack->down;
+                        break;
+                case N_EXCL:
+                        fno++;
+                        if((f = findftypes(dir, sym.name)) == nil){
+                                fprint(2, "cannot find remembered %s\n", sym.name);
+                                continue;
+                        }
+                        renumber(f->list, fno);
+                        break;
+                case N_GSYM:
+                case N_FUN:
+                case N_PSYM:
+                case N_LSYM:
+                case N_LCSYM:
+                case N_STSYM:
+                case N_RSYM:
+                        name = sym.name;
+                        if(name == nil){
+                                if(sym.type==N_FUN)
+                                        fn = nil;
+                                continue;
+                        }
+                        if((p = strchr(name, ':')) == nil)
+                                continue;
+                        name = estrndup(name, p-name);
+                        desc = ++p;
+                        c = *desc;
+                        if(c == 'c'){
+                                fprint(2, "skip constant %s\n", name);
+                                continue;
+                        }
+                        if(setjmp(kaboom)){
+                                fprint(2, "cannot parse %s\n", name);
+                                continue;
+                        }
+                        t = parsename(desc, &p);
+                        if(t == nil)
+                                continue;
+                        if(*p != 0)
+                                fprint(2, "extra desc '%s' in '%s'\n", p, desc);
+                        /* void is defined as itself */
+                        if(t->ty==Defer && t->sub==t && strcmp(name, "void")==0){
+                                t->ty = Base;
+                                t->xsizeof = 0;
+                                t->printfmt = '0';
+                        }
+                        if(*name==' ' && *(name+1) == 0)
+                                *name = 0;
+                        /* attach names to structs, unions, enums */
+                        if(c=='T' && *name && t->sue){
+                                t->suename = name;
+                                if(t->name == nil)
+                                        t->name = name;
+                                tt = typebysue(t->sue, name);
+                                tt->ty = Defer;
+                                tt->sub = t;
+                        }
+                        if(c=='t'){
+                                tt = newtype();
+                                tt->ty = Typedef;
+                                tt->name = name;
+                                tt->sub = t;
+                        }
+                        /* define base c types */
+                        if(t->ty==None || t->ty==Range){
+                                if(strcmp(name, "char") == 0){
+                                        t->ty = Base;
+                                        t->xsizeof = 1;
+                                        t->printfmt = 'x';
+                                }
+                                if(strcmp(name, "int") == 0){
+                                        t->ty = Base;
+                                        t->xsizeof = 4;
+                                        t->printfmt = 'd';
+                                }
+                        }
+                        /* record declaration in list for later. */
+                        if(c != 't' && c != 'T')
+                        switch(sym.type){
+                        case N_GSYM:
+                                *lsym = mksym(nil, name, t);
+                                lsym = &(*lsym)->next;
+                                break;
+                        case N_FUN:
+                                fn = name;
+                                break;
+                        case N_PSYM:
+                        case N_LSYM:
+                        case N_LCSYM:
+                        case N_STSYM:
+                        case N_RSYM:
+                                *lsym = mksym(fn, name, t);
+                                lsym = &(*lsym)->next;
+                                break;
+                        }
+                        break;
+                }
+        }
+
+        printtypes(b);
+
+        for(s=symbols; s; s=s->next){
+                t = s->type;
+                if(t->ty == Pointer){
+                        t = t->sub;
+                        if(t && t->equiv)
+                                t = t->equiv;
+                }
+                if(t == nil || t->ty != Aggr)
+                        continue;
+                Bprint(b, "complex %s %s%s%s;\n", nameof(t, 1),
+                        s->fn ? fixname(s->fn) : "", s->fn ? ":" : "", fixname(s->name));
+        }
+
+        return 0;
+}
diff --git a/src/cmd/acidtypes/type.c b/src/cmd/acidtypes/type.c
t@@ -0,0 +1,571 @@
+#include 
+#include 
+#include 
+#include 
+#include 
+#include "dat.h"
+
+char *prefix = "";
+
+static TypeList *thash[1021];
+static TypeList *namehash[1021];
+static TypeList *alltypes;
+
+static uint
+hash(uint num, uint num1)
+{
+        return (num*1009 + num1*1013) % nelem(thash);
+}
+
+static uint
+shash(char *s)
+{
+        uint h;
+
+        h = 0;
+        for(; *s; s++)
+                h = 37*h + *s;
+        return h%nelem(namehash);
+}
+
+void
+addnamehash(Type *t)
+{
+        uint h;
+
+        if(t->name){
+                h = shash(t->name);
+                namehash[h] = mktl(t, namehash[h]);
+        }
+}
+
+static void
+addhash(Type *t)
+{
+        uint h;
+
+        if(t->n1 || t->n2){
+                h = hash(t->n1, t->n2);
+                thash[h] = mktl(t, thash[h]);
+        }
+        if(t->name)
+                addnamehash(t);
+}
+
+Type*
+typebysue(char sue, char *name)
+{
+        Type *t;
+        TypeList *tl;
+
+        for(tl=namehash[shash(name)]; tl; tl=tl->tl){
+                t = tl->hd;
+                if(t->sue==sue && t->suename && strcmp(name, t->suename)==0)
+                        return t;
+        }
+        t = newtype();
+        if(sue=='e')
+                t->ty = Enum;
+        else
+                t->ty = Aggr;
+        if(sue=='u')
+                t->isunion = 1;
+        t->sue = sue;
+        t->suename = name;
+        addnamehash(t);
+        return t;
+}
+
+Type*
+typebynum(uint n1, uint n2)
+{
+        Type *t;
+        TypeList *tl;
+
+        if(n1 || n2){
+                for(tl=thash[hash(n1, n2)]; tl; tl=tl->tl){
+                        t = tl->hd;
+                        if(t->n1==n1 && t->n2==n2)
+                                return t;
+                }
+        }
+
+        t = newtype();
+        t->n1 = n1;
+        t->n2 = n2;
+        addhash(t);
+        return t;
+}        
+
+Type*
+newtype(void)
+{
+        Type *t;
+        static int gen;
+
+        t = emalloc(sizeof *t);
+        t->gen = ++gen;
+        alltypes = mktl(t, alltypes);
+        return t;
+}
+
+struct {
+        char *old;
+        char *new;
+} fixes[] = { /* Font Tab 4 */
+        "append",                "$append",
+        "builtin",                "$builtin",
+        "complex",                "$complex",
+        "delete",                "$delete",
+        "do",                        "$do",
+        "else",                        "$else",
+        "eval",                        "$eval",
+        "fmt",                        "$fmt",
+        "fn",                        "$fn",
+        "head",                        "$head",
+        "if",                        "$if",
+        "local",                "$local",
+        "loop",                        "$loop",
+        "ret",                        "$ret",
+        "tail",                        "$tail",
+        "then",                        "$then",
+        "whatis",                        "$whatis",
+        "while",                "$while",
+};
+
+char*
+fixname(char *name)
+{
+        int i;
+
+        if(name == nil)
+                return nil;
+        for(i=0; itl){
+                t = tl->hd;
+                t->n1 = n1;
+                addhash(t);
+        }
+}
+
+static Type*
+defer(Type *t)
+{
+        Type *u, *oldt;
+        int n;
+
+        u = t;
+        n = 0;
+        oldt = t;
+        while(t && (t->ty == Defer || t->ty == Typedef)){
+                if(n++%2)
+                        u = u->sub;
+                t = t->sub;
+                if(t == u)        /* cycle */
+                        goto cycle;
+        }
+        return t;
+
+cycle:
+        fprint(2, "cycle\n");
+        t = oldt;
+        n = 0;
+        while(t && (t->ty==Defer || t->ty==Typedef)){
+                fprint(2, "t %p/%d %s\n", t, t->ty, t->name);
+                if(t == u && n++ == 2)
+                        break;
+                t = t->sub;
+        }
+        return u;
+}
+
+static void
+dotypedef(Type *t)
+{
+        if(t->ty != Typedef && t->ty != Defer)
+                return;
+
+        if(t->didtypedef)
+                return;
+
+        t->didtypedef = 1;
+        if(t->sub == nil)
+                return;
+
+        /* push names downward to remove anonymity */
+        if(t->name && t->sub->name == nil)
+                t->sub->name = t->name;
+
+        dotypedef(t->sub);
+}
+
+static int
+countbytes(uvlong x)
+{
+        int n;
+
+        for(n=0; x; n++)
+                x>>=8;
+        return n;
+}
+
+static void
+dorange(Type *t)
+{
+        Type *tt;
+
+        if(t->ty != Range)
+                return;
+        if(t->didrange)
+                return;
+        t->didrange = 1;
+        tt = defer(t->sub);
+        if(tt == nil)
+                return;
+        dorange(tt);
+        if(t != tt && tt->ty != Base)
+                return;
+        t->ty = Base;
+        t->xsizeof = tt->xsizeof;
+        if(t->lo == 0)
+                t->printfmt = 'x';
+        else
+                t->printfmt = 'd';
+        if(t->xsizeof == 0)
+                t->xsizeof = countbytes(t->hi);
+}
+
+
+char*
+nameof(Type *t, int doanon)
+{
+        static char buf[1024];
+        char *p;
+
+        if(t->name)
+                strcpy(buf, fixname(t->name));
+        else if(t->suename)
+                snprint(buf, sizeof buf, "%s_%s", t->isunion ? "union" : "struct", t->suename);
+        else if(doanon)
+                snprint(buf, sizeof buf, "%s_%lud_", prefix, t->gen);
+        else
+                return "";
+        for(p=buf; *p; p++)
+                if(isspace(*p))
+                        *p = '_';
+        return buf;
+}
+
+static char
+basecharof(Type *t)        //XXX
+{
+        switch(t->xsizeof){
+        default:
+                return 'X';
+        case 1:
+                return 'b';
+        case 2:
+                if(t->printfmt=='d')
+                        return 'd';
+                else
+                        return 'x';
+        case 4:
+                if(t->printfmt=='d')
+                        return 'D';
+                else if(t->printfmt=='f')
+                        return 'f';
+                else
+                        return 'X';
+        case 8:
+                if(t->printfmt=='d')
+                        return 'V';
+                else if(t->printfmt=='f')
+                        return 'F';
+                else
+                        return 'Y';
+        }
+}
+
+static int
+nilstrcmp(char *a, char *b)
+{
+        if(a == b)
+                return 0;
+        if(a == nil)
+                return -1;
+        if(b == nil)
+                return 1;
+        return strcmp(a, b);
+}
+
+static int
+typecmp(Type *t, Type *u)
+{
+        int i;
+
+        if(t == u)
+                return 0;
+        if(t == nil)
+                return -1;
+        if(u == nil)
+                return 1;
+
+        if(t->ty < u->ty)
+                return -1;
+        if(t->ty > u->ty)
+                return 1;
+
+        if(t->isunion != u->isunion)
+                return t->isunion - u->isunion;
+
+        i = nilstrcmp(t->name, u->name);
+        if(i)
+                return i;
+
+        i = nilstrcmp(t->suename, u->suename);
+        if(i)
+                return i;
+
+        if(t->name || t->suename)
+                return 0;
+
+        if(t->ty==Enum){
+                if(t->n < u->n)
+                        return -1;
+                if(t->n > u->n)
+                        return 1;
+                if(t->n == 0)
+                        return 0;
+                i = strcmp(t->tname[0], u->tname[0]);
+                return i;
+        }
+        if(t < u)
+                return -1;
+        if(t > u)
+                return 1;
+        return 0;
+}
+
+static int
+qtypecmp(const void *va, const void *vb)
+{
+        Type *t, *u;
+
+        t = *(Type**)va;
+        u = *(Type**)vb;
+        return typecmp(t, u);
+}
+
+void
+printtype(Biobuf *b, Type *t)
+{
+        char *name;
+        int j, nprint;
+        Type *tt, *ttt;
+
+        if(t->printed)
+                return;
+        t->printed = 1;
+        switch(t->ty){
+        case Aggr:
+                name = nameof(t, 1);
+                Bprint(b, "sizeof%s = %lud;\n", name, t->xsizeof);
+                Bprint(b, "aggr %s {\n", name);
+                nprint = 0;
+                for(j=0; jn; j++){
+                        tt = defer(t->t[j]);
+                        if(tt && tt->equiv)
+                                tt = tt->equiv;
+                        if(tt == nil){
+                                Bprint(b, "// oops: nil type\n");
+                                continue;
+                        }
+                        switch(tt->ty){
+                        default:
+                                Bprint(b, "// oops: unknown type %d for %p/%s (%d,%d; %c,%s; %p)\n",
+                                        tt->ty, tt, fixname(t->tname[j]),
+                                        tt->n1, tt->n2, tt->sue ? tt->sue : '.', tt->suename, tt->sub);
+Bprint(b, "// t->t[j] = %p\n", ttt=t->t[j]);
+while(ttt){
+Bprint(b, "// %s %d (%d,%d) sub %p\n", ttt->name, ttt->ty, ttt->n1, ttt->n2, ttt->sub);
+ttt=ttt->sub;
+}
+                        case Base:
+                        case Pointer:
+                        case Enum:
+                        case Array:
+                        case Function:
+                                nprint++;
+                                Bprint(b, "\t'%c' %lud %s;\n", basecharof(tt), t->val[j], fixname(t->tname[j]));
+                                break;
+                        case Aggr:
+                                nprint++;
+                                Bprint(b, "\t%s %lud %s;\n", nameof(tt, 1), t->val[j], fixname(t->tname[j]));
+                                break;
+                        }
+                }
+                if(nprint == 0)
+                        Bprint(b, "\t'X' 0 __dummy;\n");
+                Bprint(b, "};\n\n");
+        
+                name = nameof(t, 1);        /* might have smashed it */
+                Bprint(b, "defn %s(addr) { indent_%s(addr, \"\"); }\n", name, name);
+                Bprint(b, "defn\nindent_%s(addr, indent) {\n", name);
+                Bprint(b, "\tcomplex %s addr;\n", name);
+                for(j=0; jn; j++){
+                        name = fixname(t->tname[j]);
+                        tt = defer(t->t[j]);
+                        if(tt == nil){
+                                Bprint(b, "// oops nil %s\n", name);
+                                continue;
+                        }
+                        switch(tt->ty){
+                        case Base:
+                        base:
+                                Bprint(b, "\tprint(indent, \"%s\t\", addr.%s, \"\\n\");\n",
+                                        name, name);
+                                break;
+                        case Pointer:
+                                ttt = defer(tt->sub);
+                                if(ttt && ttt->ty == Aggr)
+                                        Bprint(b, "\tprint(indent, \"%s\t%s(\", addr.%s, \")\\n\");\n",
+                                                name, nameof(ttt, 1), name);
+                                else
+                                        goto base;
+                                break;
+                        case Array:
+                                Bprint(b, "\tprint(indent, \"%s\t\", addr.%s\\X, \"\\n\");\n",
+                                        name, name);
+                                break;
+                        case Enum:
+                                Bprint(b, "\tprint(indent, \"%s\t\", addr.%s, \" \", %s(addr.%s), \"\\n\");\n",
+                                        name, name, nameof(tt, 1), name);
+                                break;
+                        case Aggr:
+                                Bprint(b, "\tprint(indent, \"%s\t%s{\\n\");\n",
+                                        name, nameof(tt, 0));
+                                Bprint(b, "\tindent_%s(addr+%lud, indent+\"  \");\n",
+                                        nameof(tt, 1), t->val[j]);
+                                Bprint(b, "\tprint(indent, \"\t}\\n\");\n");
+                                break;
+                        }
+                }
+                Bprint(b, "};\n\n");
+                break;
+        
+        case Enum:
+                name = nameof(t, 1);
+                Bprint(b, "// enum %s\n", name);
+                for(j=0; jn; j++)
+                        Bprint(b, "%s = %ld;\n", fixname(t->tname[j]), t->val[j]);
+        
+                Bprint(b, "vals_%s = {\n", name);
+                for(j=0; jn; j++)
+                        Bprint(b, "\t%lud,\n", t->val[j]);
+                Bprint(b, "};\n");
+                Bprint(b, "names_%s = {\n", name);
+                for(j=0; jn; j++)
+                        Bprint(b, "\t\"%s\",\n", fixname(t->tname[j]));
+                Bprint(b, "};\n");
+                Bprint(b, "defn\n%s(val) {\n", name);
+                Bprint(b, "\tlocal i;\n");
+                Bprint(b, "\ti = match(val, vals_%s);\n", name);
+                Bprint(b, "\tif i >= 0 then return names_%s[i];\n", name);
+                Bprint(b, "\treturn \"???\";\n");
+                Bprint(b, "};\n");
+                break;
+        }
+}
+
+void
+printtypes(Biobuf *b)
+{
+        int i, n, nn;
+        Type *t, *tt, **all;
+        TypeList *tl;
+
+        /* check that pointer resolved */
+        for(tl=alltypes; tl; tl=tl->tl){
+                t = tl->hd;
+                if(t->ty==None){
+                        if(t->n1 || t->n2)
+                                warn("type %d,%d referenced but not defined", t->n1, t->n2);
+                        else if(t->sue && t->suename)
+                                warn("%s %s referenced but not defined",
+                                        t->sue=='s' ? "struct" :
+                                        t->sue=='u' ? "union" :
+                                        t->sue=='e' ? "enum" : "???", t->suename);
+                }
+        }
+
+        /* push typedefs down, base types up */
+        n = 0;
+        for(tl=alltypes; tl; tl=tl->tl){
+                n++;
+                t = tl->hd;
+                if(t->ty == Typedef || t->ty == Defer)
+                        dotypedef(t);
+        }
+
+        /* push ranges around */
+        for(tl=alltypes; tl; tl=tl->tl)
+                dorange(tl->hd);
+
+        /*
+         * only take one type of a given name; acid is going to do this anyway,
+         * and this will reduce the amount of code we output considerably.
+         * we could run a DFA equivalence relaxation sort of algorithm
+         * to find the actual equivalence classes, and then rename types 
+         * appropriately, but this will do for now.
+         */
+        all = emalloc(n*sizeof(all[0]));
+        n = 0;
+        for(tl=alltypes; tl; tl=tl->tl)
+                all[n++] = tl->hd;
+
+        qsort(all, n, sizeof(all[0]), qtypecmp);
+
+        nn = 0;
+        for(i=0; iequiv = all[nn-1];
+        }
+
+        for(tl=alltypes; tl; tl=tl->tl){
+                t = tl->hd;
+                tt = defer(t);
+                if(tt && tt->equiv)
+                        t->equiv = tt->equiv;
+        }
+
+        for(i=0; i
diff --git a/src/cmd/acidtypes/util.c b/src/cmd/acidtypes/util.c
t@@ -0,0 +1,63 @@
+#include 
+#include 
+#include 
+#include 
+#include "dat.h"
+
+static int nwarn;
+
+void
+warn(char *fmt, ...)
+{
+        va_list arg;
+
+        if(++nwarn < 5){
+                va_start(arg, fmt);
+                fprint(2, "warning: ");
+                vfprint(2, fmt, arg);
+                fprint(2, "\n");
+                va_end(arg);
+        }else if(nwarn == 5)
+                fprint(2, "[additional warnings elided...]\n");
+}
+
+void*
+erealloc(void *v, uint n)
+{
+        v = realloc(v, n);
+        if(v == nil)
+                sysfatal("realloc: %r");
+        return v;
+}
+
+void*
+emalloc(uint n)
+{
+        void *v;
+
+        v = mallocz(n, 1);
+        if(v == nil)
+                sysfatal("malloc: %r");
+        return v;
+}
+
+char*
+estrdup(char *s)
+{
+        s = strdup(s);
+        if(s == nil)
+                sysfatal("strdup: %r");
+        return s;
+}
+
+TypeList*
+mktl(Type *hd, TypeList *tail)
+{
+        TypeList *tl;
+
+        tl = emalloc(sizeof(*tl));
+        tl->hd = hd;
+        tl->tl = tail;
+        return tl;
+}
+