diff options
author | joerg <joerg@FreeBSD.org> | 1995-11-05 15:56:42 +0000 |
---|---|---|
committer | joerg <joerg@FreeBSD.org> | 1995-11-05 15:56:42 +0000 |
commit | 97ea65b2cb83e7b5b2c4afcacc3a185482457c2a (patch) | |
tree | e48ecf34ec80fd6977218c2852e4419794cc64d7 /usr.bin/xlint/lint2 | |
download | FreeBSD-src-97ea65b2cb83e7b5b2c4afcacc3a185482457c2a.zip FreeBSD-src-97ea65b2cb83e7b5b2c4afcacc3a185482457c2a.tar.gz |
Jochen Pohl's lint(1) from NetBSD. Yet another import.
This is just a vendor import by now. I'll wait until i'll get the
imported files back via CTM before applying the FreeBSD patches.
Don't use it yet.
Submitted by: Jochen Pohl <jpo.drs@sni.de>
Obtained from: (NetBSD -- this version is directly from Jochen)
Diffstat (limited to 'usr.bin/xlint/lint2')
-rw-r--r-- | usr.bin/xlint/lint2/Makefile | 13 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/chk.c | 1462 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/emit2.c | 236 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/externs2.h | 87 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/hash.c | 123 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/lint2.h | 177 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/main2.c | 190 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/mem2.c | 97 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/msg.c | 157 | ||||
-rw-r--r-- | usr.bin/xlint/lint2/read.c | 1133 |
10 files changed, 3675 insertions, 0 deletions
diff --git a/usr.bin/xlint/lint2/Makefile b/usr.bin/xlint/lint2/Makefile new file mode 100644 index 0000000..0e7d405 --- /dev/null +++ b/usr.bin/xlint/lint2/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.2 1995/07/03 21:24:39 cgd Exp $ + +.PATH: ${.CURDIR}/../lint1 + +PROG= lint2 +SRCS= main2.c hash.c read.c mem.c mem2.c chk.c msg.c emit.c emit2.c +NOMAN= +CFLAGS+=-I${.CURDIR}/../lint1 +LINTFLAGS=-abehrz + +BINDIR= /usr/libexec + +.include <bsd.prog.mk> diff --git a/usr.bin/xlint/lint2/chk.c b/usr.bin/xlint/lint2/chk.c new file mode 100644 index 0000000..0aac852 --- /dev/null +++ b/usr.bin/xlint/lint2/chk.c @@ -0,0 +1,1462 @@ +/* $NetBSD: chk.c,v 1.2 1995/07/03 21:24:42 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: chk.c,v 1.2 1995/07/03 21:24:42 cgd Exp $"; +#endif + +#include <stdlib.h> +#include <ctype.h> +#include <limits.h> +#include <err.h> + +#include "lint2.h" + +/* various type information */ +ttab_t ttab[NTSPEC]; + + +static void chkund __P((hte_t *)); +static void chkdnu __P((hte_t *)); +static void chkdnud __P((hte_t *)); +static void chkmd __P((hte_t *)); +static void chkvtui __P((hte_t *, sym_t *, sym_t *)); +static void chkvtdi __P((hte_t *, sym_t *, sym_t *)); +static void chkfaui __P((hte_t *, sym_t *, sym_t *)); +static void chkau __P((hte_t *, int, sym_t *, sym_t *, pos_t *, + fcall_t *, fcall_t *, type_t *, type_t *)); +static void chkrvu __P((hte_t *, sym_t *)); +static void chkadecl __P((hte_t *, sym_t *, sym_t *)); +static void printflike __P((hte_t *,fcall_t *, int, + const char *, type_t **)); +static void scanflike __P((hte_t *, fcall_t *, int, + const char *, type_t **)); +static void badfmt __P((hte_t *, fcall_t *)); +static void inconarg __P((hte_t *, fcall_t *, int)); +static void tofewarg __P((hte_t *, fcall_t *)); +static void tomanyarg __P((hte_t *, fcall_t *)); +static int eqtype __P((type_t *, type_t *, int, int, int, int *)); +static int eqargs __P((type_t *, type_t *, int *)); +static int mnoarg __P((type_t *, int *)); + + +void +inittyp() +{ + int i; + static struct { + tspec_t it_tspec; + ttab_t it_ttab; + } ittab[] = { + { SIGNED, { 0, 0, + SIGNED, UNSIGN, + 0, 0, 0, 0, 0, "signed" } }, + { UNSIGN, { 0, 0, + SIGNED, UNSIGN, + 0, 0, 0, 0, 0, "unsigned" } }, + { CHAR, { CHAR_BIT, CHAR_BIT, + SCHAR, UCHAR, + 1, 0, 0, 1, 1, "char" } }, + { SCHAR, { CHAR_BIT, CHAR_BIT, + SCHAR, UCHAR, + 1, 0, 0, 1, 1, "signed char" } }, + { UCHAR, { CHAR_BIT, CHAR_BIT, + SCHAR, UCHAR, + 1, 1, 0, 1, 1, "unsigned char" } }, + { SHORT, { sizeof (short) * CHAR_BIT, 2 * CHAR_BIT, + SHORT, USHORT, + 1, 0, 0, 1, 1, "short" } }, + { USHORT, { sizeof (u_short) * CHAR_BIT, 2 * CHAR_BIT, + SHORT, USHORT, + 1, 1, 0, 1, 1, "unsigned short" } }, + { INT, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT, + INT, UINT, + 1, 0, 0, 1, 1, "int" } }, + { UINT, { sizeof (u_int) * CHAR_BIT, 3 * CHAR_BIT, + INT, UINT, + 1, 1, 0, 1, 1, "unsigned int" } }, + { LONG, { sizeof (long) * CHAR_BIT, 4 * CHAR_BIT, + LONG, ULONG, + 1, 0, 0, 1, 1, "long" } }, + { ULONG, { sizeof (u_long) * CHAR_BIT, 4 * CHAR_BIT, + LONG, ULONG, + 1, 1, 0, 1, 1, "unsigned long" } }, + { QUAD, { sizeof (quad_t) * CHAR_BIT, 8 * CHAR_BIT, + QUAD, UQUAD, + 1, 0, 0, 1, 1, "long long" } }, + { UQUAD, { sizeof (u_quad_t) * CHAR_BIT, 8 * CHAR_BIT, + QUAD, UQUAD, + 1, 1, 0, 1, 1, "unsigned long long" } }, + { FLOAT, { sizeof (float) * CHAR_BIT, 4 * CHAR_BIT, + FLOAT, FLOAT, + 0, 0, 1, 1, 1, "float" } }, + { DOUBLE, { sizeof (double) * CHAR_BIT, 8 * CHAR_BIT, + DOUBLE, DOUBLE, + 0, 0, 1, 1, 1, "double" } }, + { LDOUBLE, { sizeof (ldbl_t) * CHAR_BIT, 10 * CHAR_BIT, + LDOUBLE, LDOUBLE, + 0, 0, 1, 1, 1, "long double" } }, + { VOID, { -1, -1, + VOID, VOID, + 0, 0, 0, 0, 0, "void" } }, + { STRUCT, { -1, -1, + STRUCT, STRUCT, + 0, 0, 0, 0, 0, "struct" } }, + { UNION, { -1, -1, + UNION, UNION, + 0, 0, 0, 0, 0, "union" } }, + { ENUM, { sizeof (int) * CHAR_BIT, 3 * CHAR_BIT, + ENUM, ENUM, + 1, 0, 0, 1, 1, "enum" } }, + { PTR, { sizeof (void *) * CHAR_BIT, 4 * CHAR_BIT, + PTR, PTR, + 0, 1, 0, 0, 1, "pointer" } }, + { ARRAY, { -1, -1, + ARRAY, ARRAY, + 0, 0, 0, 0, 0, "array" } }, + { FUNC, { -1, -1, + FUNC, FUNC, + 0, 0, 0, 0, 0, "function" } }, + }; + + for (i = 0; i < sizeof (ittab) / sizeof (ittab[0]); i++) + STRUCT_ASSIGN(ttab[ittab[i].it_tspec], ittab[i].it_ttab); + if (!pflag) { + for (i = 0; i < NTSPEC; i++) + ttab[i].tt_psz = ttab[i].tt_sz; + } +} + + +/* + * If there is a symbol named "main", mark it as used. + */ +void +mainused() +{ + hte_t *hte; + + if ((hte = hsearch("main", 0)) != NULL) + hte->h_used = 1; +} + +/* + * Performs all tests for a single name + */ +void +chkname(hte) + hte_t *hte; +{ + sym_t *sym, *def, *pdecl, *decl; + + if (uflag) { + chkund(hte); + chkdnu(hte); + if (xflag) + chkdnud(hte); + } + chkmd(hte); + + /* Get definition, prototype declaration and declaration */ + def = pdecl = decl = NULL; + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (def == NULL && (sym->s_def == DEF || sym->s_def == TDEF)) + def = sym; + if (pdecl == NULL && sym->s_def == DECL && + TP(sym->s_type)->t_tspec == FUNC && + TP(sym->s_type)->t_proto) { + pdecl = sym; + } + if (decl == NULL && sym->s_def == DECL) + decl = sym; + } + + /* A prototype is better than an old style declaration. */ + if (pdecl != NULL) + decl = pdecl; + + chkvtui(hte, def, decl); + + chkvtdi(hte, def, decl); + + chkfaui(hte, def, decl); + + chkrvu(hte, def); + + chkadecl(hte, def, decl); +} + +/* + * Print a warning if the name has been used, but not defined. + */ +static void +chkund(hte) + hte_t *hte; +{ + fcall_t *fcall; + usym_t *usym; + + if (!hte->h_used || hte->h_def) + return; + + if ((fcall = hte->h_calls) != NULL) { + /* %s used( %s ), but not defined */ + msg(0, hte->h_name, mkpos(&fcall->f_pos)); + } else if ((usym = hte->h_usyms) != NULL) { + /* %s used( %s ), but not defined */ + msg(0, hte->h_name, mkpos(&usym->u_pos)); + } +} + +/* + * Print a warning if the name has been defined, but never used. + */ +static void +chkdnu(hte) + hte_t *hte; +{ + sym_t *sym; + + if (!hte->h_def || hte->h_used) + return; + + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (sym->s_def == DEF || sym->s_def == TDEF) { + /* %s defined( %s ), but never used */ + msg(1, hte->h_name, mkpos(&sym->s_pos)); + break; + } + } +} + +/* + * Print a warning if the name has been declared, but is not used + * or defined. + */ +static void +chkdnud(hte) + hte_t *hte; +{ + sym_t *sym; + + if (hte->h_syms == NULL || hte->h_used || hte->h_def) + return; + + if ((sym = hte->h_syms) != NULL) { + if (sym->s_def != DECL) + errx(1, "internal error: chkdnud() 1"); + /* %s declared( %s ), but never used or defined */ + msg(2, hte->h_name, mkpos(&sym->s_pos)); + } +} + +/* + * Print a warning if there is more then one definition for + * this name. + */ +static void +chkmd(hte) + hte_t *hte; +{ + sym_t *sym, *def1; + char *pos1; + + if (!hte->h_def) + return; + + def1 = NULL; + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + /* + * ANSI C allows tentative definitions of the same name in + * only one compilation unit. + */ + if (sym->s_def != DEF && (!sflag || sym->s_def != TDEF)) + continue; + if (def1 == NULL) { + def1 = sym; + continue; + } + pos1 = xstrdup(mkpos(&def1->s_pos)); + /* %s multiply defined\t%s :: %s */ + msg(3, hte->h_name, pos1, mkpos(&sym->s_pos)); + free(pos1); + } +} + +/* + * Print a warning if the return value assumed for a function call + * differs from the return value of the function definition or + * function declaration. + * + * If no definition/declaration can be found, the assumed return values + * are always int. So there is no need to compare with another function + * call as it's done for function arguments. + */ +static void +chkvtui(hte, def, decl) + hte_t *hte; + sym_t *def, *decl; +{ + fcall_t *call; + char *pos1; + type_t *tp1, *tp2; + /* LINTED (automatic hides external declaration: warn) */ + int warn, eq; + tspec_t t1; + + if (hte->h_calls == NULL) + return; + + if (def == NULL) + def = decl; + if (def == NULL) + return; + + t1 = (tp1 = TP(def->s_type)->t_subt)->t_tspec; + for (call = hte->h_calls; call != NULL; call = call->f_nxt) { + tp2 = TP(call->f_type)->t_subt; + eq = eqtype(tp1, tp2, 1, 0, 0, (warn = 0, &warn)); + if (!call->f_rused) { + /* no return value used */ + if ((t1 == STRUCT || t1 == UNION) && !eq) { + /* + * If a function returns a struct or union it + * must be declared to return a struct or + * union, also if the return value is ignored. + * This is necessary because the caller must + * allocate stack space for the return value. + * If it does not, the return value would over- + * write other data. + * XXX Following massage may be confusing + * because it appears also if the return value + * was declared inconsistently. But this + * behaviour matches pcc based lint, so it is + * accepted for now. + */ + pos1 = xstrdup(mkpos(&def->s_pos)); + /* %s value must be decl. before use %s :: %s */ + msg(17, hte->h_name, + pos1, mkpos(&call->f_pos)); + free(pos1); + } + continue; + } + if (!eq || (sflag && warn)) { + pos1 = xstrdup(mkpos(&def->s_pos)); + /* %s value used inconsistenty\t%s :: %s */ + msg(4, hte->h_name, pos1, mkpos(&call->f_pos)); + free(pos1); + } + } +} + +/* + * Print a warning if a definition/declaration does not match another + * definition/declaration of the same name. For functions, only the + * types of return values are tested. + */ +static void +chkvtdi(hte, def, decl) + hte_t *hte; + sym_t *def, *decl; +{ + sym_t *sym; + type_t *tp1, *tp2; + /* LINTED (automatic hides external declaration: warn) */ + int eq, warn; + char *pos1; + + if (def == NULL) + def = decl; + if (def == NULL) + return; + + tp1 = TP(def->s_type); + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (sym == def) + continue; + tp2 = TP(sym->s_type); + warn = 0; + if (tp1->t_tspec == FUNC && tp2->t_tspec == FUNC) { + eq = eqtype(tp1->t_subt, tp2->t_subt, 1, 0, 0, &warn); + } else { + eq = eqtype(tp1, tp2, 0, 0, 0, &warn); + } + if (!eq || (sflag && warn)) { + pos1 = xstrdup(mkpos(&def->s_pos)); + /* %s value declared inconsistently\t%s :: %s */ + msg(5, hte->h_name, pos1, mkpos(&sym->s_pos)); + free(pos1); + } + } +} + +/* + * Print a warning if a function is called with arguments which does + * not match the function definition, declaration or another call + * of the same function. + */ +static void +chkfaui(hte, def, decl) + hte_t *hte; + sym_t *def, *decl; +{ + type_t *tp1, *tp2, **ap1, **ap2; + pos_t *pos1p; + fcall_t *calls, *call, *call1; + int n, as; + char *pos1; + arginf_t *ai; + + if ((calls = hte->h_calls) == NULL) + return; + + /* + * If we find a function definition, we use this for comparision, + * otherwise the first prototype we can find. If there is no + * definition or prototype declaration, the first function call + * is used. + */ + tp1 = NULL; + call1 = NULL; + if (def != NULL) { + if ((tp1 = TP(def->s_type))->t_tspec != FUNC) + return; + pos1p = &def->s_pos; + } else if (decl != NULL && TP(decl->s_type)->t_proto) { + if ((tp1 = TP(decl->s_type))->t_tspec != FUNC) + return; + pos1p = &decl->s_pos; + } + if (tp1 == NULL) { + call1 = calls; + calls = calls->f_nxt; + if ((tp1 = TP(call1->f_type))->t_tspec != FUNC) + return; + pos1p = &call1->f_pos; + } + + n = 1; + for (call = calls; call != NULL; call = call->f_nxt) { + if ((tp2 = TP(call->f_type))->t_tspec != FUNC) + continue; + ap1 = tp1->t_args; + ap2 = tp2->t_args; + n = 0; + while (*ap1 != NULL && *ap2 != NULL) { + if (def != NULL && def->s_va && n >= def->s_nva) + break; + n++; + chkau(hte, n, def, decl, pos1p, call1, call, + *ap1, *ap2); + ap1++; + ap2++; + } + if (*ap1 == *ap2) { + /* equal # of arguments */ + } else if (def != NULL && def->s_va && n >= def->s_nva) { + /* + * function definition with VARARGS; The # of + * arguments of the call must be at least as large + * as the parameter of VARARGS. + */ + } else if (*ap2 != NULL && tp1->t_proto && tp1->t_vararg) { + /* + * prototype with ... and function call with + * at least the same # of arguments as declared + * in the prototype. + */ + } else { + pos1 = xstrdup(mkpos(pos1p)); + /* %s: variable # of args\t%s :: %s */ + msg(7, hte->h_name, pos1, mkpos(&call->f_pos)); + free(pos1); + continue; + } + + /* perform SCANFLIKE/PRINTFLIKE tests */ + if (def == NULL || (!def->s_prfl && !def->s_scfl)) + continue; + as = def->s_prfl ? def->s_nprfl : def->s_nscfl; + for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) { + if (ai->a_num == as) + break; + } + if (ai == NULL || !ai->a_fmt) + continue; + if (def->s_prfl) { + printflike(hte, call, n, ai->a_fstrg, ap2); + } else { + scanflike(hte, call, n, ai->a_fstrg, ap2); + } + } +} + +/* + * Check a single argument in a function call. + * + * hte a pointer to the hash table entry of the function + * n the number of the argument (1..) + * def the function definition or NULL + * decl prototype declaration, old style declaration or NULL + * pos1p position of definition, declaration of first call + * call1 first call, if both def and decl are old style def/decl + * call checked call + * arg1 currently checked argument of def/decl/call1 + * arg2 currently checked argument of call + * + */ +static void +chkau(hte, n, def, decl, pos1p, call1, call, arg1, arg2) + hte_t *hte; + int n; + sym_t *def, *decl; + pos_t *pos1p; + fcall_t *call1, *call; + type_t *arg1, *arg2; +{ + /* LINTED (automatic hides external declaration: warn) */ + int promote, asgn, warn; + tspec_t t1, t2; + arginf_t *ai, *ai1; + char *pos1; + + /* + * If a function definition is available (def != NULL), we compair the + * function call (call) with the definition. Otherwise, if a function + * definition is available and it is not an old style definition + * (decl != NULL && TP(decl->s_type)->t_proto), we compair the call + * with this declaration. Otherwise we compair it with the first + * call we have found (call1). + */ + + /* arg1 must be promoted if it stems from an old style definition */ + promote = def != NULL && def->s_osdef; + + /* + * If we compair with a definition or declaration, we must perform + * the same checks for qualifiers in indirected types as in + * assignments. + */ + asgn = def != NULL || (decl != NULL && TP(decl->s_type)->t_proto); + + warn = 0; + if (eqtype(arg1, arg2, 1, promote, asgn, &warn) && (!sflag || !warn)) + return; + + /* + * Other lint implementations print warnings as soon as the type + * of an argument does not match exactly the expected type. The + * result are lots of warnings which are really not neccessary. + * We print a warning only if + * (0) at least one type is not an interger type and types differ + * (1) hflag is set and types differ + * (2) types differ, except in signedness + * If the argument is an integer constant whose msb is not set, + * signedness is ignored (e.g. 0 matches both signed and unsigned + * int). This is with and without hflag. + * If the argument is an integer constant with value 0 and the + * expected argument is of type pointer and the width of the + * interger constant is the same as the width of the pointer, + * no warning is printed. + */ + t1 = arg1->t_tspec; + t2 = arg2->t_tspec; + if (isityp(t1) && isityp(t2) && !arg1->t_isenum && !arg2->t_isenum) { + if (promote) { + /* + * XXX Here is a problem: Althrough it is possible to + * pass an int where a char/short it expected, there + * may be loss in significant digits. We should first + * check for const arguments if they can be converted + * into the original parameter type. + */ + if (t1 == FLOAT) { + t1 = DOUBLE; + } else if (t1 == CHAR || t1 == SCHAR) { + t1 = INT; + } else if (t1 == UCHAR) { + t1 = tflag ? UINT : INT; + } else if (t1 == SHORT) { + t1 = INT; + } else if (t1 == USHORT) { + /* CONSTCOND */ + t1 = INT_MAX < USHRT_MAX || tflag ? UINT : INT; + } + } + + if (styp(t1) == styp(t2)) { + + /* + * types differ only in signedness; get information + * about arguments + */ + + /* + * treat a definition like a call with variable + * arguments + */ + ai1 = call1 != NULL ? call1->f_args : NULL; + + /* + * if two calls are compared, ai1 is set to the + * information for the n-th argument, if this was + * a constant, otherwise to NULL + */ + for ( ; ai1 != NULL; ai1 = ai1->a_nxt) { + if (ai1->a_num == n) + break; + } + /* + * ai is set to the information of the n-th arg + * of the (second) call, if this was a constant, + * otherwise to NULL + */ + for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) { + if (ai->a_num == n) + break; + } + + if (ai1 == NULL && ai == NULL) { + /* no constant at all */ + if (!hflag) + return; + } else if (ai1 == NULL || ai == NULL) { + /* one constant */ + if (ai == NULL) + ai = ai1; + if (ai->a_zero || ai->a_pcon) + /* same value in signed and unsigned */ + return; + /* value (not representation) differently */ + } else { + /* + * two constants, one signed, one unsigned; + * if the msb of one of the constants is set, + * the argument is used inconsistently. + */ + if (!ai1->a_ncon && !ai->a_ncon) + return; + } + } + + } else if (t1 == PTR && isityp(t2) && psize(t1) == psize(t2)) { + for (ai = call->f_args; ai != NULL; ai = ai->a_nxt) { + if (ai->a_num == n) + break; + } + if (ai != NULL && ai->a_zero) + return; + } + + pos1 = xstrdup(mkpos(pos1p)); + /* %s, arg %d used inconsistently\t%s :: %s */ + msg(6, hte->h_name, n, pos1, mkpos(&call->f_pos)); + free(pos1); +} + +/* + * Compare the types in the NULL-terminated array ap with the format + * string fmt. + */ +static void +printflike(hte, call, n, fmt, ap) + hte_t *hte; + fcall_t *call; + int n; + const char *fmt; + type_t **ap; +{ + const char *fp; + int fc; + int fwidth, prec, left, sign, space, alt, zero; + tspec_t sz, t1, t2; + type_t *tp; + + fp = fmt; + fc = *fp++; + + for ( ; ; ) { + if (fc == '\0') { + if (*ap != NULL) + tomanyarg(hte, call); + break; + } + if (fc != '%') { + badfmt(hte, call); + break; + } + fc = *fp++; + fwidth = prec = left = sign = space = alt = zero = 0; + sz = NOTSPEC; + + /* Flags */ + for ( ; ; ) { + if (fc == '-') { + if (left) + break; + left = 1; + } else if (fc == '+') { + if (sign) + break; + sign = 1; + } else if (fc == ' ') { + if (space) + break; + space = 1; + } else if (fc == '#') { + if (alt) + break; + alt = 1; + } else if (fc == '0') { + if (zero) + break; + zero = 1; + } else { + break; + } + fc = *fp++; + } + + /* field width */ + if (isdigit(fc)) { + fwidth = 1; + do { fc = *fp++; } while (isdigit(fc)) ; + } else if (fc == '*') { + fwidth = 1; + fc = *fp++; + if ((tp = *ap++) == NULL) { + tofewarg(hte, call); + break; + } + n++; + if ((t1 = tp->t_tspec) != INT && (hflag || t1 != UINT)) + inconarg(hte, call, n); + } + + /* precision */ + if (fc == '.') { + fc = *fp++; + prec = 1; + if (isdigit(fc)) { + do { fc = *fp++; } while (isdigit(fc)); + } else if (fc == '*') { + fc = *fp++; + if ((tp = *ap++) == NULL) { + tofewarg(hte, call); + break; + } + n++; + if (tp->t_tspec != INT) + inconarg(hte, call, n); + } else { + badfmt(hte, call); + break; + } + } + + if (fc == 'h') { + sz = SHORT; + } else if (fc == 'l') { + sz = LONG; + } else if (fc == 'q') { + sz = QUAD; + } else if (fc == 'L') { + sz = LDOUBLE; + } + if (sz != NOTSPEC) + fc = *fp++; + + if (fc == '%') { + if (sz != NOTSPEC || left || sign || space || + alt || zero || prec || fwidth) { + badfmt(hte, call); + } + fc = *fp++; + continue; + } + + if (fc == '\0') { + badfmt(hte, call); + break; + } + + if ((tp = *ap++) == NULL) { + tofewarg(hte, call); + break; + } + n++; + if ((t1 = tp->t_tspec) == PTR) + t2 = tp->t_subt->t_tspec; + + if (fc == 'd' || fc == 'i') { + if (alt || sz == LDOUBLE) { + badfmt(hte, call); + break; + } + int_conv: + if (sz == LONG) { + if (t1 != LONG && (hflag || t1 != ULONG)) + inconarg(hte, call, n); + } else if (sz == QUAD) { + if (t1 != QUAD && (hflag || t1 != UQUAD)) + inconarg(hte, call, n); + } else { + /* + * SHORT is always promoted to INT, USHORT + * to INT or UINT. + */ + if (t1 != INT && (hflag || t1 != UINT)) + inconarg(hte, call, n); + } + } else if (fc == 'o' || fc == 'u' || fc == 'x' || fc == 'X') { + if ((alt && fc == 'u') || sz == LDOUBLE) + badfmt(hte, call); + uint_conv: + if (sz == LONG) { + if (t1 != ULONG && (hflag || t1 != LONG)) + inconarg(hte, call, n); + } else if (sz == QUAD) { + if (t1 != UQUAD && (hflag || t1 != QUAD)) + inconarg(hte, call, n); + } else if (sz == SHORT) { + /* USHORT was promoted to INT or UINT */ + if (t1 != UINT && t1 != INT) + inconarg(hte, call, n); + } else { + if (t1 != UINT && (hflag || t1 != INT)) + inconarg(hte, call, n); + } + } else if (fc == 'D' || fc == 'O' || fc == 'U') { + if ((alt && fc != 'O') || sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = LONG; + if (fc == 'D') { + goto int_conv; + } else { + goto uint_conv; + } + } else if (fc == 'f' || fc == 'e' || fc == 'E' || + fc == 'g' || fc == 'G') { + if (sz == NOTSPEC) + sz = DOUBLE; + if (sz != DOUBLE && sz != LDOUBLE) + badfmt(hte, call); + if (t1 != sz) + inconarg(hte, call, n); + } else if (fc == 'c') { + if (sz != NOTSPEC || alt || zero) + badfmt(hte, call); + if (t1 != INT) + inconarg(hte, call, n); + } else if (fc == 's') { + if (sz != NOTSPEC || alt || zero) + badfmt(hte, call); + if (t1 != PTR || + (t2 != CHAR && t2 != UCHAR && t2 != SCHAR)) { + inconarg(hte, call, n); + } + } else if (fc == 'p') { + if (fwidth || prec || sz != NOTSPEC || alt || zero) + badfmt(hte, call); + if (t1 != PTR || (hflag && t2 != VOID)) + inconarg(hte, call, n); + } else if (fc == 'n') { + if (fwidth || prec || alt || zero || sz == LDOUBLE) + badfmt(hte, call); + if (t1 != PTR) { + inconarg(hte, call, n); + } else if (sz == LONG) { + if (t2 != LONG && t2 != ULONG) + inconarg(hte, call, n); + } else if (sz == SHORT) { + if (t2 != SHORT && t2 != USHORT) + inconarg(hte, call, n); + } else { + if (t2 != INT && t2 != UINT) + inconarg(hte, call, n); + } + } else { + badfmt(hte, call); + break; + } + + fc = *fp++; + } +} + +/* + * Compare the types in the NULL-terminated array ap with the format + * string fmt. + */ +static void +scanflike(hte, call, n, fmt, ap) + hte_t *hte; + fcall_t *call; + int n; + const char *fmt; + type_t **ap; +{ + const char *fp; + int fc; + int noasgn, fwidth; + tspec_t sz, t1, t2; + type_t *tp; + + fp = fmt; + fc = *fp++; + + for ( ; ; ) { + if (fc == '\0') { + if (*ap != NULL) + tomanyarg(hte, call); + break; + } + if (fc != '%') { + badfmt(hte, call); + break; + } + fc = *fp++; + + noasgn = fwidth = 0; + sz = NOTSPEC; + + if (fc == '*') { + noasgn = 1; + fc = *fp++; + } + + if (isdigit(fc)) { + fwidth = 1; + do { fc = *fp++; } while (isdigit(fc)); + } + + if (fc == 'h') { + sz = SHORT; + } else if (fc == 'l') { + sz = LONG; + } else if (fc == 'q') { + sz = QUAD; + } else if (fc == 'L') { + sz = LDOUBLE; + } + if (sz != NOTSPEC) + fc = *fp++; + + if (fc == '%') { + if (sz != NOTSPEC || noasgn || fwidth) + badfmt(hte, call); + fc = *fp++; + continue; + } + + if (!noasgn) { + if ((tp = *ap++) == NULL) { + tofewarg(hte, call); + break; + } + n++; + if ((t1 = tp->t_tspec) == PTR) + t2 = tp->t_subt->t_tspec; + } + + if (fc == 'd' || fc == 'i' || fc == 'n') { + if (sz == LDOUBLE) + badfmt(hte, call); + if (sz != SHORT && sz != LONG && sz != QUAD) + sz = INT; + conv: + if (!noasgn) { + if (t1 != PTR) { + inconarg(hte, call, n); + } else if (t2 != styp(sz)) { + inconarg(hte, call, n); + } else if (hflag && t2 != sz) { + inconarg(hte, call, n); + } else if (tp->t_subt->t_const) { + inconarg(hte, call, n); + } + } + } else if (fc == 'o' || fc == 'u' || fc == 'x') { + if (sz == LDOUBLE) + badfmt(hte, call); + if (sz == SHORT) { + sz = USHORT; + } else if (sz == LONG) { + sz = ULONG; + } else if (sz == QUAD) { + sz = UQUAD; + } else { + sz = UINT; + } + goto conv; + } else if (fc == 'D') { + if (sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = LONG; + goto conv; + } else if (fc == 'O') { + if (sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = ULONG; + goto conv; + } else if (fc == 'X') { + /* + * XXX valid in ANSI C, but in NetBSD's libc imple- + * mented as "lx". Thats why it should be avoided. + */ + if (sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = ULONG; + goto conv; + } else if (fc == 'E') { + /* + * XXX valid in ANSI C, but in NetBSD's libc imple- + * mented as "lf". Thats why it should be avoided. + */ + if (sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = DOUBLE; + goto conv; + } else if (fc == 'F') { + /* XXX only for backward compatibility */ + if (sz != NOTSPEC || !tflag) + badfmt(hte, call); + sz = DOUBLE; + goto conv; + } else if (fc == 'G') { + /* + * XXX valid in ANSI C, but in NetBSD's libc not + * implemented + */ + if (sz != NOTSPEC && sz != LONG && sz != LDOUBLE) + badfmt(hte, call); + goto fconv; + } else if (fc == 'e' || fc == 'f' || fc == 'g') { + fconv: + if (sz == NOTSPEC) { + sz = FLOAT; + } else if (sz == LONG) { + sz = DOUBLE; + } else if (sz != LDOUBLE) { + badfmt(hte, call); + sz = FLOAT; + } + goto conv; + } else if (fc == 's' || fc == '[' || fc == 'c') { + if (sz != NOTSPEC) + badfmt(hte, call); + if (fc == '[') { + if ((fc = *fp++) == '-') { + badfmt(hte, call); + fc = *fp++; + } + if (fc != ']') { + badfmt(hte, call); + if (fc == '\0') + break; + } + } + if (!noasgn) { + if (t1 != PTR) { + inconarg(hte, call, n); + } else if (t2 != CHAR && t2 != UCHAR && + t2 != SCHAR) { + inconarg(hte, call, n); + } + } + } else if (fc == 'p') { + if (sz != NOTSPEC) + badfmt(hte, call); + if (!noasgn) { + if (t1 != PTR || t2 != PTR) { + inconarg(hte, call, n); + } else if (tp->t_subt->t_subt->t_tspec!=VOID) { + if (hflag) + inconarg(hte, call, n); + } + } + } else { + badfmt(hte, call); + break; + } + + fc = *fp++; + } +} + +static void +badfmt(hte, call) + hte_t *hte; + fcall_t *call; +{ + /* %s: malformed format string\t%s */ + msg(13, hte->h_name, mkpos(&call->f_pos)); +} + +static void +inconarg(hte, call, n) + hte_t *hte; + fcall_t *call; + int n; +{ + /* %s, arg %d inconsistent with format\t%s(%d) */ + msg(14, hte->h_name, n, mkpos(&call->f_pos)); +} + +static void +tofewarg(hte, call) + hte_t *hte; + fcall_t *call; +{ + /* %s: too few args for format \t%s */ + msg(15, hte->h_name, mkpos(&call->f_pos)); +} + +static void +tomanyarg(hte, call) + hte_t *hte; + fcall_t *call; +{ + /* %s: too many args for format \t%s */ + msg(16, hte->h_name, mkpos(&call->f_pos)); +} + + +/* + * Print warnings for return values which are used, but not returned, + * or return values which are always or sometimes ignored. + */ +static void +chkrvu(hte, def) + hte_t *hte; + sym_t *def; +{ + fcall_t *call; + int used, ignored; + + if (def == NULL) + /* don't know wheter or not the functions returns a value */ + return; + + if (hte->h_calls == NULL) + return; + + if (def->s_rval) { + /* function has return value */ + used = ignored = 0; + for (call = hte->h_calls; call != NULL; call = call->f_nxt) { + used |= call->f_rused; + ignored |= !call->f_rused && !call->f_rdisc; + } + /* + * XXX as soon as we are able to disable single warnings + * the following dependencies from hflag should be removed. + * but for now I do'nt want to be botherd by this warnings + * which are almost always useless. + */ + if (!used && ignored) { + if (hflag) + /* %s returns value which is always ignored */ + msg(8, hte->h_name); + } else if (used && ignored) { + if (hflag) + /* %s returns value which is sometimes ign. */ + msg(9, hte->h_name); + } + } else { + /* function has no return value */ + for (call = hte->h_calls; call != NULL; call = call->f_nxt) { + if (call->f_rused) + /* %s value is used( %s ), but none ret. */ + msg(10, hte->h_name, mkpos(&call->f_pos)); + } + } +} + +/* + * Print warnings for inconsistent argument declarations. + */ +static void +chkadecl(hte, def, decl) + hte_t *hte; + sym_t *def, *decl; +{ + /* LINTED (automatic hides external declaration: warn) */ + int osdef, eq, warn, n; + sym_t *sym1, *sym; + type_t **ap1, **ap2, *tp1, *tp2; + char *pos1; + const char *pos2; + + osdef = 0; + if (def != NULL) { + osdef = def->s_osdef; + sym1 = def; + } else if (decl != NULL && TP(decl->s_type)->t_proto) { + sym1 = decl; + } else { + return; + } + if (TP(sym1->s_type)->t_tspec != FUNC) + return; + + /* + * XXX Prototypes should also be compared with old style function + * declarations. + */ + + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (sym == sym1 || !TP(sym->s_type)->t_proto) + continue; + ap1 = TP(sym1->s_type)->t_args; + ap2 = TP(sym->s_type)->t_args; + n = 0; + while (*ap1 != NULL && *ap2 != NULL) { + warn = 0; + eq = eqtype(*ap1, *ap2, 1, osdef, 0, &warn); + if (!eq || warn) { + pos1 = xstrdup(mkpos(&sym1->s_pos)); + pos2 = mkpos(&sym->s_pos); + /* %s, arg %d declared inconsistently ... */ + msg(11, hte->h_name, n + 1, pos1, pos2); + free(pos1); + } + n++; + ap1++; + ap2++; + } + if (*ap1 == *ap2) { + tp1 = TP(sym1->s_type); + tp2 = TP(sym->s_type); + if (tp1->t_vararg == tp2->t_vararg) + continue; + if (tp2->t_vararg && + sym1->s_va && sym1->s_nva == n && !sflag) { + continue; + } + } + /* %s: variable # of args declared\t%s :: %s */ + pos1 = xstrdup(mkpos(&sym1->s_pos)); + msg(12, hte->h_name, pos1, mkpos(&sym->s_pos)); + free(pos1); + } +} + + +/* + * Check compatibility of two types. Returns 1 if types are compatible, + * otherwise 0. + * + * ignqual if set, ignore qualifiers of outhermost type; used for + * function arguments + * promote if set, promote left type before comparision; used for + * comparisions of arguments with parameters of old style + * definitions + * asgn left indirected type must have at least the same qualifiers + * like right indirected type (for assignments and function + * arguments) + * *warn set to 1 if an old style declaration was compared with + * an incompatible prototype declaration + */ +static int +eqtype(tp1, tp2, ignqual, promot, asgn, warn) + type_t *tp1, *tp2; + int ignqual, promot, asgn, *warn; +{ + tspec_t t, to; + int indir; + + to = NOTSPEC; + indir = 0; + + while (tp1 != NULL && tp2 != NULL) { + + t = tp1->t_tspec; + if (promot) { + if (t == FLOAT) { + t = DOUBLE; + } else if (t == CHAR || t == SCHAR) { + t = INT; + } else if (t == UCHAR) { + t = tflag ? UINT : INT; + } else if (t == SHORT) { + t = INT; + } else if (t == USHORT) { + /* CONSTCOND */ + t = INT_MAX < USHRT_MAX || tflag ? UINT : INT; + } + } + + if (asgn && to == PTR) { + if (indir == 1 && (t == VOID || tp2->t_tspec == VOID)) + return (1); + } + + if (t != tp2->t_tspec) { + /* + * Give pointer to types which differ only in + * signedness a chance if not sflag and not hflag. + */ + if (sflag || hflag || to != PTR) + return (0); + if (styp(t) != styp(tp2->t_tspec)) + return (0); + } + + if (tp1->t_isenum && tp2->t_isenum) { + if (tp1->t_istag && tp2->t_istag) { + return (tp1->t_tag == tp2->t_tag); + } else if (tp1->t_istynam && tp2->t_istynam) { + return (tp1->t_tynam == tp2->t_tynam); + } else { + return (0); + } + } + + /* + * XXX Handle combinations of enum and int if eflag is set. + * But note: enum and 0 should be allowed. + */ + + if (asgn && indir == 1) { + if (!tp1->t_const && tp2->t_const) + return (0); + if (!tp1->t_volatile && tp2->t_volatile) + return (0); + } else if (!ignqual && !tflag) { + if (tp1->t_const != tp2->t_const) + return (0); + if (tp1->t_const != tp2->t_const) + return (0); + } + + if (t == STRUCT || t == UNION) { + if (tp1->t_istag && tp2->t_istag) { + return (tp1->t_tag == tp2->t_tag); + } else if (tp1->t_istynam && tp2->t_istynam) { + return (tp1->t_tynam == tp2->t_tynam); + } else { + return (0); + } + } + + if (t == ARRAY && tp1->t_dim != tp2->t_dim) { + if (tp1->t_dim != 0 && tp2->t_dim != 0) + return (0); + } + + if (t == FUNC) { + if (tp1->t_proto && tp2->t_proto) { + if (!eqargs(tp1, tp2, warn)) + return (0); + } else if (tp1->t_proto) { + if (!mnoarg(tp1, warn)) + return (0); + } else if (tp2->t_proto) { + if (!mnoarg(tp2, warn)) + return (0); + } + } + + tp1 = tp1->t_subt; + tp2 = tp2->t_subt; + ignqual = promot = 0; + to = t; + indir++; + + } + + return (tp1 == tp2); +} + +/* + * Compares arguments of two prototypes + */ +static int +eqargs(tp1, tp2, warn) + type_t *tp1, *tp2; + int *warn; +{ + type_t **a1, **a2; + + if (tp1->t_vararg != tp2->t_vararg) + return (0); + + a1 = tp1->t_args; + a2 = tp2->t_args; + + while (*a1 != NULL && *a2 != NULL) { + + if (eqtype(*a1, *a2, 1, 0, 0, warn) == 0) + return (0); + + a1++; + a2++; + + } + + return (*a1 == *a2); +} + +/* + * mnoarg() (matches functions with no argument type information) + * returns 1 if all parameters of a prototype are compatible with + * and old style function declaration. + * This is the case if following conditions are met: + * 1. the prototype must have a fixed number of parameters + * 2. no parameter is of type float + * 3. no parameter is converted to another type if integer promotion + * is applied on it + */ +static int +mnoarg(tp, warn) + type_t *tp; + int *warn; +{ + type_t **arg; + tspec_t t; + + if (tp->t_vararg && warn != NULL) + *warn = 1; + for (arg = tp->t_args; *arg != NULL; arg++) { + if ((t = (*arg)->t_tspec) == FLOAT) + return (0); + if (t == CHAR || t == SCHAR || t == UCHAR) + return (0); + if (t == SHORT || t == USHORT) + return (0); + } + return (1); +} + diff --git a/usr.bin/xlint/lint2/emit2.c b/usr.bin/xlint/lint2/emit2.c new file mode 100644 index 0000000..82527b5 --- /dev/null +++ b/usr.bin/xlint/lint2/emit2.c @@ -0,0 +1,236 @@ +/* $NetBSD: emit2.c,v 1.2 1995/07/03 21:24:44 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: emit2.c,v 1.2 1995/07/03 21:24:44 cgd Exp $"; +#endif + +#include <err.h> + +#include "lint2.h" + +static void outtype __P((type_t *)); +static void outdef __P((hte_t *, sym_t *)); +static void dumpname __P((hte_t *)); + +/* + * Write type into the output buffer. + */ +static void +outtype(tp) + type_t *tp; +{ + int t, s, na; + tspec_t ts; + type_t **ap; + + while (tp != NULL) { + if ((ts = tp->t_tspec) == INT && tp->t_isenum) + ts = ENUM; + switch (ts) { + case CHAR: t = 'C'; s = '\0'; break; + case SCHAR: t = 'C'; s = 's'; break; + case UCHAR: t = 'C'; s = 'u'; break; + case SHORT: t = 'S'; s = '\0'; break; + case USHORT: t = 'S'; s = 'u'; break; + case INT: t = 'I'; s = '\0'; break; + case UINT: t = 'I'; s = 'u'; break; + case LONG: t = 'L'; s = '\0'; break; + case ULONG: t = 'L'; s = 'u'; break; + case QUAD: t = 'Q'; s = '\0'; break; + case UQUAD: t = 'Q'; s = 'u'; break; + case FLOAT: t = 'D'; s = 's'; break; + case DOUBLE: t = 'D'; s = '\0'; break; + case LDOUBLE: t = 'D'; s = 'l'; break; + case VOID: t = 'V'; s = '\0'; break; + case PTR: t = 'P'; s = '\0'; break; + case ARRAY: t = 'A'; s = '\0'; break; + case ENUM: t = 'T'; s = 'e'; break; + case STRUCT: t = 'T'; s = 's'; break; + case UNION: t = 'T'; s = 'u'; break; + case FUNC: + if (tp->t_args != NULL && !tp->t_proto) { + t = 'f'; + } else { + t = 'F'; + } + s = '\0'; + break; + default: + errx(1, "internal error: outtype() 1"); + } + if (tp->t_const) + outchar('c'); + if (tp->t_volatile) + outchar('v'); + if (s != '\0') + outchar(s); + outchar(t); + if (ts == ARRAY) { + outint(tp->t_dim); + } else if (ts == ENUM || ts == STRUCT || ts == UNION) { + if (tp->t_istag) { + outint(1); + outname(tp->t_tag->h_name); + } else if (tp->t_istynam) { + outint(2); + outname(tp->t_tynam->h_name); + } else { + outint(0); + } + } else if (ts == FUNC && tp->t_args != NULL) { + na = 0; + for (ap = tp->t_args; *ap != NULL; ap++) + na++; + if (tp->t_vararg) + na++; + outint(na); + for (ap = tp->t_args; *ap != NULL; ap++) + outtype(*ap); + if (tp->t_vararg) + outchar('E'); + } + tp = tp->t_subt; + } +} + +/* + * Write a definition. + */ +static void +outdef(hte, sym) + hte_t *hte; + sym_t *sym; +{ + /* reset output buffer */ + outclr(); + + /* line number in C source file */ + outint(0); + + /* this is a definition */ + outchar('d'); + + /* index of file where symbol was defined and line number of def. */ + outint(0); + outchar('.'); + outint(0); + + /* flags */ + if (sym->s_va) { + outchar('v'); /* varargs */ + outint(sym->s_nva); + } + if (sym->s_scfl) { + outchar('S'); /* scanflike */ + outint(sym->s_nscfl); + } + if (sym->s_prfl) { + outchar('P'); /* printflike */ + outint(sym->s_nprfl); + } + /* definition or tentative definition */ + outchar(sym->s_def == DEF ? 'd' : 't'); + if (TP(sym->s_type)->t_tspec == FUNC) { + if (sym->s_rval) + outchar('r'); /* fkt. has return value */ + if (sym->s_osdef) + outchar('o'); /* old style definition */ + } + outchar('u'); /* used (no warning if not used) */ + + /* name */ + outname(hte->h_name); + + /* type */ + outtype(TP(sym->s_type)); +} + +/* + * Write the first definition of a name into the lint library. + */ +static void +dumpname(hte) + hte_t *hte; +{ + sym_t *sym, *def; + + /* static and undefined symbols are not written */ + if (hte->h_static || !hte->h_def) + return; + + /* + * If there is a definition, write it. Otherwise write a tentative + * definition. This is neccessary because more than one tentative + * definition is allowed (except with sflag). + */ + def = NULL; + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (sym->s_def == DEF) { + def = sym; + break; + } + if (sym->s_def == TDEF && def == NULL) + def = sym; + } + if (def == NULL) + errx(1, "internal error: dumpname() %s", hte->h_name); + + outdef(hte, def); +} + +/* + * Write a new lint library. + */ +void +outlib(name) + const char *name; +{ + /* Open of output file and initialisation of the output buffer */ + outopen(name); + + /* write name of lint library */ + outsrc(name); + + /* name of lint lib has index 0 */ + outclr(); + outint(0); + outchar('s'); + outstrg(name); + + /* write all definitions with external linkage */ + forall(dumpname); + + /* close the output */ + outclose(); +} diff --git a/usr.bin/xlint/lint2/externs2.h b/usr.bin/xlint/lint2/externs2.h new file mode 100644 index 0000000..2e65e53 --- /dev/null +++ b/usr.bin/xlint/lint2/externs2.h @@ -0,0 +1,87 @@ +/* $NetBSD: externs2.h,v 1.2 1995/07/03 21:24:46 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * main.c + */ +extern int xflag; +extern int uflag; +extern int Cflag; +extern const char *libname; +extern int pflag; +extern int sflag; +extern int tflag; +extern int Hflag; +extern int hflag; +extern int Fflag; + + +/* + * hash.c + */ +extern void inithash __P((void)); +extern hte_t *hsearch __P((const char *, int)); +extern void forall __P((void (*)(hte_t *))); + +/* + * read.c + */ +extern const char **fnames; +extern type_t **tlst; + +extern void readfile __P((const char *)); +extern void mkstatic __P((hte_t *)); + +/* + * mem2.c + */ +extern void initmem __P((void)); +extern void *xalloc __P((size_t)); + +/* + * chk.c + */ +extern void inittyp __P((void)); +extern void mainused __P((void)); +extern void chkname __P((hte_t *)); + +/* + * msg.c + */ +extern void msg __P((int, ...)); +extern const char *mkpos __P((pos_t *)); + +/* + * emit2.c + */ +extern void outlib __P((const char *)); diff --git a/usr.bin/xlint/lint2/hash.c b/usr.bin/xlint/lint2/hash.c new file mode 100644 index 0000000..7901802 --- /dev/null +++ b/usr.bin/xlint/lint2/hash.c @@ -0,0 +1,123 @@ +/* $NetBSD: hash.c,v 1.2 1995/07/03 21:24:47 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: hash.c,v 1.2 1995/07/03 21:24:47 cgd Exp $"; +#endif + +#include <stddef.h> +#include <string.h> +#include <limits.h> + +#include "lint2.h" + +/* pointer to hash table, initialized in inithash() */ +static hte_t **htab; + +static int hash __P((const char *)); + +/* + * Initialize hash table. + */ +void +inithash() +{ + htab = xcalloc(HSHSIZ2, sizeof (hte_t *)); +} + +/* + * Compute hash value from a string. + */ +static int +hash(s) + const char *s; +{ + u_int v; + const u_char *us; + + v = 0; + for (us = (const u_char *)s; *us != '\0'; us++) { + v = (v << sizeof (v)) + *us; + v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v)); + } + return (v % HSHSIZ2); +} + +/* + * Look for a hash table entry. If no hash table entry for the + * given name exists and mknew is set, create a new one. + */ +hte_t * +hsearch(s, mknew) + const char *s; + int mknew; +{ + int h; + hte_t *hte; + + h = hash(s); + for (hte = htab[h]; hte != NULL; hte = hte->h_link) { + if (strcmp(hte->h_name, s) == 0) + break; + } + + if (hte != NULL || !mknew) + return (hte); + + /* create a new hte */ + hte = xalloc(sizeof (hte_t)); + hte->h_name = xstrdup(s); + hte->h_lsym = &hte->h_syms; + hte->h_lcall = &hte->h_calls; + hte->h_lusym = &hte->h_usyms; + hte->h_link = htab[h]; + htab[h] = hte; + + return (hte); +} + +/* + * Call function f for each name in the hash table. + */ +void +forall(f) + void (*f) __P((hte_t *)); +{ + int i; + hte_t *hte; + + for (i = 0; i < HSHSIZ2; i++) { + for (hte = htab[i]; hte != NULL; hte = hte->h_link) + (*f)(hte); + } +} diff --git a/usr.bin/xlint/lint2/lint2.h b/usr.bin/xlint/lint2/lint2.h new file mode 100644 index 0000000..0ade110 --- /dev/null +++ b/usr.bin/xlint/lint2/lint2.h @@ -0,0 +1,177 @@ +/* $NetBSD: lint2.h,v 1.2 1995/07/03 21:24:49 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "lint.h" + +/* + * Types are described by structures of type type_t. + */ +typedef struct type { + tspec_t t_tspec; /* type specifier */ + u_int t_const : 1; /* constant */ + u_int t_volatile : 1; /* volatile */ + u_int t_vararg : 1; /* function has variable number of arguments */ + u_int t_isenum : 1; /* enum type */ + u_int t_proto : 1; /* this is a prototype */ + u_int t_istag : 1; /* tag with _t_tag valid */ + u_int t_istynam : 1; /* tag with _t_tynam valid */ + union { + int _t_dim; /* if the type is an ARRAY than this + is the dimension of the array. */ + struct hte *_t_tag; /* hash table entry of tag if + t_isenum, STRUCT or UNION */ + struct hte *_t_tynam; /* hash table entry of typename if + t_isenum, STRUCT or UNION */ + struct type **_t_args; /* list of argument types if this + is a prototype */ + } t_u; + struct type *t_subt; /* indirected type (array element, pointed to + type, type of return value) */ +} type_t; + +#define t_dim t_u._t_dim +#define t_tag t_u._t_tag +#define t_tynam t_u._t_tynam +#define t_args t_u._t_args + +/* + * argument information + * + * Such a structure is created for each argument of a function call + * which is an integer constant or a constant string. + */ +typedef struct arginf { + int a_num; /* # of argument (1..) */ + u_int a_zero : 1; /* argument is 0 */ + u_int a_pcon : 1; /* msb of argument is not set */ + u_int a_ncon : 1; /* msb of argument is set */ + u_int a_fmt : 1; /* a_fstrg points to format string */ + char *a_fstrg; /* format string */ + struct arginf *a_nxt; /* information for next const. argument */ +} arginf_t; + +/* + * Keeps information about position in source file. + */ +typedef struct { + u_short p_src; /* index of name of translation unit + (the name which was specified at the + command line) */ + u_short p_line; /* line number in p_src */ + u_short p_isrc; /* index of (included) file */ + u_short p_iline; /* line number in p_iline */ +} pos_t; + +/* + * Used for definitions and declarations + * + * To save memory, variable sized structures are used. If + * all s_va, s_prfl and s_scfl are not set, the memory allocated + * for a symbol is only large enough to keep the first member of + * struct sym, s_s. + */ +typedef struct sym { + struct { + pos_t s_pos; /* pos of def./decl. */ +#ifndef lint + u_int s_def : 3; /* DECL, TDEF or DEF */ +#else + def_t s_def; +#endif + u_int s_rval : 1; /* function has return value */ + u_int s_osdef : 1; /* old style function definition */ + u_int s_static : 1; /* symbol is static */ + u_int s_va : 1; /* check only first s_nva arguments */ + u_int s_prfl : 1; /* printflike */ + u_int s_scfl : 1; /* scanflike */ + u_short s_type; /* type */ + struct sym *s_nxt; /* next symbol with same name */ + } s_s; + short s_nva; + short s_nprfl; + short s_nscfl; +} sym_t; + +#define s_pos s_s.s_pos +#define s_rval s_s.s_rval +#define s_osdef s_s.s_osdef +#define s_static s_s.s_static +#define s_def s_s.s_def +#define s_va s_s.s_va +#define s_prfl s_s.s_prfl +#define s_scfl s_s.s_scfl +#define s_type s_s.s_type +#define s_nxt s_s.s_nxt + +/* + * Used to store informations about function calls. + */ +typedef struct fcall { + pos_t f_pos; /* position of call */ + u_int f_rused : 1; /* return value used */ + u_int f_rdisc : 1; /* return value discarded (casted to void) */ + u_short f_type; /* types of expected return value and args */ + arginf_t *f_args; /* information about constant arguments */ + struct fcall *f_nxt; /* next call of same function */ +} fcall_t; + +/* + * Used to store information about usage of symbols other + * than for function calls. + */ +typedef struct usym { + pos_t u_pos; /* position */ + struct usym *u_nxt; /* next usage */ +} usym_t; + +/* + * hash table entry + */ +typedef struct hte { + const char *h_name; /* name */ + u_int h_used : 1; /* symbol is used */ + u_int h_def : 1; /* symbol is defined */ + u_int h_static : 1; /* static symbol */ + sym_t *h_syms; /* declarations and definitions */ + sym_t **h_lsym; /* points to s_nxt of last decl./def. */ + fcall_t *h_calls; /* function calls */ + fcall_t **h_lcall; /* points to f_nxt of last call */ + usym_t *h_usyms; /* usage info */ + usym_t **h_lusym; /* points to u_nxt of last usage info */ + struct hte *h_link; /* next hte with same hash function */ +} hte_t; + +/* maps type indices into pointers to type structs */ +#define TP(idx) (tlst[idx]) + +#include "externs2.h" diff --git a/usr.bin/xlint/lint2/main2.c b/usr.bin/xlint/lint2/main2.c new file mode 100644 index 0000000..171344a --- /dev/null +++ b/usr.bin/xlint/lint2/main2.c @@ -0,0 +1,190 @@ +/* $NetBSD: main2.c,v 1.2 1995/07/03 21:24:53 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: main2.c,v 1.2 1995/07/03 21:24:53 cgd Exp $"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "lint2.h" + +/* warnings for symbols which are declared but not defined or used */ +int xflag; + +/* + * warnings for symbols which are used and not defined or defined + * and not used + */ +int uflag = 1; + +/* Create a lint library in the current directory with name libname. */ +int Cflag; +const char *libname; + +int pflag; + +/* + * warnings for (tentative) definitions of the same name in more then + * one translation unit + */ +int sflag; + +int tflag; + +/* + * If a complaint stems from a included file, print the name of the included + * file instead of the name spezified at the command line followed by '?' + */ +int Hflag; + +int hflag; + +/* Print full path names, not only the last component */ +int Fflag; + +/* + * List of libraries (from -l flag). These libraries are read after all + * other input files has been read and, for Cflag, after the new lint library + * has been written. + */ +const char **libs; + +static void usage __P((void)); + + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c, i; + size_t len; + char *lname; + + libs = xcalloc(1, sizeof (char *)); + + opterr = 0; + while ((c = getopt(argc, argv, "hpstxuC:HFl:")) != -1) { + switch (c) { + case 's': + sflag = 1; + break; + case 't': + tflag = 1; + break; + case 'u': + uflag = 0; + break; + case 'x': + xflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'C': + len = strlen(optarg); + lname = xmalloc(len + 10); + (void)sprintf(lname, "llib-l%s.ln", optarg); + libname = lname; + Cflag = 1; + uflag = 0; + break; + case 'H': + Hflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'F': + Fflag = 1; + break; + case 'l': + for (i = 0; libs[i] != NULL; i++) ; + libs = xrealloc(libs, (i + 2) * sizeof (char *)); + libs[i] = xstrdup(optarg); + libs[i + 1] = NULL; + break; + case '?': + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + initmem(); + + /* initialize hash table */ + inithash(); + + inittyp(); + + for (i = 0; i < argc; i++) + readfile(argv[i]); + + /* write the lint library */ + if (Cflag) { + forall(mkstatic); + outlib(libname); + } + + /* read additional libraries */ + for (i = 0; libs[i] != NULL; i++) + readfile(libs[i]); + + forall(mkstatic); + + mainused(); + + /* perform all tests */ + forall(chkname); + + exit(0); + /* NOTREACHED */ +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: lint2 -hpstxuHF -Clib -l lib ... src1 ...\n"); + exit(1); +} + diff --git a/usr.bin/xlint/lint2/mem2.c b/usr.bin/xlint/lint2/mem2.c new file mode 100644 index 0000000..06d7491 --- /dev/null +++ b/usr.bin/xlint/lint2/mem2.c @@ -0,0 +1,97 @@ +/* $NetBSD: mem2.c,v 1.3 1995/10/02 17:27:11 jpo Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: mem2.c,v 1.3 1995/10/02 17:27:11 jpo Exp $"; +#endif + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/mman.h> +#include <unistd.h> +#include <string.h> +#include <err.h> + +#include "lint2.h" + +/* length of new allocated memory blocks */ +static size_t mblklen; + +/* offset of next free byte in mbuf */ +static size_t nxtfree; + +/* current buffer to server memory requests from */ +static void *mbuf; + +void +initmem() +{ + int pgsz; + + pgsz = getpagesize(); + mblklen = ((MBLKSIZ + pgsz - 1) / pgsz) * pgsz; + + nxtfree = mblklen; +} + +/* + * Allocate memory in large chunks to avoid space and time overhead of + * malloc(). This is possible because memory allocated by xalloc() + * need never to be freed. + */ +void * +xalloc(sz) + size_t sz; +{ + void *ptr; + int prot, flags; + + sz = ALIGN(sz); + if (nxtfree + sz > mblklen) { + /* use mmap() instead of malloc() to avoid malloc overhead. */ + prot = PROT_READ | PROT_WRITE; + flags = MAP_ANON | MAP_PRIVATE; + mbuf = mmap(NULL, mblklen, prot, flags, -1, (off_t)0); + if (mbuf == (void *)-1) + err(1, "can't map memory"); + if (ALIGN((u_long)mbuf) != (u_long)mbuf) + errx(1, "mapped address is not aligned"); + (void)memset(mbuf, 0, mblklen); + nxtfree = 0; + } + + ptr = (char *)mbuf + nxtfree; + nxtfree += sz; + + return (ptr); +} diff --git a/usr.bin/xlint/lint2/msg.c b/usr.bin/xlint/lint2/msg.c new file mode 100644 index 0000000..c55ba96 --- /dev/null +++ b/usr.bin/xlint/lint2/msg.c @@ -0,0 +1,157 @@ +/* $NetBSD: msg.c,v 1.2 1995/07/03 21:24:56 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: msg.c,v 1.2 1995/07/03 21:24:56 cgd Exp $"; +#endif + +#include <string.h> + +#include <stdio.h> +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +#include "lint2.h" + + +static const char *msgs[] = { + "%s used( %s ), but not defined", /* 0 */ + "%s defined( %s ), but never used", /* 1 */ + "%s declared( %s ), but never used or defined", /* 2 */ + "%s multiply defined \t%s :: %s", /* 3 */ + "%s value used inconsistently \t%s :: %s", /* 4 */ + "%s value declared inconsistently \t%s :: %s", /* 5 */ + "%s, arg %d used inconsistently \t%s :: %s", /* 6 */ + "%s: variable # of args \t%s :: %s", /* 7 */ + "%s returns value which is always ignored", /* 8 */ + "%s returns value which is sometimes ignored", /* 9 */ + "%s value is used( %s ), but none returned", /* 10 */ + "%s, arg %d declared inconsistently \t%s :: %s", /* 11 */ + "%s: variable # of args declared \t%s :: %s", /* 12 */ + "%s: malformed format string \t%s", /* 13 */ + "%s, arg %d inconsistent with format \t%s", /* 14 */ + "%s: too few args for format \t%s", /* 15 */ + "%s: too many args for format \t%s", /* 16 */ + "%s function value must be declared before use \t%s :: %s",/* 17 */ +}; + +static const char *basename __P((const char *)); + +#ifdef __STDC__ +void +msg(int n, ...) +{ +#else +void +msg(va_alist) + va_dcl + int n; +{ +#endif + va_list ap; + +#ifdef __STDC__ + va_start(ap, n); +#else + va_start(ap); + n = va_arg(ap, int); +#endif + + (void)vprintf(msgs[n], ap); + (void)printf("\n"); + + va_end(ap); +} + +/* + * Return a pointer to the last component of a path. + */ +static const char * +basename(path) + const char *path; +{ + const char *cp, *cp1, *cp2; + + if (Fflag) + return (path); + + cp = cp1 = cp2 = path; + while (*cp != '\0') { + if (*cp++ == '/') { + cp2 = cp1; + cp1 = cp; + } + } + return (*cp1 == '\0' ? cp2 : cp1); +} + +/* + * Create a string which describes a position in a source file. + */ +const char * +mkpos(posp) + pos_t *posp; +{ + size_t len; + const char *fn; + static char *buf; + static size_t blen = 0; + int qm, src, line; + + if (Hflag && posp->p_src != posp->p_isrc) { + src = posp->p_isrc; + line = posp->p_iline; + } else { + src = posp->p_src; + line = posp->p_line; + } + qm = !Hflag && posp->p_src != posp->p_isrc; + + len = strlen(fn = basename(fnames[src])); + len += 3 * sizeof (u_short) + 4; + + if (len > blen) + buf = xrealloc(buf, blen = len); + if (line != 0) { + (void)sprintf(buf, "%s%s(%hu)", + fn, qm ? "?" : "", line); + } else { + (void)sprintf(buf, "%s", fn); + } + + return (buf); +} + diff --git a/usr.bin/xlint/lint2/read.c b/usr.bin/xlint/lint2/read.c new file mode 100644 index 0000000..daffe1e --- /dev/null +++ b/usr.bin/xlint/lint2/read.c @@ -0,0 +1,1133 @@ +/* $NetBSD: read.c,v 1.2 1995/07/03 21:24:59 cgd Exp $ */ + +/* + * Copyright (c) 1994, 1995 Jochen Pohl + * All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jochen Pohl for + * The NetBSD Project. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef lint +static char rcsid[] = "$NetBSD: read.c,v 1.2 1995/07/03 21:24:59 cgd Exp $"; +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> +#include <err.h> + +#include "lint2.h" + + +/* index of current (included) source file */ +static int srcfile; + +/* + * The array pointed to by inpfns maps the file name indices of input files + * to the file name indices used in lint2 + */ +static short *inpfns; +static size_t ninpfns; + +/* + * The array pointed to by *fnames maps file name indizes to file names. + * Indices of type short are used instead of pointers to save memory. + */ +const char **fnames; +static size_t nfnames; + +/* + * Types are shared (to save memory for the types itself) and accessed + * via indices (to save memory for references to types (indices are short)). + * To share types, a equal type must be located fast. This is done by a + * hash table. Access by indices is done via an array of pointers to the + * types. + */ +typedef struct thtab { + const char *th_name; + u_short th_idx; + struct thtab *th_nxt; +} thtab_t; +static thtab_t **thtab; /* hash table */ +type_t **tlst; /* array for indexed access */ +static size_t tlstlen; /* length of tlst */ + +/* index of current C source file (as spezified at the command line) */ +static int csrcfile; + + +static void inperr __P((void)); +static void setsrc __P((const char *)); +static void setfnid __P((int, const char *)); +static void funccall __P((pos_t *, const char *)); +static void decldef __P((pos_t *, const char *)); +static void usedsym __P((pos_t *, const char *)); +static u_short inptype __P((const char *, const char **)); +static int gettlen __P((const char *, const char **)); +static u_short findtype __P((const char *, size_t, int)); +static u_short storetyp __P((type_t *, const char *, size_t, int)); +static int thash __P((const char *, size_t)); +static char *inpqstrg __P((const char *, const char **)); +static const char *inpname __P((const char *, const char **)); +static int getfnidx __P((const char *)); + +void +readfile(name) + const char *name; +{ + FILE *inp; + size_t len; + const char *cp; + char *line, *eptr, rt; + int cline, isrc, iline; + pos_t pos; + + if (inpfns == NULL) + inpfns = xcalloc(ninpfns = 128, sizeof (short)); + if (fnames == NULL) + fnames = xcalloc(nfnames = 256, sizeof (char *)); + if (tlstlen == 0) + tlst = xcalloc(tlstlen = 256, sizeof (type_t *)); + if (thtab == NULL) + thtab = xcalloc(THSHSIZ2, sizeof (thtab_t)); + + srcfile = getfnidx(name); + + if ((inp = fopen(name, "r")) == NULL) + err(1, "cannot open %s", name); + + while ((line = fgetln(inp, &len)) != NULL) { + + if (len == 0 || line[len - 1] != '\n') + inperr(); + line[len - 1] = '\0'; + cp = line; + + /* line number in csrcfile */ + cline = (int)strtol(cp, &eptr, 10); + if (cp == eptr) { + cline = -1; + } else { + cp = eptr; + } + + /* record type */ + if (*cp != '\0') { + rt = *cp++; + } else { + inperr(); + } + + if (rt == 'S') { + setsrc(cp); + continue; + } else if (rt == 's') { + setfnid(cline, cp); + continue; + } + + /* + * Index of (included) source file. If this index is + * different from csrcfile, it refers to an included + * file. + */ + isrc = (int)strtol(cp, &eptr, 10); + if (cp == eptr) + inperr(); + cp = eptr; + isrc = inpfns[isrc]; + + /* line number in isrc */ + if (*cp++ != '.') + inperr(); + iline = (int)strtol(cp, &eptr, 10); + if (cp == eptr) + inperr(); + cp = eptr; + + pos.p_src = (u_short)csrcfile; + pos.p_line = (u_short)cline; + pos.p_isrc = (u_short)isrc; + pos.p_iline = (u_short)iline; + + /* process rest of this record */ + switch (rt) { + case 'c': + funccall(&pos, cp); + break; + case 'd': + decldef(&pos, cp); + break; + case 'u': + usedsym(&pos, cp); + break; + default: + inperr(); + } + + } + + if (ferror(inp)) + err(1, "read error on %s", name); + + (void)fclose(inp); +} + + +static void +inperr() +{ + errx(1, "input file error: %s", fnames[srcfile]); +} + +/* + * Set the name of the C source file of the .ln file which is + * currently read. + */ +static void +setsrc(cp) + const char *cp; +{ + csrcfile = getfnidx(cp); +} + +/* + * setfnid() gets as input an index as used in an input file and the + * associated file name. If neccessary, it creates a new lint2 file + * name index for this file name and creates the mapping of the index + * as used in the input file to the index used in lint2. + */ +static void +setfnid(fid, cp) + int fid; + const char *cp; +{ + if (fid == -1) + inperr(); + + if (fid >= ninpfns) { + inpfns = xrealloc(inpfns, (ninpfns * 2) * sizeof (short)); + (void)memset(inpfns + ninpfns, 0, ninpfns * sizeof (short)); + ninpfns *= 2; + } + /* + * Should always be true because indices written in the output + * file by lint1 are always the previous index + 1. + */ + if (fid >= ninpfns) + errx(1, "internal error: setfnid()"); + inpfns[fid] = (u_short)getfnidx(cp); +} + +/* + * Process a function call record (c-record). + */ +static void +funccall(posp, cp) + pos_t *posp; + const char *cp; +{ + arginf_t *ai, **lai; + char c, *eptr; + int rused, rdisc; + hte_t *hte; + fcall_t *fcall; + + fcall = xalloc(sizeof (fcall_t)); + STRUCT_ASSIGN(fcall->f_pos, *posp); + + /* read flags */ + rused = rdisc = 0; + lai = &fcall->f_args; + while ((c = *cp) == 'u' || c == 'i' || c == 'd' || + c == 'z' || c == 'p' || c == 'n' || c == 's') { + cp++; + switch (c) { + case 'u': + if (rused || rdisc) + inperr(); + rused = 1; + break; + case 'i': + if (rused || rdisc) + inperr(); + break; + case 'd': + if (rused || rdisc) + inperr(); + rdisc = 1; + break; + case 'z': + case 'p': + case 'n': + case 's': + ai = xalloc(sizeof (arginf_t)); + ai->a_num = (int)strtol(cp, &eptr, 10); + if (cp == eptr) + inperr(); + cp = eptr; + if (c == 'z') { + ai->a_pcon = ai->a_zero = 1; + } else if (c == 'p') { + ai->a_pcon = 1; + } else if (c == 'n') { + ai->a_ncon = 1; + } else { + ai->a_fmt = 1; + ai->a_fstrg = inpqstrg(cp, &cp); + } + *lai = ai; + lai = &ai->a_nxt; + break; + } + } + fcall->f_rused = rused; + fcall->f_rdisc = rdisc; + + /* read name of function */ + hte = hsearch(inpname(cp, &cp), 1); + hte->h_used = 1; + + fcall->f_type = inptype(cp, &cp); + + *hte->h_lcall = fcall; + hte->h_lcall = &fcall->f_nxt; + + if (*cp != '\0') + inperr(); +} + +/* + * Process a declaration or definition (d-record). + */ +static void +decldef(posp, cp) + pos_t *posp; + const char *cp; +{ + sym_t *symp, sym; + char c, *ep; + int used; + hte_t *hte; + + (void)memset(&sym, 0, sizeof (sym)); + STRUCT_ASSIGN(sym.s_pos, *posp); + sym.s_def = NODECL; + + used = 0; + + while ((c = *cp) == 't' || c == 'd' || c == 'e' || c == 'u' || + c == 'r' || c == 'o' || c == 's' || c == 'v' || + c == 'P' || c == 'S') { + cp++; + switch (c) { + case 't': + if (sym.s_def != NODECL) + inperr(); + sym.s_def = TDEF; + break; + case 'd': + if (sym.s_def != NODECL) + inperr(); + sym.s_def = DEF; + break; + case 'e': + if (sym.s_def != NODECL) + inperr(); + sym.s_def = DECL; + break; + case 'u': + if (used) + inperr(); + used = 1; + break; + case 'r': + if (sym.s_rval) + inperr(); + sym.s_rval = 1; + break; + case 'o': + if (sym.s_osdef) + inperr(); + sym.s_osdef = 1; + break; + case 's': + if (sym.s_static) + inperr(); + sym.s_static = 1; + break; + case 'v': + if (sym.s_va) + inperr(); + sym.s_va = 1; + sym.s_nva = (short)strtol(cp, &ep, 10); + if (cp == ep) + inperr(); + cp = ep; + break; + case 'P': + if (sym.s_prfl) + inperr(); + sym.s_prfl = 1; + sym.s_nprfl = (short)strtol(cp, &ep, 10); + if (cp == ep) + inperr(); + cp = ep; + break; + case 'S': + if (sym.s_scfl) + inperr(); + sym.s_scfl = 1; + sym.s_nscfl = (short)strtol(cp, &ep, 10); + if (cp == ep) + inperr(); + cp = ep; + break; + } + } + + /* read symbol name */ + hte = hsearch(inpname(cp, &cp), 1); + hte->h_used |= used; + if (sym.s_def == DEF || sym.s_def == TDEF) + hte->h_def = 1; + + sym.s_type = inptype(cp, &cp); + + /* + * Allocate memory for this symbol only if it was not already + * declared or tentatively defined at the same location with + * the same type. Works only for symbols with external linkage, + * because static symbols, tentatively defined at the same location + * but in different translation units are really different symbols. + */ + for (symp = hte->h_syms; symp != NULL; symp = symp->s_nxt) { + if (symp->s_pos.p_isrc == sym.s_pos.p_isrc && + symp->s_pos.p_iline == sym.s_pos.p_iline && + symp->s_type == sym.s_type && + ((symp->s_def == DECL && sym.s_def == DECL) || + (!sflag && symp->s_def == TDEF && sym.s_def == TDEF)) && + !symp->s_static && !sym.s_static) { + break; + } + } + + if (symp == NULL) { + /* allocsym reserviert keinen Platz fuer s_nva */ + if (sym.s_va || sym.s_prfl || sym.s_scfl) { + symp = xalloc(sizeof (sym_t)); + STRUCT_ASSIGN(*symp, sym); + } else { + symp = xalloc(sizeof (symp->s_s)); + STRUCT_ASSIGN(symp->s_s, sym.s_s); + } + *hte->h_lsym = symp; + hte->h_lsym = &symp->s_nxt; + } + + if (*cp != '\0') + inperr(); +} + +/* + * Read an u-record (emited by lint1 if a symbol was used). + */ +static void +usedsym(posp, cp) + pos_t *posp; + const char *cp; +{ + usym_t *usym; + hte_t *hte; + + usym = xalloc(sizeof (usym_t)); + STRUCT_ASSIGN(usym->u_pos, *posp); + + /* needed as delimiter between two numbers */ + if (*cp++ != 'x') + inperr(); + + hte = hsearch(inpname(cp, &cp), 1); + hte->h_used = 1; + + *hte->h_lusym = usym; + hte->h_lusym = &usym->u_nxt; +} + +/* + * Read a type and return the index of this type. + */ +static u_short +inptype(cp, epp) + const char *cp, **epp; +{ + char c, s, *eptr; + const char *ep; + type_t *tp; + int narg, i, osdef; + size_t tlen; + u_short tidx; + int h; + + /* If we have this type already, return it's index. */ + tlen = gettlen(cp, &ep); + h = thash(cp, tlen); + if ((tidx = findtype(cp, tlen, h)) != 0) { + *epp = ep; + return (tidx); + } + + /* No, we must create a new type. */ + tp = xalloc(sizeof (type_t)); + + tidx = storetyp(tp, cp, tlen, h); + + c = *cp++; + + while (c == 'c' || c == 'v') { + if (c == 'c') { + tp->t_const = 1; + } else { + tp->t_volatile = 1; + } + c = *cp++; + } + + if (c == 's' || c == 'u' || c == 'l' || c == 'e') { + s = c; + c = *cp++; + } else { + s = '\0'; + } + + switch (c) { + case 'C': + tp->t_tspec = s == 's' ? SCHAR : (s == 'u' ? UCHAR : CHAR); + break; + case 'S': + tp->t_tspec = s == 'u' ? USHORT : SHORT; + break; + case 'I': + tp->t_tspec = s == 'u' ? UINT : INT; + break; + case 'L': + tp->t_tspec = s == 'u' ? ULONG : LONG; + break; + case 'Q': + tp->t_tspec = s == 'u' ? UQUAD : QUAD; + break; + case 'D': + tp->t_tspec = s == 's' ? FLOAT : (s == 'l' ? LDOUBLE : DOUBLE); + break; + case 'V': + tp->t_tspec = VOID; + break; + case 'P': + tp->t_tspec = PTR; + break; + case 'A': + tp->t_tspec = ARRAY; + break; + case 'F': + case 'f': + osdef = c == 'f'; + tp->t_tspec = FUNC; + break; + case 'T': + tp->t_tspec = s == 'e' ? ENUM : (s == 's' ? STRUCT : UNION); + break; + } + + switch (tp->t_tspec) { + case ARRAY: + tp->t_dim = (int)strtol(cp, &eptr, 10); + cp = eptr; + tp->t_subt = TP(inptype(cp, &cp)); + break; + case PTR: + tp->t_subt = TP(inptype(cp, &cp)); + break; + case FUNC: + c = *cp; + if (isdigit((u_char)c)) { + if (!osdef) + tp->t_proto = 1; + narg = (int)strtol(cp, &eptr, 10); + cp = eptr; + tp->t_args = xcalloc((size_t)(narg + 1), + sizeof (type_t *)); + for (i = 0; i < narg; i++) { + if (i == narg - 1 && *cp == 'E') { + tp->t_vararg = 1; + cp++; + } else { + tp->t_args[i] = TP(inptype(cp, &cp)); + } + } + } + tp->t_subt = TP(inptype(cp, &cp)); + break; + case ENUM: + tp->t_tspec = INT; + tp->t_isenum = 1; + /* FALLTHROUGH */ + case STRUCT: + case UNION: + switch (*cp++) { + case '0': + break; + case '1': + tp->t_istag = 1; + tp->t_tag = hsearch(inpname(cp, &cp), 1); + break; + case '2': + tp->t_istynam = 1; + tp->t_tynam = hsearch(inpname(cp, &cp), 1); + break; + } + break; + /* LINTED (enumeration value(s) not handled in switch) */ + } + + *epp = cp; + return (tidx); +} + +/* + * Get the length of a type string. + */ +static int +gettlen(cp, epp) + const char *cp, **epp; +{ + const char *cp1; + char c, s, *eptr; + tspec_t t; + int narg, i, cm, vm; + + cp1 = cp; + + c = *cp++; + + cm = vm = 0; + + while (c == 'c' || c == 'v') { + if (c == 'c') { + if (cm) + inperr(); + cm = 1; + } else { + if (vm) + inperr(); + vm = 1; + } + c = *cp++; + } + + if (c == 's' || c == 'u' || c == 'l' || c == 'e') { + s = c; + c = *cp++; + } else { + s = '\0'; + } + + t = NOTSPEC; + + switch (c) { + case 'C': + if (s == 's') { + t = SCHAR; + } else if (s == 'u') { + t = UCHAR; + } else if (s == '\0') { + t = CHAR; + } + break; + case 'S': + if (s == 'u') { + t = USHORT; + } else if (s == '\0') { + t = SHORT; + } + break; + case 'I': + if (s == 'u') { + t = UINT; + } else if (s == '\0') { + t = INT; + } + break; + case 'L': + if (s == 'u') { + t = ULONG; + } else if (s == '\0') { + t = LONG; + } + break; + case 'Q': + if (s == 'u') { + t = UQUAD; + } else if (s == '\0') { + t = QUAD; + } + break; + case 'D': + if (s == 's') { + t = FLOAT; + } else if (s == 'l') { + t = LDOUBLE; + } else if (s == '\0') { + t = DOUBLE; + } + break; + case 'V': + if (s == '\0') + t = VOID; + break; + case 'P': + if (s == '\0') + t = PTR; + break; + case 'A': + if (s == '\0') + t = ARRAY; + break; + case 'F': + case 'f': + if (s == '\0') + t = FUNC; + break; + case 'T': + if (s == 'e') { + t = ENUM; + } else if (s == 's') { + t = STRUCT; + } else if (s == 'u') { + t = UNION; + } + break; + default: + inperr(); + } + + if (t == NOTSPEC) + inperr(); + + switch (t) { + case ARRAY: + (void)strtol(cp, &eptr, 10); + if (cp == eptr) + inperr(); + cp = eptr; + (void)gettlen(cp, &cp); + break; + case PTR: + (void)gettlen(cp, &cp); + break; + case FUNC: + c = *cp; + if (isdigit((u_char)c)) { + narg = (int)strtol(cp, &eptr, 10); + cp = eptr; + for (i = 0; i < narg; i++) { + if (i == narg - 1 && *cp == 'E') { + cp++; + } else { + (void)gettlen(cp, &cp); + } + } + } + (void)gettlen(cp, &cp); + break; + case ENUM: + case STRUCT: + case UNION: + switch (*cp++) { + case '0': + break; + case '1': + (void)inpname(cp, &cp); + break; + case '2': + (void)inpname(cp, &cp); + break; + default: + inperr(); + } + break; + /* LINTED (enumeration value(s) not handled in switch) */ + } + + *epp = cp; + return (cp - cp1); +} + +/* + * Search a type by it's type string. + */ +static u_short +findtype(cp, len, h) + const char *cp; + size_t len; + int h; +{ + thtab_t *thte; + + for (thte = thtab[h]; thte != NULL; thte = thte->th_nxt) { + if (strncmp(thte->th_name, cp, len) != 0) + continue; + if (thte->th_name[len] == '\0') + return (thte->th_idx); + } + + return (0); +} + +/* + * Store a type and it's type string so we can later share this type + * if we read the same type string from the input file. + */ +static u_short +storetyp(tp, cp, len, h) + type_t *tp; + const char *cp; + size_t len; + int h; +{ + /* 0 ist reserved */ + static u_int tidx = 1; + thtab_t *thte; + char *name; + + if (tidx >= USHRT_MAX) + errx(1, "sorry, too many types"); + + if (tidx == tlstlen - 1) { + tlst = xrealloc(tlst, (tlstlen * 2) * sizeof (type_t *)); + (void)memset(tlst + tlstlen, 0, tlstlen * sizeof (type_t *)); + tlstlen *= 2; + } + + tlst[tidx] = tp; + + /* create a hash table entry */ + name = xalloc(len + 1); + (void)memcpy(name, cp, len); + name[len] = '\0'; + + thte = xalloc(sizeof (thtab_t)); + thte->th_name = name; + thte->th_idx = tidx; + thte->th_nxt = thtab[h]; + thtab[h] = thte; + + return ((u_short)tidx++); +} + +/* + * Hash function for types + */ +static int +thash(s, len) + const char *s; + size_t len; +{ + u_int v; + + v = 0; + while (len-- != 0) { + v = (v << sizeof (v)) + (u_char)*s++; + v ^= v >> (sizeof (v) * CHAR_BIT - sizeof (v)); + } + return (v % THSHSIZ2); +} + +/* + * Read a string enclosed by "". This string may contain quoted chars. + */ +static char * +inpqstrg(src, epp) + const char *src, **epp; +{ + char *strg, *dst; + size_t slen; + int c; + int v; + + dst = strg = xmalloc(slen = 32); + + if ((c = *src++) != '"') + inperr(); + if ((c = *src++) == '\0') + inperr(); + + while (c != '"') { + if (c == '\\') { + if ((c = *src++) == '\0') + inperr(); + switch (c) { + case 'n': + c = '\n'; + break; + case 't': + c = '\t'; + break; + case 'v': +#ifdef __STDC__ + c = '\v'; +#else + c = '\013'; +#endif + break; + case 'b': + c = '\b'; + break; + case 'r': + c = '\r'; + break; + case 'f': + c = '\f'; + break; + case 'a': +#ifdef __STDC__ + c = '\a'; +#else + c = '\007'; +#endif + break; + case '\\': + c = '\\'; + break; + case '"': + c = '"'; + break; + case '\'': + c = '\''; + break; + case '0': case '1': case '2': case '3': + v = (c - '0') << 6; + if ((c = *src++) < '0' || c > '7') + inperr(); + v |= (c - '0') << 3; + if ((c = *src++) < '0' || c > '7') + inperr(); + v |= c - '0'; + c = (u_char)v; + break; + default: + inperr(); + } + } + /* keep space for trailing '\0' */ + if (dst - strg == slen - 1) { + strg = xrealloc(strg, slen * 2); + dst = strg + (slen - 1); + slen *= 2; + } + *dst++ = (char)c; + if ((c = *src++) == '\0') + inperr(); + } + *dst = '\0'; + + *epp = src; + return (strg); +} + +/* + * Read the name of a symbol in static memory. + */ +static const char * +inpname(cp, epp) + const char *cp, **epp; +{ + static char *buf; + static size_t blen = 0; + size_t len, i; + char *eptr, c; + + len = (int)strtol(cp, &eptr, 10); + if (cp == eptr) + inperr(); + cp = eptr; + if (len + 1 > blen) + buf = xrealloc(buf, blen = len + 1); + for (i = 0; i < len; i++) { + c = *cp++; + if (!isalnum(c) && c != '_') + inperr(); + buf[i] = c; + } + buf[i] = '\0'; + + *epp = cp; + return (buf); +} + +/* + * Return the index of a file name. If the name cannot be found, create + * a new entry and return the index of the newly created entry. + */ +static int +getfnidx(fn) + const char *fn; +{ + int i; + + /* 0 ist reserved */ + for (i = 1; fnames[i] != NULL; i++) { + if (strcmp(fnames[i], fn) == 0) + break; + } + if (fnames[i] != NULL) + return (i); + + if (i == nfnames - 1) { + fnames = xrealloc(fnames, (nfnames * 2) * sizeof (char *)); + (void)memset(fnames + nfnames, 0, nfnames * sizeof (char *)); + nfnames *= 2; + } + + fnames[i] = xstrdup(fn); + return (i); +} + +/* + * Separate symbols with static and external linkage. + */ +void +mkstatic(hte) + hte_t *hte; +{ + sym_t *sym1, **symp, *sym; + fcall_t **callp, *call; + usym_t **usymp, *usym; + hte_t *nhte; + int ofnd; + + /* Look for first static definition */ + for (sym1 = hte->h_syms; sym1 != NULL; sym1 = sym1->s_nxt) { + if (sym1->s_static) + break; + } + if (sym1 == NULL) + return; + + /* Do nothing if this name is used only in one translation unit. */ + ofnd = 0; + for (sym = hte->h_syms; sym != NULL && !ofnd; sym = sym->s_nxt) { + if (sym->s_pos.p_src != sym1->s_pos.p_src) + ofnd = 1; + } + for (call = hte->h_calls; call != NULL && !ofnd; call = call->f_nxt) { + if (call->f_pos.p_src != sym1->s_pos.p_src) + ofnd = 1; + } + for (usym = hte->h_usyms; usym != NULL && !ofnd; usym = usym->u_nxt) { + if (usym->u_pos.p_src != sym1->s_pos.p_src) + ofnd = 1; + } + if (!ofnd) { + hte->h_used = 1; + /* errors about undef. static symbols are printed in lint1 */ + hte->h_def = 1; + hte->h_static = 1; + return; + } + + /* + * Create a new hash table entry + * + * XXX this entry should be put at the beginning of the list to + * avoid to process the same symbol twice. + */ + for (nhte = hte; nhte->h_link != NULL; nhte = nhte->h_link) ; + nhte->h_link = xalloc(sizeof (hte_t)); + nhte = nhte->h_link; + nhte->h_name = hte->h_name; + nhte->h_static = 1; + nhte->h_used = 1; + nhte->h_def = 1; /* error in lint1 */ + nhte->h_lsym = &nhte->h_syms; + nhte->h_lcall = &nhte->h_calls; + nhte->h_lusym = &nhte->h_usyms; + + /* + * move all symbols used in this translation unit into the new + * hash table entry. + */ + for (symp = &hte->h_syms; (sym = *symp) != NULL; ) { + if (sym->s_pos.p_src == sym1->s_pos.p_src) { + sym->s_static = 1; + (*symp) = sym->s_nxt; + if (hte->h_lsym == &sym->s_nxt) + hte->h_lsym = symp; + sym->s_nxt = NULL; + *nhte->h_lsym = sym; + nhte->h_lsym = &sym->s_nxt; + } else { + symp = &sym->s_nxt; + } + } + for (callp = &hte->h_calls; (call = *callp) != NULL; ) { + if (call->f_pos.p_src == sym1->s_pos.p_src) { + (*callp) = call->f_nxt; + if (hte->h_lcall == &call->f_nxt) + hte->h_lcall = callp; + call->f_nxt = NULL; + *nhte->h_lcall = call; + nhte->h_lcall = &call->f_nxt; + } else { + callp = &call->f_nxt; + } + } + for (usymp = &hte->h_usyms; (usym = *usymp) != NULL; ) { + if (usym->u_pos.p_src == sym1->s_pos.p_src) { + (*usymp) = usym->u_nxt; + if (hte->h_lusym == &usym->u_nxt) + hte->h_lusym = usymp; + usym->u_nxt = NULL; + *nhte->h_lusym = usym; + nhte->h_lusym = &usym->u_nxt; + } else { + usymp = &usym->u_nxt; + } + } + + /* h_def must be recalculated for old hte */ + hte->h_def = nhte->h_def = 0; + for (sym = hte->h_syms; sym != NULL; sym = sym->s_nxt) { + if (sym->s_def == DEF || sym->s_def == TDEF) { + hte->h_def = 1; + break; + } + } + + mkstatic(hte); +} |