root/src/cmd/cc/dpchk.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. argflag
  2. getflag
  3. newprot
  4. newname
  5. arginit
  6. getquoted
  7. pragvararg
  8. nextarg
  9. checkargs
  10. dpcheck
  11. pragpack
  12. pragfpround
  13. pragtextflag
  14. pragdataflag
  15. pragincomplete
  16. getimpsym
  17. more
  18. pragcgo

// Inferno utils/cc/dpchk.c
// http://code.google.com/p/inferno-os/source/browse/utils/cc/dpchk.c
//
//      Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
//      Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
//      Portions Copyright © 1997-1999 Vita Nuova Limited
//      Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
//      Portions Copyright © 2004,2006 Bruce Ellis
//      Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
//      Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
//      Portions Copyright © 2009 The Go Authors.  All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include        <u.h>
#include        "cc.h"
#include        "y.tab.h"

enum
{
        Fnone   = 0,
        Fl,
        Fvl,
        Fignor,
        Fstar,
        Fadj,

        Fverb   = 10,
};

typedef struct  Tprot   Tprot;
struct  Tprot
{
        Type*   type;
        Bits    flag;
        Tprot*  link;
};

typedef struct  Tname   Tname;
struct  Tname
{
        char*   name;
        int     param;
        int     count;
        Tname*  link;
        Tprot*  prot;
};

static  Type*   indchar;
static  uchar   flagbits[512];
static  char*   lastfmt;
static  int     lastadj;
static  int     lastverb;
static  int     nstar;
static  Tprot*  tprot;
static  Tname*  tname;

void
argflag(int c, int v)
{

        switch(v) {
        case Fignor:
        case Fstar:
        case Fl:
        case Fvl:
                flagbits[c] = v;
                break;
        case Fverb:
                flagbits[c] = lastverb;
/*print("flag-v %c %d\n", c, lastadj);*/
                lastverb++;
                break;
        case Fadj:
                flagbits[c] = lastadj;
/*print("flag-l %c %d\n", c, lastadj);*/
                lastadj++;
                break;
        }
}

Bits
getflag(char *s)
{
        Bits flag;
        int f;
        Fmt fmt;
        Rune c;

        flag = zbits;
        nstar = 0;
        fmtstrinit(&fmt);
        for(;;) {
                s += chartorune(&c, s);
                if(c == 0 || c >= nelem(flagbits))
                        break;
                fmtrune(&fmt, c);
                f = flagbits[c];
                switch(f) {
                case Fnone:
                        argflag(c, Fverb);
                        f = flagbits[c];
                        break;
                case Fstar:
                        nstar++;
                case Fignor:
                        continue;
                case Fl:
                        if(bset(flag, Fl))
                                flag = bor(flag, blsh(Fvl));
                }
                flag = bor(flag, blsh(f));
                if(f >= Fverb)
                        break;
        }
        free(lastfmt);
        lastfmt = fmtstrflush(&fmt);
        return flag;
}

static void
newprot(Sym *m, Type *t, char *s, Tprot **prot)
{
        Bits flag;
        Tprot *l;

        if(t == T) {
                warn(Z, "%s: newprot: type not defined", m->name);
                return;
        }
        flag = getflag(s);
        for(l=*prot; l; l=l->link)
                if(beq(flag, l->flag) && sametype(t, l->type))
                        return;
        l = alloc(sizeof(*l));
        l->type = t;
        l->flag = flag;
        l->link = *prot;
        *prot = l;
}

static Tname*
newname(char *s, int p, int count)
{
        Tname *l;

        for(l=tname; l; l=l->link)
                if(strcmp(l->name, s) == 0) {
                        if(p >= 0 && l->param != p)
                                yyerror("vargck %s already defined\n", s);
                        return l;
                }
        if(p < 0)
                return nil;

        l = alloc(sizeof(*l));
        l->name = s;
        l->param = p;
        l->link = tname;
        l->count = count;
        tname = l;
        return l;
}

void
arginit(void)
{
        int i;

/* debug['F'] = 1;*/
/* debug['w'] = 1;*/

        lastadj = Fadj;
        lastverb = Fverb;
        indchar = typ(TIND, types[TCHAR]);

        memset(flagbits, Fnone, sizeof(flagbits));

        for(i='0'; i<='9'; i++)
                argflag(i, Fignor);
        argflag('.', Fignor);
        argflag('#', Fignor);
        argflag('u', Fignor);
        argflag('h', Fignor);
        argflag('+', Fignor);
        argflag('-', Fignor);

        argflag('*', Fstar);
        argflag('l', Fl);

        argflag('o', Fverb);
        flagbits['x'] = flagbits['o'];
        flagbits['X'] = flagbits['o'];
}

static char*
getquoted(void)
{
        int c;
        Rune r;
        Fmt fmt;

        c = getnsc();
        if(c != '"')
                return nil;
        fmtstrinit(&fmt);
        for(;;) {
                r = getr();
                if(r == '\n') {
                        free(fmtstrflush(&fmt));
                        return nil;
                }
                if(r == '"')
                        break;
                fmtrune(&fmt, r);
        }
        free(lastfmt);
        lastfmt = fmtstrflush(&fmt);
        return strdup(lastfmt);
}

void
pragvararg(void)
{
        Sym *s;
        int n, c;
        char *t;
        Type *ty;
        Tname *l;

        if(!debug['F'])
                goto out;
        s = getsym();
        if(s && strcmp(s->name, "argpos") == 0)
                goto ckpos;
        if(s && strcmp(s->name, "type") == 0)
                goto cktype;
        if(s && strcmp(s->name, "flag") == 0)
                goto ckflag;
        if(s && strcmp(s->name, "countpos") == 0)
                goto ckcount;
        yyerror("syntax in #pragma varargck");
        goto out;

ckpos:
/*#pragma       varargck        argpos  warn    2*/
        s = getsym();
        if(s == S)
                goto bad;
        n = getnsn();
        if(n < 0)
                goto bad;
        newname(s->name, n, 0);
        goto out;

ckcount:
/*#pragma       varargck        countpos        name 2*/
        s = getsym();
        if(s == S)
                goto bad;
        n = getnsn();
        if(n < 0)
                goto bad;
        newname(s->name, 0, n);
        goto out;

ckflag:
/*#pragma       varargck        flag    'c'*/
        c = getnsc();
        if(c != '\'')
                goto bad;
        c = getr();
        if(c == '\\')
                c = getr();
        else if(c == '\'')
                goto bad;
        if(c == '\n')
                goto bad;
        if(getc() != '\'')
                goto bad;
        argflag(c, Fignor);
        goto out;

cktype:
        c = getnsc();
        unget(c);
        if(c != '"') {
/*#pragma       varargck        type    name    int*/
                s = getsym();
                if(s == S)
                        goto bad;
                l = newname(s->name, -1, -1);
                s = getsym();
                if(s == S)
                        goto bad;
                ty = s->type;
                while((c = getnsc()) == '*')
                        ty = typ(TIND, ty);
                unget(c);
                newprot(s, ty, "a", &l->prot);
                goto out;
        }

/*#pragma       varargck        type    O       int*/
        t = getquoted();
        if(t == nil)
                goto bad;
        s = getsym();
        if(s == S)
                goto bad;
        ty = s->type;
        while((c = getnsc()) == '*')
                ty = typ(TIND, ty);
        unget(c);
        newprot(s, ty, t, &tprot);
        goto out;

bad:
        yyerror("syntax in #pragma varargck");

out:
        while(getnsc() != '\n')
                ;
}

Node*
nextarg(Node *n, Node **a)
{
        if(n == Z) {
                *a = Z;
                return Z;
        }
        if(n->op == OLIST) {
                *a = n->left;
                return n->right;
        }
        *a = n;
        return Z;
}

void
checkargs(Node *nn, char *s, int pos)
{
        Node *a, *n;
        Bits flag;
        Tprot *l;

        if(!debug['F'])
                return;
        n = nn;
        for(;;) {
                s = strchr(s, '%');
                if(s == 0) {
                        nextarg(n, &a);
                        if(a != Z)
                                warn(nn, "more arguments than format %T",
                                        a->type);
                        return;
                }
                s++;
                flag = getflag(s);
                while(nstar > 0) {
                        n = nextarg(n, &a);
                        pos++;
                        nstar--;
                        if(a == Z) {
                                warn(nn, "more format than arguments %s",
                                        lastfmt);
                                return;
                        }
                        if(a->type == T)
                                continue;
                        if(!sametype(types[TINT], a->type) &&
                           !sametype(types[TUINT], a->type))
                                warn(nn, "format mismatch '*' in %s %T, arg %d",
                                        lastfmt, a->type, pos);
                }
                for(l=tprot; l; l=l->link)
                        if(sametype(types[TVOID], l->type)) {
                                if(beq(flag, l->flag)) {
                                        s++;
                                        goto loop;
                                }
                        }

                n = nextarg(n, &a);
                pos++;
                if(a == Z) {
                        warn(nn, "more format than arguments %s",
                                lastfmt);
                        return;
                }
                if(a->type == 0)
                        continue;
                for(l=tprot; l; l=l->link)
                        if(sametype(a->type, l->type)) {
/*print("checking %T/%ux %T/%ux\n", a->type, flag.b[0], l->type, l->flag.b[0]);*/
                                if(beq(flag, l->flag))
                                        goto loop;
                        }
                warn(nn, "format mismatch %s %T, arg %d", lastfmt, a->type, pos);
        loop:;
        }
}

void
dpcheck(Node *n)
{
        char *s;
        Node *a, *b;
        Tname *l;
        Tprot *tl;
        int i, j;

        if(n == Z)
                return;
        b = n->left;
        if(b == Z || b->op != ONAME)
                return;
        s = b->sym->name;
        for(l=tname; l; l=l->link)
                if(strcmp(s, l->name) == 0)
                        break;
        if(l == 0)
                return;

        if(l->count > 0) {
                // fetch count, then check remaining length
                i = l->count;
                a = nil;
                b = n->right;
                while(i > 0) {
                        b = nextarg(b, &a);
                        i--;
                }
                if(a == Z) {
                        diag(n, "can't find count arg");
                        return;
                }
                if(a->op != OCONST || !typechl[a->type->etype]) {
                        diag(n, "count is invalid constant");
                        return;
                }
                j = a->vconst;
                i = 0;
                while(b != Z) {
                        b = nextarg(b, &a);
                        i++;
                }
                if(i != j)
                        diag(n, "found %d argument%s after count %d", i, i == 1 ? "" : "s", j);
        }

        if(l->prot != nil) {
                // check that all arguments after param or count
                // are listed in type list.
                i = l->count;
                if(i == 0)
                        i = l->param;
                if(i == 0)
                        return;
                a = nil;
                b = n->right;
                while(i > 0) {
                        b = nextarg(b, &a);
                        i--;
                }
                if(a == Z) {
                        diag(n, "can't find count/param arg");
                        return;
                }
                while(b != Z) {
                        b = nextarg(b, &a);
                        for(tl=l->prot; tl; tl=tl->link)
                                if(sametype(a->type, tl->type))
                                        break;
                        if(tl == nil)
                                diag(a, "invalid type %T in call to %s", a->type, s);
                }
        }

        if(l->param <= 0)
                return;
        i = l->param;
        a = nil;
        b = n->right;
        while(i > 0) {
                b = nextarg(b, &a);
                i--;
        }
        if(a == Z) {
                diag(n, "can't find format arg");
                return;
        }
        if(!sametype(indchar, a->type)) {
                diag(n, "format arg type %T", a->type);
                return;
        }
        if(a->op != OADDR || a->left->op != ONAME || a->left->sym != symstring) {
/*              warn(n, "format arg not constant string");*/
                return;
        }
        s = a->left->cstring;
        checkargs(b, s, l->param);
}

void
pragpack(void)
{
        Sym *s;

        packflg = 0;
        s = getsym();
        if(s) {
                packflg = atoi(s->name+1);
                if(strcmp(s->name, "on") == 0 ||
                   strcmp(s->name, "yes") == 0)
                        packflg = 1;
        }
        while(getnsc() != '\n')
                ;
        if(debug['f'])
                if(packflg)
                        print("%4d: pack %d\n", lineno, packflg);
                else
                        print("%4d: pack off\n", lineno);
}

void
pragfpround(void)
{
        Sym *s;

        fproundflg = 0;
        s = getsym();
        if(s) {
                fproundflg = atoi(s->name+1);
                if(strcmp(s->name, "on") == 0 ||
                   strcmp(s->name, "yes") == 0)
                        fproundflg = 1;
        }
        while(getnsc() != '\n')
                ;
        if(debug['f'])
                if(fproundflg)
                        print("%4d: fproundflg %d\n", lineno, fproundflg);
                else
                        print("%4d: fproundflg off\n", lineno);
}

void
pragtextflag(void)
{
        Sym *s;

        s = getsym();
        if(s == S) {
                textflag = getnsn();
        } else {
                if(s->macro) {
                        macexpand(s, symb);
                }
                if(symb[0] < '0' || symb[0] > '9')
                        yyerror("pragma textflag not an integer");
                textflag = atoi(symb);
        }
        while(getnsc() != '\n')
                ;
        if(debug['f'])
                print("%4d: textflag %d\n", lineno, textflag);
}

void
pragdataflag(void)
{
        Sym *s;

        s = getsym();
        if(s == S) {
                dataflag = getnsn();
        } else {
                if(s->macro) {
                        macexpand(s, symb);
                }
                if(symb[0] < '0' || symb[0] > '9')
                        yyerror("pragma dataflag not an integer");
                dataflag = atoi(symb);
        }
        while(getnsc() != '\n')
                ;
        if(debug['f'])
                print("%4d: dataflag %d\n", lineno, dataflag);
}

void
pragincomplete(void)
{
        Sym *s;
        Type *t;
        int istag, w, et;

        istag = 0;
        s = getsym();
        if(s == nil)
                goto out;
        et = 0;
        w = s->lexical;
        if(w == LSTRUCT)
                et = TSTRUCT;
        else if(w == LUNION)
                et = TUNION;
        if(et != 0){
                s = getsym();
                if(s == nil){
                        yyerror("missing struct/union tag in pragma incomplete");
                        goto out;
                }
                if(s->lexical != LNAME && s->lexical != LTYPE){
                        yyerror("invalid struct/union tag: %s", s->name);
                        goto out;
                }
                dotag(s, et, 0);
                istag = 1;
        }else if(strcmp(s->name, "_off_") == 0){
                debug['T'] = 0;
                goto out;
        }else if(strcmp(s->name, "_on_") == 0){
                debug['T'] = 1;
                goto out;
        }
        t = s->type;
        if(istag)
                t = s->suetag;
        if(t == T)
                yyerror("unknown type %s in pragma incomplete", s->name);
        else if(!typesu[t->etype])
                yyerror("not struct/union type in pragma incomplete: %s", s->name);
        else
                t->garb |= GINCOMPLETE;
out:
        while(getnsc() != '\n')
                ;
        if(debug['f'])
                print("%s incomplete\n", s->name);
}

Sym*
getimpsym(void)
{
        int c;
        char *cp;

        c = getnsc();
        if(isspace(c) || c == '"') {
                unget(c);
                return S;
        }
        for(cp = symb;;) {
                if(cp <= symb+NSYMB-4)
                        *cp++ = c;
                c = getc();
                if(c > 0 && !isspace(c) && c != '"')
                        continue;
                unget(c);
                break;
        }
        *cp = 0;
        if(cp > symb+NSYMB-4)
                yyerror("symbol too large: %s", symb);
        return lookup();
}

static int
more(void)
{
        int c;
        
        do
                c = getnsc();
        while(c == ' ' || c == '\t');
        unget(c);
        return c != '\n';
}

void
pragcgo(char *verb)
{
        Sym *local, *remote;
        char *p;

        if(strcmp(verb, "cgo_dynamic_linker") == 0 || strcmp(verb, "dynlinker") == 0) {
                p = getquoted();
                if(p == nil)
                        goto err1;
                fmtprint(&pragcgobuf, "cgo_dynamic_linker %q\n", p);
                goto out;
        
        err1:
                yyerror("usage: #pragma cgo_dynamic_linker \"path\"");
                goto out;
        }       
        
        if(strcmp(verb, "dynexport") == 0)
                verb = "cgo_export_dynamic";
        if(strcmp(verb, "cgo_export_static") == 0 || strcmp(verb, "cgo_export_dynamic") == 0) {
                local = getimpsym();
                if(local == nil)
                        goto err2;
                if(!more()) {
                        fmtprint(&pragcgobuf, "%s %q\n", verb, local->name);
                        goto out;
                }
                remote = getimpsym();
                if(remote == nil)
                        goto err2;
                fmtprint(&pragcgobuf, "%s %q %q\n", verb, local->name, remote->name);
                goto out;
        
        err2:
                yyerror("usage: #pragma %s local [remote]", verb);
                goto out;
        }
        
        if(strcmp(verb, "cgo_import_dynamic") == 0 || strcmp(verb, "dynimport") == 0) {
                local = getimpsym();
                if(local == nil)
                        goto err3;
                if(!more()) {
                        fmtprint(&pragcgobuf, "cgo_import_dynamic %q\n", local->name);
                        goto out;
                }
                remote = getimpsym();
                if(remote == nil)
                        goto err3;
                if(!more()) {
                        fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q\n", local->name, remote->name);
                        goto out;
                }
                p = getquoted();
                if(p == nil)    
                        goto err3;
                fmtprint(&pragcgobuf, "cgo_import_dynamic %q %q %q\n", local->name, remote->name, p);
                goto out;
        
        err3:
                yyerror("usage: #pragma cgo_import_dynamic local [remote [\"library\"]]");
                goto out;
        }
        
        if(strcmp(verb, "cgo_import_static") == 0) {
                local = getimpsym();
                if(local == nil)
                        goto err4;
                fmtprint(&pragcgobuf, "cgo_import_static %q\n", local->name);
                goto out;

        err4:
                yyerror("usage: #pragma cgo_import_static local [remote]");
                goto out;
        }
        
        if(strcmp(verb, "cgo_ldflag") == 0) {
                p = getquoted();
                if(p == nil)
                        goto err5;
                fmtprint(&pragcgobuf, "cgo_ldflag %q\n", p);
                goto out;

        err5:
                yyerror("usage: #pragma cgo_ldflag \"arg\"");
                goto out;
        }
        
out:
        while(getnsc() != '\n')
                ;
}

/* [<][>][^][v][top][bottom][index][help] */