From 97ea65b2cb83e7b5b2c4afcacc3a185482457c2a Mon Sep 17 00:00:00 2001 From: joerg Date: Sun, 5 Nov 1995 15:56:42 +0000 Subject: 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 Obtained from: (NetBSD -- this version is directly from Jochen) --- usr.bin/xlint/Makefile | 5 + usr.bin/xlint/lint1/Makefile | 19 + usr.bin/xlint/lint1/cgram.y | 1675 +++++++++++++++++ usr.bin/xlint/lint1/decl.c | 3134 +++++++++++++++++++++++++++++++ usr.bin/xlint/lint1/emit.c | 241 +++ usr.bin/xlint/lint1/emit1.c | 587 ++++++ usr.bin/xlint/lint1/err.c | 543 ++++++ usr.bin/xlint/lint1/externs.h | 56 + usr.bin/xlint/lint1/externs1.h | 281 +++ usr.bin/xlint/lint1/func.c | 1261 +++++++++++++ usr.bin/xlint/lint1/init.c | 513 +++++ usr.bin/xlint/lint1/lint.h | 118 ++ usr.bin/xlint/lint1/lint1.h | 380 ++++ usr.bin/xlint/lint1/main1.c | 183 ++ usr.bin/xlint/lint1/mem.c | 91 + usr.bin/xlint/lint1/mem1.c | 358 ++++ usr.bin/xlint/lint1/op.h | 120 ++ usr.bin/xlint/lint1/param.h | 120 ++ usr.bin/xlint/lint1/scan.l | 1426 ++++++++++++++ usr.bin/xlint/lint1/tree.c | 3924 +++++++++++++++++++++++++++++++++++++++ usr.bin/xlint/lint2/Makefile | 13 + usr.bin/xlint/lint2/chk.c | 1462 +++++++++++++++ usr.bin/xlint/lint2/emit2.c | 236 +++ usr.bin/xlint/lint2/externs2.h | 87 + usr.bin/xlint/lint2/hash.c | 123 ++ usr.bin/xlint/lint2/lint2.h | 177 ++ usr.bin/xlint/lint2/main2.c | 190 ++ usr.bin/xlint/lint2/mem2.c | 97 + usr.bin/xlint/lint2/msg.c | 157 ++ usr.bin/xlint/lint2/read.c | 1133 +++++++++++ usr.bin/xlint/llib/Makefile | 20 + usr.bin/xlint/llib/llib-lposix | 311 ++++ usr.bin/xlint/llib/llib-lstdc | 252 +++ usr.bin/xlint/xlint/Makefile | 17 + usr.bin/xlint/xlint/lint.1 | 509 +++++ usr.bin/xlint/xlint/pathnames.h | 38 + usr.bin/xlint/xlint/xlint.c | 761 ++++++++ 37 files changed, 20618 insertions(+) create mode 100644 usr.bin/xlint/Makefile create mode 100644 usr.bin/xlint/lint1/Makefile create mode 100644 usr.bin/xlint/lint1/cgram.y create mode 100644 usr.bin/xlint/lint1/decl.c create mode 100644 usr.bin/xlint/lint1/emit.c create mode 100644 usr.bin/xlint/lint1/emit1.c create mode 100644 usr.bin/xlint/lint1/err.c create mode 100644 usr.bin/xlint/lint1/externs.h create mode 100644 usr.bin/xlint/lint1/externs1.h create mode 100644 usr.bin/xlint/lint1/func.c create mode 100644 usr.bin/xlint/lint1/init.c create mode 100644 usr.bin/xlint/lint1/lint.h create mode 100644 usr.bin/xlint/lint1/lint1.h create mode 100644 usr.bin/xlint/lint1/main1.c create mode 100644 usr.bin/xlint/lint1/mem.c create mode 100644 usr.bin/xlint/lint1/mem1.c create mode 100644 usr.bin/xlint/lint1/op.h create mode 100644 usr.bin/xlint/lint1/param.h create mode 100644 usr.bin/xlint/lint1/scan.l create mode 100644 usr.bin/xlint/lint1/tree.c create mode 100644 usr.bin/xlint/lint2/Makefile create mode 100644 usr.bin/xlint/lint2/chk.c create mode 100644 usr.bin/xlint/lint2/emit2.c create mode 100644 usr.bin/xlint/lint2/externs2.h create mode 100644 usr.bin/xlint/lint2/hash.c create mode 100644 usr.bin/xlint/lint2/lint2.h create mode 100644 usr.bin/xlint/lint2/main2.c create mode 100644 usr.bin/xlint/lint2/mem2.c create mode 100644 usr.bin/xlint/lint2/msg.c create mode 100644 usr.bin/xlint/lint2/read.c create mode 100644 usr.bin/xlint/llib/Makefile create mode 100644 usr.bin/xlint/llib/llib-lposix create mode 100644 usr.bin/xlint/llib/llib-lstdc create mode 100644 usr.bin/xlint/xlint/Makefile create mode 100644 usr.bin/xlint/xlint/lint.1 create mode 100644 usr.bin/xlint/xlint/pathnames.h create mode 100644 usr.bin/xlint/xlint/xlint.c diff --git a/usr.bin/xlint/Makefile b/usr.bin/xlint/Makefile new file mode 100644 index 0000000..01557ad --- /dev/null +++ b/usr.bin/xlint/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.2 1995/07/03 21:23:45 cgd Exp $ + +SUBDIR= lint1 lint2 xlint llib + +.include diff --git a/usr.bin/xlint/lint1/Makefile b/usr.bin/xlint/lint1/Makefile new file mode 100644 index 0000000..d5e519a --- /dev/null +++ b/usr.bin/xlint/lint1/Makefile @@ -0,0 +1,19 @@ +# $NetBSD: Makefile,v 1.3 1995/07/04 01:53:05 cgd Exp $ + +PROG= lint1 +SRCS= cgram.c scan.c mem1.c mem.c err.c main1.c decl.c tree.c func.c \ + init.c emit.c emit1.c +NOMAN= +LDADD+= -ll +DPADD+= ${LIBL} +YFLAGS= -d +CFLAGS+=-I. +LINTFLAGS=-aehpz +CLEANFILES+=y.tab.h cgram.c scan.c + +BINDIR= /usr/libexec + +# XXX: -O causes the gcc to die on the i386, when compiling tree.o +CFLAGS+= -DXXX_BROKEN_GCC + +.include diff --git a/usr.bin/xlint/lint1/cgram.y b/usr.bin/xlint/lint1/cgram.y new file mode 100644 index 0000000..e65584a --- /dev/null +++ b/usr.bin/xlint/lint1/cgram.y @@ -0,0 +1,1675 @@ +%{ +/* $NetBSD: cgram.y,v 1.8 1995/10/02 17:31:35 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: cgram.y,v 1.8 1995/10/02 17:31:35 jpo Exp $"; +#endif + +#include +#include + +#include "lint1.h" + +/* + * Contains the level of current declaration. 0 is extern. + * Used for symbol table entries. + */ +int blklev; + +/* + * level for memory allocation. Normaly the same as blklev. + * An exeption is the declaration of arguments in prototypes. Memory + * for these can't be freed after the declaration, but symbols must + * be removed from the symbol table after the declaration. + */ +int mblklev; + +static int toicon __P((tnode_t *)); +static void idecl __P((sym_t *, int)); +static void ignuptorp __P((void)); + +%} + +%union { + int y_int; + val_t *y_val; + sbuf_t *y_sb; + sym_t *y_sym; + op_t y_op; + scl_t y_scl; + tspec_t y_tspec; + tqual_t y_tqual; + type_t *y_type; + tnode_t *y_tnode; + strg_t *y_strg; + pqinf_t *y_pqinf; +}; + +%token T_LBRACE T_RBRACE T_LBRACK T_RBRACK T_LPARN T_RPARN +%token T_STROP +%token T_UNOP +%token T_INCDEC +%token T_SIZEOF +%token T_MULT +%token T_DIVOP +%token T_ADDOP +%token T_SHFTOP +%token T_RELOP +%token T_EQOP +%token T_AND +%token T_XOR +%token T_OR +%token T_LOGAND +%token T_LOGOR +%token T_QUEST +%token T_COLON +%token T_ASSIGN +%token T_OPASS +%token T_COMMA +%token T_SEMI +%token T_ELLIPSE + +/* storage classes (extern, static, auto, register and typedef) */ +%token T_SCLASS + +/* types (char, int, short, long, unsigned, signed, float, double, void) */ +%token T_TYPE + +/* qualifiers (const, volatile) */ +%token T_QUAL + +/* struct or union */ +%token T_SOU + +/* enum */ +%token T_ENUM + +/* remaining keywords */ +%token T_CASE +%token T_DEFAULT +%token T_IF +%token T_ELSE +%token T_SWITCH +%token T_DO +%token T_WHILE +%token T_FOR +%token T_GOTO +%token T_CONTINUE +%token T_BREAK +%token T_RETURN +%token T_ASM + +%left T_COMMA +%right T_ASSIGN T_OPASS +%right T_QUEST T_COLON +%left T_LOGOR +%left T_LOGAND +%left T_OR +%left T_XOR +%left T_AND +%left T_EQOP +%left T_RELOP +%left T_SHFTOP +%left T_ADDOP +%left T_MULT T_DIVOP +%right T_UNOP T_INCDEC T_SIZEOF +%left T_LPARN T_LBRACK T_STROP + +%token T_NAME +%token T_TYPENAME +%token T_CON +%token T_STRING + +%type func_decl +%type notype_decl +%type type_decl +%type typespec +%type clrtyp_typespec +%type notype_typespec +%type struct_spec +%type enum_spec +%type struct_tag +%type enum_tag +%type struct +%type struct_declaration +%type identifier +%type member_declaration_list_with_rbrace +%type member_declaration_list +%type member_declaration +%type notype_member_decls +%type type_member_decls +%type notype_member_decl +%type type_member_decl +%type constant +%type enum_declaration +%type enums_with_opt_comma +%type enums +%type enumerator +%type ename +%type notype_direct_decl +%type type_direct_decl +%type pointer +%type asterisk +%type param_decl +%type param_list +%type abs_decl_param_list +%type direct_param_decl +%type notype_param_decl +%type direct_notype_param_decl +%type type_qualifier_list +%type type_qualifier +%type identifier_list +%type abs_decl +%type direct_abs_decl +%type vararg_parameter_type_list +%type parameter_type_list +%type parameter_declaration +%type expr +%type term +%type func_arg_list +%type point_or_arrow +%type type_name +%type abstract_declaration +%type do_while_expr +%type opt_expr +%type string +%type string2 + + +%% + +program: + /* empty */ { + if (sflag) { + /* empty translation unit */ + error(272); + } else if (!tflag) { + /* empty translation unit */ + warning(272); + } + } + | translation_unit + ; + +translation_unit: + ext_decl + | translation_unit ext_decl + ; + +ext_decl: + func_def { + glclup(0); + clrwflgs(); + } + | data_def { + glclup(0); + clrwflgs(); + } + ; + +data_def: + T_SEMI { + if (sflag) { + /* syntax error: empty declaration */ + error(0); + } else if (!tflag) { + /* syntax error: empty declaration */ + warning(0); + } + } + | clrtyp deftyp notype_init_decls T_SEMI { + if (sflag) { + /* old style declaration; add "int" */ + error(1); + } else if (!tflag) { + /* old style declaration; add "int" */ + warning(1); + } + } + | declmods deftyp T_SEMI { + if (dcs->d_scl == TYPEDEF) { + /* typedef declares no type name */ + warning(72); + } else { + /* empty declaration */ + warning(2); + } + } + | declmods deftyp notype_init_decls T_SEMI + | declspecs deftyp T_SEMI { + if (dcs->d_scl == TYPEDEF) { + /* typedef declares no type name */ + warning(72); + } else if (!dcs->d_nedecl) { + /* empty declaration */ + warning(2); + } + } + | declspecs deftyp type_init_decls T_SEMI + | error T_SEMI { + globclup(); + } + | error T_RBRACE { + globclup(); + } + ; + +func_def: + func_decl { + if ($1->s_type->t_tspec != FUNC) { + /* syntax error */ + error(249); + YYERROR; + } + if ($1->s_type->t_typedef) { + /* ()-less function definition */ + error(64); + YYERROR; + } + funcdef($1); + blklev++; + pushdecl(ARG); + } opt_arg_declaration_list { + popdecl(); + blklev--; + cluparg(); + pushctrl(0); + } comp_stmnt { + funcend(); + popctrl(0); + } + ; + +func_decl: + clrtyp deftyp notype_decl { + $$ = $3; + } + | declmods deftyp notype_decl { + $$ = $3; + } + | declspecs deftyp type_decl { + $$ = $3; + } + ; + +opt_arg_declaration_list: + /* empty */ + | arg_declaration_list + ; + +arg_declaration_list: + arg_declaration + | arg_declaration_list arg_declaration + /* XXX or better "arg_declaration error" ? */ + | error + ; + +/* + * "arg_declaration" is separated from "declaration" because it + * needs other error handling. + */ + +arg_declaration: + declmods deftyp T_SEMI { + /* empty declaration */ + warning(2); + } + | declmods deftyp notype_init_decls T_SEMI + | declspecs deftyp T_SEMI { + if (!dcs->d_nedecl) { + /* empty declaration */ + warning(2); + } else { + tspec_t ts = dcs->d_type->t_tspec; + /* %s declared in argument declaration list */ + warning(3, ts == STRUCT ? "struct" : + (ts == UNION ? "union" : "enum")); + } + } + | declspecs deftyp type_init_decls T_SEMI { + if (dcs->d_nedecl) { + tspec_t ts = dcs->d_type->t_tspec; + /* %s declared in argument declaration list */ + warning(3, ts == STRUCT ? "struct" : + (ts == UNION ? "union" : "enum")); + } + } + | declmods error + | declspecs error + ; + +declaration: + declmods deftyp T_SEMI { + if (dcs->d_scl == TYPEDEF) { + /* typedef declares no type name */ + warning(72); + } else { + /* empty declaration */ + warning(2); + } + } + | declmods deftyp notype_init_decls T_SEMI + | declspecs deftyp T_SEMI { + if (dcs->d_scl == TYPEDEF) { + /* typedef declares no type name */ + warning(72); + } else if (!dcs->d_nedecl) { + /* empty declaration */ + warning(2); + } + } + | declspecs deftyp type_init_decls T_SEMI + | error T_SEMI + ; + +clrtyp: + { + clrtyp(); + } + ; + +deftyp: + /* empty */ { + deftyp(); + } + ; + +declspecs: + clrtyp_typespec { + addtype($1); + } + | declmods typespec { + addtype($2); + } + | declspecs declmod + | declspecs notype_typespec { + addtype($2); + } + ; + +declmods: + clrtyp T_QUAL { + addqual($2); + } + | clrtyp T_SCLASS { + addscl($2); + } + | declmods declmod + ; + +declmod: + T_QUAL { + addqual($1); + } + | T_SCLASS { + addscl($1); + } + ; + +clrtyp_typespec: + clrtyp notype_typespec { + $$ = $2; + } + | T_TYPENAME clrtyp { + $$ = getsym($1)->s_type; + } + ; + +typespec: + notype_typespec { + $$ = $1; + } + | T_TYPENAME { + $$ = getsym($1)->s_type; + } + ; + +notype_typespec: + T_TYPE { + $$ = gettyp($1); + } + | struct_spec { + popdecl(); + $$ = $1; + } + | enum_spec { + popdecl(); + $$ = $1; + } + ; + +struct_spec: + struct struct_tag { + /* + * STDC requires that "struct a;" always introduces + * a new tag if "a" is not declared at current level + * + * yychar is valid because otherwise the parser would + * not been able to deceide if he must shift or reduce + */ + $$ = mktag($2, $1, 0, yychar == T_SEMI); + } + | struct struct_tag { + dcs->d_tagtyp = mktag($2, $1, 1, 0); + } struct_declaration { + $$ = compltag(dcs->d_tagtyp, $4); + } + | struct { + dcs->d_tagtyp = mktag(NULL, $1, 1, 0); + } struct_declaration { + $$ = compltag(dcs->d_tagtyp, $3); + } + | struct error { + symtyp = FVFT; + $$ = gettyp(INT); + } + ; + +struct: + T_SOU { + symtyp = FTAG; + pushdecl($1 == STRUCT ? MOS : MOU); + dcs->d_offset = 0; + dcs->d_stralign = CHAR_BIT; + $$ = $1; + } + ; + +struct_tag: + identifier { + $$ = getsym($1); + } + ; + +struct_declaration: + struct_decl_lbrace member_declaration_list_with_rbrace { + $$ = $2; + } + ; + +struct_decl_lbrace: + T_LBRACE { + symtyp = FVFT; + } + ; + +member_declaration_list_with_rbrace: + member_declaration_list T_SEMI T_RBRACE { + $$ = $1; + } + | member_declaration_list T_RBRACE { + if (sflag) { + /* syntax req. ";" after last struct/union member */ + error(66); + } else { + /* syntax req. ";" after last struct/union member */ + warning(66); + } + $$ = $1; + } + | T_RBRACE { + $$ = NULL; + } + ; + +member_declaration_list: + member_declaration { + $$ = $1; + } + | member_declaration_list T_SEMI member_declaration { + $$ = lnklst($1, $3); + } + ; + +member_declaration: + noclass_declmods deftyp { + /* too late, i know, but getsym() compensates it */ + symtyp = FMOS; + } notype_member_decls { + symtyp = FVFT; + $$ = $4; + } + | noclass_declspecs deftyp { + symtyp = FMOS; + } type_member_decls { + symtyp = FVFT; + $$ = $4; + } + | noclass_declmods deftyp { + /* struct or union member must be named */ + warning(49); + $$ = NULL; + } + | noclass_declspecs deftyp { + /* struct or union member must be named */ + warning(49); + $$ = NULL; + } + | error { + symtyp = FVFT; + $$ = NULL; + } + ; + +noclass_declspecs: + clrtyp_typespec { + addtype($1); + } + | noclass_declmods typespec { + addtype($2); + } + | noclass_declspecs T_QUAL { + addqual($2); + } + | noclass_declspecs notype_typespec { + addtype($2); + } + ; + +noclass_declmods: + clrtyp T_QUAL { + addqual($2); + } + | noclass_declmods T_QUAL { + addqual($2); + } + ; + +notype_member_decls: + notype_member_decl { + $$ = decl1str($1); + } + | notype_member_decls { + symtyp = FMOS; + } T_COMMA type_member_decl { + $$ = lnklst($1, decl1str($4)); + } + ; + +type_member_decls: + type_member_decl { + $$ = decl1str($1); + } + | type_member_decls { + symtyp = FMOS; + } T_COMMA type_member_decl { + $$ = lnklst($1, decl1str($4)); + } + ; + +notype_member_decl: + notype_decl { + $$ = $1; + } + | notype_decl T_COLON constant { + $$ = bitfield($1, toicon($3)); + } + | { + symtyp = FVFT; + } T_COLON constant { + $$ = bitfield(NULL, toicon($3)); + } + ; + +type_member_decl: + type_decl { + $$ = $1; + } + | type_decl T_COLON constant { + $$ = bitfield($1, toicon($3)); + } + | { + symtyp = FVFT; + } T_COLON constant { + $$ = bitfield(NULL, toicon($3)); + } + ; + +enum_spec: + enum enum_tag { + $$ = mktag($2, ENUM, 0, 0); + } + | enum enum_tag { + dcs->d_tagtyp = mktag($2, ENUM, 1, 0); + } enum_declaration { + $$ = compltag(dcs->d_tagtyp, $4); + } + | enum { + dcs->d_tagtyp = mktag(NULL, ENUM, 1, 0); + } enum_declaration { + $$ = compltag(dcs->d_tagtyp, $3); + } + | enum error { + symtyp = FVFT; + $$ = gettyp(INT); + } + ; + +enum: + T_ENUM { + symtyp = FTAG; + pushdecl(ENUMCON); + } + ; + +enum_tag: + identifier { + $$ = getsym($1); + } + ; + +enum_declaration: + enum_decl_lbrace enums_with_opt_comma T_RBRACE { + $$ = $2; + } + ; + +enum_decl_lbrace: + T_LBRACE { + symtyp = FVFT; + enumval = 0; + } + ; + +enums_with_opt_comma: + enums { + $$ = $1; + } + | enums T_COMMA { + if (sflag) { + /* trailing "," prohibited in enum declaration */ + error(54); + } else { + /* trailing "," prohibited in enum declaration */ + warning(54); + } + $$ = $1; + } + ; + +enums: + enumerator { + $$ = $1; + } + | enums T_COMMA enumerator { + $$ = lnklst($1, $3); + } + | error { + $$ = NULL; + } + ; + +enumerator: + ename { + $$ = ename($1, enumval, 1); + } + | ename T_ASSIGN constant { + $$ = ename($1, toicon($3), 0); + } + ; + +ename: + identifier { + $$ = getsym($1); + } + ; + + +notype_init_decls: + notype_init_decl + | notype_init_decls T_COMMA type_init_decl + ; + +type_init_decls: + type_init_decl + | type_init_decls T_COMMA type_init_decl + ; + +notype_init_decl: + notype_decl opt_asm_spec { + idecl($1, 0); + chksz($1); + } + | notype_decl opt_asm_spec { + idecl($1, 1); + } T_ASSIGN initializer { + chksz($1); + } + ; + +type_init_decl: + type_decl opt_asm_spec { + idecl($1, 0); + chksz($1); + } + | type_decl opt_asm_spec { + idecl($1, 1); + } T_ASSIGN initializer { + chksz($1); + } + ; + +notype_decl: + notype_direct_decl { + $$ = $1; + } + | pointer notype_direct_decl { + $$ = addptr($2, $1); + } + ; + +notype_direct_decl: + T_NAME { + $$ = dname(getsym($1)); + } + | T_LPARN type_decl T_RPARN { + $$ = $2; + } + | notype_direct_decl T_LBRACK T_RBRACK { + $$ = addarray($1, 0, 0); + } + | notype_direct_decl T_LBRACK constant T_RBRACK { + $$ = addarray($1, 1, toicon($3)); + } + | notype_direct_decl param_list { + $$ = addfunc($1, $2); + popdecl(); + blklev--; + } + ; + +type_decl: + type_direct_decl { + $$ = $1; + } + | pointer type_direct_decl { + $$ = addptr($2, $1); + } + ; + +type_direct_decl: + identifier { + $$ = dname(getsym($1)); + } + | T_LPARN type_decl T_RPARN { + $$ = $2; + } + | type_direct_decl T_LBRACK T_RBRACK { + $$ = addarray($1, 0, 0); + } + | type_direct_decl T_LBRACK constant T_RBRACK { + $$ = addarray($1, 1, toicon($3)); + } + | type_direct_decl param_list { + $$ = addfunc($1, $2); + popdecl(); + blklev--; + } + ; + +/* + * param_decl and notype_param_decl exist to avoid a conflict in + * argument lists. A typename enclosed in parens should always be + * treated as a typename, not an argument. + * "typedef int a; f(int (a));" is "typedef int a; f(int foo(a));" + * not "typedef int a; f(int a);" + */ +param_decl: + direct_param_decl { + $$ = $1; + } + | pointer direct_param_decl { + $$ = addptr($2, $1); + } + ; + +direct_param_decl: + identifier { + $$ = dname(getsym($1)); + } + | T_LPARN notype_param_decl T_RPARN { + $$ = $2; + } + | direct_param_decl T_LBRACK T_RBRACK { + $$ = addarray($1, 0, 0); + } + | direct_param_decl T_LBRACK constant T_RBRACK { + $$ = addarray($1, 1, toicon($3)); + } + | direct_param_decl param_list { + $$ = addfunc($1, $2); + popdecl(); + blklev--; + } + ; + +notype_param_decl: + direct_notype_param_decl { + $$ = $1; + } + | pointer direct_notype_param_decl { + $$ = addptr($2, $1); + } + ; + +direct_notype_param_decl: + T_NAME { + $$ = dname(getsym($1)); + } + | T_LPARN notype_param_decl T_RPARN { + $$ = $2; + } + | direct_notype_param_decl T_LBRACK T_RBRACK { + $$ = addarray($1, 0, 0); + } + | direct_notype_param_decl T_LBRACK constant T_RBRACK { + $$ = addarray($1, 1, toicon($3)); + } + | direct_notype_param_decl param_list { + $$ = addfunc($1, $2); + popdecl(); + blklev--; + } + ; + +pointer: + asterisk { + $$ = $1; + } + | asterisk type_qualifier_list { + $$ = mergepq($1, $2); + } + | asterisk pointer { + $$ = mergepq($1, $2); + } + | asterisk type_qualifier_list pointer { + $$ = mergepq(mergepq($1, $2), $3); + } + ; + +asterisk: + T_MULT { + $$ = xcalloc(1, sizeof (pqinf_t)); + $$->p_pcnt = 1; + } + ; + +type_qualifier_list: + type_qualifier { + $$ = $1; + } + | type_qualifier_list type_qualifier { + $$ = mergepq($1, $2); + } + ; + +type_qualifier: + T_QUAL { + $$ = xcalloc(1, sizeof (pqinf_t)); + if ($1 == CONST) { + $$->p_const = 1; + } else { + $$->p_volatile = 1; + } + } + ; + +param_list: + id_list_lparn identifier_list T_RPARN { + $$ = $2; + } + | abs_decl_param_list { + $$ = $1; + } + ; + +id_list_lparn: + T_LPARN { + blklev++; + pushdecl(PARG); + } + ; + +identifier_list: + T_NAME { + $$ = iname(getsym($1)); + } + | identifier_list T_COMMA T_NAME { + $$ = lnklst($1, iname(getsym($3))); + } + | identifier_list error { + $$ = $1; + } + ; + +abs_decl_param_list: + abs_decl_lparn T_RPARN { + $$ = NULL; + } + | abs_decl_lparn vararg_parameter_type_list T_RPARN { + dcs->d_proto = 1; + $$ = $2; + } + | abs_decl_lparn error T_RPARN { + $$ = NULL; + } + ; + +abs_decl_lparn: + T_LPARN { + blklev++; + pushdecl(PARG); + } + ; + +vararg_parameter_type_list: + parameter_type_list { + $$ = $1; + } + | parameter_type_list T_COMMA T_ELLIPSE { + dcs->d_vararg = 1; + $$ = $1; + } + | T_ELLIPSE { + if (sflag) { + /* ANSI C requires formal parameter before "..." */ + error(84); + } else if (!tflag) { + /* ANSI C requires formal parameter before "..." */ + warning(84); + } + dcs->d_vararg = 1; + $$ = NULL; + } + ; + +parameter_type_list: + parameter_declaration opt_asm_spec { + $$ = $1; + } + | parameter_type_list T_COMMA parameter_declaration opt_asm_spec { + $$ = lnklst($1, $3); + } + ; + +parameter_declaration: + declmods deftyp { + $$ = decl1arg(aname(), 0); + } + | declspecs deftyp { + $$ = decl1arg(aname(), 0); + } + | declmods deftyp notype_param_decl { + $$ = decl1arg($3, 0); + } + /* + * param_decl is needed because of following conflict: + * "typedef int a; f(int (a));" could be parsed as + * "function with argument a of type int", or + * "function with an abstract argument of type function". + * This grammar realizes the second case. + */ + | declspecs deftyp param_decl { + $$ = decl1arg($3, 0); + } + | declmods deftyp abs_decl { + $$ = decl1arg($3, 0); + } + | declspecs deftyp abs_decl { + $$ = decl1arg($3, 0); + } + ; + +opt_asm_spec: + /* empty */ + | T_ASM T_LPARN T_STRING T_RPARN { + freeyyv(&$3, T_STRING); + } + ; + +initializer: + init_expr + ; + +init_expr: + expr %prec T_COMMA { + mkinit($1); + } + | init_lbrace init_expr_list init_rbrace + | init_lbrace init_expr_list T_COMMA init_rbrace + | error + ; + +init_expr_list: + init_expr %prec T_COMMA + | init_expr_list T_COMMA init_expr + ; + +init_lbrace: + T_LBRACE { + initlbr(); + } + ; + +init_rbrace: + T_RBRACE { + initrbr(); + } + ; + +type_name: + { + pushdecl(ABSTRACT); + } abstract_declaration { + popdecl(); + $$ = $2->s_type; + } + ; + +abstract_declaration: + noclass_declmods deftyp { + $$ = decl1abs(aname()); + } + | noclass_declspecs deftyp { + $$ = decl1abs(aname()); + } + | noclass_declmods deftyp abs_decl { + $$ = decl1abs($3); + } + | noclass_declspecs deftyp abs_decl { + $$ = decl1abs($3); + } + ; + +abs_decl: + pointer { + $$ = addptr(aname(), $1); + } + | direct_abs_decl { + $$ = $1; + } + | pointer direct_abs_decl { + $$ = addptr($2, $1); + } + ; + +direct_abs_decl: + T_LPARN abs_decl T_RPARN { + $$ = $2; + } + | T_LBRACK T_RBRACK { + $$ = addarray(aname(), 0, 0); + } + | T_LBRACK constant T_RBRACK { + $$ = addarray(aname(), 1, toicon($2)); + } + | direct_abs_decl T_LBRACK T_RBRACK { + $$ = addarray($1, 0, 0); + } + | direct_abs_decl T_LBRACK constant T_RBRACK { + $$ = addarray($1, 1, toicon($3)); + } + | abs_decl_param_list { + $$ = addfunc(aname(), $1); + popdecl(); + blklev--; + } + | direct_abs_decl abs_decl_param_list { + $$ = addfunc($1, $2); + popdecl(); + blklev--; + } + ; + +stmnt: + labeled_stmnt + | expr_stmnt + | comp_stmnt + | selection_stmnt + | iteration_stmnt + | jump_stmnt { + ftflg = 0; + } + | asm_stmnt + ; + +labeled_stmnt: + label stmnt + ; + +label: + identifier T_COLON { + symtyp = FLAB; + label(T_NAME, getsym($1), NULL); + } + | T_CASE constant T_COLON { + label(T_CASE, NULL, $2); + ftflg = 1; + } + | T_DEFAULT T_COLON { + label(T_DEFAULT, NULL, NULL); + ftflg = 1; + } + ; + +comp_stmnt: + compstmnt_lbrace declaration_list opt_stmnt_list compstmnt_rbrace + | compstmnt_lbrace opt_stmnt_list compstmnt_rbrace + ; + +compstmnt_lbrace: + T_LBRACE { + blklev++; + mblklev++; + pushdecl(AUTO); + } + ; + +compstmnt_rbrace: + T_RBRACE { + popdecl(); + freeblk(); + mblklev--; + blklev--; + ftflg = 0; + } + ; + +opt_stmnt_list: + /* empty */ + | stmnt_list + ; + +stmnt_list: + stmnt { + clrwflgs(); + } + | stmnt_list stmnt { + clrwflgs(); + } + | stmnt_list error T_SEMI { + clrwflgs(); + } + ; + +expr_stmnt: + expr T_SEMI { + expr($1, 0, 0); + ftflg = 0; + } + | T_SEMI { + ftflg = 0; + } + ; + +selection_stmnt: + if_without_else { + if2(); + if3(0); + } + | if_without_else T_ELSE { + if2(); + } stmnt { + if3(1); + } + | if_without_else T_ELSE error { + if3(0); + } + | switch_expr stmnt { + switch2(); + } + | switch_expr error { + switch2(); + } + ; + +if_without_else: + if_expr stmnt + | if_expr error + ; + +if_expr: + T_IF T_LPARN expr T_RPARN { + if1($3); + clrwflgs(); + } + ; + +switch_expr: + T_SWITCH T_LPARN expr T_RPARN { + switch1($3); + clrwflgs(); + } + ; + +iteration_stmnt: + while_expr stmnt { + while2(); + } + | while_expr error { + while2(); + } + | do stmnt do_while_expr { + do2($3); + ftflg = 0; + } + | do error { + do2(NULL); + } + | for_exprs stmnt { + for2(); + } + | for_exprs error { + for2(); + } + ; + +while_expr: + T_WHILE T_LPARN expr T_RPARN { + while1($3); + clrwflgs(); + } + ; + +do: + T_DO { + do1(); + } + ; + +do_while_expr: + T_WHILE T_LPARN expr T_RPARN T_SEMI { + $$ = $3; + } + ; + +for_exprs: + T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN { + for1($3, $5, $7); + clrwflgs(); + } + ; + +opt_expr: + /* empty */ { + $$ = NULL; + } + | expr { + $$ = $1; + } + ; + +jump_stmnt: + goto identifier T_SEMI { + dogoto(getsym($2)); + } + | goto error T_SEMI { + symtyp = FVFT; + } + | T_CONTINUE T_SEMI { + docont(); + } + | T_BREAK T_SEMI { + dobreak(); + } + | T_RETURN T_SEMI { + doreturn(NULL); + } + | T_RETURN expr T_SEMI { + doreturn($2); + } + ; + +goto: + T_GOTO { + symtyp = FLAB; + } + ; + +asm_stmnt: + T_ASM T_LPARN read_until_rparn T_SEMI { + setasm(); + } + | T_ASM T_QUAL T_LPARN read_until_rparn T_SEMI { + setasm(); + } + | T_ASM error + ; + +read_until_rparn: + /* empty */ { + ignuptorp(); + } + ; + +declaration_list: + declaration { + clrwflgs(); + } + | declaration_list declaration { + clrwflgs(); + } + ; + +constant: + expr %prec T_COMMA { + $$ = $1; + } + ; + +expr: + expr T_MULT expr { + $$ = build(MULT, $1, $3); + } + | expr T_DIVOP expr { + $$ = build($2, $1, $3); + } + | expr T_ADDOP expr { + $$ = build($2, $1, $3); + } + | expr T_SHFTOP expr { + $$ = build($2, $1, $3); + } + | expr T_RELOP expr { + $$ = build($2, $1, $3); + } + | expr T_EQOP expr { + $$ = build($2, $1, $3); + } + | expr T_AND expr { + $$ = build(AND, $1, $3); + } + | expr T_XOR expr { + $$ = build(XOR, $1, $3); + } + | expr T_OR expr { + $$ = build(OR, $1, $3); + } + | expr T_LOGAND expr { + $$ = build(LOGAND, $1, $3); + } + | expr T_LOGOR expr { + $$ = build(LOGOR, $1, $3); + } + | expr T_QUEST expr T_COLON expr { + $$ = build(QUEST, $1, build(COLON, $3, $5)); + } + | expr T_ASSIGN expr { + $$ = build(ASSIGN, $1, $3); + } + | expr T_OPASS expr { + $$ = build($2, $1, $3); + } + | expr T_COMMA expr { + $$ = build(COMMA, $1, $3); + } + | term { + $$ = $1; + } + ; + +term: + T_NAME { + /* XXX realy neccessary? */ + if (yychar < 0) + yychar = yylex(); + $$ = getnnode(getsym($1), yychar); + } + | string { + $$ = getsnode($1); + } + | T_CON { + $$ = getcnode(gettyp($1->v_tspec), $1); + } + | T_LPARN expr T_RPARN { + if ($2 != NULL) + $2->tn_parn = 1; + $$ = $2; + } + | term T_INCDEC { + $$ = build($2 == INC ? INCAFT : DECAFT, $1, NULL); + } + | T_INCDEC term { + $$ = build($1 == INC ? INCBEF : DECBEF, $2, NULL); + } + | T_MULT term { + $$ = build(STAR, $2, NULL); + } + | T_AND term { + $$ = build(AMPER, $2, NULL); + } + | T_UNOP term { + $$ = build($1, $2, NULL); + } + | T_ADDOP term { + if (tflag && $1 == PLUS) { + /* unary + is illegal in traditional C */ + warning(100); + } + $$ = build($1 == PLUS ? UPLUS : UMINUS, $2, NULL); + } + | term T_LBRACK expr T_RBRACK { + $$ = build(STAR, build(PLUS, $1, $3), NULL); + } + | term T_LPARN T_RPARN { + $$ = funccall($1, NULL); + } + | term T_LPARN func_arg_list T_RPARN { + $$ = funccall($1, $3); + } + | term point_or_arrow T_NAME { + if ($1 != NULL) { + sym_t *msym; + /* XXX strmemb should be integrated in build() */ + if ($2 == ARROW) { + /* must to this before strmemb is called */ + $1 = cconv($1); + } + msym = strmemb($1, $2, getsym($3)); + $$ = build($2, $1, getnnode(msym, 0)); + } else { + $$ = NULL; + } + } + | T_SIZEOF term %prec T_SIZEOF { + if (($$ = $2 == NULL ? NULL : bldszof($2->tn_type)) != NULL) + chkmisc($2, 0, 0, 0, 0, 0, 1); + } + | T_SIZEOF T_LPARN type_name T_RPARN %prec T_SIZEOF { + $$ = bldszof($3); + } + | T_LPARN type_name T_RPARN term %prec T_UNOP { + $$ = cast($4, $2); + } + ; + +string: + T_STRING { + $$ = $1; + } + | T_STRING string2 { + $$ = catstrg($1, $2); + } + ; + +string2: + T_STRING { + if (tflag) { + /* concatenated strings are illegal in traditional C */ + warning(219); + } + $$ = $1; + } + | string2 T_STRING { + $$ = catstrg($1, $2); + } + ; + +func_arg_list: + expr %prec T_COMMA { + $$ = funcarg(NULL, $1); + } + | func_arg_list T_COMMA expr { + $$ = funcarg($1, $3); + } + ; + +point_or_arrow: + T_STROP { + symtyp = FMOS; + $$ = $1; + } + ; + +identifier: + T_NAME { + $$ = $1; + } + | T_TYPENAME { + $$ = $1; + } + ; + +%% + +/* ARGSUSED */ +int +yyerror(msg) + char *msg; +{ + error(249); + if (++sytxerr >= 5) + norecover(); + return (0); +} + +/* + * Gets a node for a constant and returns the value of this constant + * as integer. + * Is the node not constant or too large for int or of type float, + * a warning will be printed. + * + * toicon() should be used only inside declarations. If it is used in + * expressions, it frees the memory used for the expression. + */ +static int +toicon(tn) + tnode_t *tn; +{ + int i; + tspec_t t; + val_t *v; + + v = constant(tn); + + /* + * Abstract declarations are used inside expression. To free + * the memory would be a fatal error. + */ + if (dcs->d_ctx != ABSTRACT) + tfreeblk(); + + if ((t = v->v_tspec) == FLOAT || t == DOUBLE || t == LDOUBLE) { + i = (int)v->v_ldbl; + /* integral constant expression expected */ + error(55); + } else { + i = (int)v->v_quad; + if (isutyp(t)) { + if ((u_quad_t)v->v_quad > INT_MAX) { + /* integral constant too large */ + warning(56); + } + } else { + if (v->v_quad > INT_MAX || v->v_quad < INT_MIN) { + /* integral constant too large */ + warning(56); + } + } + } + free(v); + return (i); +} + +static void +idecl(decl, initflg) + sym_t *decl; + int initflg; +{ + initerr = 0; + initsym = decl; + + switch (dcs->d_ctx) { + case EXTERN: + decl1ext(decl, initflg); + break; + case ARG: + (void)decl1arg(decl, initflg); + break; + case AUTO: + decl1loc(decl, initflg); + break; + default: + lerror("idecl()"); + } + + if (initflg && !initerr) + prepinit(); +} + +/* + * Discard all input tokens up to and including the next + * unmatched right paren + */ +void +ignuptorp() +{ + int level; + + if (yychar < 0) + yychar = yylex(); + freeyyv(&yylval, yychar); + + level = 1; + while (yychar != T_RPARN || --level > 0) { + if (yychar == T_LPARN) { + level++; + } else if (yychar <= 0) { + break; + } + freeyyv(&yylval, yychar = yylex()); + } + + yyclearin; +} diff --git a/usr.bin/xlint/lint1/decl.c b/usr.bin/xlint/lint1/decl.c new file mode 100644 index 0000000..fa023b7 --- /dev/null +++ b/usr.bin/xlint/lint1/decl.c @@ -0,0 +1,3134 @@ +/* $NetBSD: decl.c,v 1.11 1995/10/02 17:34:16 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: decl.c,v 1.11 1995/10/02 17:34:16 jpo Exp $"; +#endif + +#include +#include +#include +#include + +#include "lint1.h" + +const char *unnamed = ""; + +/* contains various information and classification on types */ +ttab_t ttab[NTSPEC]; + +/* shared type structures for arithmtic types and void */ +static type_t *typetab; + +/* value of next enumerator during declaration of enum types */ +int enumval; + +/* + * pointer to top element of a stack which contains informations local + * to nested declarations + */ +dinfo_t *dcs; + +static type_t *tdeferr __P((type_t *, tspec_t)); +static void settdsym __P((type_t *, sym_t *)); +static tspec_t mrgtspec __P((tspec_t, tspec_t)); +static void align __P((int, int)); +static sym_t *newtag __P((sym_t *, scl_t, int, int)); +static int eqargs __P((type_t *, type_t *, int *)); +static int mnoarg __P((type_t *, int *)); +static int chkosdef __P((sym_t *, sym_t *)); +static int chkptdecl __P((sym_t *, sym_t *)); +static sym_t *nsfunc __P((sym_t *, sym_t *)); +static void osfunc __P((sym_t *, sym_t *)); +static void ledecl __P((sym_t *)); +static int chkinit __P((sym_t *)); +static void chkausg __P((int, sym_t *)); +static void chkvusg __P((int, sym_t *)); +static void chklusg __P((sym_t *)); +static void chktusg __P((sym_t *)); +static void chkglvar __P((sym_t *)); +static void glchksz __P((sym_t *)); + +/* + * initializes all global vars used in declarations + */ +void +initdecl() +{ + 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" } }, + }; + + /* declaration stack */ + dcs = xcalloc(1, sizeof (dinfo_t)); + dcs->d_ctx = EXTERN; + dcs->d_ldlsym = &dcs->d_dlsyms; + + /* type information and classification */ + 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; + } + + /* shared type structures */ + typetab = xcalloc(NTSPEC, sizeof (type_t)); + for (i = 0; i < NTSPEC; i++) + typetab[i].t_tspec = NOTSPEC; + typetab[CHAR].t_tspec = CHAR; + typetab[SCHAR].t_tspec = SCHAR; + typetab[UCHAR].t_tspec = UCHAR; + typetab[SHORT].t_tspec = SHORT; + typetab[USHORT].t_tspec = USHORT; + typetab[INT].t_tspec = INT; + typetab[UINT].t_tspec = UINT; + typetab[LONG].t_tspec = LONG; + typetab[ULONG].t_tspec = ULONG; + typetab[QUAD].t_tspec = QUAD; + typetab[UQUAD].t_tspec = UQUAD; + typetab[FLOAT].t_tspec = FLOAT; + typetab[DOUBLE].t_tspec = DOUBLE; + typetab[LDOUBLE].t_tspec = LDOUBLE; + typetab[VOID].t_tspec = VOID; + /* + * Next two are not real types. They are only used by the parser + * to return keywords "signed" and "unsigned" + */ + typetab[SIGNED].t_tspec = SIGNED; + typetab[UNSIGN].t_tspec = UNSIGN; +} + +/* + * Returns a shared type structure vor arithmetic types and void. + * + * It's important do duplicate this structure (using duptyp() or tdupdyp()) + * if it is to be modified (adding qualifiers or anything else). + */ +type_t * +gettyp(t) + tspec_t t; +{ + return (&typetab[t]); +} + +type_t * +duptyp(tp) + const type_t *tp; +{ + type_t *ntp; + + ntp = getblk(sizeof (type_t)); + STRUCT_ASSIGN(*ntp, *tp); + return (ntp); +} + +/* + * Use tduptyp() instead of duptyp() inside expressions (if the + * allocated memory should be freed after the expr). + */ +type_t * +tduptyp(tp) + const type_t *tp; +{ + type_t *ntp; + + ntp = tgetblk(sizeof (type_t)); + STRUCT_ASSIGN(*ntp, *tp); + return (ntp); +} + +/* + * Returns 1 if the argument is void or an incomplete array, + * struct, union or enum type. + */ +int +incompl(tp) + type_t *tp; +{ + tspec_t t; + + if ((t = tp->t_tspec) == VOID) { + return (1); + } else if (t == ARRAY) { + return (tp->t_aincompl); + } else if (t == STRUCT || t == UNION) { + return (tp->t_str->sincompl); + } else if (t == ENUM) { + return (tp->t_enum->eincompl); + } + return (0); +} + +/* + * Set the flag for (in)complete array, struct, union or enum + * types. + */ +void +setcompl(tp, ic) + type_t *tp; + int ic; +{ + tspec_t t; + + if ((t = tp->t_tspec) == ARRAY) { + tp->t_aincompl = ic; + } else if (t == STRUCT || t == UNION) { + tp->t_str->sincompl = ic; + } else { + if (t != ENUM) + lerror("setcompl() 1"); + tp->t_enum->eincompl = ic; + } +} + +/* + * Remember the storage class of the current declaration in dcs->d_scl + * (the top element of the declaration stack) and detect multiple + * storage classes. + */ +void +addscl(sc) + scl_t sc; +{ + if (sc == INLINE) { + if (dcs->d_inline) + /* duplicate '%s' */ + warning(10, "inline"); + dcs->d_inline = 1; + return; + } + if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || + dcs->d_smod != NOTSPEC || dcs->d_lmod != NOTSPEC) { + /* storage class after type is obsolescent */ + warning(83); + } + if (dcs->d_scl == NOSCL) { + dcs->d_scl = sc; + } else { + /* + * multiple storage classes. An error will be reported in + * deftyp(). + */ + dcs->d_mscl = 1; + } +} + +/* + * Remember the type, modifier or typedef name returned by the parser + * in *dcs (top element of decl stack). This information is used in + * deftyp() to build the type used for all declarators in this + * declaration. + * + * Is tp->t_typedef 1, the type comes from a previously defined typename. + * Otherwise it comes from a type specifier (int, long, ...) or a + * struct/union/enum tag. + */ +void +addtype(tp) + type_t *tp; +{ + tspec_t t; + + if (tp->t_typedef) { + if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || + dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) { + /* + * something like "typedef int a; int a b;" + * This should not happen with current grammar. + */ + lerror("addtype()"); + } + dcs->d_type = tp; + return; + } + + t = tp->t_tspec; + + if (t == STRUCT || t == UNION || t == ENUM) { + /* + * something like "int struct a ..." + * struct/union/enum with anything else is not allowed + */ + if (dcs->d_type != NULL || dcs->d_atyp != NOTSPEC || + dcs->d_lmod != NOTSPEC || dcs->d_smod != NOTSPEC) { + /* + * remember that an error must be reported in + * deftyp(). + */ + dcs->d_terr = 1; + dcs->d_atyp = dcs->d_lmod = dcs->d_smod = NOTSPEC; + } + dcs->d_type = tp; + return; + } + + if (dcs->d_type != NULL && !dcs->d_type->t_typedef) { + /* + * something like "struct a int" + * struct/union/enum with anything else is not allowed + */ + dcs->d_terr = 1; + return; + } + + if (t == LONG && dcs->d_lmod == LONG) { + /* "long long" or "long ... long" */ + t = QUAD; + dcs->d_lmod = NOTSPEC; + if (!quadflg) + /* %s C does not support 'long long' */ + (void)gnuism(265, tflag ? "traditional" : "ANSI"); + } + + if (dcs->d_type != NULL && dcs->d_type->t_typedef) { + /* something like "typedef int a; a long ..." */ + dcs->d_type = tdeferr(dcs->d_type, t); + return; + } + + /* now it can be only a combination of arithmetic types and void */ + if (t == SIGNED || t == UNSIGN) { + /* remeber specifiers "signed" and "unsigned" in dcs->d_smod */ + if (dcs->d_smod != NOTSPEC) + /* + * more then one "signed" and/or "unsigned"; print + * an error in deftyp() + */ + dcs->d_terr = 1; + dcs->d_smod = t; + } else if (t == SHORT || t == LONG || t == QUAD) { + /* + * remember specifiers "short", "long" and "long long" in + * dcs->d_lmod + */ + if (dcs->d_lmod != NOTSPEC) + /* more than one, print error in deftyp() */ + dcs->d_terr = 1; + dcs->d_lmod = t; + } else { + /* + * remember specifiers "void", "char", "int", "float" or + * "double" int dcs->d_atyp + */ + if (dcs->d_atyp != NOTSPEC) + /* more than one, print error in deftyp() */ + dcs->d_terr = 1; + dcs->d_atyp = t; + } +} + +/* + * called if a list of declaration specifiers contains a typedef name + * and other specifiers (except struct, union, enum, typedef name) + */ +static type_t * +tdeferr(td, t) + type_t *td; + tspec_t t; +{ + tspec_t t2; + + t2 = td->t_tspec; + + switch (t) { + case SIGNED: + case UNSIGN: + if (t2 == CHAR || t2 == SHORT || t2 == INT || t2 == LONG || + t2 == QUAD) { + if (!tflag) + /* modifying typedef with ... */ + warning(5, ttab[t].tt_name); + td = duptyp(gettyp(mrgtspec(t2, t))); + td->t_typedef = 1; + return (td); + } + break; + case SHORT: + if (t2 == INT || t2 == UINT) { + /* modifying typedef with ... */ + warning(5, "short"); + td = duptyp(gettyp(t2 == INT ? SHORT : USHORT)); + td->t_typedef = 1; + return (td); + } + break; + case LONG: + if (t2 == INT || t2 == UINT || t2 == LONG || t2 == ULONG || + t2 == FLOAT || t2 == DOUBLE) { + /* modifying typedef with ... */ + warning(5, "long"); + if (t2 == INT) { + td = gettyp(LONG); + } else if (t2 == UINT) { + td = gettyp(ULONG); + } else if (t2 == LONG) { + td = gettyp(QUAD); + } else if (t2 == ULONG) { + td = gettyp(UQUAD); + } else if (t2 == FLOAT) { + td = gettyp(DOUBLE); + } else if (t2 == DOUBLE) { + td = gettyp(LDOUBLE); + } + td = duptyp(td); + td->t_typedef = 1; + return (td); + } + break; + /* LINTED (enumeration values not handled in switch) */ + } + + /* Anything other is not accepted. */ + + dcs->d_terr = 1; + return (td); +} + +/* + * Remember the symbol of a typedef name (2nd arg) in a struct, union + * or enum tag if the typedef name is the first defined for this tag. + * + * If the tag is unnamed, the typdef name is used for identification + * of this tag in lint2. Although its possible that more then one typedef + * name is defined for one tag, the first name defined should be unique + * if the tag is unnamed. + */ +static void +settdsym(tp, sym) + type_t *tp; + sym_t *sym; +{ + tspec_t t; + + if ((t = tp->t_tspec) == STRUCT || t == UNION) { + if (tp->t_str->stdef == NULL) + tp->t_str->stdef = sym; + } else if (t == ENUM) { + if (tp->t_enum->etdef == NULL) + tp->t_enum->etdef = sym; + } +} + +/* + * Remember a qualifier which is part of the declaration specifiers + * (and not the declarator) in the top element of the declaration stack. + * Also detect multiple qualifiers of the same kind. + + * The rememberd qualifier is used by deftyp() to construct the type + * for all declarators. + */ +void +addqual(q) + tqual_t q; +{ + if (q == CONST) { + if (dcs->d_const) { + /* duplicate "%s" */ + warning(10, "const"); + } + dcs->d_const = 1; + } else { + if (q != VOLATILE) + lerror("addqual() 1"); + if (dcs->d_volatile) { + /* duplicate "%s" */ + warning(10, "volatile"); + } + dcs->d_volatile = 1; + } +} + +/* + * Go to the next declaration level (structs, nested structs, blocks, + * argument declaration lists ...) + */ +void +pushdecl(sc) + scl_t sc; +{ + dinfo_t *di; + + if (dflag) + (void)printf("pushdecl(%d)\n", (int)sc); + + /* put a new element on the declaration stack */ + di = xcalloc(1, sizeof (dinfo_t)); + di->d_nxt = dcs; + dcs = di; + di->d_ctx = sc; + di->d_ldlsym = &di->d_dlsyms; +} + +/* + * Go back to previous declaration level + */ +void +popdecl() +{ + dinfo_t *di; + + if (dflag) + (void)printf("popdecl(%d)\n", (int)dcs->d_ctx); + + if (dcs->d_nxt == NULL) + lerror("popdecl() 1"); + di = dcs; + dcs = di->d_nxt; + switch (di->d_ctx) { + case EXTERN: + /* there is nothing after external declarations */ + lerror("popdecl() 2"); + /* NOTREACHED */ + case MOS: + case MOU: + case ENUMCON: + /* + * Symbols declared in (nested) structs or enums are + * part of the next level (they are removed from the + * symbol table if the symbols of the outher level are + * removed) + */ + if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL) + dcs->d_ldlsym = di->d_ldlsym; + break; + case ARG: + /* + * All symbols in dcs->d_dlsyms are introduced in old style + * argument declarations (it's not clean, but possible). + * They are appended to the list of symbols declared in + * an old style argument identifier list or a new style + * parameter type list. + */ + if (di->d_dlsyms != NULL) { + *di->d_ldlsym = dcs->d_fpsyms; + dcs->d_fpsyms = di->d_dlsyms; + } + break; + case ABSTRACT: + /* + * casts and sizeof + * Append all symbols declared in the abstract declaration + * to the list of symbols declared in the surounding decl. + * or block. + * XXX I'm not sure whether they should be removed from the + * symbol table now or later. + */ + if ((*dcs->d_ldlsym = di->d_dlsyms) != NULL) + dcs->d_ldlsym = di->d_ldlsym; + break; + case AUTO: + /* check usage of local vars */ + chkusage(di); + /* FALLTHROUGH */ + case PARG: + /* usage of arguments will be checked by funcend() */ + rmsyms(di->d_dlsyms); + break; + default: + lerror("popdecl() 3"); + } + free(di); +} + +/* + * Set flag d_asm in all declaration stack elements up to the + * outermost one. + * + * This is used to mark compound statements which have, possibly in + * nested compound statements, asm statements. For these compound + * statements no warnings about unused or unitialized variables are + * printed. + * + * There is no need to clear d_asm in dinfo structs with context AUTO, + * because these structs are freed at the end of the compound statement. + * But it must be cleard in the outermost dinfo struct, which has + * context EXTERN. This could be done in clrtyp() and would work for + * C, but not for C++ (due to mixed statements and declarations). Thus + * we clear it in glclup(), which is used to do some cleanup after + * global declarations/definitions. + */ +void +setasm() +{ + dinfo_t *di; + + for (di = dcs; di != NULL; di = di->d_nxt) + di->d_asm = 1; +} + +/* + * Clean all elements of the top element of declaration stack which + * will be used by the next declaration + */ +void +clrtyp() +{ + dcs->d_atyp = dcs->d_smod = dcs->d_lmod = NOTSPEC; + dcs->d_scl = NOSCL; + dcs->d_type = NULL; + dcs->d_const = dcs->d_volatile = 0; + dcs->d_inline = 0; + dcs->d_mscl = dcs->d_terr = 0; + dcs->d_nedecl = 0; + dcs->d_notyp = 0; +} + +/* + * Create a type structure from the informations gathered in + * the declaration stack. + * Complain about storage classes which are not possible in current + * context. + */ +void +deftyp() +{ + tspec_t t, s, l; + type_t *tp; + scl_t scl; + + t = dcs->d_atyp; /* CHAR, INT, FLOAT, DOUBLE, VOID */ + s = dcs->d_smod; /* SIGNED, UNSIGNED */ + l = dcs->d_lmod; /* SHORT, LONG, QUAD */ + tp = dcs->d_type; + scl = dcs->d_scl; + + if (t == NOTSPEC && s == NOTSPEC && l == NOTSPEC && tp == NULL) + dcs->d_notyp = 1; + + if (tp != NULL && (t != NOTSPEC || s != NOTSPEC || l != NOTSPEC)) { + /* should never happen */ + lerror("deftyp() 1"); + } + + if (tp == NULL) { + switch (t) { + case NOTSPEC: + t = INT; + /* FALLTHROUGH */ + case INT: + if (s == NOTSPEC) + s = SIGNED; + break; + case CHAR: + if (l != NOTSPEC) { + dcs->d_terr = 1; + l = NOTSPEC; + } + break; + case FLOAT: + if (l == LONG) { + l = NOTSPEC; + t = DOUBLE; + if (!tflag) + /* use 'double' instead of ... */ + warning(6); + } + break; + case DOUBLE: + if (l == LONG) { + l = NOTSPEC; + t = LDOUBLE; + if (tflag) + /* 'long double' is illegal in ... */ + warning(266); + } + break; + case VOID: + break; + default: + lerror("deftyp() 2"); + } + if (t != INT && t != CHAR && (s != NOTSPEC || l != NOTSPEC)) { + dcs->d_terr = 1; + l = s = NOTSPEC; + } + if (l != NOTSPEC) + t = l; + dcs->d_type = gettyp(mrgtspec(t, s)); + } + + if (dcs->d_mscl) { + /* only one storage class allowed */ + error(7); + } + if (dcs->d_terr) { + /* illegal type combination */ + error(4); + } + + if (dcs->d_ctx == EXTERN) { + if (scl == REG || scl == AUTO) { + /* illegal storage class */ + error(8); + scl = NOSCL; + } + } else if (dcs->d_ctx == ARG || dcs->d_ctx == PARG) { + if (scl != NOSCL && scl != REG) { + /* only "register" valid ... */ + error(9); + scl = NOSCL; + } + } + + dcs->d_scl = scl; + + if (dcs->d_const && dcs->d_type->t_const) { + if (!dcs->d_type->t_typedef) + lerror("deftyp() 3"); + /* typedef already qualified with "%s" */ + warning(68, "const"); + } + if (dcs->d_volatile && dcs->d_type->t_volatile) { + if (!dcs->d_type->t_typedef) + lerror("deftyp() 4"); + /* typedef already qualified with "%s" */ + warning(68, "volatile"); + } + + if (dcs->d_const || dcs->d_volatile) { + dcs->d_type = duptyp(dcs->d_type); + dcs->d_type->t_const |= dcs->d_const; + dcs->d_type->t_volatile |= dcs->d_volatile; + } +} + +/* + * Merge type specifiers (char, ..., long long, signed, unsigned). + */ +static tspec_t +mrgtspec(t, s) + tspec_t t, s; +{ + if (s == SIGNED || s == UNSIGN) { + if (t == CHAR) { + t = s == SIGNED ? SCHAR : UCHAR; + } else if (t == SHORT) { + t = s == SIGNED ? SHORT : USHORT; + } else if (t == INT) { + t = s == SIGNED ? INT : UINT; + } else if (t == LONG) { + t = s == SIGNED ? LONG : ULONG; + } else if (t == QUAD) { + t = s == SIGNED ? QUAD : UQUAD; + } + } + + return (t); +} + +/* + * Return the length of a type in bit. + * + * Printing a message if the outhermost dimension of an array is 0 must + * be done by the caller. All other problems are reported by length() + * if name is not NULL. + */ +int +length(tp, name) + type_t *tp; + const char *name; +{ + int elem, elsz; + + elem = 1; + while (tp->t_tspec == ARRAY) { + elem *= tp->t_dim; + tp = tp->t_subt; + } + switch (tp->t_tspec) { + case FUNC: + /* compiler takes size of function */ + lerror(msgs[12]); + /* NOTREACHED */ + case STRUCT: + case UNION: + if (incompl(tp) && name != NULL) { + /* incomplete structure or union %s: %s */ + error(31, tp->t_str->stag->s_name, name); + } + elsz = tp->t_str->size; + break; + case ENUM: + if (incompl(tp) && name != NULL) { + /* incomplete enum type: %s */ + warning(13, name); + } + /* FALLTHROUGH */ + default: + elsz = size(tp->t_tspec); + if (elsz <= 0) + lerror("length()"); + break; + } + return (elem * elsz); +} + +/* + * Get the alignment of the given Type in bits. + */ +int +getbound(tp) + type_t *tp; +{ + int a; + tspec_t t; + + while (tp->t_tspec == ARRAY) + tp = tp->t_subt; + + if ((t = tp->t_tspec) == STRUCT || t == UNION) { + a = tp->t_str->align; + } else if (t == FUNC) { + /* compiler takes alignment of function */ + error(14); + a = ALIGN(1) * CHAR_BIT; + } else { + if ((a = size(t)) == 0) { + a = CHAR_BIT; + } else if (a > ALIGN(1) * CHAR_BIT) { + a = ALIGN(1) * CHAR_BIT; + } + } + if (a < CHAR_BIT || a > ALIGN(1) * CHAR_BIT) + lerror("getbound() 1"); + return (a); +} + +/* + * Concatenate two lists of symbols by s_nxt. Used by declarations of + * struct/union/enum elements and parameters. + */ +sym_t * +lnklst(l1, l2) + sym_t *l1, *l2; +{ + sym_t *l; + + if ((l = l1) == NULL) + return (l2); + while (l1->s_nxt != NULL) + l1 = l1->s_nxt; + l1->s_nxt = l2; + return (l); +} + +/* + * Check if the type of the given symbol is valid and print an error + * message if it is not. + * + * Invalid types are: + * - arrays of incomlete types or functions + * - functions returning arrays or functions + * - void types other than type of function or pointer + */ +void +chktyp(sym) + sym_t *sym; +{ + tspec_t to, t; + type_t **tpp, *tp; + + tpp = &sym->s_type; + to = NOTSPEC; + while ((tp = *tpp) != NULL) { + t = tp->t_tspec; + /* + * If this is the type of an old style function definition, + * a better warning is printed in funcdef(). + */ + if (t == FUNC && !tp->t_proto && + !(to == NOTSPEC && sym->s_osdef)) { + if (sflag && hflag) + /* function declaration is not a prototype */ + warning(287); + } + if (to == FUNC) { + if (t == FUNC || t == ARRAY) { + /* function returns illegal type */ + error(15); + if (t == FUNC) { + *tpp = incref(*tpp, PTR); + } else { + *tpp = incref((*tpp)->t_subt, PTR); + } + return; + } else if (tp->t_const || tp->t_volatile) { + if (sflag) { /* XXX oder better !tflag ? */ + /* function cannot return const... */ + warning(228); + } + } + } if (to == ARRAY) { + if (t == FUNC) { + /* array of function is illegal */ + error(16); + *tpp = gettyp(INT); + return; + } else if (t == ARRAY && tp->t_dim == 0) { + /* null dimension */ + error(17); + return; + } else if (t == VOID) { + /* illegal use of void */ + error(18); + *tpp = gettyp(INT); +#if 0 /* errors are produced by length() */ + } else if (incompl(tp)) { + /* array of incomplete type */ + if (sflag) { + error(301); + } else { + warning(301); + } +#endif + } + } else if (to == NOTSPEC && t == VOID) { + if (dcs->d_ctx == PARG) { + if (sym->s_scl != ABSTRACT) { + if (sym->s_name == unnamed) + lerror("chktyp()"); + /* void param cannot have name: %s */ + error(61, sym->s_name); + *tpp = gettyp(INT); + } + } else if (dcs->d_ctx == ABSTRACT) { + /* ok */ + } else if (sym->s_scl != TYPEDEF) { + /* void type for %s */ + error(19, sym->s_name); + *tpp = gettyp(INT); + } + } + if (t == VOID && to != PTR) { + if (tp->t_const || tp->t_volatile) { + /* inappropriate qualifiers with "void" */ + warning(69); + tp->t_const = tp->t_volatile = 0; + } + } + tpp = &tp->t_subt; + to = t; + } +} + +/* + * Process the declarator of a struct/union element. + */ +sym_t * +decl1str(dsym) + sym_t *dsym; +{ + type_t *tp; + tspec_t t; + int sz, o, len; + scl_t sc; + + if ((sc = dsym->s_scl) != MOS && sc != MOU) + lerror("decl1str() 1"); + + if (dcs->d_rdcsym != NULL) { + if ((sc = dcs->d_rdcsym->s_scl) != MOS && sc != MOU) + /* should be ensured by storesym() */ + lerror("decl1str() 2"); + if (dsym->s_styp == dcs->d_rdcsym->s_styp) { + /* duplicate member name: %s */ + error(33, dsym->s_name); + rmsym(dcs->d_rdcsym); + } + } + + chktyp(dsym); + + t = (tp = dsym->s_type)->t_tspec; + + if (dsym->s_field) { + /* + * bit field + * + * only unsigned und signed int are protable bit-field types + *(at least in ANSI C, in traditional C only unsigned int) + */ + if (t == CHAR || t == UCHAR || t == SCHAR || + t == SHORT || t == USHORT || t == ENUM) { + if (sflag) { + /* bit-field type '%s' invalid in ANSI C */ + warning(273, tyname(tp)); + } else if (pflag) { + /* nonportable bit-field type */ + warning(34); + } + } else if (t == INT && dcs->d_smod == NOTSPEC) { + if (pflag) { + /* nonportable bit-field type */ + warning(34); + } + } else if (t != INT && t != UINT) { + /* illegal bit-field type */ + error(35); + sz = tp->t_flen; + dsym->s_type = tp = duptyp(gettyp(t = INT)); + if ((tp->t_flen = sz) > size(t)) + tp->t_flen = size(t); + } + if ((len = tp->t_flen) < 0 || len > size(t)) { + /* illegal bit-field size */ + error(36); + tp->t_flen = size(t); + } else if (len == 0 && dsym->s_name != unnamed) { + /* zero size bit-field */ + error(37); + tp->t_flen = size(t); + } + if (dsym->s_scl == MOU) { + /* illegal use of bit-field */ + error(41); + dsym->s_type->t_isfield = 0; + dsym->s_field = 0; + } + } else if (t == FUNC) { + /* function illegal in structure or union */ + error(38); + dsym->s_type = tp = incref(tp, t = PTR); + } + + /* + * bit-fields of length 0 are not warned about because length() + * does not return the length of the bit-field but the length + * of the type the bit-field is packed in (its ok) + */ + if ((sz = length(dsym->s_type, dsym->s_name)) == 0) { + if (t == ARRAY && dsym->s_type->t_dim == 0) { + /* illegal zero sized structure member: %s */ + warning(39, dsym->s_name); + } + } + + if (dcs->d_ctx == MOU) { + o = dcs->d_offset; + dcs->d_offset = 0; + } + if (dsym->s_field) { + align(getbound(tp), tp->t_flen); + dsym->s_value.v_quad = (dcs->d_offset / size(t)) * size(t); + tp->t_foffs = dcs->d_offset - (int)dsym->s_value.v_quad; + dcs->d_offset += tp->t_flen; + } else { + align(getbound(tp), 0); + dsym->s_value.v_quad = dcs->d_offset; + dcs->d_offset += sz; + } + if (dcs->d_ctx == MOU) { + if (o > dcs->d_offset) + dcs->d_offset = o; + } + + chkfdef(dsym, 0); + + return (dsym); +} + +/* + * Aligns next structure element as required. + * + * al contains the required alignment, len the length of a bit-field. + */ +static void +align(al, len) + int al, len; +{ + int no; + + /* + * The alignment of the current element becomes the alignment of + * the struct/union if it is larger than the current alignment + * of the struct/union. + */ + if (al > dcs->d_stralign) + dcs->d_stralign = al; + + no = (dcs->d_offset + (al - 1)) & ~(al - 1); + if (len == 0 || dcs->d_offset + len > no) + dcs->d_offset = no; +} + +/* + * Remember the width of the field in its type structure. + */ +sym_t * +bitfield(dsym, len) + sym_t *dsym; + int len; +{ + if (dsym == NULL) { + dsym = getblk(sizeof (sym_t)); + dsym->s_name = unnamed; + dsym->s_kind = FMOS; + dsym->s_scl = MOS; + dsym->s_type = gettyp(INT); + dsym->s_blklev = -1; + } + dsym->s_type = duptyp(dsym->s_type); + dsym->s_type->t_isfield = 1; + dsym->s_type->t_flen = len; + dsym->s_field = 1; + return (dsym); +} + +/* + * Collect informations about a sequence of asterisks and qualifiers + * in a list of type pqinf_t. + * Qualifiers refer always to the left asterisk. The rightmost asterisk + * will be at the top of the list. + */ +pqinf_t * +mergepq(p1, p2) + pqinf_t *p1, *p2; +{ + pqinf_t *p; + + if (p2->p_pcnt != 0) { + /* left '*' at the end of the list */ + for (p = p2; p->p_nxt != NULL; p = p->p_nxt) ; + p->p_nxt = p1; + return (p2); + } else { + if (p2->p_const) { + if (p1->p_const) { + /* duplicate %s */ + warning(10, "const"); + } + p1->p_const = 1; + } + if (p2->p_volatile) { + if (p1->p_volatile) { + /* duplicate %s */ + warning(10, "volatile"); + } + p1->p_volatile = 1; + } + free(p2); + return (p1); + } +} + +/* + * Followint 3 functions extend the type of a declarator with + * pointer, function and array types. + * + * The current type is the Type built by deftyp() (dcs->d_type) and + * pointer, function and array types already added for this + * declarator. The new type extension is inserted between both. + */ +sym_t * +addptr(decl, pi) + sym_t *decl; + pqinf_t *pi; +{ + type_t **tpp, *tp; + pqinf_t *npi; + + tpp = &decl->s_type; + while (*tpp != dcs->d_type) + tpp = &(*tpp)->t_subt; + + while (pi != NULL) { + *tpp = tp = getblk(sizeof (type_t)); + tp->t_tspec = PTR; + tp->t_const = pi->p_const; + tp->t_volatile = pi->p_volatile; + *(tpp = &tp->t_subt) = dcs->d_type; + npi = pi->p_nxt; + free(pi); + pi = npi; + } + return (decl); +} + +/* + * If a dimension was specified, dim is 1, otherwise 0 + * n is the specified dimension + */ +sym_t * +addarray(decl, dim, n) + sym_t *decl; + int dim, n; +{ + type_t **tpp, *tp; + + tpp = &decl->s_type; + while (*tpp != dcs->d_type) + tpp = &(*tpp)->t_subt; + + *tpp = tp = getblk(sizeof (type_t)); + tp->t_tspec = ARRAY; + tp->t_subt = dcs->d_type; + tp->t_dim = n; + + if (n < 0) { + /* zero or negative array dimension */ + error(20); + n = 0; + } else if (n == 0 && dim) { + /* zero or negative array dimension */ + warning(20); + } else if (n == 0 && !dim) { + /* is incomplete type */ + setcompl(tp, 1); + } + + return (decl); +} + +sym_t * +addfunc(decl, args) + sym_t *decl, *args; +{ + type_t **tpp, *tp; + + if (dcs->d_proto) { + if (tflag) + /* function prototypes are illegal in traditional C */ + warning(270); + args = nsfunc(decl, args); + } else { + osfunc(decl, args); + } + + /* + * The symbols are removed from the symbol table by popdecl() after + * addfunc(). To be able to restore them if this is a function + * definition, a pointer to the list of all symbols is stored in + * dcs->d_nxt->d_fpsyms. Also a list of the arguments (concatenated + * by s_nxt) is stored in dcs->d_nxt->d_fargs. + * (dcs->d_nxt must be used because *dcs is the declaration stack + * element created for the list of params and is removed after + * addfunc()) + */ + if (dcs->d_nxt->d_ctx == EXTERN && + decl->s_type == dcs->d_nxt->d_type) { + dcs->d_nxt->d_fpsyms = dcs->d_dlsyms; + dcs->d_nxt->d_fargs = args; + } + + tpp = &decl->s_type; + while (*tpp != dcs->d_nxt->d_type) + tpp = &(*tpp)->t_subt; + + *tpp = tp = getblk(sizeof (type_t)); + tp->t_tspec = FUNC; + tp->t_subt = dcs->d_nxt->d_type; + if ((tp->t_proto = dcs->d_proto) != 0) + tp->t_args = args; + tp->t_vararg = dcs->d_vararg; + + return (decl); +} + +/* + * Called for new style function declarations. + */ +/* ARGSUSED */ +static sym_t * +nsfunc(decl, args) + sym_t *decl, *args; +{ + sym_t *arg, *sym; + scl_t sc; + int n; + + /* + * Declarations of structs/unions/enums in param lists are legal, + * but senseless. + */ + for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) { + sc = sym->s_scl; + if (sc == STRTAG || sc == UNIONTAG || sc == ENUMTAG) { + /* dubious tag declaration: %s %s */ + warning(85, scltoa(sc), sym->s_name); + } + } + + n = 1; + for (arg = args; arg != NULL; arg = arg->s_nxt) { + if (arg->s_type->t_tspec == VOID) { + if (n > 1 || arg->s_nxt != NULL) { + /* "void" must be sole parameter */ + error(60); + arg->s_type = gettyp(INT); + } + } + n++; + } + + /* return NULL if first param is VOID */ + return (args != NULL && args->s_type->t_tspec != VOID ? args : NULL); +} + +/* + * Called for old style function declarations. + */ +static void +osfunc(decl, args) + sym_t *decl, *args; +{ + /* + * Remember list of params only if this is really seams to be + * a function definition. + */ + if (dcs->d_nxt->d_ctx == EXTERN && + decl->s_type == dcs->d_nxt->d_type) { + /* + * We assume that this becomes a function definition. If + * we are wrong, its corrected in chkfdef(). + */ + if (args != NULL) { + decl->s_osdef = 1; + decl->s_args = args; + } + } else { + if (args != NULL) + /* function prototype parameters must have types */ + warning(62); + } +} + +/* + * Lists of Identifiers in functions declarations are allowed only if + * its also a function definition. If this is not the case, print a + * error message. + */ +void +chkfdef(sym, msg) + sym_t *sym; + int msg; +{ + if (sym->s_osdef) { + if (msg) { + /* incomplete or misplaced function definition */ + error(22); + } + sym->s_osdef = 0; + sym->s_args = NULL; + } +} + +/* + * Process the name in a declarator. + * If the symbol does already exists, a new one is created. + * The symbol becomes one of the storage classes EXTERN, STATIC, AUTO or + * TYPEDEF. + * s_def and s_reg are valid after dname(). + */ +sym_t * +dname(sym) + sym_t *sym; +{ + scl_t sc; + + if (sym->s_scl == NOSCL) { + dcs->d_rdcsym = NULL; + } else if (sym->s_defarg) { + sym->s_defarg = 0; + dcs->d_rdcsym = NULL; + } else { + dcs->d_rdcsym = sym; + sym = pushdown(sym); + } + + switch (dcs->d_ctx) { + case MOS: + case MOU: + /* Parent setzen */ + sym->s_styp = dcs->d_tagtyp->t_str; + sym->s_def = DEF; + sym->s_value.v_tspec = INT; + sc = dcs->d_ctx; + break; + case EXTERN: + /* + * static and external symbols without "extern" are + * considered to be tentative defined, external + * symbols with "extern" are declared, and typedef names + * are defined. Tentative defined and declared symbols + * may become defined if an initializer is present or + * this is a function definition. + */ + if ((sc = dcs->d_scl) == NOSCL) { + sc = EXTERN; + sym->s_def = TDEF; + } else if (sc == STATIC) { + sym->s_def = TDEF; + } else if (sc == TYPEDEF) { + sym->s_def = DEF; + } else if (sc == EXTERN) { + sym->s_def = DECL; + } else { + lerror("dname() 1"); + } + break; + case PARG: + sym->s_arg = 1; + /* FALLTHROUGH */ + case ARG: + if ((sc = dcs->d_scl) == NOSCL) { + sc = AUTO; + } else if (sc == REG) { + sym->s_reg = 1; + sc = AUTO; + } else { + lerror("dname() 2"); + } + sym->s_def = DEF; + break; + case AUTO: + if ((sc = dcs->d_scl) == NOSCL) { + /* + * XXX somewhat ugly because we dont know whether + * this is AUTO or EXTERN (functions). If we are + * wrong it must be corrected in decl1loc(), where + * we have the neccessary type information. + */ + sc = AUTO; + sym->s_def = DEF; + } else if (sc == AUTO || sc == STATIC || sc == TYPEDEF) { + sym->s_def = DEF; + } else if (sc == REG) { + sym->s_reg = 1; + sc = AUTO; + sym->s_def = DEF; + } else if (sc == EXTERN) { + sym->s_def = DECL; + } else { + lerror("dname() 3"); + } + break; + default: + lerror("dname() 4"); + } + sym->s_scl = sc; + + sym->s_type = dcs->d_type; + + dcs->d_fpsyms = NULL; + + return (sym); +} + +/* + * Process a name in the list of formal params in an old style function + * definition. + */ +sym_t * +iname(sym) + sym_t *sym; +{ + if (sym->s_scl != NOSCL) { + if (blklev == sym->s_blklev) { + /* redeclaration of formal parameter %s */ + error(21, sym->s_name); + if (!sym->s_defarg) + lerror("iname()"); + } + sym = pushdown(sym); + } + sym->s_type = gettyp(INT); + sym->s_scl = AUTO; + sym->s_def = DEF; + sym->s_defarg = sym->s_arg = 1; + return (sym); +} + +/* + * Create the type of a tag. + * + * tag points to the symbol table entry of the tag + * kind is the kind of the tag (STRUCT/UNION/ENUM) + * decl is 1 if the type of the tag will be completed in this declaration + * (the following token is T_LBRACE) + * semi is 1 if the following token is T_SEMI + */ +type_t * +mktag(tag, kind, decl, semi) + sym_t *tag; + tspec_t kind; + int decl, semi; +{ + scl_t scl; + type_t *tp; + + if (kind == STRUCT) { + scl = STRTAG; + } else if (kind == UNION) { + scl = UNIONTAG; + } else if (kind == ENUM) { + scl = ENUMTAG; + } else { + lerror("mktag()"); + } + + if (tag != NULL) { + if (tag->s_scl != NOSCL) { + tag = newtag(tag, scl, decl, semi); + } else { + /* a new tag, no empty declaration */ + dcs->d_nxt->d_nedecl = 1; + if (scl == ENUMTAG && !decl) { + if (!tflag && (sflag || pflag)) + /* forward reference to enum type */ + warning(42); + } + } + if (tag->s_scl == NOSCL) { + tag->s_scl = scl; + tag->s_type = tp = getblk(sizeof (type_t)); + } else { + tp = tag->s_type; + } + } else { + tag = getblk(sizeof (sym_t)); + tag->s_name = unnamed; + STRUCT_ASSIGN(tag->s_dpos, curr_pos); + tag->s_kind = FTAG; + tag->s_scl = scl; + tag->s_blklev = -1; + tag->s_type = tp = getblk(sizeof (type_t)); + dcs->d_nxt->d_nedecl = 1; + } + + if (tp->t_tspec == NOTSPEC) { + tp->t_tspec = kind; + if (kind != ENUM) { + tp->t_str = getblk(sizeof (str_t)); + tp->t_str->align = CHAR_BIT; + tp->t_str->stag = tag; + } else { + tp->t_isenum = 1; + tp->t_enum = getblk(sizeof (enum_t)); + tp->t_enum->etag = tag; + } + /* ist unvollstaendiger Typ */ + setcompl(tp, 1); + } + + return (tp); +} + +/* + * Checks all possible cases of tag redeclarations. + * decl is 1 if T_LBRACE follows + * semi is 1 if T_SEMI follows + */ +static sym_t * +newtag(tag, scl, decl, semi) + sym_t *tag; + scl_t scl; + int decl, semi; +{ + if (tag->s_blklev < blklev) { + if (semi) { + /* "struct a;" */ + if (!tflag) { + if (!sflag) + /* decl. introduces new type ... */ + warning(44, scltoa(scl), tag->s_name); + tag = pushdown(tag); + } else if (tag->s_scl != scl) { + /* base type is really "%s %s" */ + warning(45, scltoa(tag->s_scl), tag->s_name); + } + dcs->d_nxt->d_nedecl = 1; + } else if (decl) { + /* "struct a { ..." */ + if (hflag) + /* redefinition hides earlier one: %s */ + warning(43, tag->s_name); + tag = pushdown(tag); + dcs->d_nxt->d_nedecl = 1; + } else if (tag->s_scl != scl) { + /* base type is really "%s %s" */ + warning(45, scltoa(tag->s_scl), tag->s_name); + /* declaration introduces new type in ANSI C: %s %s */ + if (!sflag) + warning(44, scltoa(scl), tag->s_name); + tag = pushdown(tag); + dcs->d_nxt->d_nedecl = 1; + } + } else { + if (tag->s_scl != scl) { + /* (%s) tag redeclared */ + error(46, scltoa(tag->s_scl)); + prevdecl(-1, tag); + tag = pushdown(tag); + dcs->d_nxt->d_nedecl = 1; + } else if (decl && !incompl(tag->s_type)) { + /* (%s) tag redeclared */ + error(46, scltoa(tag->s_scl)); + prevdecl(-1, tag); + tag = pushdown(tag); + dcs->d_nxt->d_nedecl = 1; + } else if (semi || decl) { + dcs->d_nxt->d_nedecl = 1; + } + } + return (tag); +} + +const char * +scltoa(sc) + scl_t sc; +{ + const char *s; + + switch (sc) { + case EXTERN: s = "extern"; break; + case STATIC: s = "static"; break; + case AUTO: s = "auto"; break; + case REG: s = "register"; break; + case TYPEDEF: s = "typedef"; break; + case STRTAG: s = "struct"; break; + case UNIONTAG: s = "union"; break; + case ENUMTAG: s = "enum"; break; + default: lerror("tagttoa()"); + } + return (s); +} + +/* + * Completes the type of a tag in a struct/union/enum declaration. + * tp points to the type of the, tag, fmem to the list of members/enums. + */ +type_t * +compltag(tp, fmem) + type_t *tp; + sym_t *fmem; +{ + tspec_t t; + str_t *sp; + int n; + sym_t *mem; + + /* from now a complete type */ + setcompl(tp, 0); + + if ((t = tp->t_tspec) != ENUM) { + align(dcs->d_stralign, 0); + sp = tp->t_str; + sp->align = dcs->d_stralign; + sp->size = dcs->d_offset; + sp->memb = fmem; + if (sp->size == 0) { + /* zero sized %s */ + (void)gnuism(47, ttab[t].tt_name); + } else { + n = 0; + for (mem = fmem; mem != NULL; mem = mem->s_nxt) { + if (mem->s_name != unnamed) + n++; + } + if (n == 0) { + /* %s has no named members */ + warning(65, + t == STRUCT ? "structure" : "union"); + } + } + } else { + tp->t_enum->elem = fmem; + } + return (tp); +} + +/* + * Processes the name of an enumerator in en enum declaration. + * + * sym points to the enumerator + * val is the value of the enumerator + * impl is 1 if the the value of the enumerator was not explicit specified. + */ +sym_t * +ename(sym, val, impl) + sym_t *sym; + int val, impl; +{ + if (sym->s_scl) { + if (sym->s_blklev == blklev) { + /* no hflag, because this is illegal!!! */ + if (sym->s_arg) { + /* enumeration constant hides parameter: %s */ + warning(57, sym->s_name); + } else { + /* redeclaration of %s */ + error(27, sym->s_name); + /* + * inside blocks it should not too complicated + * to find the position of the previous + * declaration + */ + if (blklev == 0) + prevdecl(-1, sym); + } + } else { + if (hflag) + /* redefinition hides earlier one: %s */ + warning(43, sym->s_name); + } + sym = pushdown(sym); + } + sym->s_scl = ENUMCON; + sym->s_type = dcs->d_tagtyp; + sym->s_value.v_tspec = INT; + sym->s_value.v_quad = val; + if (impl && val - 1 == INT_MAX) { + /* overflow in enumeration values: %s */ + warning(48, sym->s_name); + } + enumval = val + 1; + return (sym); +} + +/* + * Process a single external declarator. + */ +void +decl1ext(dsym, initflg) + sym_t *dsym; + int initflg; +{ + int warn, rval, redec; + sym_t *rdsym; + + chkfdef(dsym, 1); + + chktyp(dsym); + + if (initflg && !(initerr = chkinit(dsym))) + dsym->s_def = DEF; + + /* + * Declarations of functions are marked as "tentative" in dname(). + * This is wrong because there are no tentative function + * definitions. + */ + if (dsym->s_type->t_tspec == FUNC && dsym->s_def == TDEF) + dsym->s_def = DECL; + + if (dcs->d_inline) { + if (dsym->s_type->t_tspec == FUNC) { + dsym->s_inline = 1; + } else { + /* variable declared inline: %s */ + warning(268, dsym->s_name); + } + } + + /* Write the declaration into the output file */ + if (plibflg && llibflg && + dsym->s_type->t_tspec == FUNC && dsym->s_type->t_proto) { + /* + * With both LINTLIBRARY and PROTOLIB the prototyp is + * written as a function definition to the output file. + */ + rval = dsym->s_type->t_subt->t_tspec != VOID; + outfdef(dsym, &dsym->s_dpos, rval, 0, NULL); + } else { + outsym(dsym, dsym->s_scl, dsym->s_def); + } + + if ((rdsym = dcs->d_rdcsym) != NULL) { + + /* + * If the old symbol stems from a old style function definition + * we have remembered the params in rdsmy->s_args and compare + * them with the params of the prototype. + */ + if (rdsym->s_osdef && dsym->s_type->t_proto) { + redec = chkosdef(rdsym, dsym); + } else { + redec = 0; + } + + if (!redec && !isredec(dsym, (warn = 0, &warn))) { + + if (warn) { + /* redeclaration of %s */ + (*(sflag ? error : warning))(27, dsym->s_name); + prevdecl(-1, rdsym); + } + + /* + * Overtake the rememberd params if the new symbol + * is not a prototype. + */ + if (rdsym->s_osdef && !dsym->s_type->t_proto) { + dsym->s_osdef = rdsym->s_osdef; + dsym->s_args = rdsym->s_args; + STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); + } + + /* + * Remember the position of the declaration if the + * old symbol was a prototype and the new is not. + * Also remember the position if the old symbol + * was defined and the new is not. + */ + if (rdsym->s_type->t_proto && !dsym->s_type->t_proto) { + STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); + } else if (rdsym->s_def == DEF && dsym->s_def != DEF) { + STRUCT_ASSIGN(dsym->s_dpos, rdsym->s_dpos); + } + + /* + * Copy informations about usage of the name into + * the new symbol. + */ + cpuinfo(dsym, rdsym); + + /* Once a name is defined, it remains defined. */ + if (rdsym->s_def == DEF) + dsym->s_def = DEF; + + /* once a function is inline, it remains inline */ + if (rdsym->s_inline) + dsym->s_inline = 1; + + compltyp(dsym, rdsym); + + } + + rmsym(rdsym); + } + + if (dsym->s_scl == TYPEDEF) { + dsym->s_type = duptyp(dsym->s_type); + dsym->s_type->t_typedef = 1; + settdsym(dsym->s_type, dsym); + } + +} + +/* + * Copies informations about usage into a new symbol table entry of + * the same symbol. + */ +void +cpuinfo(sym, rdsym) + sym_t *sym, *rdsym; +{ + sym->s_spos = rdsym->s_spos; + sym->s_upos = rdsym->s_upos; + sym->s_set = rdsym->s_set; + sym->s_used = rdsym->s_used; +} + +/* + * Prints an error and returns 1 if a symbol is redeclared/redefined. + * Otherwise returns 0 and, in some cases of minor problems, prints + * a warning. + */ +int +isredec(dsym, warn) + sym_t *dsym; + int *warn; +{ + sym_t *rsym; + + if ((rsym = dcs->d_rdcsym)->s_scl == ENUMCON) { + /* redeclaration of %s */ + error(27, dsym->s_name); + prevdecl(-1, rsym); + return (1); + } + if (rsym->s_scl == TYPEDEF) { + /* typedef redeclared: %s */ + error(89, dsym->s_name); + prevdecl(-1, rsym); + return (1); + } + if (dsym->s_scl == TYPEDEF) { + /* redeclaration of %s */ + error(27, dsym->s_name); + prevdecl(-1, rsym); + return (1); + } + if (rsym->s_def == DEF && dsym->s_def == DEF) { + /* redefinition of %s */ + error(28, dsym->s_name); + prevdecl(-1, rsym); + return(1); + } + if (!eqtype(rsym->s_type, dsym->s_type, 0, 0, warn)) { + /* redeclaration of %s */ + error(27, dsym->s_name); + prevdecl(-1, rsym); + return(1); + } + if (rsym->s_scl == EXTERN && dsym->s_scl == EXTERN) + return(0); + if (rsym->s_scl == STATIC && dsym->s_scl == STATIC) + return(0); + if (rsym->s_scl == STATIC && dsym->s_def == DECL) + return(0); + if (rsym->s_scl == EXTERN && rsym->s_def == DEF) { + /* + * All cases except "int a = 1; static int a;" are catched + * above with or without a warning + */ + /* redeclaration of %s */ + error(27, dsym->s_name); + prevdecl(-1, rsym); + return(1); + } + if (rsym->s_scl == EXTERN) { + /* previously declared extern, becomes static: %s */ + warning(29, dsym->s_name); + prevdecl(-1, rsym); + return(0); + } + /* + * Now its on of: + * "static a; int a;", "static a; int a = 1;", "static a = 1; int a;" + */ + /* redeclaration of %s; ANSI C requires "static" */ + if (sflag) { + warning(30, dsym->s_name); + prevdecl(-1, rsym); + } + dsym->s_scl = STATIC; + return (0); +} + +/* + * Checks if two types are compatible. Returns 0 if not, otherwise 1. + * + * ignqual ignore qualifiers of type; used for function params + * promot promote left type; used for comparision of params of + * old style function definitions with params of prototypes. + * *warn set to 1 if an old style function declaration is not + * compatible with a prototype + */ +int +eqtype(tp1, tp2, ignqual, promot, warn) + type_t *tp1, *tp2; + int ignqual, promot, *warn; +{ + tspec_t t; + + 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 (t != tp2->t_tspec) + return (0); + + if (tp1->t_const != tp2->t_const && !ignqual && !tflag) + return (0); + + if (tp1->t_volatile != tp2->t_volatile && !ignqual && !tflag) + return (0); + + if (t == STRUCT || t == UNION) + return (tp1->t_str == tp2->t_str); + + if (t == ARRAY && tp1->t_dim != tp2->t_dim) { + if (tp1->t_dim != 0 && tp2->t_dim != 0) + return (0); + } + + /* dont check prototypes for traditional */ + if (t == FUNC && !tflag) { + 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; + + } + + return (tp1 == tp2); +} + +/* + * Compares the parameter types of two prototypes. + */ +static int +eqargs(tp1, tp2, warn) + type_t *tp1, *tp2; + int *warn; +{ + sym_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->s_type, a2->s_type, 1, 0, warn) == 0) + return (0); + + a1 = a1->s_nxt; + a2 = a2->s_nxt; + + } + + 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; +{ + sym_t *arg; + tspec_t t; + + if (tp->t_vararg) { + if (warn != NULL) + *warn = 1; + } + for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) { + if ((t = arg->s_type->t_tspec) == FLOAT || + t == CHAR || t == SCHAR || t == UCHAR || + t == SHORT || t == USHORT) { + if (warn != NULL) + *warn = 1; + } + } + return (1); +} + +/* + * Compares a prototype declaration with the remembered arguments of + * a previous old style function definition. + */ +static int +chkosdef(rdsym, dsym) + sym_t *rdsym, *dsym; +{ + sym_t *args, *pargs, *arg, *parg; + int narg, nparg, n; + int warn, msg; + + args = rdsym->s_args; + pargs = dsym->s_type->t_args; + + msg = 0; + + narg = nparg = 0; + for (arg = args; arg != NULL; arg = arg->s_nxt) + narg++; + for (parg = pargs; parg != NULL; parg = parg->s_nxt) + nparg++; + if (narg != nparg) { + /* prototype does not match old-style definition */ + error(63); + msg = 1; + goto end; + } + + arg = args; + parg = pargs; + n = 1; + while (narg--) { + warn = 0; + /* + * If it does not match due to promotion and sflag is + * not set we print only a warning. + */ + if (!eqtype(arg->s_type, parg->s_type, 1, 1, &warn) || warn) { + /* prototype does not match old-style def., arg #%d */ + error(299, n); + msg = 1; + } + arg = arg->s_nxt; + parg = parg->s_nxt; + n++; + } + + end: + if (msg) + /* old style definition */ + prevdecl(300, rdsym); + + return (msg); +} + +/* + * Complets a type by copying the dimension and prototype information + * from a second compatible type. + * + * Following lines are legal: + * "typedef a[]; a b; a b[10]; a c; a c[20];" + * "typedef ft(); ft f; f(int); ft g; g(long);" + * This means that, if a type is completed, the type structure must + * be duplicated. + */ +void +compltyp(dsym, ssym) + sym_t *dsym, *ssym; +{ + type_t **dstp, *src; + type_t *dst; + + dstp = &dsym->s_type; + src = ssym->s_type; + + while ((dst = *dstp) != NULL) { + if (src == NULL || dst->t_tspec != src->t_tspec) + lerror("compltyp() 1"); + if (dst->t_tspec == ARRAY) { + if (dst->t_dim == 0 && src->t_dim != 0) { + *dstp = dst = duptyp(dst); + dst->t_dim = src->t_dim; + /* now a complete Typ */ + setcompl(dst, 0); + } + } else if (dst->t_tspec == FUNC) { + if (!dst->t_proto && src->t_proto) { + *dstp = dst = duptyp(dst); + dst->t_proto = 1; + dst->t_args = src->t_args; + } + } + dstp = &dst->t_subt; + src = src->t_subt; + } +} + +/* + * Completes the declaration of a single argument. + */ +sym_t * +decl1arg(sym, initflg) + sym_t *sym; + int initflg; +{ + tspec_t t; + + chkfdef(sym, 1); + + chktyp(sym); + + if (dcs->d_rdcsym != NULL && dcs->d_rdcsym->s_blklev == blklev) { + /* redeclaration of formal parameter %s */ + error(237, sym->s_name); + rmsym(dcs->d_rdcsym); + sym->s_arg = 1; + } + + if (!sym->s_arg) { + /* declared argument %s is missing */ + error(53, sym->s_name); + sym->s_arg = 1; + } + + if (initflg) { + /* cannot initialize parameter: %s */ + error(52, sym->s_name); + initerr = 1; + } + + if ((t = sym->s_type->t_tspec) == ARRAY) { + sym->s_type = incref(sym->s_type->t_subt, PTR); + } else if (t == FUNC) { + if (tflag) + /* a function is declared as an argument: %s */ + warning(50, sym->s_name); + sym->s_type = incref(sym->s_type, PTR); + } else if (t == FLOAT) { + if (tflag) + sym->s_type = gettyp(DOUBLE); + } + + if (dcs->d_inline) + /* argument declared inline: %s */ + warning(269, sym->s_name); + + /* + * Arguments must have complete types. lengths() prints the needed + * error messages (null dimension is impossible because arrays are + * converted to pointers). + */ + if (sym->s_type->t_tspec != VOID) + (void)length(sym->s_type, sym->s_name); + + setsflg(sym); + + return (sym); +} + +/* + * Does some checks for lint directives which apply to functions. + * Processes arguments in old style function definitions which default + * to int. + * Checks compatiblility of old style function definition with previous + * prototype. + */ +void +cluparg() +{ + sym_t *args, *arg, *pargs, *parg; + int narg, nparg, n, msg; + tspec_t t; + + args = funcsym->s_args; + pargs = funcsym->s_type->t_args; + + /* check for illegal combinations of lint directives */ + if (prflstrg != -1 && scflstrg != -1) { + /* can't be used together: ** PRINTFLIKE ** ** SCANFLIKE ** */ + warning(289); + prflstrg = scflstrg = -1; + } + if (nvararg != -1 && (prflstrg != -1 || scflstrg != -1)) { + /* dubious use of ** VARARGS ** with ** %s ** */ + warning(288, prflstrg != -1 ? "PRINTFLIKE" : "SCANFLIKE"); + nvararg = -1; + } + + /* + * check if the argument of a lint directive is compatible with the + * number of arguments. + */ + narg = 0; + for (arg = dcs->d_fargs; arg != NULL; arg = arg->s_nxt) + narg++; + if (nargusg > narg) { + /* argument number mismatch with directive: ** %s ** */ + warning(283, "ARGSUSED"); + nargusg = 0; + } + if (nvararg > narg) { + /* argument number mismatch with directive: ** %s ** */ + warning(283, "VARARGS"); + nvararg = 0; + } + if (prflstrg > narg) { + /* argument number mismatch with directive: ** %s ** */ + warning(283, "PRINTFLIKE"); + prflstrg = -1; + } else if (prflstrg == 0) { + prflstrg = -1; + } + if (scflstrg > narg) { + /* argument number mismatch with directive: ** %s ** */ + warning(283, "SCANFLIKE"); + scflstrg = -1; + } else if (scflstrg == 0) { + scflstrg = -1; + } + if (prflstrg != -1 || scflstrg != -1) { + narg = prflstrg != -1 ? prflstrg : scflstrg; + arg = dcs->d_fargs; + for (n = 1; n < narg; n++) + arg = arg->s_nxt; + if (arg->s_type->t_tspec != PTR || + ((t = arg->s_type->t_subt->t_tspec) != CHAR && + t != UCHAR && t != SCHAR)) { + /* arg. %d must be 'char *' for PRINTFLIKE/SCANFLIKE */ + warning(293, narg); + prflstrg = scflstrg = -1; + } + } + + /* + * print a warning for each argument off an old style function + * definition which defaults to int + */ + for (arg = args; arg != NULL; arg = arg->s_nxt) { + if (arg->s_defarg) { + /* argument type defaults to int: %s */ + warning(32, arg->s_name); + arg->s_defarg = 0; + setsflg(arg); + } + } + + /* + * If this is an old style function definition and a prototyp + * exists, compare the types of arguments. + */ + if (funcsym->s_osdef && funcsym->s_type->t_proto) { + /* + * If the number of arguments does not macht, we need not + * continue. + */ + narg = nparg = 0; + msg = 0; + for (parg = pargs; parg != NULL; parg = parg->s_nxt) + nparg++; + for (arg = args; arg != NULL; arg = arg->s_nxt) + narg++; + if (narg != nparg) { + /* parameter mismatch: %d declared, %d defined */ + error(51, nparg, narg); + msg = 1; + } else { + parg = pargs; + arg = args; + while (narg--) { + msg |= chkptdecl(arg, parg); + parg = parg->s_nxt; + arg = arg->s_nxt; + } + } + if (msg) + /* prototype declaration */ + prevdecl(285, dcs->d_rdcsym); + + /* from now the prototype is valid */ + funcsym->s_osdef = 0; + funcsym->s_args = NULL; + + } + +} + +/* + * Checks compatibility of an old style function definition with a previous + * prototype declaration. + * Returns 1 if the position of the previous declaration should be reported. + */ +static int +chkptdecl(arg, parg) + sym_t *arg, *parg; +{ + type_t *tp, *ptp; + int warn, msg; + + tp = arg->s_type; + ptp = parg->s_type; + + msg = 0; + warn = 0; + + if (!eqtype(tp, ptp, 1, 1, &warn)) { + if (eqtype(tp, ptp, 1, 0, &warn)) { + /* type does not match prototype: %s */ + msg = gnuism(58, arg->s_name); + } else { + /* type does not match prototype: %s */ + error(58, arg->s_name); + msg = 1; + } + } else if (warn) { + /* type does not match prototype: %s */ + (*(sflag ? error : warning))(58, arg->s_name); + msg = 1; + } + + return (msg); +} + +/* + * Completes a single local declaration/definition. + */ +void +decl1loc(dsym, initflg) + sym_t *dsym; + int initflg; +{ + /* Correct a mistake done in dname(). */ + if (dsym->s_type->t_tspec == FUNC) { + dsym->s_def = DECL; + if (dcs->d_scl == NOSCL) + dsym->s_scl = EXTERN; + } + + if (dsym->s_type->t_tspec == FUNC) { + if (dsym->s_scl == STATIC) { + /* dubious static function at block level: %s */ + warning(93, dsym->s_name); + dsym->s_scl = EXTERN; + } else if (dsym->s_scl != EXTERN && dsym->s_scl != TYPEDEF) { + /* function has illegal storage class: %s */ + error(94, dsym->s_name); + dsym->s_scl = EXTERN; + } + } + + /* + * functions may be declared inline at local scope, although + * this has no effect for a later definition of the same + * function. + * XXX it should have an effect if tflag is set. this would + * also be the way gcc behaves. + */ + if (dcs->d_inline) { + if (dsym->s_type->t_tspec == FUNC) { + dsym->s_inline = 1; + } else { + /* variable declared inline: %s */ + warning(268, dsym->s_name); + } + } + + chkfdef(dsym, 1); + + chktyp(dsym); + + if (dcs->d_rdcsym != NULL && dsym->s_scl == EXTERN) + ledecl(dsym); + + if (dsym->s_scl == EXTERN) { + /* + * XXX wenn die statische Variable auf Ebene 0 erst + * spaeter definiert wird, haben wir die Brille auf. + */ + if (dsym->s_xsym == NULL) { + outsym(dsym, EXTERN, dsym->s_def); + } else { + outsym(dsym, dsym->s_xsym->s_scl, dsym->s_def); + } + } + + if (dcs->d_rdcsym != NULL) { + + if (dcs->d_rdcsym->s_blklev == 0) { + + switch (dsym->s_scl) { + case AUTO: + /* automatic hides external declaration: %s */ + if (hflag) + warning(86, dsym->s_name); + break; + case STATIC: + /* static hides external declaration: %s */ + if (hflag) + warning(87, dsym->s_name); + break; + case TYPEDEF: + /* typedef hides external declaration: %s */ + if (hflag) + warning(88, dsym->s_name); + break; + case EXTERN: + /* + * Warnings and errors are printed in ledecl() + */ + break; + default: + lerror("decl1loc() 1"); + } + + } else if (dcs->d_rdcsym->s_blklev == blklev) { + + /* no hflag, because its illegal! */ + if (dcs->d_rdcsym->s_arg) { + /* + * if !tflag, a "redeclaration of %s" error + * is produced below + */ + if (tflag) { + if (hflag) + /* decl. hides parameter: %s */ + warning(91, dsym->s_name); + rmsym(dcs->d_rdcsym); + } + } + + } else if (dcs->d_rdcsym->s_blklev < blklev) { + + if (hflag) + /* declaration hides earlier one: %s */ + warning(95, dsym->s_name); + + } + + if (dcs->d_rdcsym->s_blklev == blklev) { + + /* redeclaration of %s */ + error(27, dsym->s_name); + rmsym(dcs->d_rdcsym); + + } + + } + + if (initflg && !(initerr = chkinit(dsym))) { + dsym->s_def = DEF; + setsflg(dsym); + } + + if (dsym->s_scl == TYPEDEF) { + dsym->s_type = duptyp(dsym->s_type); + dsym->s_type->t_typedef = 1; + settdsym(dsym->s_type, dsym); + } + + /* + * Before we can check the size we must wait for a initialisation + * which may follow. + */ +} + +/* + * Processes (re)declarations of external Symbols inside blocks. + */ +static void +ledecl(dsym) + sym_t *dsym; +{ + int eqt, warn; + sym_t *esym; + + /* look for a symbol with the same name */ + esym = dcs->d_rdcsym; + while (esym != NULL && esym->s_blklev != 0) { + while ((esym = esym->s_link) != NULL) { + if (esym->s_kind != FVFT) + continue; + if (strcmp(dsym->s_name, esym->s_name) == 0) + break; + } + } + if (esym == NULL) + return; + if (esym->s_scl != EXTERN && esym->s_scl != STATIC) { + /* gcc accepts this without a warning, pcc prints an error. */ + /* redeclaration of %s */ + warning(27, dsym->s_name); + prevdecl(-1, esym); + return; + } + + warn = 0; + eqt = eqtype(esym->s_type, dsym->s_type, 0, 0, &warn); + + if (!eqt || warn) { + if (esym->s_scl == EXTERN) { + /* inconsistent redeclaration of extern: %s */ + warning(90, dsym->s_name); + prevdecl(-1, esym); + } else { + /* inconsistent redeclaration of static: %s */ + warning(92, dsym->s_name); + prevdecl(-1, esym); + } + } + + if (eqt) { + /* + * Remember the external symbol so we can update usage + * information at the end of the block. + */ + dsym->s_xsym = esym; + } +} + +/* + * Print an error or a warning if the symbol cant be initialized due + * to type/storage class. Returnvalue is 1 if an error has been + * detected. + */ +static int +chkinit(sym) + sym_t *sym; +{ + int err; + + err = 0; + + if (sym->s_type->t_tspec == FUNC) { + /* cannot initialize function: %s */ + error(24, sym->s_name); + err = 1; + } else if (sym->s_scl == TYPEDEF) { + /* cannot initialize typedef: %s */ + error(25, sym->s_name); + err = 1; + } else if (sym->s_scl == EXTERN && sym->s_def == DECL) { + /* cannot initialize "extern" declaration: %s */ + if (dcs->d_ctx == EXTERN) { + warning(26, sym->s_name); + } else { + error(26, sym->s_name); + err = 1; + } + } + + return (err); +} + +/* + * Create a symbole for an abstract declaration. + */ +sym_t * +aname() +{ + sym_t *sym; + + if (dcs->d_ctx != ABSTRACT && dcs->d_ctx != PARG) + lerror("aname()"); + + sym = getblk(sizeof (sym_t)); + + sym->s_name = unnamed; + sym->s_def = DEF; + sym->s_scl = ABSTRACT; + sym->s_blklev = -1; + + if (dcs->d_ctx == PARG) + sym->s_arg = 1; + + sym->s_type = dcs->d_type; + dcs->d_rdcsym = NULL; + dcs->d_vararg = 0; + + return (sym); +} + +/* + * Removes anything which has nothing to do on global level. + */ +void +globclup() +{ + while (dcs->d_nxt != NULL) + popdecl(); + + cleanup(); + blklev = 0; + mblklev = 0; + + /* + * remove all informations about pending lint directives without + * warnings. + */ + glclup(1); +} + +/* + * Process an abstract type declaration + */ +sym_t * +decl1abs(sym) + sym_t *sym; +{ + chkfdef(sym, 1); + chktyp(sym); + return (sym); +} + +/* + * Checks size after declarations of variables and their initialisation. + */ +void +chksz(dsym) + sym_t *dsym; +{ + /* + * check size only for symbols which are defined and no function and + * not typedef name + */ + if (dsym->s_def != DEF) + return; + if (dsym->s_scl == TYPEDEF) + return; + if (dsym->s_type->t_tspec == FUNC) + return; + + if (length(dsym->s_type, dsym->s_name) == 0 && + dsym->s_type->t_tspec == ARRAY && dsym->s_type->t_dim == 0) { + /* empty array declaration: %s */ + if (tflag) { + warning(190, dsym->s_name); + } else { + error(190, dsym->s_name); + } + } +} + +/* + * Mark an object as set if it is not already + */ +void +setsflg(sym) + sym_t *sym; +{ + if (!sym->s_set) { + sym->s_set = 1; + STRUCT_ASSIGN(sym->s_spos, curr_pos); + } +} + +/* + * Mark an object as used if it is not already + */ +void +setuflg(sym, fcall, szof) + sym_t *sym; + int fcall, szof; +{ + if (!sym->s_used) { + sym->s_used = 1; + STRUCT_ASSIGN(sym->s_upos, curr_pos); + } + /* + * for function calls another record is written + * + * XXX Should symbols used in sizeof() treated as used or not? + * Probably not, because there is no sense to declare an + * external variable only to get their size. + */ + if (!fcall && !szof && sym->s_kind == FVFT && sym->s_scl == EXTERN) + outusg(sym); +} + +/* + * Prints warnings for a list of variables and labels (concatenated + * with s_dlnxt) if these are not used or only set. + */ +void +chkusage(di) + dinfo_t *di; +{ + sym_t *sym; + int mknowarn; + + /* for this warnings LINTED has no effect */ + mknowarn = nowarn; + nowarn = 0; + + for (sym = di->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) + chkusg1(di->d_asm, sym); + + nowarn = mknowarn; +} + +/* + * Prints a warning for a single variable or label if it is not used or + * only set. + */ +void +chkusg1(novar, sym) + int novar; + sym_t *sym; +{ + pos_t cpos; + + if (sym->s_blklev == -1) + return; + + STRUCT_ASSIGN(cpos, curr_pos); + + if (sym->s_kind == FVFT) { + if (sym->s_arg) { + chkausg(novar, sym); + } else { + chkvusg(novar, sym); + } + } else if (sym->s_kind == FLAB) { + chklusg(sym); + } else if (sym->s_kind == FTAG) { + chktusg(sym); + } + + STRUCT_ASSIGN(curr_pos, cpos); +} + +static void +chkausg(novar, arg) + int novar; + sym_t *arg; +{ + if (!arg->s_set) + lerror("chkausg() 1"); + + if (novar) + return; + + if (!arg->s_used && vflag) { + STRUCT_ASSIGN(curr_pos, arg->s_dpos); + /* argument %s unused in function %s */ + warning(231, arg->s_name, funcsym->s_name); + } +} + +static void +chkvusg(novar, sym) + int novar; + sym_t *sym; +{ + scl_t sc; + sym_t *xsym; + + if (blklev == 0 || sym->s_blklev == 0) + lerror("chkvusg() 1"); + + /* errors in expressions easily cause lots of these warnings */ + if (nerr != 0) + return; + + /* + * XXX Only variables are checkd, although types should + * probably also be checked + */ + if ((sc = sym->s_scl) != EXTERN && sc != STATIC && + sc != AUTO && sc != REG) { + return; + } + + if (novar) + return; + + if (sc == EXTERN) { + if (!sym->s_used && !sym->s_set) { + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + /* %s unused in function %s */ + warning(192, sym->s_name, funcsym->s_name); + } + } else { + if (sym->s_set && !sym->s_used) { + STRUCT_ASSIGN(curr_pos, sym->s_spos); + /* %s set but not used in function %s */ + warning(191, sym->s_name, funcsym->s_name); + } else if (!sym->s_used) { + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + /* %s unused in function %s */ + warning(192, sym->s_name, funcsym->s_name); + } + } + + if (sc == EXTERN) { + /* + * information about usage is taken over into the symbol + * tabel entry at level 0 if the symbol was locally declared + * as an external symbol. + * + * XXX This is wrong for symbols declared static at level 0 + * if the usage information stems from sizeof(). This is + * because symbols at level 0 only used in sizeof() are + * considered to not be used. + */ + if ((xsym = sym->s_xsym) != NULL) { + if (sym->s_used && !xsym->s_used) { + xsym->s_used = 1; + STRUCT_ASSIGN(xsym->s_upos, sym->s_upos); + } + if (sym->s_set && !xsym->s_set) { + xsym->s_set = 1; + STRUCT_ASSIGN(xsym->s_spos, sym->s_spos); + } + } + } +} + +static void +chklusg(lab) + sym_t *lab; +{ + if (blklev != 1 || lab->s_blklev != 1) + lerror("chklusg() 1"); + + if (lab->s_set && !lab->s_used) { + STRUCT_ASSIGN(curr_pos, lab->s_spos); + /* label %s unused in function %s */ + warning(192, lab->s_name, funcsym->s_name); + } else if (!lab->s_set) { + STRUCT_ASSIGN(curr_pos, lab->s_upos); + /* undefined label %s */ + warning(23, lab->s_name); + } +} + +static void +chktusg(sym) + sym_t *sym; +{ + if (!incompl(sym->s_type)) + return; + + /* complain alwasy about incomplet tags declared inside blocks */ + if (!zflag || dcs->d_ctx != EXTERN) + return; + + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + switch (sym->s_type->t_tspec) { + case STRUCT: + /* struct %s never defined */ + warning(233, sym->s_name); + break; + case UNION: + /* union %s never defined */ + warning(234, sym->s_name); + break; + case ENUM: + /* enum %s never defined */ + warning(235, sym->s_name); + break; + default: + lerror("chktusg() 1"); + } +} + +/* + * Called after the entire translation unit has been parsed. + * Changes tentative definitions in definitions. + * Performs some tests on global Symbols. Detected Problems are: + * - defined variables of incomplete type + * - constant variables which are not initialized + * - static symbols which are never used + */ +void +chkglsyms() +{ + sym_t *sym; + pos_t cpos; + + if (blklev != 0 || dcs->d_nxt != NULL) + norecover(); + + STRUCT_ASSIGN(cpos, curr_pos); + + for (sym = dcs->d_dlsyms; sym != NULL; sym = sym->s_dlnxt) { + if (sym->s_blklev == -1) + continue; + if (sym->s_kind == FVFT) { + chkglvar(sym); + } else if (sym->s_kind == FTAG) { + chktusg(sym); + } else { + if (sym->s_kind != FMOS) + lerror("chkglsyms() 1"); + } + } + + STRUCT_ASSIGN(curr_pos, cpos); +} + +static void +chkglvar(sym) + sym_t *sym; +{ + if (sym->s_scl == TYPEDEF || sym->s_scl == ENUMCON) + return; + + if (sym->s_scl != EXTERN && sym->s_scl != STATIC) + lerror("chkglvar() 1"); + + glchksz(sym); + + if (sym->s_scl == STATIC) { + if (sym->s_type->t_tspec == FUNC) { + if (sym->s_used && sym->s_def != DEF) { + STRUCT_ASSIGN(curr_pos, sym->s_upos); + /* static func. called but not def.. */ + error(225, sym->s_name); + } + } + if (!sym->s_used) { + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + if (sym->s_type->t_tspec == FUNC) { + if (sym->s_def == DEF) { + if (!sym->s_inline) + /* static function %s unused */ + warning(236, sym->s_name); + } else { + /* static function %s decl. but ... */ + warning(290, sym->s_name); + } + } else if (!sym->s_set) { + /* static variable %s unused */ + warning(226, sym->s_name); + } else { + /* static variable %s set but not used */ + warning(307, sym->s_name); + } + } + if (!tflag && sym->s_def == TDEF && sym->s_type->t_const) { + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + /* const object %s should have initializer */ + warning(227, sym->s_name); + } + } +} + +static void +glchksz(sym) + sym_t *sym; +{ + if (sym->s_def == TDEF) { + if (sym->s_type->t_tspec == FUNC) + /* + * this can happen if an syntax error occured + * after a function declaration + */ + return; + STRUCT_ASSIGN(curr_pos, sym->s_dpos); + if (length(sym->s_type, sym->s_name) == 0 && + sym->s_type->t_tspec == ARRAY && sym->s_type->t_dim == 0) { + /* empty array declaration: %s */ + if (tflag || (sym->s_scl == EXTERN && !sflag)) { + warning(190, sym->s_name); + } else { + error(190, sym->s_name); + } + } + } +} + +/* + * Prints information about location of previous definition/declaration. + */ +void +prevdecl(msg, psym) + int msg; + sym_t *psym; +{ + pos_t cpos; + + if (!rflag) + return; + + STRUCT_ASSIGN(cpos, curr_pos); + STRUCT_ASSIGN(curr_pos, psym->s_dpos); + if (msg != -1) { + message(msg, psym->s_name); + } else if (psym->s_def == DEF || psym->s_def == TDEF) { + /* previous definition of %s */ + message(261, psym->s_name); + } else { + /* previous declaration of %s */ + message(260, psym->s_name); + } + STRUCT_ASSIGN(curr_pos, cpos); +} diff --git a/usr.bin/xlint/lint1/emit.c b/usr.bin/xlint/lint1/emit.c new file mode 100644 index 0000000..f98f540 --- /dev/null +++ b/usr.bin/xlint/lint1/emit.c @@ -0,0 +1,241 @@ +/* $NetBSD: emit.c,v 1.2 1995/07/03 21:24:00 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: emit.c,v 1.2 1995/07/03 21:24:00 cgd Exp $"; +#endif + +#include +#include +#include +#include + +#include "lint.h" + +/* name and handle of output file */ +static const char *loname; +static FILE *lout; + +/* output buffer data */ +ob_t ob; + +static void outxbuf __P((void)); + + +/* + * initialize output + */ +void +outopen(name) + const char *name; +{ + loname = name; + + /* Ausgabedatei oeffnen */ + if ((lout = fopen(name, "w")) == NULL) + err(1, "cannot open '%s'", name); + + /* Ausgabepuffer anlegen */ + ob.o_len = 1024; + ob.o_end = (ob.o_buf = ob.o_nxt = xmalloc(ob.o_len)) + ob.o_len; +} + +/* + * flush output buffer and close file + */ +void +outclose() +{ + outclr(); + if (fclose(lout) == EOF) + err(1, "cannot close '%s'", loname); +} + +/* + * resize output buffer + */ +static void +outxbuf() +{ + ptrdiff_t coffs; + + coffs = ob.o_nxt - ob.o_buf; + ob.o_len *= 2; + ob.o_end = (ob.o_buf = xrealloc(ob.o_buf, ob.o_len)) + ob.o_len; + ob.o_nxt = ob.o_buf + coffs; +} + +/* + * reset output buffer + * if it is not empty, it is flushed + */ +void +outclr() +{ + size_t sz; + + if (ob.o_buf != ob.o_nxt) { + outchar('\n'); + sz = ob.o_nxt - ob.o_buf; + if (sz > ob.o_len) + errx(1, "internal error: outclr() 1"); + if (fwrite(ob.o_buf, sz, 1, lout) != 1) + err(1, "cannot write to %s", loname); + ob.o_nxt = ob.o_buf; + } +} + +/* + * write a character to the output buffer + */ +void +outchar(c) + int c; +{ + if (ob.o_nxt == ob.o_end) + outxbuf(); + *ob.o_nxt++ = (char)c; +} + +/* + * write a character to the output buffer, qouted if necessary + */ +void +outqchar(c) + int c; +{ + if (isprint(c) && c != '\\' && c != '"' && c != '\'') { + outchar(c); + } else { + outchar('\\'); + switch (c) { + case '\\': + outchar('\\'); + break; + case '"': + outchar('"'); + break; + case '\'': + outchar('\''); + break; + case '\b': + outchar('b'); + break; + case '\t': + outchar('t'); + break; + case '\n': + outchar('n'); + break; + case '\f': + outchar('f'); + break; + case '\r': + outchar('r'); + break; +#ifdef __STDC__ + case '\v': +#else + case '\013': +#endif + outchar('v'); + break; +#ifdef __STDC__ + case '\a': +#else + case '\007': +#endif + outchar('a'); + break; + default: + outchar((((u_int)c >> 6) & 07) + '0'); + outchar((((u_int)c >> 3) & 07) + '0'); + outchar((c & 07) + '0'); + break; + } + } +} + +/* + * write a strint to the output buffer + * the string must not contain any characters which + * should be quoted + */ +void +outstrg(s) + const char *s; +{ + while (*s != '\0') { + if (ob.o_nxt == ob.o_end) + outxbuf(); + *ob.o_nxt++ = *s++; + } +} + +/* + * write an integer value to toe output buffer + */ +void +outint(i) + int i; +{ + if ((ob.o_end - ob.o_nxt) < 3 * sizeof (int)) + outxbuf(); + ob.o_nxt += sprintf(ob.o_nxt, "%d", i); +} + +/* + * write the name of a symbol to the output buffer + * the name is preceeded by its length + */ +void +outname(name) + const char *name; +{ + if (name == NULL) + errx(1, "internal error: outname() 1"); + outint((int)strlen(name)); + outstrg(name); +} + +/* + * write the name of the .c source + */ +void +outsrc(name) + const char *name; +{ + outclr(); + outchar('S'); + outstrg(name); +} diff --git a/usr.bin/xlint/lint1/emit1.c b/usr.bin/xlint/lint1/emit1.c new file mode 100644 index 0000000..ee2434e --- /dev/null +++ b/usr.bin/xlint/lint1/emit1.c @@ -0,0 +1,587 @@ +/* $NetBSD: emit1.c,v 1.4 1995/10/02 17:21:28 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: emit1.c,v 1.4 1995/10/02 17:21:28 jpo Exp $"; +#endif + +#include + +#include "lint1.h" + +static void outtt __P((sym_t *, sym_t *)); +static void outfstrg __P((strg_t *)); + +/* + * Write type into the output buffer. + * The type is written as a sequence of substrings, each of which describes a + * node of type type_t + * a node is coded as follows: + * char C + * signed char s C + * unsigned char u C + * short S + * unsigned short u S + * int I + * unsigned int u I + * long L + * unsigned long u L + * long long Q + * unsigned long long u Q + * float s D + * double D + * long double l D + * void V + * * P + * [n] A n + * () F + * (void) F 0 + * (n arguments) F n arg1 arg2 ... argn + * (n arguments, ...) F n arg1 arg2 ... argn-1 E + * (a, b, c, ...) f n arg1 arg2 ... + * enum tag e T tag_or_typename + * struct tag s T tag_or_typename + * union tag u T tag_or_typename + * + * tag_or_typename 0 no tag or type name + * 1 n tag Tag + * 2 n typename only type name + * + * spaces are only for better readability + * additionaly it is possible to prepend the characters 'c' (for const) + * and 'v' (for volatile) + */ +void +outtype(tp) + type_t *tp; +{ + int t, s, na; + sym_t *arg; + tspec_t ts; + + 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 FUNC: t = 'F'; s = '\0'; break; + case ENUM: t = 'T'; s = 'e'; break; + case STRUCT: t = 'T'; s = 's'; break; + case UNION: t = 'T'; s = 'u'; break; + default: + lerror("outtyp() 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) { + outtt(tp->t_enum->etag, tp->t_enum->etdef); + } else if (ts == STRUCT || ts == UNION) { + outtt(tp->t_str->stag, tp->t_str->stdef); + } else if (ts == FUNC && tp->t_proto) { + na = 0; + for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) + na++; + if (tp->t_vararg) + na++; + outint(na); + for (arg = tp->t_args; arg != NULL; arg = arg->s_nxt) + outtype(arg->s_type); + if (tp->t_vararg) + outchar('E'); + } + tp = tp->t_subt; + } +} + +/* + * type to string + * used for debugging output + * + * it uses its own output buffer for conversion + */ +const char * +ttos(tp) + type_t *tp; +{ + static ob_t tob; + ob_t tmp; + + if (tob.o_buf == NULL) { + tob.o_len = 64; + tob.o_buf = tob.o_nxt = xmalloc(tob.o_len); + tob.o_end = tob.o_buf + tob.o_len; + } + + tmp = ob; + ob = tob; + ob.o_nxt = ob.o_buf; + outtype(tp); + outchar('\0'); + tob = ob; + ob = tmp; + + return (tob.o_buf); +} + +/* + * write the name of a tag or typename + * + * if the tag is named, the name of the + * tag is written, otherwise, if a typename exists which + * refers to this tag, this typename is written + */ +static void +outtt(tag, tdef) + sym_t *tag, *tdef; +{ + if (tag->s_name != unnamed) { + outint(1); + outname(tag->s_name); + } else if (tdef != NULL) { + outint(2); + outname(tdef->s_name); + } else { + outint(0); + } +} + +/* + * write information about an global declared/defined symbol + * with storage class extern + * + * informations about function definitions are written in outfdef(), + * not here + */ +void +outsym(sym, sc, def) + sym_t *sym; + scl_t sc; + def_t def; +{ + /* + * Static function declarations must also be written to the output + * file. Compatibility of function declarations (for both static + * and extern functions) must be checked in lint2. Lint1 can't do + * this, especially not, if functions are declared at block level + * before their first declaration at level 0. + */ + if (sc != EXTERN && !(sc == STATIC && sym->s_type->t_tspec == FUNC)) + return; + + /* reset buffer */ + outclr(); + + /* + * line number of .c source, 'd' for declaration, Id of current + * source (.c or .h), and line in current source. + */ + outint(csrc_pos.p_line); + outchar('d'); + outint(getfnid(sym->s_dpos.p_file)); + outchar('.'); + outint(sym->s_dpos.p_line); + + /* flags */ + + switch (def) { + case DEF: + /* defined */ + outchar('d'); + break; + case TDEF: + /* tentative defined */ + outchar('t'); + break; + case DECL: + /* declared */ + outchar('e'); + break; + default: + lerror("outsym() 2"); + } + if (llibflg && def != DECL) { + /* + * mark it as used so we get no warnings from lint2 about + * unused symbols in libraries. + */ + outchar('u'); + } + + if (sc == STATIC) + outchar('s'); + + /* name of the symbol */ + outname(sym->s_name); + + /* type of the symbol */ + outtype(sym->s_type); +} + +/* + * write information about function definition + * + * this is also done for static functions so we are able to check if + * they are called with proper argument types + */ +void +outfdef(fsym, posp, rval, osdef, args) + sym_t *fsym, *args; + pos_t *posp; + int rval, osdef; +{ + int narg; + sym_t *arg; + + /* reset the buffer */ + outclr(); + + /* + * line number of .c source, 'd' for declaration, Id of current + * source (.c or .h), and line in current source + * + * we are already at the end of the function. If we are in the + * .c source, posp->p_line is correct, otherwise csrc_pos.p_line + * (for functions defined in header files). + */ + if (posp->p_file == csrc_pos.p_file) { + outint(posp->p_line); + } else { + outint(csrc_pos.p_line); + } + outchar('d'); + outint(getfnid(posp->p_file)); + outchar('.'); + outint(posp->p_line); + + /* flags */ + + /* both SCANFLIKE and PRINTFLIKE imply VARARGS */ + if (prflstrg != -1) { + nvararg = prflstrg; + } else if (scflstrg != -1) { + nvararg = scflstrg; + } + + if (nvararg != -1) { + outchar('v'); + outint(nvararg); + } + if (scflstrg != -1) { + outchar('S'); + outint(scflstrg); + } + if (prflstrg != -1) { + outchar('P'); + outint(prflstrg); + } + nvararg = prflstrg = scflstrg = -1; + + outchar('d'); + + if (rval) + /* has return value */ + outchar('r'); + + if (llibflg) + /* + * mark it as used so lint2 does not complain about + * unused symbols in libraries + */ + outchar('u'); + + if (osdef) + /* old style function definition */ + outchar('o'); + + if (fsym->s_scl == STATIC) + outchar('s'); + + /* name of function */ + outname(fsym->s_name); + + /* argument types and return value */ + if (osdef) { + narg = 0; + for (arg = args; arg != NULL; arg = arg->s_nxt) + narg++; + outchar('f'); + outint(narg); + for (arg = args; arg != NULL; arg = arg->s_nxt) + outtype(arg->s_type); + outtype(fsym->s_type->t_subt); + } else { + outtype(fsym->s_type); + } +} + +/* + * write out all information necessary for lint2 to check function + * calls + * + * rvused is set if the return value is used (asigned to a variable) + * rvdisc is set if the return value is not used and not ignored + * (casted to void) + */ +void +outcall(tn, rvused, rvdisc) + tnode_t *tn; + int rvused, rvdisc; +{ + tnode_t *args, *arg; + int narg, n, i; + quad_t q; + tspec_t t; + + /* reset buffer */ + outclr(); + + /* + * line number of .c source, 'c' for function call, Id of current + * source (.c or .h), and line in current source + */ + outint(csrc_pos.p_line); + outchar('c'); + outint(getfnid(curr_pos.p_file)); + outchar('.'); + outint(curr_pos.p_line); + + /* + * flags; 'u' and 'i' must be last to make sure a letter + * is between the numeric argument of a flag and the name of + * the function + */ + narg = 0; + args = tn->tn_right; + for (arg = args; arg != NULL; arg = arg->tn_right) + narg++; + /* informations about arguments */ + for (n = 1; n <= narg; n++) { + /* the last argument is the top one in the tree */ + for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ; + arg = arg->tn_left; + if (arg->tn_op == CON) { + if (isityp(t = arg->tn_type->t_tspec)) { + /* + * XXX it would probably be better to + * explizitly test the sign + */ + if ((q = arg->tn_val->v_quad) == 0) { + /* zero constant */ + outchar('z'); + } else if (msb(q, t, 0) == 0) { + /* positive if casted to signed */ + outchar('p'); + } else { + /* negative if casted to signed */ + outchar('n'); + } + outint(n); + } + } else if (arg->tn_op == AMPER && + arg->tn_left->tn_op == STRING && + arg->tn_left->tn_strg->st_tspec == CHAR) { + /* constant string, write all format specifiers */ + outchar('s'); + outint(n); + outfstrg(arg->tn_left->tn_strg); + } + + } + /* return value discarded/used/ignored */ + outchar(rvdisc ? 'd' : (rvused ? 'u' : 'i')); + + /* name of the called function */ + outname(tn->tn_left->tn_left->tn_sym->s_name); + + /* types of arguments */ + outchar('f'); + outint(narg); + for (n = 1; n <= narg; n++) { + /* the last argument is the top one in the tree */ + for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ; + outtype(arg->tn_left->tn_type); + } + /* expected type of return value */ + outtype(tn->tn_type); +} + +/* + * extracts potential format specifiers for printf() and scanf() and + * writes them, enclosed in "" and qouted if necessary, to the output buffer + */ +static void +outfstrg(strg) + strg_t *strg; +{ + int c, oc, first; + u_char *cp; + + if (strg->st_tspec != CHAR) + lerror("outfstrg() 1"); + + cp = strg->st_cp; + + outchar('"'); + + c = *cp++; + + while (c != '\0') { + + if (c != '%') { + c = *cp++; + continue; + } + + outqchar('%'); + c = *cp++; + + /* flags for printf and scanf and *-fieldwidth for printf */ + while (c != '\0' && (c == '-' || c == '+' || c == ' ' || + c == '#' || c == '0' || c == '*')) { + outqchar(c); + c = *cp++; + } + + /* numeric field width */ + while (c != '\0' && isdigit(c)) { + outqchar(c); + c = *cp++; + } + + /* precision for printf */ + if (c == '.') { + outqchar(c); + if ((c = *cp++) == '*') { + outqchar(c); + c = *cp++; + } else { + while (c != '\0' && isdigit(c)) { + outqchar(c); + c = *cp++; + } + } + } + + /* h, l, L and q flags fpr printf and scanf */ + if (c == 'h' || c == 'l' || c == 'L' || c == 'q') { + outqchar(c); + c = *cp++; + } + + /* + * The last character. It is always written so we can detect + * invalid format specifiers. + */ + if (c != '\0') { + outqchar(c); + oc = c; + c = *cp++; + /* + * handle [ for scanf. [-] means that a minus sign + * was found at an undefined position. + */ + if (oc == '[') { + if (c == '^') + c = *cp++; + if (c == ']') + c = *cp++; + first = 1; + while (c != '\0' && c != ']') { + if (c == '-') { + if (!first && *cp != ']') + outqchar(c); + } + first = 0; + c = *cp++; + } + if (c == ']') { + outqchar(c); + c = *cp++; + } + } + } + + } + + outchar('"'); +} + +/* + * writes a record if sym was used + */ +void +outusg(sym) + sym_t *sym; +{ + /* reset buffer */ + outclr(); + + /* + * line number of .c source, 'u' for used, Id of current + * source (.c or .h), and line in current source + */ + outint(csrc_pos.p_line); + outchar('u'); + outint(getfnid(curr_pos.p_file)); + outchar('.'); + outint(curr_pos.p_line); + + /* necessary to delimit both numbers */ + outchar('x'); + + /* Den Namen des Symbols ausgeben */ + outname(sym->s_name); +} diff --git a/usr.bin/xlint/lint1/err.c b/usr.bin/xlint/lint1/err.c new file mode 100644 index 0000000..32c80c4 --- /dev/null +++ b/usr.bin/xlint/lint1/err.c @@ -0,0 +1,543 @@ +/* $NetBSD: err.c,v 1.8 1995/10/02 17:37:00 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: err.c,v 1.8 1995/10/02 17:37:00 jpo Exp $"; +#endif + +/* number of errors found */ +int nerr; + +/* number of syntax errors */ +int sytxerr; + +#include +#ifdef __STDC__ +#include +#else +#include +#endif + +#include "lint1.h" + +static const char *basename __P((const char *)); +static void verror __P((int, va_list)); +static void vwarning __P((int, va_list)); + + +const char *msgs[] = { + "syntax error: empty declaration", /* 0 */ + "old style declaration; add int", /* 1 */ + "empty declaration", /* 2 */ + "%s declared in argument declaration list", /* 3 */ + "illegal type combination", /* 4 */ + "modifying typedef with '%s'; only qualifiers allowed", /* 5 */ + "use 'double' instead of 'long float'", /* 6 */ + "only one storage class allowed", /* 7 */ + "illegal storage class", /* 8 */ + "only register valid as formal parameter storage class", /* 9 */ + "duplicate '%s'", /* 10 */ + "bit-field initializer out of range", /* 11 */ + "compiler takes size of function", /* 12 */ + "incomplete enum type: %s", /* 13 */ + "compiler takes alignment of function", /* 14 */ + "function returns illegal type", /* 15 */ + "array of function is illegal", /* 16 */ + "null dimension", /* 17 */ + "illegal use of 'void'", /* 18 */ + "void type for %s", /* 19 */ + "zero or negative array dimension", /* 20 */ + "redeclaration of formal parameter %s", /* 21 */ + "incomplete or misplaced function definition", /* 22 */ + "undefined label %s", /* 23 */ + "cannot initialize function: %s", /* 24 */ + "cannot initialize typedef: %s", /* 25 */ + "cannot initialize extern declaration: %s", /* 26 */ + "redeclaration of %s", /* 27 */ + "redefinition of %s", /* 28 */ + "previously declared extern, becomes static: %s", /* 29 */ + "redeclaration of %s; ANSI C requires static", /* 30 */ + "incomplete structure or union %s: %s", /* 31 */ + "argument type defaults to 'int': %s", /* 32 */ + "duplicate member name: %s", /* 33 */ + "nonportable bit-field type", /* 34 */ + "illegal bit-field type", /* 35 */ + "illegal bit-field size", /* 36 */ + "zero size bit-field", /* 37 */ + "function illegal in structure or union", /* 38 */ + "illegal zero sized structure member: %s", /* 39 */ + "unknown size: %s", /* 40 */ + "illegal use of bit-field", /* 41 */ + "forward reference to enum type", /* 42 */ + "redefinition hides earlier one: %s", /* 43 */ + "declaration introduces new type in ANSI C: %s %s", /* 44 */ + "base type is really '%s %s'", /* 45 */ + "(%s) tag redeclared", /* 46 */ + "zero sized %s", /* 47 */ + "overflow in enumeration values: %s", /* 48 */ + "struct or union member must be named", /* 49 */ + "a function is declared as an argument: %s", /* 50 */ + "parameter mismatch: %d declared, %d defined", /* 51 */ + "cannot initialize parameter: %s", /* 52 */ + "declared argument %s is missing", /* 53 */ + "trailing ',' prohibited in enum declaration", /* 54 */ + "integral constant expression expected", /* 55 */ + "integral constant too large", /* 56 */ + "enumeration constant hides parameter: %s", /* 57 */ + "type does not match prototype: %s", /* 58 */ + "formal parameter lacks name: param #%d", /* 59 */ + "void must be sole parameter", /* 60 */ + "void parameter cannot have name: %s", /* 61 */ + "function prototype parameters must have types", /* 62 */ + "prototype does not match old-style definition", /* 63 */ + "()-less function definition", /* 64 */ + "%s has no named members", /* 65 */ + "syntax requires ';' after last struct/union member", /* 66 */ + "cannot return incomplete type", /* 67 */ + "typedef already qualified with '%s'", /* 68 */ + "inappropriate qualifiers with 'void'", /* 69 */ + "%soperand of '%s' is unsigned in ANSI C", /* 70 */ + "too many characters in character constant", /* 71 */ + "typedef declares no type name", /* 72 */ + "empty character constant", /* 73 */ + "no hex digits follow \\x", /* 74 */ + "overflow in hex escape", /* 75 */ + "character escape does not fit in character", /* 76 */ + "bad octal digit %c", /* 77 */ + "nonportable character escape", /* 78 */ + "dubious escape \\%c", /* 79 */ + "dubious escape \\%o", /* 80 */ + "\\a undefined in traditional C", /* 81 */ + "\\x undefined in traditional C", /* 82 */ + "storage class after type is obsolescent", /* 83 */ + "ANSI C requires formal parameter before '...'", /* 84 */ + "dubious tag declaration: %s %s", /* 85 */ + "automatic hides external declaration: %s", /* 86 */ + "static hides external declaration: %s", /* 87 */ + "typedef hides external declaration: %s", /* 88 */ + "typedef redeclared: %s", /* 89 */ + "inconsistent redeclaration of extern: %s", /* 90 */ + "declaration hides parameter: %s", /* 91 */ + "inconsistent redeclaration of static: %s", /* 92 */ + "dubious static function at block level: %s", /* 93 */ + "function has illegal storage class: %s", /* 94 */ + "declaration hides earlier one: %s", /* 95 */ + "cannot dereference non-pointer type", /* 96 */ + "suffix U is illegal in traditional C", /* 97 */ + "suffixes F and L are illegal in traditional C", /* 98 */ + "%s undefined", /* 99 */ + "unary + is illegal in traditional C", /* 100 */ + "undefined struct/union member: %s", /* 101 */ + "illegal member use: %s", /* 102 */ + "left operand of '.' must be struct/union object", /* 103 */ + "left operand of '->' must be pointer to struct/union", /* 104 */ + "non-unique member requires struct/union %s", /* 105 */ + "left operand of '->' must be pointer", /* 106 */ + "operands of '%s' have incompatible types", /* 107 */ + "operand of '%s' has incompatible type", /* 108 */ + "void type illegal in expression", /* 109 */ + "pointer to function is not allowed here", /* 110 */ + "unacceptable operand of '%s'", /* 111 */ + "cannot take address of bit-field", /* 112 */ + "cannot take address of register %s", /* 113 */ + "%soperand of '%s' must be lvalue", /* 114 */ + "%soperand of '%s' must be modifiable lvalue", /* 115 */ + "illegal pointer subtraction", /* 116 */ + "bitwise operation on signed value possibly nonportable", /* 117 */ + "semantics of '%s' change in ANSI C; use explicit cast", /* 118 */ + "conversion of '%s' to '%s' is out of range", /* 119 */ + "bitwise operation on signed value nonportable", /* 120 */ + "negative shift", /* 121 */ + "shift greater than size of object", /* 122 */ + "illegal combination of pointer and integer, op %s", /* 123 */ + "illegal pointer combination, op %s", /* 124 */ + "ANSI C forbids ordered comparisons of pointers to functions",/* 125 */ + "incompatible types in conditional", /* 126 */ + "'&' before array or function: ignored", /* 127 */ + "operands have incompatible pointer types, op %s", /* 128 */ + "expression has null effect", /* 129 */ + "enum type mismatch, op %s", /* 130 */ + "conversion to '%s' may sign-extend incorrectly", /* 131 */ + "conversion from '%s' may lose accuracy", /* 132 */ + "conversion of pointer to '%s' loses bits", /* 133 */ + "conversion of pointer to '%s' may lose bits", /* 134 */ + "possible pointer alignment problem", /* 135 */ + "cannot do pointer arithmetic on operand of unknown size", /* 136 */ + "use of incomplete enum type, op %s", /* 137 */ + "unknown operand size, op %s", /* 138 */ + "division by 0", /* 139 */ + "modulus by 0", /* 140 */ + "integer overflow detected, op %s", /* 141 */ + "floating point overflow detected, op %s", /* 142 */ + "cannot take size of incomplete type", /* 143 */ + "cannot take size of function", /* 144 */ + "cannot take size of bit-field", /* 145 */ + "cannot take size of void", /* 146 */ + "invalid cast expression", /* 147 */ + "improper cast of void expression", /* 148 */ + "illegal function", /* 149 */ + "argument mismatch: %d arg%s passed, %d expected", /* 150 */ + "void expressions may not be arguments, arg #%d", /* 151 */ + "argument cannot have unknown size, arg #%d", /* 152 */ + "argument has incompatible pointer type, arg #%d", /* 153 */ + "illegal combination of pointer and integer, arg #%d", /* 154 */ + "argument is incompatible with prototype, arg #%d", /* 155 */ + "enum type mismatch, arg #%d", /* 156 */ + "ANSI C treats constant as unsigned", /* 157 */ + "%s may be used before set", /* 158 */ + "assignment in conditional context", /* 159 */ + "operator '==' found where '=' was expected", /* 160 */ + "constant in conditional context", /* 161 */ + "comparision of %s with %s, op %s", /* 162 */ + "a cast does not yield an lvalue", /* 163 */ + "assignment of negative constant to unsigned type", /* 164 */ + "constant truncated by assignment", /* 165 */ + "precision lost in bit-field assignment", /* 166 */ + "array subscript cannot be negative: %ld", /* 167 */ + "array subscript cannot be > %d: %ld", /* 168 */ + "precedence confusion possible: parenthesize!", /* 169 */ + "first operand must have scalar type, op ? :", /* 170 */ + "assignment type mismatch", /* 171 */ + "too many struct/union initializers", /* 172 */ + "too many array initializers", /* 173 */ + "too many initializers", /* 174 */ + "initialisation of an incomplete type", /* 175 */ + "invalid initializer", /* 176 */ + "non-constant initializer", /* 177 */ + "initializer does not fit", /* 178 */ + "cannot initialize struct/union with no named member", /* 179 */ + "bit-field initializer does not fit", /* 180 */ + "{}-enclosed initializer required", /* 181 */ + "incompatible pointer types", /* 182 */ + "illegal combination of pointer and integer", /* 183 */ + "illegal pointer combination", /* 184 */ + "initialisation type mismatch", /* 185 */ + "bit-field initialisation is illegal in traditional C", /* 186 */ + "non-null byte ignored in string initializer", /* 187 */ + "no automatic aggregate initialization in traditional C", /* 188 */ + "assignment of struct/union illegal in traditional C", /* 189 */ + "empty array declaration: %s", /* 190 */ + "%s set but not used in function %s", /* 191 */ + "%s unused in function %s", /* 192 */ + "statement not reached", /* 193 */ + "label %s redefined", /* 194 */ + "case not in switch", /* 195 */ + "case label affected by conversion", /* 196 */ + "non-constant case expression", /* 197 */ + "non-integral case expression", /* 198 */ + "duplicate case in switch: %ld", /* 199 */ + "duplicate case in switch: %lu", /* 200 */ + "default outside switch", /* 201 */ + "duplicate default in switch", /* 202 */ + "case label must be of type `int' in traditional C", /* 203 */ + "controlling expressions must have scalar type", /* 204 */ + "switch expression must have integral type", /* 205 */ + "enumeration value(s) not handled in switch", /* 206 */ + "loop not entered at top", /* 207 */ + "break outside loop or switch", /* 208 */ + "continue outside loop", /* 209 */ + "enum type mismatch in initialisation", /* 210 */ + "return value type mismatch", /* 211 */ + "cannot return incomplete type", /* 212 */ + "void function %s cannot return value", /* 213 */ + "function %s expects to return value", /* 214 */ + "function implicitly declared to return int", /* 215 */ + "function %s has return (e); and return;", /* 216 */ + "function %s falls off bottom without returning value", /* 217 */ + "ANSI C treats constant as unsigned, op %s", /* 218 */ + "concatenated strings are illegal in traditional C", /* 219 */ + "fallthrough on case statement", /* 220 */ + "initialisation of unsigned with negative constant", /* 221 */ + "conversion of negative constant to unsigned type", /* 222 */ + "end-of-loop code not reached", /* 223 */ + "cannot recover from previous errors", /* 224 */ + "static function called but not defined: %s()", /* 225 */ + "static variable %s unused", /* 226 */ + "const object %s should have initializer", /* 227 */ + "function cannot return const or volatile object", /* 228 */ + "questionable conversion of function pointer", /* 229 */ + "nonportable character comparision, op %s", /* 230 */ + "argument %s unused in function %s", /* 231 */ + "label %s unused in function %s", /* 232 */ + "struct %s never defined", /* 233 */ + "union %s never defined", /* 234 */ + "enum %s never defined", /* 235 */ + "static function %s unused", /* 236 */ + "redeclaration of formal parameter %s", /* 237 */ + "initialisation of union is illegal in traditional C", /* 238 */ + "constant argument to NOT", /* 239 */ + "assignment of different structures", /* 240 */ + "dubious operation on enum, op %s", /* 241 */ + "combination of '%s' and '%s', op %s", /* 242 */ + "dubious comparision of enums, op %s", /* 243 */ + "illegal structure pointer combination", /* 244 */ + "illegal structure pointer combination, op %s", /* 245 */ + "dubious conversion of enum to '%s'", /* 246 */ + "pointer casts may be troublesome", /* 247 */ + "floating-point constant out of range", /* 248 */ + "syntax error", /* 249 */ + "unknown character \\%o", /* 250 */ + "malformed integer constant", /* 251 */ + "integer constant out of range", /* 252 */ + "unterminated character constant", /* 253 */ + "newline in string or char constant", /* 254 */ + "undefined or invalid # directive", /* 255 */ + "unterminated comment", /* 256 */ + "extra characters in lint comment", /* 257 */ + "unterminated string constant", /* 258 */ + "conversion to '%s' due to prototype, arg #%d", /* 259 */ + "previous declaration of %s", /* 260 */ + "previous definition of %s", /* 261 */ + "\\\" inside character constants undefined in traditional C", /* 262 */ + "\\? undefined in traditional C", /* 263 */ + "\\v undefined in traditional C", /* 264 */ + "%s C does not support 'long long'", /* 265 */ + "'long double' is illegal in traditional C", /* 266 */ + "shift equal to size of object", /* 267 */ + "variable declared inline: %s", /* 268 */ + "argument declared inline: %s", /* 269 */ + "function prototypes are illegal in traditional C", /* 270 */ + "switch expression must be of type `int' in traditional C", /* 271 */ + "empty translation unit", /* 272 */ + "bit-field type '%s' invalid in ANSI C", /* 273 */ + "ANSI C forbids comparision of %s with %s", /* 274 */ + "cast discards 'const' from pointer target type", /* 275 */ + "", /* 276 */ + "initialisation of '%s' with '%s'", /* 277 */ + "combination of '%s' and '%s', arg #%d", /* 278 */ + "combination of '%s' and '%s' in return", /* 279 */ + "must be outside function: /* %s */", /* 280 */ + "duplicate use of /* %s */", /* 281 */ + "must precede function definition: /* %s */", /* 282 */ + "argument number mismatch with directive: /* %s */", /* 283 */ + "fallthrough on default statement", /* 284 */ + "prototype declaration", /* 285 */ + "function definition is not a prototype", /* 286 */ + "function declaration is not a prototype", /* 287 */ + "dubious use of /* VARARGS */ with /* %s */", /* 288 */ + "can't be used together: /* PRINTFLIKE */ /* SCANFLIKE */", /* 289 */ + "static function %s declared but not defined", /* 290 */ + "invalid multibyte character", /* 291 */ + "cannot concatenate wide and regular string literals", /* 292 */ + "argument %d must be 'char *' for PRINTFLIKE/SCANFLIKE", /* 293 */ + "multi-character character constant", /* 294 */ + "conversion of '%s' to '%s' is out of range, arg #%d", /* 295 */ + "conversion of negative constant to unsigned type, arg #%d", /* 296 */ + "conversion to '%s' may sign-extend incorrectly, arg #%d", /* 297 */ + "conversion from '%s' may lose accuracy, arg #%d", /* 298 */ + "prototype does not match old style definition, arg #%d", /* 299 */ + "old style definition", /* 300 */ + "array of incomplete type", /* 301 */ + "%s returns pointer to automatic object", /* 302 */ + "ANSI C forbids conversion of %s to %s", /* 303 */ + "ANSI C forbids conversion of %s to %s, arg #%d", /* 304 */ + "ANSI C forbids conversion of %s to %s, op %s", /* 305 */ + "constant truncated by conversion, op %s", /* 306 */ + "static variable %s set but not used", /* 307 */ + "", /* 308 */ + "extra bits set to 0 in conversion of '%s' to '%s', op %s", /* 309 */ +}; + +/* + * If Fflag is not set basename() returns a pointer to the last + * component of the path, otherwise it returns the argument. + */ +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); +} + +static void +verror(n, ap) + int n; + va_list ap; +{ + const char *fn; + + fn = basename(curr_pos.p_file); + (void)printf("%s(%d): ", fn, curr_pos.p_line); + (void)vprintf(msgs[n], ap); + (void)printf("\n"); + nerr++; +} + +static void +vwarning(n, ap) + int n; + va_list ap; +{ + const char *fn; + + if (nowarn) + /* this warning is suppressed by a LINTED comment */ + return; + + fn = basename(curr_pos.p_file); + (void)printf("%s(%d): warning: ", fn, curr_pos.p_line); + (void)vprintf(msgs[n], ap); + (void)printf("\n"); +} + +void +#ifdef __STDC__ +error(int n, ...) +#else +error(n, va_alist) + int n; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start(ap, n); +#else + va_start(ap); +#endif + verror(n, ap); + va_end(ap); +} + +void +#ifdef __STDC__ +lerror(const char *msg, ...) +#else +lerror(msg, va_alist) + const char *msg; + va_dcl +#endif +{ + va_list ap; + const char *fn; + +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); +#endif + fn = basename(curr_pos.p_file); + (void)fprintf(stderr, "%s(%d): lint error: ", fn, curr_pos.p_line); + (void)vfprintf(stderr, msg, ap); + (void)fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +void +#ifdef __STDC__ +warning(int n, ...) +#else +warning(n, va_alist) + int n; + va_dcl +#endif +{ + va_list ap; + +#ifdef __STDC__ + va_start(ap, n); +#else + va_start(ap); +#endif + vwarning(n, ap); + va_end(ap); +} + +void +#ifdef __STDC__ +message(int n, ...) +#else +message(n, va_alist) + int n; + va_dcl +#endif +{ + va_list ap; + const char *fn; + +#ifdef __STDC__ + va_start(ap, n); +#else + va_start(ap); +#endif + fn = basename(curr_pos.p_file); + (void)printf("%s(%d): ", fn, curr_pos.p_line); + (void)vprintf(msgs[n], ap); + (void)printf("\n"); + va_end(ap); +} + +int +#ifdef __STDC__ +gnuism(int n, ...) +#else +gnuism(n, va_alist) + int n; + va_dcl +#endif +{ + va_list ap; + int msg; + +#ifdef __STDC__ + va_start(ap, n); +#else + va_start(ap); +#endif + if (sflag && !gflag) { + verror(n, ap); + msg = 1; + } else if (!sflag && gflag) { + msg = 0; + } else { + vwarning(n, ap); + msg = 1; + } + va_end(ap); + + return (msg); +} diff --git a/usr.bin/xlint/lint1/externs.h b/usr.bin/xlint/lint1/externs.h new file mode 100644 index 0000000..e61f8d0 --- /dev/null +++ b/usr.bin/xlint/lint1/externs.h @@ -0,0 +1,56 @@ +/* $NetBSD: externs.h,v 1.2 1995/07/03 21:24:06 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. + */ + +/* + * mem.c + */ +extern void *xmalloc __P((size_t)); +extern void *xcalloc __P((size_t, size_t)); +extern void *xrealloc __P((void *, size_t)); +extern char *xstrdup __P((const char *)); +extern void nomem __P((void)); + +/* + * emit.c + */ +extern ob_t ob; + +extern void outopen __P((const char *)); +extern void outclose __P((void)); +extern void outclr __P((void)); +extern void outchar __P((int)); +extern void outqchar __P((int)); +extern void outstrg __P((const char *)); +extern void outint __P((int)); +extern void outname __P((const char *)); +extern void outsrc __P((const char *)); diff --git a/usr.bin/xlint/lint1/externs1.h b/usr.bin/xlint/lint1/externs1.h new file mode 100644 index 0000000..e9923c7 --- /dev/null +++ b/usr.bin/xlint/lint1/externs1.h @@ -0,0 +1,281 @@ +/* $NetBSD: externs1.h,v 1.7 1995/10/02 17:31:39 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. + */ + +/* + * main.c + */ +extern int aflag; +extern int bflag; +extern int cflag; +extern int dflag; +extern int eflag; +extern int Fflag; +extern int gflag; +extern int hflag; +extern int pflag; +extern int rflag; +extern int sflag; +extern int tflag; +extern int uflag; +extern int vflag; +extern int yflag; +extern int zflag; + +extern void norecover __P((void)); + +/* + * cgram.y + */ +extern int blklev; +extern int mblklev; +extern int yydebug; + +extern int yyerror __P((char *)); +extern int yyparse __P((void)); + +/* + * scan.l + */ +extern pos_t curr_pos; +extern pos_t csrc_pos; +extern symt_t symtyp; +extern FILE *yyin; +extern u_quad_t qbmasks[], qlmasks[], qumasks[]; + +extern void initscan __P((void)); +extern int sign __P((quad_t, tspec_t, int)); +extern int msb __P((quad_t, tspec_t, int)); +extern quad_t xsign __P((quad_t, tspec_t, int)); +extern void clrwflgs __P((void)); +extern sym_t *getsym __P((sbuf_t *)); +extern void cleanup __P((void)); +extern sym_t *pushdown __P((sym_t *)); +extern void rmsym __P((sym_t *)); +extern void rmsyms __P((sym_t *)); +extern void inssym __P((int, sym_t *)); +extern void freeyyv __P((void *, int)); +extern int yylex __P((void)); + +/* + * mem1.c + */ +extern const char *fnalloc __P((const char *)); +extern const char *fnnalloc __P((const char *, size_t)); +extern int getfnid __P((const char *)); + +extern void initmem __P((void)); + +extern void *getblk __P((size_t)); +extern void *getlblk __P((int, size_t)); +extern void freeblk __P((void)); +extern void freelblk __P((int)); + +extern void *tgetblk __P((size_t)); +extern tnode_t *getnode __P((void)); +extern void tfreeblk __P((void)); +extern struct mbl *tsave __P((void)); +extern void trestor __P((struct mbl *)); + +/* + * err.c + */ +extern int nerr; +extern int sytxerr; +extern const char *msgs[]; + +extern void error __P((int, ...)); +extern void warning __P((int, ...)); +extern void message __P((int, ...)); +extern int gnuism __P((int, ...)); +extern void lerror __P((const char *, ...)); + +/* + * decl.c + */ +extern dinfo_t *dcs; +extern const char *unnamed; +extern int enumval; + +extern void initdecl __P((void)); +extern type_t *gettyp __P((tspec_t)); +extern type_t *duptyp __P((const type_t *)); +extern type_t *tduptyp __P((const type_t *)); +extern int incompl __P((type_t *)); +extern void setcompl __P((type_t *, int)); +extern void addscl __P((scl_t)); +extern void addtype __P((type_t *)); +extern void addqual __P((tqual_t)); +extern void pushdecl __P((scl_t)); +extern void popdecl __P((void)); +extern void setasm __P((void)); +extern void clrtyp __P((void)); +extern void deftyp __P((void)); +extern int length __P((type_t *, const char *)); +extern int getbound __P((type_t *)); +extern sym_t *lnklst __P((sym_t *, sym_t *)); +extern void chktyp __P((sym_t *)); +extern sym_t *decl1str __P((sym_t *)); +extern sym_t *bitfield __P((sym_t *, int)); +extern pqinf_t *mergepq __P((pqinf_t *, pqinf_t *)); +extern sym_t *addptr __P((sym_t *, pqinf_t *)); +extern sym_t *addarray __P((sym_t *, int, int)); +extern sym_t *addfunc __P((sym_t *, sym_t *)); +extern void chkfdef __P((sym_t *, int)); +extern sym_t *dname __P((sym_t *)); +extern sym_t *iname __P((sym_t *)); +extern type_t *mktag __P((sym_t *, tspec_t, int, int)); +extern const char *scltoa __P((scl_t)); +extern type_t *compltag __P((type_t *, sym_t *)); +extern sym_t *ename __P((sym_t *, int, int)); +extern void decl1ext __P((sym_t *, int)); +extern void cpuinfo __P((sym_t *, sym_t *)); +extern int isredec __P((sym_t *, int *)); +extern int eqtype __P((type_t *, type_t *, int, int, int *)); +extern void compltyp __P((sym_t *, sym_t *)); +extern sym_t *decl1arg __P((sym_t *, int)); +extern void cluparg __P((void)); +extern void decl1loc __P((sym_t *, int)); +extern sym_t *aname __P((void)); +extern void globclup __P((void)); +extern sym_t *decl1abs __P((sym_t *)); +extern void chksz __P((sym_t *)); +extern void setsflg __P((sym_t *)); +extern void setuflg __P((sym_t *, int, int)); +extern void chkusage __P((dinfo_t *)); +extern void chkusg1 __P((int, sym_t *)); +extern void chkglsyms __P((void)); +extern void prevdecl __P((int, sym_t *)); + +/* + * tree.c + */ +extern void initmtab __P((void)); +extern type_t *incref __P((type_t *, tspec_t)); +extern type_t *tincref __P((type_t *, tspec_t)); +extern tnode_t *getcnode __P((type_t *, val_t *)); +extern tnode_t *getnnode __P((sym_t *, int)); +extern tnode_t *getsnode __P((strg_t *)); +extern sym_t *strmemb __P((tnode_t *, op_t, sym_t *)); +extern tnode_t *build __P((op_t, tnode_t *, tnode_t *)); +extern tnode_t *cconv __P((tnode_t *)); +extern int typeok __P((op_t, int, tnode_t *, tnode_t *)); +extern tnode_t *promote __P((op_t, int, tnode_t *)); +extern tnode_t *convert __P((op_t, int, type_t *, tnode_t *)); +extern void cvtcon __P((op_t, int, type_t *, val_t *, val_t *)); +extern const char *tyname __P((type_t *)); +extern tnode_t *bldszof __P((type_t *)); +extern tnode_t *cast __P((tnode_t *, type_t *)); +extern tnode_t *funcarg __P((tnode_t *, tnode_t *)); +extern tnode_t *funccall __P((tnode_t *, tnode_t *)); +extern val_t *constant __P((tnode_t *)); +extern void expr __P((tnode_t *, int, int)); +extern void chkmisc __P((tnode_t *, int, int, int, int, int, int)); +extern int conaddr __P((tnode_t *, sym_t **, ptrdiff_t *)); +extern strg_t *catstrg __P((strg_t *, strg_t *)); + +/* + * func.c + */ +extern sym_t *funcsym; +extern int reached; +extern int rchflg; +extern int ftflg; +extern int nargusg; +extern pos_t aupos; +extern int nvararg; +extern pos_t vapos; +extern int prflstrg; +extern pos_t prflpos; +extern int scflstrg; +extern pos_t scflpos; +extern int ccflg; +extern int llibflg; +extern int nowarn; +extern int plibflg; +extern int quadflg; + +extern void pushctrl __P((int)); +extern void popctrl __P((int)); +extern void chkreach __P((void)); +extern void funcdef __P((sym_t *)); +extern void funcend __P((void)); +extern void label __P((int, sym_t *, tnode_t *)); +extern void if1 __P((tnode_t *)); +extern void if2 __P((void)); +extern void if3 __P((int)); +extern void switch1 __P((tnode_t *)); +extern void switch2 __P((void)); +extern void while1 __P((tnode_t *)); +extern void while2 __P((void)); +extern void do1 __P((void)); +extern void do2 __P((tnode_t *)); +extern void for1 __P((tnode_t *, tnode_t *, tnode_t *)); +extern void for2 __P((void)); +extern void dogoto __P((sym_t *)); +extern void docont __P((void)); +extern void dobreak __P((void)); +extern void doreturn __P((tnode_t *)); +extern void glclup __P((int)); +extern void argsused __P((int)); +extern void constcond __P((int)); +extern void fallthru __P((int)); +extern void notreach __P((int)); +extern void lintlib __P((int)); +extern void linted __P((int)); +extern void varargs __P((int)); +extern void printflike __P((int)); +extern void scanflike __P((int)); +extern void protolib __P((int)); +extern void longlong __P((int)); + +/* + * init.c + */ +extern int initerr; +extern sym_t *initsym; +extern int startinit; + +extern void prepinit __P((void)); +extern void initrbr __P((void)); +extern void initlbr __P((void)); +extern void mkinit __P((tnode_t *)); + +/* + * emit.c + */ +extern void outtype __P((type_t *)); +extern const char *ttos __P((type_t *)); +extern void outsym __P((sym_t *, scl_t, def_t)); +extern void outfdef __P((sym_t *, pos_t *, int, int, sym_t *)); +extern void outcall __P((tnode_t *, int, int)); +extern void outusg __P((sym_t *)); diff --git a/usr.bin/xlint/lint1/func.c b/usr.bin/xlint/lint1/func.c new file mode 100644 index 0000000..5d3a172 --- /dev/null +++ b/usr.bin/xlint/lint1/func.c @@ -0,0 +1,1261 @@ +/* $NetBSD: func.c,v 1.7 1995/10/02 17:31:40 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: func.c,v 1.7 1995/10/02 17:31:40 jpo Exp $"; +#endif + +#include +#include + +#include "lint1.h" +#include "y.tab.h" + +/* + * Contains a pointer to the symbol table entry of the current function + * definition. + */ +sym_t *funcsym; + +/* Is set as long as a statement can be reached. Must be set at level 0. */ +int reached = 1; + +/* + * Is set as long as NOTREACHED is in effect. + * Is reset everywhere where reached can become 0. + */ +int rchflg; + +/* + * In conjunction with reached ontrols printing of "fallthrough on ..." + * warnings. + * Reset by each statement and set by FALLTHROUGH, switch (switch1()) + * and case (label()). + * + * Control statements if, for, while and switch do not reset ftflg because + * this must be done by the controled statement. At least for if this is + * important because ** FALLTHROUGH ** after "if (expr) stmnt" is evaluated + * befor the following token, wich causes reduction of above, is read. + * This means that ** FALLTHROUGH ** after "if ..." would always be ignored. + */ +int ftflg; + +/* Top element of stack for control statements */ +cstk_t *cstk; + +/* + * Number of arguments which will be checked for usage in following + * function definition. -1 stands for all arguments. + * + * The position of the last ARGSUSED comment is stored in aupos. + */ +int nargusg = -1; +pos_t aupos; + +/* + * Number of arguments of the following function definition whose types + * shall be checked by lint2. -1 stands for all arguments. + * + * The position of the last VARARGS comment is stored in vapos. + */ +int nvararg = -1; +pos_t vapos; + +/* + * Both prflstr and scflstrg contain the number of the argument which + * shall be used to check the types of remaining arguments (for PRINTFLIKE + * and SCANFLIKE). + * + * prflpos and scflpos are the positions of the last PRINTFLIKE or + * SCANFLIKE comment. + */ +int prflstrg = -1; +int scflstrg = -1; +pos_t prflpos; +pos_t scflpos; + +/* + * Are both plibflg and llibflg set, prototypes are writen as function + * definitions to the output file. + */ +int plibflg; + +/* + * Nonzero means that no warnings about constands in conditional + * context are printed. + */ +int ccflg; + +/* + * llibflg is set if a lint library shall be created. The effect of + * llibflg is that all defined symbols are treated as used. + * (The LINTLIBRARY comment also resets vflag.) + */ +int llibflg; + +/* + * Nonzero if warnings are suppressed by a LINTED directive + */ +int nowarn; + +/* + * Nonzero if complaints about use of "long long" are suppressed in + * the next statement or declaration. + */ +int quadflg; + +/* + * Puts a new element at the top of the stack used for control statements. + */ +void +pushctrl(env) + int env; +{ + cstk_t *ci; + + ci = xcalloc(1, sizeof (cstk_t)); + ci->c_env = env; + ci->c_nxt = cstk; + cstk = ci; +} + +/* + * Removes the top element of the stack used for control statements. + */ +void +popctrl(env) + int env; +{ + cstk_t *ci; + clst_t *cl; + + if (cstk == NULL || cstk->c_env != env) + lerror("popctrl() 1"); + + cstk = (ci = cstk)->c_nxt; + + while ((cl = ci->c_clst) != NULL) { + ci->c_clst = cl->cl_nxt; + free(cl); + } + + if (ci->c_swtype != NULL) + free(ci->c_swtype); + + free(ci); +} + +/* + * Prints a warning if a statement cannot be reached. + */ +void +chkreach() +{ + if (!reached && !rchflg) { + /* statement not reached */ + warning(193); + reached = 1; + } +} + +/* + * Called after a function declaration which introduces a function definition + * and before an (optional) old style argument declaration list. + * + * Puts all symbols declared in the Prototype or in an old style argument + * list back to the symbol table. + * + * Does the usual checking of storage class, type (return value), + * redeclaration etc.. + */ +void +funcdef(fsym) + sym_t *fsym; +{ + int n, warn; + sym_t *arg, *sym, *rdsym; + + funcsym = fsym; + + /* + * Put all symbols declared in the argument list back to the + * symbol table. + */ + for (sym = dcs->d_fpsyms; sym != NULL; sym = sym->s_dlnxt) { + if (sym->s_blklev != -1) { + if (sym->s_blklev != 1) + lerror("funcdef() 1"); + inssym(1, sym); + } + } + + /* + * In osfunc() we did not know whether it is an old style function + * definition or only an old style declaration, if there are no + * arguments inside the argument list ("f()"). + */ + if (!fsym->s_type->t_proto && fsym->s_args == NULL) + fsym->s_osdef = 1; + + chktyp(fsym); + + /* + * chktyp() checks for almost all possible errors, but not for + * incomplete return values (these are allowed in declarations) + */ + if (fsym->s_type->t_subt->t_tspec != VOID && + incompl(fsym->s_type->t_subt)) { + /* cannot return incomplete type */ + error(67); + } + + fsym->s_def = DEF; + + if (fsym->s_scl == TYPEDEF) { + fsym->s_scl = EXTERN; + /* illegal storage class */ + error(8); + } + + if (dcs->d_inline) + fsym->s_inline = 1; + + /* + * Arguments in new style function declarations need a name. + * (void is already removed from the list of arguments) + */ + n = 1; + for (arg = fsym->s_type->t_args; arg != NULL; arg = arg->s_nxt) { + if (arg->s_scl == ABSTRACT) { + if (arg->s_name != unnamed) + lerror("funcdef() 2"); + /* formal parameter lacks name: param #%d */ + error(59, n); + } else { + if (arg->s_name == unnamed) + lerror("funcdef() 3"); + } + n++; + } + + /* + * We must also remember the position. s_dpos is overwritten + * if this is an old style definition and we had already a + * prototype. + */ + STRUCT_ASSIGN(dcs->d_fdpos, fsym->s_dpos); + + if ((rdsym = dcs->d_rdcsym) != NULL) { + + if (!isredec(fsym, (warn = 0, &warn))) { + + /* + * Print nothing if the newly defined function + * is defined in old style. A better warning will + * be printed in cluparg(). + */ + if (warn && !fsym->s_osdef) { + /* redeclaration of %s */ + (*(sflag ? error : warning))(27, fsym->s_name); + prevdecl(-1, rdsym); + } + + /* copy usage information */ + cpuinfo(fsym, rdsym); + + /* + * If the old symbol was a prototype and the new + * one is none, overtake the position of the + * declaration of the prototype. + */ + if (fsym->s_osdef && rdsym->s_type->t_proto) + STRUCT_ASSIGN(fsym->s_dpos, rdsym->s_dpos); + + /* complete the type */ + compltyp(fsym, rdsym); + + /* once a function is inline it remains inline */ + if (rdsym->s_inline) + fsym->s_inline = 1; + + } + + /* remove the old symbol from the symbol table */ + rmsym(rdsym); + + } + + if (fsym->s_osdef && !fsym->s_type->t_proto) { + if (sflag && hflag && strcmp(fsym->s_name, "main") != 0) + /* function definition is not a prototyp */ + warning(286); + } + + if (dcs->d_notyp) + /* return value is implizitly declared to be int */ + fsym->s_rimpl = 1; + + reached = 1; +} + +/* + * Called at the end of a function definition. + */ +void +funcend() +{ + sym_t *arg; + int n; + + if (reached) { + cstk->c_noretval = 1; + if (funcsym->s_type->t_subt->t_tspec != VOID && + !funcsym->s_rimpl) { + /* func. %s falls off bottom without returning value */ + warning(217, funcsym->s_name); + } + } + + /* + * This warning is printed only if the return value was implizitly + * declared to be int. Otherwise the wrong return statement + * has already printed a warning. + */ + if (cstk->c_noretval && cstk->c_retval && funcsym->s_rimpl) + /* function %s has return (e); and return; */ + warning(216, funcsym->s_name); + + /* Print warnings for unused arguments */ + arg = dcs->d_fargs; + n = 0; + while (arg != NULL && (nargusg == -1 || n < nargusg)) { + chkusg1(dcs->d_asm, arg); + arg = arg->s_nxt; + n++; + } + nargusg = -1; + + /* + * write the information about the function definition to the + * output file + * inline functions explicitely declared extern are written as + * declarations only. + */ + if (dcs->d_scl == EXTERN && funcsym->s_inline) { + outsym(funcsym, funcsym->s_scl, DECL); + } else { + outfdef(funcsym, &dcs->d_fdpos, cstk->c_retval, + funcsym->s_osdef, dcs->d_fargs); + } + + /* + * remove all symbols declared during argument declaration from + * the symbol table + */ + if (dcs->d_nxt != NULL || dcs->d_ctx != EXTERN) + lerror("funcend() 1"); + rmsyms(dcs->d_fpsyms); + + /* must be set on level 0 */ + reached = 1; +} + +/* + * Process a label. + * + * typ type of the label (T_NAME, T_DEFAULT or T_CASE). + * sym symbol table entry of label if typ == T_NAME + * tn expression if typ == T_CASE + */ +void +label(typ, sym, tn) + int typ; + sym_t *sym; + tnode_t *tn; +{ + cstk_t *ci; + clst_t *cl; + val_t *v, *nv; + tspec_t t; + + switch (typ) { + + case T_NAME: + if (sym->s_set) { + /* label %s redefined */ + error(194, sym->s_name); + } else { + setsflg(sym); + } + break; + + case T_CASE: + + /* find the stack entry for the innermost switch statement */ + for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) ; + + if (ci == NULL) { + /* case not in switch */ + error(195); + tn = NULL; + } else if (tn != NULL && tn->tn_op != CON) { + /* non-constant case expression */ + error(197); + tn = NULL; + } else if (tn != NULL && !isityp(tn->tn_type->t_tspec)) { + /* non-integral case expression */ + error(198); + tn = NULL; + } + + if (tn != NULL) { + + if (ci->c_swtype == NULL) + lerror("label() 1"); + + if (reached && !ftflg) { + if (hflag) + /* fallthrough on case statement */ + warning(220); + } + + t = tn->tn_type->t_tspec; + if (t == LONG || t == ULONG || + t == QUAD || t == UQUAD) { + if (tflag) + /* case label must be of type ... */ + warning(203); + } + + /* + * get the value of the expression and convert it + * to the type of the switch expression + */ + v = constant(tn); + nv = xcalloc(1, sizeof (val_t)); + cvtcon(CASE, 0, ci->c_swtype, nv, v); + free(v); + + /* look if we had this value already */ + for (cl = ci->c_clst; cl != NULL; cl = cl->cl_nxt) { + if (cl->cl_val.v_quad == nv->v_quad) + break; + } + if (cl != NULL && isutyp(nv->v_tspec)) { + /* duplicate case in switch, %lu */ + error(200, (u_long)nv->v_quad); + } else if (cl != NULL) { + /* duplicate case in switch, %ld */ + error(199, (long)nv->v_quad); + } else { + /* + * append the value to the list of + * case values + */ + cl = xcalloc(1, sizeof (clst_t)); + STRUCT_ASSIGN(cl->cl_val, *nv); + cl->cl_nxt = ci->c_clst; + ci->c_clst = cl; + } + } + tfreeblk(); + break; + + case T_DEFAULT: + + /* find the stack entry for the innermost switch statement */ + for (ci = cstk; ci != NULL && !ci->c_switch; ci = ci->c_nxt) ; + + if (ci == NULL) { + /* default outside switch */ + error(201); + } else if (ci->c_default) { + /* duplicate default in switch */ + error(202); + } else { + if (reached && !ftflg) { + if (hflag) + /* fallthrough on default statement */ + warning(284); + } + ci->c_default = 1; + } + break; + }; + reached = 1; +} + +/* + * T_IF T_LPARN expr T_RPARN + */ +void +if1(tn) + tnode_t *tn; +{ + if (tn != NULL) + tn = cconv(tn); + if (tn != NULL) + tn = promote(NOOP, 0, tn); + expr(tn, 0, 1); + pushctrl(T_IF); +} + +/* + * if_without_else + * if_without_else T_ELSE + */ +void +if2() +{ + cstk->c_rchif = reached ? 1 : 0; + reached = 1; +} + +/* + * if_without_else + * if_without_else T_ELSE stmnt + */ +void +if3(els) + int els; +{ + if (els) { + reached |= cstk->c_rchif; + } else { + reached = 1; + } + popctrl(T_IF); +} + +/* + * T_SWITCH T_LPARN expr T_RPARN + */ +void +switch1(tn) + tnode_t *tn; +{ + tspec_t t; + type_t *tp; + + if (tn != NULL) + tn = cconv(tn); + if (tn != NULL) + tn = promote(NOOP, 0, tn); + if (tn != NULL && !isityp(tn->tn_type->t_tspec)) { + /* switch expression must have integral type */ + error(205); + tn = NULL; + } + if (tn != NULL && tflag) { + t = tn->tn_type->t_tspec; + if (t == LONG || t == ULONG || t == QUAD || t == UQUAD) { + /* switch expr. must be of type `int' in trad. C */ + warning(271); + } + } + + /* + * Remember the type of the expression. Because its possible + * that (*tp) is allocated on tree memory the type must be + * duplicated. This is not too complicated because it is + * only an integer type. + */ + tp = xcalloc(1, sizeof (type_t)); + if (tn != NULL) { + tp->t_tspec = tn->tn_type->t_tspec; + if ((tp->t_isenum = tn->tn_type->t_isenum) != 0) + tp->t_enum = tn->tn_type->t_enum; + } else { + tp->t_tspec = INT; + } + + expr(tn, 1, 0); + + pushctrl(T_SWITCH); + cstk->c_switch = 1; + cstk->c_swtype = tp; + + reached = rchflg = 0; + ftflg = 1; +} + +/* + * switch_expr stmnt + */ +void +switch2() +{ + int nenum, nclab; + sym_t *esym; + clst_t *cl; + + if (cstk->c_swtype == NULL) + lerror("switch2() 1"); + + /* + * If the switch expression was of type enumeration, count the case + * labels and the number of enumerators. If both counts are not + * equal print a warning. + */ + if (cstk->c_swtype->t_isenum) { + nenum = nclab = 0; + if (cstk->c_swtype->t_enum == NULL) + lerror("switch2() 2"); + for (esym = cstk->c_swtype->t_enum->elem; + esym != NULL; esym = esym->s_nxt) { + nenum++; + } + for (cl = cstk->c_clst; cl != NULL; cl = cl->cl_nxt) + nclab++; + if (hflag && eflag && nenum != nclab && !cstk->c_default) { + /* enumeration value(s) not handled in switch */ + warning(206); + } + } + + if (cstk->c_break) { + /* + * end of switch alway reached (c_break is only set if the + * break statement can be reached). + */ + reached = 1; + } else if (!cstk->c_default && + (!hflag || !cstk->c_swtype->t_isenum || nenum != nclab)) { + /* + * there are possible values which are not handled in + * switch + */ + reached = 1; + } /* + * otherwise the end of the switch expression is reached + * if the end of the last statement inside it is reached. + */ + + popctrl(T_SWITCH); +} + +/* + * T_WHILE T_LPARN expr T_RPARN + */ +void +while1(tn) + tnode_t *tn; +{ + if (!reached) { + /* loop not entered at top */ + warning(207); + reached = 1; + } + + if (tn != NULL) + tn = cconv(tn); + if (tn != NULL) + tn = promote(NOOP, 0, tn); + if (tn != NULL && !issclt(tn->tn_type->t_tspec)) { + /* controlling expressions must have scalar type */ + error(204); + tn = NULL; + } + + pushctrl(T_WHILE); + cstk->c_loop = 1; + if (tn != NULL && tn->tn_op == CON) { + if (isityp(tn->tn_type->t_tspec)) { + cstk->c_infinite = tn->tn_val->v_quad != 0; + } else { + cstk->c_infinite = tn->tn_val->v_ldbl != 0.0; + } + } + + expr(tn, 0, 1); +} + +/* + * while_expr stmnt + * while_expr error + */ +void +while2() +{ + /* + * The end of the loop can be reached if it is no endless loop + * or there was a break statement which was reached. + */ + reached = !cstk->c_infinite || cstk->c_break; + rchflg = 0; + + popctrl(T_WHILE); +} + +/* + * T_DO + */ +void +do1() +{ + if (!reached) { + /* loop not entered at top */ + warning(207); + reached = 1; + } + + pushctrl(T_DO); + cstk->c_loop = 1; +} + +/* + * do stmnt do_while_expr + * do error + */ +void +do2(tn) + tnode_t *tn; +{ + /* + * If there was a continue statement the expression controlling the + * loop is reached. + */ + if (cstk->c_cont) + reached = 1; + + if (tn != NULL) + tn = cconv(tn); + if (tn != NULL) + tn = promote(NOOP, 0, tn); + if (tn != NULL && !issclt(tn->tn_type->t_tspec)) { + /* controlling expressions must have scalar type */ + error(204); + tn = NULL; + } + + if (tn != NULL && tn->tn_op == CON) { + if (isityp(tn->tn_type->t_tspec)) { + cstk->c_infinite = tn->tn_val->v_quad != 0; + } else { + cstk->c_infinite = tn->tn_val->v_ldbl != 0.0; + } + } + + expr(tn, 0, 1); + + /* + * The end of the loop is only reached if it is no endless loop + * or there was a break statement which could be reached. + */ + reached = !cstk->c_infinite || cstk->c_break; + rchflg = 0; + + popctrl(T_DO); +} + +/* + * T_FOR T_LPARN opt_expr T_SEMI opt_expr T_SEMI opt_expr T_RPARN + */ +void +for1(tn1, tn2, tn3) + tnode_t *tn1, *tn2, *tn3; +{ + /* + * If there is no initialisation expression it is possible that + * it is intended not to enter the loop at top. + */ + if (tn1 != NULL && !reached) { + /* loop not entered at top */ + warning(207); + reached = 1; + } + + pushctrl(T_FOR); + cstk->c_loop = 1; + + /* + * Store the tree memory for the reinitialisation expression. + * Also remember this expression itself. We must check it at + * the end of the loop to get "used but not set" warnings correct. + */ + cstk->c_fexprm = tsave(); + cstk->c_f3expr = tn3; + STRUCT_ASSIGN(cstk->c_fpos, curr_pos); + STRUCT_ASSIGN(cstk->c_cfpos, csrc_pos); + + if (tn1 != NULL) + expr(tn1, 0, 0); + + if (tn2 != NULL) + tn2 = cconv(tn2); + if (tn2 != NULL) + tn2 = promote(NOOP, 0, tn2); + if (tn2 != NULL && !issclt(tn2->tn_type->t_tspec)) { + /* controlling expressions must have scalar type */ + error(204); + tn2 = NULL; + } + if (tn2 != NULL) + expr(tn2, 0, 1); + + if (tn2 == NULL) { + cstk->c_infinite = 1; + } else if (tn2->tn_op == CON) { + if (isityp(tn2->tn_type->t_tspec)) { + cstk->c_infinite = tn2->tn_val->v_quad != 0; + } else { + cstk->c_infinite = tn2->tn_val->v_ldbl != 0.0; + } + } + + /* Checking the reinitialisation expression is done in for2() */ + + reached = 1; +} + +/* + * for_exprs stmnt + * for_exprs error + */ +void +for2() +{ + pos_t cpos, cspos; + tnode_t *tn3; + + if (cstk->c_cont) + reached = 1; + + STRUCT_ASSIGN(cpos, curr_pos); + STRUCT_ASSIGN(cspos, csrc_pos); + + /* Restore the tree memory for the reinitialisation expression */ + trestor(cstk->c_fexprm); + tn3 = cstk->c_f3expr; + STRUCT_ASSIGN(curr_pos, cstk->c_fpos); + STRUCT_ASSIGN(csrc_pos, cstk->c_cfpos); + + /* simply "statement not reached" would be confusing */ + if (!reached && !rchflg) { + /* end-of-loop code not reached */ + warning(223); + reached = 1; + } + + if (tn3 != NULL) { + expr(tn3, 0, 0); + } else { + tfreeblk(); + } + + STRUCT_ASSIGN(curr_pos, cpos); + STRUCT_ASSIGN(csrc_pos, cspos); + + /* An endless loop without break will never terminate */ + reached = cstk->c_break || !cstk->c_infinite; + rchflg = 0; + + popctrl(T_FOR); +} + +/* + * T_GOTO identifier T_SEMI + * T_GOTO error T_SEMI + */ +void +dogoto(lab) + sym_t *lab; +{ + setuflg(lab, 0, 0); + + chkreach(); + + reached = rchflg = 0; +} + +/* + * T_BREAK T_SEMI + */ +void +dobreak() +{ + cstk_t *ci; + + ci = cstk; + while (ci != NULL && !ci->c_loop && !ci->c_switch) + ci = ci->c_nxt; + + if (ci == NULL) { + /* break outside loop or switch */ + error(208); + } else { + if (reached) + ci->c_break = 1; + } + + if (bflag) + chkreach(); + + reached = rchflg = 0; +} + +/* + * T_CONTINUE T_SEMI + */ +void +docont() +{ + cstk_t *ci; + + for (ci = cstk; ci != NULL && !ci->c_loop; ci = ci->c_nxt) ; + + if (ci == NULL) { + /* continue outside loop */ + error(209); + } else { + ci->c_cont = 1; + } + + chkreach(); + + reached = rchflg = 0; +} + +/* + * T_RETURN T_SEMI + * T_RETURN expr T_SEMI + */ +void +doreturn(tn) + tnode_t *tn; +{ + tnode_t *ln, *rn; + cstk_t *ci; + op_t op; + + for (ci = cstk; ci->c_nxt != NULL; ci = ci->c_nxt) ; + + if (tn != NULL) { + ci->c_retval = 1; + } else { + ci->c_noretval = 1; + } + + if (tn != NULL && funcsym->s_type->t_subt->t_tspec == VOID) { + /* void function %s cannot return value */ + error(213, funcsym->s_name); + tfreeblk(); + tn = NULL; + } else if (tn == NULL && funcsym->s_type->t_subt->t_tspec != VOID) { + /* + * Assume that the function has a return value only if it + * is explicitly declared. + */ + if (!funcsym->s_rimpl) + /* function %s expects to return value */ + warning(214, funcsym->s_name); + } + + if (tn != NULL) { + + /* Create a temporary node for the left side */ + ln = tgetblk(sizeof (tnode_t)); + ln->tn_op = NAME; + ln->tn_type = tduptyp(funcsym->s_type->t_subt); + ln->tn_type->t_const = 0; + ln->tn_lvalue = 1; + ln->tn_sym = funcsym; /* better than nothing */ + + tn = build(RETURN, ln, tn); + + if (tn != NULL) { + rn = tn->tn_right; + while ((op = rn->tn_op) == CVT || op == PLUS) + rn = rn->tn_left; + if (rn->tn_op == AMPER && rn->tn_left->tn_op == NAME && + rn->tn_left->tn_sym->s_scl == AUTO) { + /* %s returns pointer to automatic object */ + warning(302, funcsym->s_name); + } + } + + expr(tn, 1, 0); + + } else { + + chkreach(); + + } + + reached = rchflg = 0; +} + +/* + * Do some cleanup after a global declaration or definition. + * Especially remove informations about unused lint comments. + */ +void +glclup(silent) + int silent; +{ + pos_t cpos; + + STRUCT_ASSIGN(cpos, curr_pos); + + if (nargusg != -1) { + if (!silent) { + STRUCT_ASSIGN(curr_pos, aupos); + /* must precede function definition: %s */ + warning(282, "ARGSUSED"); + } + nargusg = -1; + } + if (nvararg != -1) { + if (!silent) { + STRUCT_ASSIGN(curr_pos, vapos); + /* must precede function definition: %s */ + warning(282, "VARARGS"); + } + nvararg = -1; + } + if (prflstrg != -1) { + if (!silent) { + STRUCT_ASSIGN(curr_pos, prflpos); + /* must precede function definition: %s */ + warning(282, "PRINTFLIKE"); + } + prflstrg = -1; + } + if (scflstrg != -1) { + if (!silent) { + STRUCT_ASSIGN(curr_pos, scflpos); + /* must precede function definition: %s */ + warning(282, "SCANFLIKE"); + } + scflstrg = -1; + } + + STRUCT_ASSIGN(curr_pos, cpos); + + dcs->d_asm = 0; +} + +/* + * ARGSUSED comment + * + * Only the first n arguments of the following function are checked + * for usage. A missing argument is taken to be 0. + */ +void +argsused(n) + int n; +{ + if (n == -1) + n = 0; + + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "ARGSUSED"); + return; + } + if (nargusg != -1) { + /* duplicate use of ** %s ** */ + warning(281, "ARGSUSED"); + } + nargusg = n; + STRUCT_ASSIGN(aupos, curr_pos); +} + +/* + * VARARGS comment + * + * Makes that lint2 checks only the first n arguments for compatibility + * to the function definition. A missing argument is taken to be 0. + */ +void +varargs(n) + int n; +{ + if (n == -1) + n = 0; + + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "VARARGS"); + return; + } + if (nvararg != -1) { + /* duplicate use of ** %s ** */ + warning(281, "VARARGS"); + } + nvararg = n; + STRUCT_ASSIGN(vapos, curr_pos); +} + +/* + * PRINTFLIKE comment + * + * Check all arguments until the (n-1)-th as usual. The n-th argument is + * used the check the types of remaining arguments. + */ +void +printflike(n) + int n; +{ + if (n == -1) + n = 0; + + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "PRINTFLIKE"); + return; + } + if (prflstrg != -1) { + /* duplicate use of ** %s ** */ + warning(281, "PRINTFLIKE"); + } + prflstrg = n; + STRUCT_ASSIGN(prflpos, curr_pos); +} + +/* + * SCANFLIKE comment + * + * Check all arguments until the (n-1)-th as usual. The n-th argument is + * used the check the types of remaining arguments. + */ +void +scanflike(n) + int n; +{ + if (n == -1) + n = 0; + + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "SCANFLIKE"); + return; + } + if (scflstrg != -1) { + /* duplicate use of ** %s ** */ + warning(281, "SCANFLIKE"); + } + scflstrg = n; + STRUCT_ASSIGN(scflpos, curr_pos); +} + +/* + * Set the linenumber for a CONSTCOND comment. At this and the following + * line no warnings about constants in conditional contexts are printed. + */ +/* ARGSUSED */ +void +constcond(n) + int n; +{ + ccflg = 1; +} + +/* + * Suppress printing of "fallthrough on ..." warnings until next + * statement. + */ +/* ARGSUSED */ +void +fallthru(n) + int n; +{ + ftflg = 1; +} + +/* + * Stop warnings about statements which cannot be reached. Also tells lint + * that the following statements cannot be reached (e.g. after exit()). + */ +/* ARGSUSED */ +void +notreach(n) + int n; +{ + reached = 0; + rchflg = 1; +} + +/* ARGSUSED */ +void +lintlib(n) + int n; +{ + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "LINTLIBRARY"); + return; + } + llibflg = 1; + vflag = 0; +} + +/* + * Suppress most warnings at the current and the following line. + */ +/* ARGSUSED */ +void +linted(n) + int n; +{ + nowarn = 1; +} + +/* + * PROTOTLIB in conjunction with LINTLIBRARY can be used to handle + * prototypes like function definitions. This is done if the argument + * to PROTOLIB is nonzero. Otherwise prototypes are handled normaly. + */ +void +protolib(n) + int n; +{ + if (dcs->d_ctx != EXTERN) { + /* must be outside function: ** %s ** */ + warning(280, "PROTOLIB"); + return; + } + plibflg = n == 0 ? 0 : 1; +} + +/* + * Set quadflg to nonzero which means that the next statement/declaration + * may use "long long" without an error or warning. + */ +/* ARGSUSED */ +void +longlong(n) + int n; +{ + quadflg = 1; +} diff --git a/usr.bin/xlint/lint1/init.c b/usr.bin/xlint/lint1/init.c new file mode 100644 index 0000000..dbd216b --- /dev/null +++ b/usr.bin/xlint/lint1/init.c @@ -0,0 +1,513 @@ +/* $NetBSD: init.c,v 1.4 1995/10/02 17:21:37 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: init.c,v 1.4 1995/10/02 17:21:37 jpo Exp $"; +#endif + +#include + +#include "lint1.h" + +/* + * initerr is set as soon as a fatal error occured in an initialisation. + * The effect is that the rest of the initialisation is ignored (parsed + * by yacc, expression trees built, but no initialisation takes place). + */ +int initerr; + +/* Pointer to the symbol which is to be initialized. */ +sym_t *initsym; + +/* Points to the top element of the initialisation stack. */ +istk_t *initstk; + + +static void popi2 __P((void)); +static void popinit __P((int)); +static void pushinit __P((void)); +static void testinit __P((void)); +static void nextinit __P((int)); +static int strginit __P((tnode_t *)); + + +/* + * Initialize the initialisation stack by putting an entry for the variable + * which is to be initialized on it. + */ +void +prepinit() +{ + istk_t *istk; + + if (initerr) + return; + + /* free memory used in last initialisation */ + while ((istk = initstk) != NULL) { + initstk = istk->i_nxt; + free(istk); + } + + /* + * If the type which is to be initialized is an incomplete type, + * it must be duplicated. + */ + if (initsym->s_type->t_tspec == ARRAY && incompl(initsym->s_type)) + initsym->s_type = duptyp(initsym->s_type); + + istk = initstk = xcalloc(1, sizeof (istk_t)); + istk->i_subt = initsym->s_type; + istk->i_cnt = 1; + +} + +static void +popi2() +{ + istk_t *istk; + sym_t *m; + + initstk = (istk = initstk)->i_nxt; + if (initstk == NULL) + lerror("popi2() 1"); + free(istk); + + istk = initstk; + + istk->i_cnt--; + if (istk->i_cnt < 0) + lerror("popi2() 3"); + + /* + * If the removed element was a structure member, we must go + * to the next structure member. + */ + if (istk->i_cnt > 0 && istk->i_type->t_tspec == STRUCT) { + do { + m = istk->i_mem = istk->i_mem->s_nxt; + if (m == NULL) + lerror("popi2() 2"); + } while (m->s_field && m->s_name == unnamed); + istk->i_subt = m->s_type; + } +} + +static void +popinit(brace) + int brace; +{ + if (brace) { + /* + * Take all entries, including the first which requires + * a closing brace, from the stack. + */ + do { + brace = initstk->i_brace; + popi2(); + } while (!brace); + } else { + /* + * Take all entries which cannot be used for further + * initializers from the stack, but do this only if + * they do not require a closing brace. + */ + while (!initstk->i_brace && + initstk->i_cnt == 0 && !initstk->i_nolimit) { + popi2(); + } + } +} + +static void +pushinit() +{ + istk_t *istk; + int cnt; + sym_t *m; + + istk = initstk; + + /* Extend an incomplete array type by one element */ + if (istk->i_cnt == 0) { + /* + * Inside of other aggregate types must not be an incomplete + * type. + */ + if (istk->i_nxt->i_nxt != NULL) + lerror("pushinit() 1"); + istk->i_cnt = 1; + if (istk->i_type->t_tspec != ARRAY) + lerror("pushinit() 2"); + istk->i_type->t_dim++; + /* from now its an complete type */ + setcompl(istk->i_type, 0); + } + + if (istk->i_cnt <= 0) + lerror("pushinit() 3"); + if (istk->i_type != NULL && issclt(istk->i_type->t_tspec)) + lerror("pushinit() 4"); + + initstk = xcalloc(1, sizeof (istk_t)); + initstk->i_nxt = istk; + initstk->i_type = istk->i_subt; + if (initstk->i_type->t_tspec == FUNC) + lerror("pushinit() 5"); + + istk = initstk; + + switch (istk->i_type->t_tspec) { + case ARRAY: + if (incompl(istk->i_type) && istk->i_nxt->i_nxt != NULL) { + /* initialisation of an incomplete type */ + error(175); + initerr = 1; + return; + } + istk->i_subt = istk->i_type->t_subt; + istk->i_nolimit = incompl(istk->i_type); + istk->i_cnt = istk->i_type->t_dim; + break; + case UNION: + if (tflag) + /* initialisation of union is illegal in trad. C */ + warning(238); + /* FALLTHROUGH */ + case STRUCT: + if (incompl(istk->i_type)) { + /* initialisation of an incomplete type */ + error(175); + initerr = 1; + return; + } + cnt = 0; + for (m = istk->i_type->t_str->memb; m != NULL; m = m->s_nxt) { + if (m->s_field && m->s_name == unnamed) + continue; + if (++cnt == 1) { + istk->i_mem = m; + istk->i_subt = m->s_type; + } + } + if (cnt == 0) { + /* cannot init. struct/union with no named member */ + error(179); + initerr = 1; + return; + } + istk->i_cnt = istk->i_type->t_tspec == STRUCT ? cnt : 1; + break; + default: + istk->i_cnt = 1; + break; + } +} + +static void +testinit() +{ + istk_t *istk; + + istk = initstk; + + /* + * If a closing brace is expected we have at least one initializer + * too much. + */ + if (istk->i_cnt == 0 && !istk->i_nolimit) { + switch (istk->i_type->t_tspec) { + case ARRAY: + /* too many array initializers */ + error(173); + break; + case STRUCT: + case UNION: + /* too many struct/union initializers */ + error(172); + break; + default: + /* too many initializers */ + error(174); + break; + } + initerr = 1; + } +} + +static void +nextinit(brace) + int brace; +{ + if (!brace) { + if (initstk->i_type == NULL && + !issclt(initstk->i_subt->t_tspec)) { + /* {}-enclosed initializer required */ + error(181); + } + /* + * Make sure an entry with a scalar type is at the top + * of the stack. + */ + if (!initerr) + testinit(); + while (!initerr && (initstk->i_type == NULL || + !issclt(initstk->i_type->t_tspec))) { + if (!initerr) + pushinit(); + } + } else { + if (initstk->i_type != NULL && + issclt(initstk->i_type->t_tspec)) { + /* invalid initializer */ + error(176); + initerr = 1; + } + if (!initerr) + testinit(); + if (!initerr) + pushinit(); + if (!initerr) + initstk->i_brace = 1; + } +} + +void +initlbr() +{ + if (initerr) + return; + + if ((initsym->s_scl == AUTO || initsym->s_scl == REG) && + initstk->i_nxt == NULL) { + if (tflag && !issclt(initstk->i_subt->t_tspec)) + /* no automatic aggregate initialization in trad. C*/ + warning(188); + } + + /* + * Remove all entries which cannot be used for further initializers + * and do not expect a closing brace. + */ + popinit(0); + + nextinit(1); +} + +void +initrbr() +{ + if (initerr) + return; + + popinit(1); +} + +void +mkinit(tn) + tnode_t *tn; +{ + ptrdiff_t offs; + sym_t *sym; + tspec_t lt, rt; + tnode_t *ln; + struct mbl *tmem; + scl_t sc; + + if (initerr || tn == NULL) + goto end; + + sc = initsym->s_scl; + + /* + * Do not test for automatic aggregat initialisation. If the + * initalizer starts with a brace we have the warning already. + * If not, an error will be printed that the initializer must + * be enclosed by braces. + */ + + /* + * Local initialisation of non-array-types with only one expression + * without braces is done by ASSIGN + */ + if ((sc == AUTO || sc == REG) && + initsym->s_type->t_tspec != ARRAY && initstk->i_nxt == NULL) { + ln = getnnode(initsym, 0); + ln->tn_type = tduptyp(ln->tn_type); + ln->tn_type->t_const = 0; + tn = build(ASSIGN, ln, tn); + expr(tn, 0, 0); + goto end; + } + + /* + * Remove all entries which cannot be used for further initializers + * and do not require a closing brace. + */ + popinit(0); + + /* Initialisations by strings are done in strginit(). */ + if (strginit(tn)) + goto end; + + nextinit(0); + if (initerr || tn == NULL) + goto end; + + initstk->i_cnt--; + + /* Create a temporary node for the left side. */ + ln = tgetblk(sizeof (tnode_t)); + ln->tn_op = NAME; + ln->tn_type = tduptyp(initstk->i_type); + ln->tn_type->t_const = 0; + ln->tn_lvalue = 1; + ln->tn_sym = initsym; /* better than nothing */ + + tn = cconv(tn); + + lt = ln->tn_type->t_tspec; + rt = tn->tn_type->t_tspec; + + if (!issclt(lt)) + lerror("mkinit() 1"); + + if (!typeok(INIT, 0, ln, tn)) + goto end; + + /* + * Store the tree memory. This is nessesary because otherwise + * expr() would free it. + */ + tmem = tsave(); + expr(tn, 1, 0); + trestor(tmem); + + if (isityp(lt) && ln->tn_type->t_isfield && !isityp(rt)) { + /* + * Bit-fields can be initialized in trad. C only by integer + * constants. + */ + if (tflag) + /* bit-field initialisation is illegal in trad. C */ + warning(186); + } + + if (lt != rt || (initstk->i_type->t_isfield && tn->tn_op == CON)) + tn = convert(INIT, 0, initstk->i_type, tn); + + if (tn != NULL && tn->tn_op != CON) { + sym = NULL; + offs = 0; + if (conaddr(tn, &sym, &offs) == -1) { + if (sc == AUTO || sc == REG) { + /* non-constant initializer */ + (void)gnuism(177); + } else { + /* non-constant initializer */ + error(177); + } + } + } + + end: + tfreeblk(); +} + + +static int +strginit(tn) + tnode_t *tn; +{ + tspec_t t; + istk_t *istk; + int len; + strg_t *strg; + + if (tn->tn_op != STRING) + return (0); + + istk = initstk; + strg = tn->tn_strg; + + /* + * Check if we have an array type which can be initialized by + * the string. + */ + if (istk->i_subt->t_tspec == ARRAY) { + t = istk->i_subt->t_subt->t_tspec; + if (!((strg->st_tspec == CHAR && + (t == CHAR || t == UCHAR || t == SCHAR)) || + (strg->st_tspec == WCHAR && t == WCHAR))) { + return (0); + } + /* Put the array at top of stack */ + pushinit(); + istk = initstk; + } else if (istk->i_type != NULL && istk->i_type->t_tspec == ARRAY) { + t = istk->i_type->t_subt->t_tspec; + if (!((strg->st_tspec == CHAR && + (t == CHAR || t == UCHAR || t == SCHAR)) || + (strg->st_tspec == WCHAR && t == WCHAR))) { + return (0); + } + /* + * If the array is already partly initialized, we are + * wrong here. + */ + if (istk->i_cnt != istk->i_type->t_dim) + return (0); + } else { + return (0); + } + + /* Get length without trailing NUL character. */ + len = strg->st_len; + + if (istk->i_nolimit) { + istk->i_nolimit = 0; + istk->i_type->t_dim = len + 1; + /* from now complete type */ + setcompl(istk->i_type, 0); + } else { + if (istk->i_type->t_dim < len) { + /* non-null byte ignored in string initializer */ + warning(187); + } + } + + /* In every case the array is initialized completely. */ + istk->i_cnt = 0; + + return (1); +} diff --git a/usr.bin/xlint/lint1/lint.h b/usr.bin/xlint/lint1/lint.h new file mode 100644 index 0000000..cadcd7a --- /dev/null +++ b/usr.bin/xlint/lint1/lint.h @@ -0,0 +1,118 @@ +/* $NetBSD: lint.h,v 1.2 1995/07/03 21:24:18 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 +#include +#include + +#include "param.h" + +/* + * Type specifiers, used in type structures (type_t) and otherwere. + */ +typedef enum { + NOTSPEC, + SIGNED, /* keyword "signed", only used in the parser */ + UNSIGN, /* keyword "unsigned", only used in the parser */ + CHAR, /* char */ + SCHAR, /* signed char */ + UCHAR, /* unsigned char */ + SHORT, /* (signed) short */ + USHORT, /* unsigned short */ + INT, /* (signed) int */ + UINT, /* unsigned int */ + LONG, /* (signed) long */ + ULONG, /* unsigned long */ + QUAD, /* (signed) long long */ + UQUAD, /* unsigned long long */ + FLOAT, /* float */ + DOUBLE, /* double or, with tflag, long float */ + LDOUBLE, /* long double */ + VOID, /* void */ + STRUCT, /* structure tag */ + UNION, /* union tag */ + ENUM, /* enum tag */ + PTR, /* pointer */ + ARRAY, /* array */ + FUNC /* function */ +#define NTSPEC ((int)FUNC + 1) +} tspec_t; + +/* + * size of types, name and classification + */ +typedef struct { + int tt_sz; /* size in bits */ + int tt_psz; /* size, different from tt_sz + if pflag is set */ + tspec_t tt_styp; /* signed counterpart */ + tspec_t tt_utyp; /* unsigned counterpart */ + u_int tt_isityp : 1; /* 1 if integer type */ + u_int tt_isutyp : 1; /* 1 if unsigned integer type */ + u_int tt_isftyp : 1; /* 1 if floating point type */ + u_int tt_isatyp : 1; /* 1 if arithmetic type */ + u_int tt_issclt : 1; /* 1 if scalar type */ + char *tt_name; /* Bezeichnung des Typs */ +} ttab_t; + +#define size(t) (ttab[t].tt_sz) +#define psize(t) (ttab[t].tt_psz) +#define styp(t) (ttab[t].tt_styp) +#define utyp(t) (ttab[t].tt_utyp) +#define isityp(t) (ttab[t].tt_isityp) +#define isutyp(t) (ttab[t].tt_isutyp) +#define isftyp(t) (ttab[t].tt_isftyp) +#define isatyp(t) (ttab[t].tt_isatyp) +#define issclt(t) (ttab[t].tt_issclt) + +extern ttab_t ttab[]; + + +typedef enum { + NODECL, /* until now not declared */ + DECL, /* declared */ + TDEF, /* tentative defined */ + DEF /* defined */ +} def_t; + +/* + * Following structure contains some data used for the output buffer. + */ +typedef struct ob { + char *o_buf; /* buffer */ + char *o_end; /* first byte after buffer */ + size_t o_len; /* length of buffer */ + char *o_nxt; /* next free byte in buffer */ +} ob_t; + +#include "externs.h" diff --git a/usr.bin/xlint/lint1/lint1.h b/usr.bin/xlint/lint1/lint1.h new file mode 100644 index 0000000..019c98d --- /dev/null +++ b/usr.bin/xlint/lint1/lint1.h @@ -0,0 +1,380 @@ +/* $NetBSD: lint1.h,v 1.6 1995/10/02 17:31:41 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. + */ + +#include "lint.h" +#include "op.h" + +/* + * Describes the position of a declaration or anything else. + */ +typedef struct { + int p_line; + const char *p_file; +} pos_t; + +/* + * Strings cannot be referenced to simply by a pointer to its first + * char. This is because strings can contain NUL characters other than the + * trailing NUL. + * + * Strings are stored with a trailing NUL. + */ +typedef struct strg { + tspec_t st_tspec; /* CHAR or WCHAR */ + size_t st_len; /* length without trailing NUL */ + union { + u_char *_st_cp; + wchar_t *_st_wcp; + } st_u; +} strg_t; + +#define st_cp st_u._st_cp +#define st_wcp st_u._st_wcp + +/* + * qualifiers (only for lex/yacc interface) + */ +typedef enum { + CONST, VOLATILE +} tqual_t; + +/* + * Integer and floating point values are stored in this structure + */ +typedef struct { + tspec_t v_tspec; + int v_ansiu; /* set if an integer constant is + unsigned in ANSI C */ + union { + quad_t _v_quad; /* integers */ + ldbl_t _v_ldbl; /* floats */ + } v_u; +} val_t; + +#define v_quad v_u._v_quad +#define v_ldbl v_u._v_ldbl + +/* + * Structures of type str_t uniqely identify structures. This can't + * be done in structures of type type_t, because these are copied + * if they must be modified. So it would not be possible to check + * if to structures are identical by comparing the pointers to + * the type structures. + * + * The typename is used if the structure is unnamed to identify + * the structure type in pass 2. + */ +typedef struct { + u_int size; /* size in bit */ + u_int align : 15; /* alignment in bit */ + u_int sincompl : 1; /* set if incomplete type */ + struct sym *memb; /* list of members */ + struct sym *stag; /* symbol table entry of tag */ + struct sym *stdef; /* symbol table entry of first typename */ +} str_t; + +/* + * same as above for enums + */ +typedef struct { + u_int eincompl : 1; /* incomplete enum type */ + struct sym *elem; /* list of enumerators */ + struct sym *etag; /* symbol table entry of tag */ + struct sym *etdef; /* symbol table entry of first typename */ +} enum_t; + +/* + * Types are represented by concatenation of structures of type type_t + * via t_subt. + */ +typedef struct type { + tspec_t t_tspec; /* type specifier */ + u_int t_aincompl : 1; /* incomplete array type */ + u_int t_const : 1; /* const modifier */ + u_int t_volatile : 1; /* volatile modifier */ + u_int t_proto : 1; /* function prototype (t_args valid) */ + u_int t_vararg : 1; /* protoype with ... */ + u_int t_typedef : 1; /* type defined with typedef */ + u_int t_isfield : 1; /* type is bitfield */ + u_int t_isenum : 1; /* type is (or was) enum (t_enum valid) */ + union { + int _t_dim; /* dimension */ + str_t *_t_str; /* struct/union tag */ + enum_t *_t_enum; /* enum tag */ + struct sym *_t_args; /* arguments (if t_proto) */ + struct { + u_int _t_flen : 8; /* length of bit-field */ + u_int _t_foffs : 24; /* offset of bit-field */ + } _t_u; + } t_u; + struct type *t_subt; /* element type (arrays), return value + (functions), or type pointer points to */ +} type_t; + +#define t_dim t_u._t_dim +#define t_str t_u._t_str +#define t_field t_u._t_field +#define t_enum t_u._t_enum +#define t_args t_u._t_args +#define t_flen t_u._t_u._t_flen +#define t_foffs t_u._t_u._t_foffs + +/* + * types of symbols + */ +typedef enum { + FVFT, /* variables, functions, type names, enums */ + FMOS, /* members of structs or unions */ + FTAG, /* tags */ + FLAB /* labels */ +} symt_t; + +/* + * storage classes + */ +typedef enum { + NOSCL, + EXTERN, /* external symbols (indep. of decl_t) */ + STATIC, /* static symbols (local and global) */ + AUTO, /* automatic symbols (except register) */ + REG, /* register */ + TYPEDEF, /* typedef */ + STRTAG, + UNIONTAG, + ENUMTAG, + MOS, /* member of struct */ + MOU, /* member of union */ + ENUMCON, /* enumerator */ + ABSTRACT, /* abstract symbol (sizeof, casts, unnamed argument) */ + ARG, /* argument */ + PARG, /* used in declaration stack during prototype + declaration */ + INLINE /* only used by the parser */ +} scl_t; + +/* + * symbol table entry + */ +typedef struct sym { + const char *s_name; /* name */ + pos_t s_dpos; /* position of last (prototype)definition, + prototypedeclaration, no-prototype-def., + tentative definition or declaration, + in this order */ + pos_t s_spos; /* position of first initialisation */ + pos_t s_upos; /* position of first use */ + symt_t s_kind; /* type of symbol */ + u_int s_keyw : 1; /* keyword */ + u_int s_field : 1; /* bit-field */ + u_int s_set : 1; /* variable set, label defined */ + u_int s_used : 1; /* variable/label used */ + u_int s_arg : 1; /* symbol is function argument */ + u_int s_reg : 1; /* symbol is register variable */ + u_int s_defarg : 1; /* undefined symbol in old style function + definition */ + u_int s_rimpl : 1; /* return value of function implizit decl. */ + u_int s_osdef : 1; /* symbol stems from old style function def. */ + u_int s_inline : 1; /* true if this is a inline function */ + struct sym *s_xsym; /* for local declared external symbols pointer + to external symbol with same name */ + def_t s_def; /* declared, tentative defined, defined */ + scl_t s_scl; /* storage class */ + int s_blklev; /* level of declaration, -1 if not in symbol + table */ + type_t *s_type; /* type */ + val_t s_value; /* value (if enumcon) */ + union { + str_t *_s_st; /* tag, if it is a struct/union member */ + enum_t *_s_et; /* tag, if it is a enumerator */ + tspec_t _s_tsp; /* type (only for keywords) */ + tqual_t _s_tqu; /* qualifier (only for keywords) */ + struct sym *_s_args; /* arguments in old style function + definitions */ + } u; + struct sym *s_link; /* next symbol with same hash value */ + struct sym **s_rlink; /* pointer to s_link of prev. symbol */ + struct sym *s_nxt; /* next struct/union member, enumerator, + argument */ + struct sym *s_dlnxt; /* next symbol declared on same level */ +} sym_t; + +#define s_styp u._s_st +#define s_etyp u._s_et +#define s_tspec u._s_tsp +#define s_tqual u._s_tqu +#define s_args u._s_args + +/* + * Used to keep some informations about symbols before they are entered + * into the symbol table. + */ +typedef struct sbuf { + const char *sb_name; /* name of symbol */ + size_t sb_len; /* length (without '\0') */ + int sb_hash; /* hash value */ + sym_t *sb_sym; /* symbol table entry */ + struct sbuf *sb_nxt; /* for freelist */ +} sbuf_t; + + +/* + * tree node + */ +typedef struct tnode { + op_t tn_op; /* operator */ + type_t *tn_type; /* type */ + u_int tn_lvalue : 1; /* node is lvalue */ + u_int tn_cast : 1; /* if tn_op == CVT its an explizit cast */ + u_int tn_parn : 1; /* node parenthesized */ + union { + struct { + struct tnode *_tn_left; /* (left) operand */ + struct tnode *_tn_right; /* right operand */ + } tn_s; + sym_t *_tn_sym; /* symbol if op == NAME */ + val_t *_tn_val; /* value if op == CON */ + strg_t *_tn_strg; /* string if op == STRING */ + } tn_u; +} tnode_t; + +#define tn_left tn_u.tn_s._tn_left +#define tn_right tn_u.tn_s._tn_right +#define tn_sym tn_u._tn_sym +#define tn_val tn_u._tn_val +#define tn_strg tn_u._tn_strg + +/* + * For nested declarations a stack exists, which holds all information + * needed for the current level. dcs points to the top element of this + * stack. + * + * ctx describes the context of the current declaration. Its value is + * one of + * EXTERN global declarations + * MOS oder MOU declarations of struct or union members + * ENUMCON declarations of enums + * ARG declaration of arguments in old style function definitions + * PARG declaration of arguments in function prototypes + * AUTO declaration of local symbols + * ABSTRACT abstract declarations (sizeof, casts) + * + */ +typedef struct dinfo { + tspec_t d_atyp; /* VOID, CHAR, INT, FLOAT or DOUBLE */ + tspec_t d_smod; /* SIGNED or UNSIGN */ + tspec_t d_lmod; /* SHORT, LONG or QUAD */ + scl_t d_scl; /* storage class */ + type_t *d_type; /* after deftyp() pointer to the type used + for all declarators */ + sym_t *d_rdcsym; /* redeclared symbol */ + int d_offset; /* offset of next structure member */ + int d_stralign; /* alignment required for current structure */ + scl_t d_ctx; /* context of declaration */ + u_int d_const : 1; /* const in declaration specifiers */ + u_int d_volatile : 1; /* volatile in declaration specifiers */ + u_int d_inline : 1; /* inline in declaration specifiers */ + u_int d_mscl : 1; /* multiple storage classes */ + u_int d_terr : 1; /* invalid type combination */ + u_int d_nedecl : 1; /* 1 if at least a tag is declared */ + u_int d_vararg : 1; /* ... in in current function decl. */ + u_int d_proto : 1; /* current funct. decl. is prototype */ + u_int d_notyp : 1; /* set if no type specifier was present */ + u_int d_asm : 1; /* set if d_ctx == AUTO and asm() present */ + type_t *d_tagtyp; /* tag during member declaration */ + sym_t *d_fargs; /* list of arguments during function def. */ + pos_t d_fdpos; /* position of function definition */ + sym_t *d_dlsyms; /* first symbol declared at this level */ + sym_t **d_ldlsym; /* points to s_dlnxt in last symbol decl. + at this level */ + sym_t *d_fpsyms; /* symbols defined in prototype */ + struct dinfo *d_nxt; /* next level */ +} dinfo_t; + +/* + * Type of stack which is used for initialisation of aggregate types. + */ +typedef struct istk { + type_t *i_type; /* type of initialisation */ + type_t *i_subt; /* type of next level */ + u_int i_brace : 1; /* need } for pop */ + u_int i_nolimit : 1; /* incomplete array type */ + sym_t *i_mem; /* next structure member */ + int i_cnt; /* # of remaining elements */ + struct istk *i_nxt; /* previous level */ +} istk_t; + +/* + * Used to collect information about pointers and qualifiers in + * declarators. + */ +typedef struct pqinf { + int p_pcnt; /* number of asterisks */ + u_int p_const : 1; + u_int p_volatile : 1; + struct pqinf *p_nxt; +} pqinf_t; + +/* + * Case values are stored in a list of type clst_t. + */ +typedef struct clst { + val_t cl_val; + struct clst *cl_nxt; +} clst_t; + +/* + * Used to keep informations about nested control statements. + */ +typedef struct cstk { + int c_env; /* type of statement (T_IF, ...) */ + u_int c_loop : 1; /* continue && break are valid */ + u_int c_switch : 1; /* case && break are valid */ + u_int c_break : 1; /* loop/switch has break */ + u_int c_cont : 1; /* loop has continue */ + u_int c_default : 1; /* switch has default */ + u_int c_infinite : 1; /* break condition always false + (for (;;), while (1)) */ + u_int c_rchif : 1; /* end of if-branch reached */ + u_int c_noretval : 1; /* had "return;" */ + u_int c_retval : 1; /* had "return (e);" */ + type_t *c_swtype; /* type of switch expression */ + clst_t *c_clst; /* list of case values */ + struct mbl *c_fexprm; /* saved memory for end of loop + expression in for() */ + tnode_t *c_f3expr; /* end of loop expr in for() */ + pos_t c_fpos; /* position of end of loop expr */ + pos_t c_cfpos; /* same for csrc_pos */ + struct cstk *c_nxt; /* outer control statement */ +} cstk_t; + +#include "externs1.h" diff --git a/usr.bin/xlint/lint1/main1.c b/usr.bin/xlint/lint1/main1.c new file mode 100644 index 0000000..0add206 --- /dev/null +++ b/usr.bin/xlint/lint1/main1.c @@ -0,0 +1,183 @@ +/* $NetBSD: main1.c,v 1.3 1995/10/02 17:29:56 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: main1.c,v 1.3 1995/10/02 17:29:56 jpo Exp $"; +#endif + +#include +#include +#include +#include + +#include "lint1.h" + +/* set yydebug to 1*/ +int yflag; + +/* + * Print warnings if an assignment of an integertype to another integertype + * causes an implizit narrowing conversion. If aflag is 1, these warnings + * are printed only if the source type is at least as wide as long. If aflag + * is greather then 1, they are always printed. + */ +int aflag; + +/* Print a warning if a break statement cannot be reached. */ +int bflag; + +/* Print warnings for pointer casts. */ +int cflag; + +/* Print various debug information. */ +int dflag; + +/* Perform stricter checking of enum types and operations on enum types. */ +int eflag; + +/* Print complete pathnames, not only the basename. */ +int Fflag; + +/* Enable some extensions of gcc */ +int gflag; + +/* + * Apply a number of heuristic tests to attempt to intuit bugs, improve + * style, and reduce waste. + */ +int hflag; + +/* Attempt to check portability to other dialects of C. */ +int pflag; + +/* + * In case of redeclarations/redefinitions print the location of the + * previous declaration/definition. + */ +int rflag; + +/* Strict ANSI C mode. */ +int sflag; + +/* Traditional C mode. */ +int tflag; + +/* + * Complain about functions and external variables used and not defined, + * or defined and not used. + */ +int uflag = 1; + +/* Complain about unused function arguments. */ +int vflag = 1; + +/* Complain about structures which are never defined. */ +int zflag = 1; + +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + + while ((c = getopt(argc, argv, "abcdeghprstuvyzF")) != -1) { + switch (c) { + case 'a': aflag++; break; + case 'b': bflag = 1; break; + case 'c': cflag = 1; break; + case 'd': dflag = 1; break; + case 'e': eflag = 1; break; + case 'F': Fflag = 1; break; + case 'g': gflag = 1; break; + case 'h': hflag = 1; break; + case 'p': pflag = 1; break; + case 'r': rflag = 1; break; + case 's': sflag = 1; break; + case 't': tflag = 1; break; + case 'u': uflag = 0; break; + case 'v': vflag = 0; break; + case 'y': yflag = 1; break; + case 'z': zflag = 0; break; + case '?': usage(); + } + } + argc -= optind; + argv += optind; + + if (argc != 2) + usage(); + + /* open the input file */ + if ((yyin = fopen(argv[0], "r")) == NULL) + err(1, "cannot open '%s'", argv[0]); + + /* initialize output */ + outopen(argv[1]); + + if (yflag) + yydebug = 1; + + initmem(); + initdecl(); + initscan(); + initmtab(); + + yyparse(); + + /* Following warnings cannot be suppressed by LINTED */ + nowarn = 0; + + chkglsyms(); + + outclose(); + + return (nerr != 0); +} + +static void +usage() +{ + (void)fprintf(stderr, "usage: lint1 [-abcdeghprstuvyzF] src dest\n"); + exit(1); +} + +void +norecover() +{ + /* cannot recover from previous errors */ + error(224); + exit(1); +} diff --git a/usr.bin/xlint/lint1/mem.c b/usr.bin/xlint/lint1/mem.c new file mode 100644 index 0000000..71fba06 --- /dev/null +++ b/usr.bin/xlint/lint1/mem.c @@ -0,0 +1,91 @@ +/* $NetBSD: mem.c,v 1.2 1995/07/03 21:24:24 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: mem.c,v 1.2 1995/07/03 21:24:24 cgd Exp $"; +#endif + +#include +#include +#include + +#include "lint.h" + +void * +xmalloc(s) + size_t s; +{ + void *p; + + if ((p = malloc(s)) == NULL) + nomem(); + return (p); +} + +void * +xcalloc(n, s) + size_t n, s; +{ + void *p; + + if ((p = calloc(n, s)) == NULL) + nomem(); + return (p); +} + +void * +xrealloc(p, s) + void *p; + size_t s; +{ + if ((p = realloc(p, s)) == NULL) + nomem(); + return (p); +} + +char * +xstrdup(s) + const char *s; +{ + char *s2; + + if ((s2 = strdup(s)) == NULL) + nomem(); + return (s2); +} + +void +nomem() +{ + errx(1, "virtual memory exhausted"); +} diff --git a/usr.bin/xlint/lint1/mem1.c b/usr.bin/xlint/lint1/mem1.c new file mode 100644 index 0000000..df880b1 --- /dev/null +++ b/usr.bin/xlint/lint1/mem1.c @@ -0,0 +1,358 @@ +/* $NetBSD: mem1.c,v 1.2 1995/07/03 21:24:25 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: mem1.c,v 1.2 1995/07/03 21:24:25 cgd Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "lint1.h" + +/* + * Filenames allocated by fnalloc() and fnnalloc() are shared. + */ +typedef struct fn { + char *fn_name; + size_t fn_len; + int fn_id; + struct fn *fn_nxt; +} fn_t; + +static fn_t *fnames; + +static fn_t *srchfn __P((const char *, size_t)); + +/* + * Look for a Filename of length l. + */ +static fn_t * +srchfn(s, len) + const char *s; + size_t len; +{ + fn_t *fn; + + for (fn = fnames; fn != NULL; fn = fn->fn_nxt) { + if (fn->fn_len == len && memcmp(fn->fn_name, s, len) == 0) + break; + } + return (fn); +} + +/* + * Return a shared string for filename s. + */ +const char * +fnalloc(s) + const char *s; +{ + return (s != NULL ? fnnalloc(s, strlen(s)) : NULL); +} + +const char * +fnnalloc(s, len) + const char *s; + size_t len; +{ + fn_t *fn; + + static int nxt_id = 0; + + if (s == NULL) + return (NULL); + + if ((fn = srchfn(s, len)) == NULL) { + fn = xmalloc(sizeof (fn_t)); + /* Do not used strdup() because string is not NUL-terminated.*/ + fn->fn_name = xmalloc(len + 1); + (void)memcpy(fn->fn_name, s, len); + fn->fn_name[len] = '\0'; + fn->fn_len = len; + fn->fn_id = nxt_id++; + fn->fn_nxt = fnames; + fnames = fn; + /* Write id of this filename to the output file. */ + outclr(); + outint(fn->fn_id); + outchar('s'); + outstrg(fn->fn_name); + } + return (fn->fn_name); +} + +/* + * Get id of a filename. + */ +int +getfnid(s) + const char *s; +{ + fn_t *fn; + + if (s == NULL || (fn = srchfn(s, strlen(s))) == NULL) + return (-1); + return (fn->fn_id); +} + +/* + * Memory for declarations and other things which must be available + * until the end of a block (or the end of the translation unit) + * are assoziated with the level (mblklev) of the block (or wiht 0). + * Because these memory is allocated in large blocks associated with + * a given level it can be freed easily at the end of a block. + */ +#define ML_INC ((size_t)32) /* Increment for length of *mblks */ + +typedef struct mbl { + void *blk; /* beginning of memory block */ + void *ffree; /* first free byte */ + size_t nfree; /* # of free bytes */ + size_t size; /* total size of memory block */ + struct mbl *nxt; /* next block */ +} mbl_t; + +/* + * Array of pointers to lists of memory blocks. mblklev is used as + * index into this array. + */ +static mbl_t **mblks; + +/* number of elements in *mblks */ +static size_t nmblks; + +/* free list for memory blocks */ +static mbl_t *frmblks; + +/* length of new allocated memory blocks */ +static size_t mblklen; + +static void *xgetblk __P((mbl_t **, size_t)); +static void xfreeblk __P((mbl_t **)); +static mbl_t *xnewblk __P((void)); + +static mbl_t * +xnewblk() +{ + mbl_t *mb; + int prot, flags; + + mb = xmalloc(sizeof (mbl_t)); + + /* use mmap instead of malloc to avoid malloc's size overhead */ + + prot = PROT_READ | PROT_WRITE; + flags = MAP_ANON | MAP_PRIVATE; + mb->blk = mmap(NULL, mblklen, prot, flags, -1, (off_t)0); + if (mb->blk == (void *)-1) + err(1, "can't map memory"); + if (ALIGN((u_long)mb->blk) != (u_long)mb->blk) + errx(1, "mapped address is not aligned"); + + mb->size = mblklen; + + return (mb); +} + +/* + * Allocate new memory. If the first block of the list has not enough + * free space, or there is no first block, get a new block. The new + * block is taken from the free list or, if there is no block on the + * free list, is allocated using xnewblk(). If a new block is allocated + * it is initialized with zero. Blocks taken from the free list are + * zero'd in xfreeblk(). + */ +static void * +xgetblk(mbp, s) + mbl_t **mbp; + size_t s; +{ + mbl_t *mb; + void *p; + + s = ALIGN(s); + if ((mb = *mbp) == NULL || mb->nfree < s) { + if ((mb = frmblks) == NULL) { + mb = xnewblk(); + (void)memset(mb->blk, 0, mb->size); + } else { + frmblks = mb->nxt; + } + mb->ffree = mb->blk; + mb->nfree = mb->size;; + mb->nxt = *mbp; + *mbp = mb; + } + p = mb->ffree; + mb->ffree = (char *)mb->ffree + s; + mb->nfree -= s; + return (p); +} + +/* + * Move all blocks from list *fmbp to free list. For each block, set all + * used memory to zero. + */ +static void +xfreeblk(fmbp) + mbl_t **fmbp; +{ + mbl_t *mb; + + while ((mb = *fmbp) != NULL) { + *fmbp = mb->nxt; + mb->nxt = frmblks; + frmblks = mb; + (void)memset(mb->blk, 0, mb->size - mb->nfree); + } +} + +void +initmem() +{ + int pgsz; + + pgsz = getpagesize(); + mblklen = ((MBLKSIZ + pgsz - 1) / pgsz) * pgsz; + + mblks = xcalloc(nmblks = ML_INC, sizeof (mbl_t *)); +} + + +/* + * Allocate memory associated with level l. + */ +void * +getlblk(l, s) + int l; + size_t s; +{ + while (l >= nmblks) { + mblks = xrealloc(mblks, (nmblks + ML_INC) * sizeof (mbl_t *)); + (void)memset(&mblks[nmblks], 0, ML_INC * sizeof (mbl_t *)); + nmblks += ML_INC; + } + return (xgetblk(&mblks[l], s)); +} + +void * +getblk(s) + size_t s; +{ + return (getlblk(mblklev, s)); +} + +/* + * Free all memory associated with level l. + */ +void +freelblk(l) + int l; +{ + xfreeblk(&mblks[l]); +} + +void +freeblk() +{ + freelblk(mblklev); +} + +/* + * tgetblk() returns memory which is associated with the current + * expression. + */ +static mbl_t *tmblk; + +void * +tgetblk(s) + size_t s; +{ + return (xgetblk(&tmblk, s)); +} + +/* + * Get memory for a new tree node. + */ +tnode_t * +getnode() +{ + return (tgetblk(sizeof (tnode_t))); +} + +/* + * Free all memory which is allocated by the the current expression. + */ +void +tfreeblk() +{ + xfreeblk(&tmblk); +} + +/* + * Save the memory which is used by the current expression. This memory + * is not freed by the next tfreeblk() call. The pointer returned can be + * used to restore the memory. + */ +mbl_t * +tsave() +{ + mbl_t *tmem; + + tmem = tmblk; + tmblk = NULL; + return (tmem); +} + +/* + * Free all memory used for the current expression and the memory used + * be a previous expression and saved by tsave(). The next call to + * tfreeblk() frees the restored memory. + */ +void +trestor(tmem) + mbl_t *tmem; +{ + tfreeblk(); + if (tmblk != NULL) { + free(tmblk->blk); + free(tmblk); + } + tmblk = tmem; +} diff --git a/usr.bin/xlint/lint1/op.h b/usr.bin/xlint/lint1/op.h new file mode 100644 index 0000000..11f05a5 --- /dev/null +++ b/usr.bin/xlint/lint1/op.h @@ -0,0 +1,120 @@ +/* $NetBSD: op.h,v 1.2 1995/07/03 21:24:27 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. + */ + +/* + * Various information about operators + */ +typedef struct { + u_int m_binary : 1; /* binary op. */ + u_int m_logop : 1; /* logical op., result is int */ + u_int m_rqint : 1; /* operands must have integer type */ + u_int m_rqsclt : 1; /* operands must have scalar type */ + u_int m_rqatyp : 1; /* operands must have arithmetic type */ + u_int m_fold : 1; /* operands should be folded */ + u_int m_vctx : 1; /* value context for left operand */ + u_int m_tctx : 1; /* test context for left operand */ + u_int m_balance : 1; /* op. requires balancing */ + u_int m_sideeff : 1; /* op. has side effect */ + u_int m_tlansiu : 1; /* warning if left op. is unsign. in ANSI C */ + u_int m_transiu : 1; /* warning if right op. is unsign. in ANSI C */ + u_int m_tpconf : 1; /* test possible precedence confusion */ + u_int m_comp : 1; /* op. performs comparision */ + u_int m_enumop : 1; /* valid operation on enums */ + u_int m_badeop : 1; /* dubious operation on enums */ + u_int m_eqwarn : 1; /* warning if on operand stems from == */ + const char *m_name; /* name of op. */ +} mod_t; + +typedef enum { + NOOP = 0, + ARROW, + POINT, + NOT, + COMPL, + INC, + DEC, + INCBEF, + DECBEF, + INCAFT, + DECAFT, + UPLUS, + UMINUS, + STAR, + AMPER, + MULT, + DIV, + MOD, + PLUS, + MINUS, + SHL, + SHR, + LT, + LE, + GT, + GE, + EQ, + NE, + AND, + XOR, + OR, + LOGAND, + LOGOR, + QUEST, + COLON, + ASSIGN, + MULASS, + DIVASS, + MODASS, + ADDASS, + SUBASS, + SHLASS, + SHRASS, + ANDASS, + XORASS, + ORASS, + NAME, + CON, + STRING, + FSEL, + CALL, + COMMA, + CVT, + ICALL, + LOAD, + PUSH, + RETURN, + INIT, /* pseudo op, not used in trees */ + CASE, /* pseudo op, not used in trees */ + FARG /* pseudo op, not used in trees */ +#define NOPS ((int)FARG + 1) +} op_t; diff --git a/usr.bin/xlint/lint1/param.h b/usr.bin/xlint/lint1/param.h new file mode 100644 index 0000000..17e266e --- /dev/null +++ b/usr.bin/xlint/lint1/param.h @@ -0,0 +1,120 @@ +/* $NetBSD: param.h,v 1.4 1995/07/23 18:14:41 ragge 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. + */ + +/* + * Minimun size of string buffer. If this is not enough, the buffer + * is enlarged in steps of STRBLEN bytes. + */ +#define STRBLEN 256 + +/* + * This defines the size of memory blocks which are used to allocate + * memory in larger chunks. + */ +#define MBLKSIZ ((size_t)0x4000) + +/* + * Sizes of hash tables + * Should be a prime. Possible primes are + * 307, 401, 503, 601, 701, 809, 907, 1009, 1103, 1201, 1301, 1409, 1511. + * + * HSHSIZ1 symbol table 1st pass + * HSHSIZ2 symbol table 2nd pass + * THSHSIZ2 type table 2nd pass + */ +#define HSHSIZ1 503 +#define HSHSIZ2 1009 +#define THSHSIZ2 1009 + +/* + * Should be set to 1 if the difference of two pointers is of type long + * or the value of sizeof is of type unsigned long. + */ +#ifdef __alpha__ +#define PTRDIFF_IS_LONG 1 +#define SIZEOF_IS_ULONG 1 +#elif __i386__ +#define PTRDIFF_IS_LONG 0 +#define SIZEOF_IS_ULONG 0 +#elif __m68k__ +#define PTRDIFF_IS_LONG 0 +#define SIZEOF_IS_ULONG 0 +#elif __ns32k__ +#define PTRDIFF_IS_LONG 0 +#define SIZEOF_IS_ULONG 0 +#elif __sparc__ +#define PTRDIFF_IS_LONG 0 +#define SIZEOF_IS_ULONG 0 +#elif __vax__ +#define PTRDIFF_IS_LONG 0 +#define SIZEOF_IS_ULONG 0 +#else +#error unknown machine type +#endif + +/* + * Make sure this matches wchar_t. + */ +#define WCHAR SHORT + +#ifndef __GNUC__ +#ifndef lint +#ifndef QUAD_MAX /* necessary for mkdep */ +#define QUAD_MAX LONG_MAX +#define QUAD_MIN LONG_MIN +#define UQUAD_MAX ULONG_MAX +#endif +typedef long quad_t; +typedef u_long u_quad_t; +#endif +#endif + + +/* + * long double only in ANSI C. + */ +#ifdef __STDC__ +typedef long double ldbl_t; +#else +typedef double ldbl_t; +#endif + +/* + * Some traditional compilers are not able to assign structures. + */ +#ifdef __STDC__ +#define STRUCT_ASSIGN(dest, src) (dest) = (src) +#else +#define STRUCT_ASSIGN(dest, src) (void)memcpy(&(dest), &(src), \ + sizeof (dest)); +#endif diff --git a/usr.bin/xlint/lint1/scan.l b/usr.bin/xlint/lint1/scan.l new file mode 100644 index 0000000..5e23c6b --- /dev/null +++ b/usr.bin/xlint/lint1/scan.l @@ -0,0 +1,1426 @@ +%{ +/* $NetBSD: scan.l,v 1.8 1995/10/23 13:38:51 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: scan.l,v 1.8 1995/10/23 13:38:51 jpo Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lint1.h" +#include "y.tab.h" + +#define CHAR_MASK (~(~0 << CHAR_BIT)) + +/* XXX declaration of strtouq() is missing in stdlib.h ? */ +extern u_quad_t strtouq __P((const char *, char **, int)); + +/* Current position (its also updated when an included file is parsed) */ +pos_t curr_pos = { 1, "" }; + +/* + * Current position in C source (not updated when an included file is + * parsed). + */ +pos_t csrc_pos = { 1, "" }; + +static void incline __P((void)); +static void badchar __P((int)); +static sbuf_t *allocsb __P((void)); +static void freesb __P((sbuf_t *)); +static int inpc __P((void)); +static int hash __P((const char *)); +static sym_t *search __P((sbuf_t *)); +static int name __P((void)); +static int keyw __P((sym_t *)); +static int icon __P((int)); +static int fcon __P((void)); +static int operator __P((int, op_t)); +static int ccon __P((void)); +static int wccon __P((void)); +static int getescc __P((int)); +static void directive __P((void)); +static void comment __P((void)); +static int string __P((void)); +static int wcstrg __P((void)); + +%} + +L [_A-Za-z] +D [0-9] +NZD [1-9] +OD [0-7] +HD [0-9A-Fa-f] +EX ([eE][+-]?[0-9]+) + +%% + +{L}({L}|{D})* return (name()); +0{OD}*[lLuU]* return (icon(8)); +{NZD}{D}*[lLuU]* return (icon(10)); +0[xX]{HD}+[lLuU]* return (icon(16)); +{D}+\.{D}*{EX}?[fFlL]? | +{D}+{EX}[fFlL]? | +\.{D}+{EX}?[fFlL]? return (fcon()); +"=" return (operator(T_ASSIGN, ASSIGN)); +"*=" return (operator(T_OPASS, MULASS)); +"/=" return (operator(T_OPASS, DIVASS)); +"%=" return (operator(T_OPASS, MODASS)); +"+=" return (operator(T_OPASS, ADDASS)); +"-=" return (operator(T_OPASS, SUBASS)); +"<<=" return (operator(T_OPASS, SHLASS)); +">>=" return (operator(T_OPASS, SHRASS)); +"&=" return (operator(T_OPASS, ANDASS)); +"^=" return (operator(T_OPASS, XORASS)); +"|=" return (operator(T_OPASS, ORASS)); +"||" return (operator(T_LOGOR, LOGOR)); +"&&" return (operator(T_LOGAND, LOGAND)); +"|" return (operator(T_OR, OR)); +"&" return (operator(T_AND, AND)); +"^" return (operator(T_XOR, XOR)); +"==" return (operator(T_EQOP, EQ)); +"!=" return (operator(T_EQOP, NE)); +"<" return (operator(T_RELOP, LT)); +">" return (operator(T_RELOP, GT)); +"<=" return (operator(T_RELOP, LE)); +">=" return (operator(T_RELOP, GE)); +"<<" return (operator(T_SHFTOP, SHL)); +">>" return (operator(T_SHFTOP, SHR)); +"++" return (operator(T_INCDEC, INC)); +"--" return (operator(T_INCDEC, DEC)); +"->" return (operator(T_STROP, ARROW)); +"." return (operator(T_STROP, POINT)); +"+" return (operator(T_ADDOP, PLUS)); +"-" return (operator(T_ADDOP, MINUS)); +"*" return (operator(T_MULT, MULT)); +"/" return (operator(T_DIVOP, DIV)); +"%" return (operator(T_DIVOP, MOD)); +"!" return (operator(T_UNOP, NOT)); +"~" return (operator(T_UNOP, COMPL)); +"\"" return (string()); +"L\"" return (wcstrg()); +";" return (T_SEMI); +"{" return (T_LBRACE); +"}" return (T_RBRACE); +"," return (T_COMMA); +":" return (T_COLON); +"?" return (T_QUEST); +"[" return (T_LBRACK); +"]" return (T_RBRACK); +"(" return (T_LPARN); +")" return (T_RPARN); +"..." return (T_ELLIPSE); +"'" return (ccon()); +"L'" return (wccon()); +^#.*$ directive(); +\n incline(); +\t|" "|\f|\v ; +"/*" comment(); +. badchar(yytext[0]); + +%% + +static void +incline() +{ + curr_pos.p_line++; + if (curr_pos.p_file == csrc_pos.p_file) + csrc_pos.p_line++; +} + +static void +badchar(c) + int c; +{ + /* unknown character \%o */ + error(250, c); +} + +/* + * Keywords. + * During initialisation they are written to the symbol table. + */ +static struct kwtab { + const char *kw_name; /* keyword */ + int kw_token; /* token returned by yylex() */ + scl_t kw_scl; /* storage class if kw_token T_SCLASS */ + tspec_t kw_tspec; /* type spec. if kw_token T_TYPE or T_SOU */ + tqual_t kw_tqual; /* type qual. fi kw_token T_QUAL */ + u_int kw_stdc : 1; /* STDC keyword */ + u_int kw_gcc : 1; /* GCC keyword */ +} kwtab[] = { + { "asm", T_ASM, 0, 0, 0, 0, 1 }, + { "__asm", T_ASM, 0, 0, 0, 0, 0 }, + { "__asm__", T_ASM, 0, 0, 0, 0, 0 }, + { "auto", T_SCLASS, AUTO, 0, 0, 0, 0 }, + { "break", T_BREAK, 0, 0, 0, 0, 0 }, + { "case", T_CASE, 0, 0, 0, 0, 0 }, + { "char", T_TYPE, 0, CHAR, 0, 0, 0 }, + { "const", T_QUAL, 0, 0, CONST, 1, 0 }, + { "__const__", T_QUAL, 0, 0, CONST, 0, 0 }, + { "__const", T_QUAL, 0, 0, CONST, 0, 0 }, + { "continue", T_CONTINUE, 0, 0, 0, 0, 0 }, + { "default", T_DEFAULT, 0, 0, 0, 0, 0 }, + { "do", T_DO, 0, 0, 0, 0, 0 }, + { "double", T_TYPE, 0, DOUBLE, 0, 0, 0 }, + { "else", T_ELSE, 0, 0, 0, 0, 0 }, + { "enum", T_ENUM, 0, 0, 0, 0, 0 }, + { "extern", T_SCLASS, EXTERN, 0, 0, 0, 0 }, + { "float", T_TYPE, 0, FLOAT, 0, 0, 0 }, + { "for", T_FOR, 0, 0, 0, 0, 0 }, + { "goto", T_GOTO, 0, 0, 0, 0, 0 }, + { "if", T_IF, 0, 0, 0, 0, 0 }, + { "inline", T_SCLASS, INLINE, 0, 0, 0, 1 }, + { "__inline__", T_SCLASS, INLINE, 0, 0, 0, 0 }, + { "__inline", T_SCLASS, INLINE, 0, 0, 0, 0 }, + { "int", T_TYPE, 0, INT, 0, 0, 0 }, + { "long", T_TYPE, 0, LONG, 0, 0, 0 }, + { "register", T_SCLASS, REG, 0, 0, 0, 0 }, + { "return", T_RETURN, 0, 0, 0, 0, 0 }, + { "short", T_TYPE, 0, SHORT, 0, 0, 0 }, + { "signed", T_TYPE, 0, SIGNED, 0, 1, 0 }, + { "__signed__", T_TYPE, 0, SIGNED, 0, 0, 0 }, + { "__signed", T_TYPE, 0, SIGNED, 0, 0, 0 }, + { "sizeof", T_SIZEOF, 0, 0, 0, 0, 0 }, + { "static", T_SCLASS, STATIC, 0, 0, 0, 0 }, + { "struct", T_SOU, 0, STRUCT, 0, 0, 0 }, + { "switch", T_SWITCH, 0, 0, 0, 0, 0 }, + { "typedef", T_SCLASS, TYPEDEF, 0, 0, 0, 0 }, + { "union", T_SOU, 0, UNION, 0, 0, 0 }, + { "unsigned", T_TYPE, 0, UNSIGN, 0, 0, 0 }, + { "void", T_TYPE, 0, VOID, 0, 0, 0 }, + { "volatile", T_QUAL, 0, 0, VOLATILE, 1, 0 }, + { "__volatile__", T_QUAL, 0, 0, VOLATILE, 0, 0 }, + { "__volatile", T_QUAL, 0, 0, VOLATILE, 0, 0 }, + { "while", T_WHILE, 0, 0, 0, 0, 0 }, + { NULL, 0, 0, 0, 0, 0, 0 } +}; + +/* Symbol table */ +static sym_t *symtab[HSHSIZ1]; + +/* bit i of the entry with index i is set */ +u_quad_t qbmasks[sizeof(u_quad_t) * CHAR_BIT]; + +/* least significant i bits are set in the entry with index i */ +u_quad_t qlmasks[sizeof(u_quad_t) * CHAR_BIT + 1]; + +/* least significant i bits are not set in the entry with index i */ +u_quad_t qumasks[sizeof(u_quad_t) * CHAR_BIT + 1]; + +/* free list for sbuf structures */ +static sbuf_t *sbfrlst; + +/* Typ of next expected symbol */ +symt_t symtyp; + + +/* + * All keywords are written to the symbol table. This saves us looking + * in a extra table for each name we found. + */ +void +initscan() +{ + struct kwtab *kw; + sym_t *sym; + int h, i; + u_quad_t uq; + + for (kw = kwtab; kw->kw_name != NULL; kw++) { + if (kw->kw_stdc && tflag) + continue; + if (kw->kw_gcc && !gflag) + continue; + sym = getblk(sizeof (sym_t)); + sym->s_name = kw->kw_name; + sym->s_keyw = 1; + sym->s_value.v_quad = kw->kw_token; + if (kw->kw_token == T_TYPE || kw->kw_token == T_SOU) { + sym->s_tspec = kw->kw_tspec; + } else if (kw->kw_token == T_SCLASS) { + sym->s_scl = kw->kw_scl; + } else if (kw->kw_token == T_QUAL) { + sym->s_tqual = kw->kw_tqual; + } + h = hash(sym->s_name); + if ((sym->s_link = symtab[h]) != NULL) + symtab[h]->s_rlink = &sym->s_link; + (symtab[h] = sym)->s_rlink = &symtab[h]; + } + + /* initialize bit-masks for quads */ + for (i = 0; i < sizeof (u_quad_t) * CHAR_BIT; i++) { + qbmasks[i] = (u_quad_t)1 << i; + uq = ~(u_quad_t)0 << i; + qumasks[i] = uq; + qlmasks[i] = ~uq; + } + qumasks[i] = 0; + qlmasks[i] = ~(u_quad_t)0; +} + +/* + * Get a free sbuf structure, if possible from the free list + */ +static sbuf_t * +allocsb() +{ + sbuf_t *sb; + + if ((sb = sbfrlst) != NULL) { + sbfrlst = sb->sb_nxt; + } else { + sb = xmalloc(sizeof (sbuf_t)); + } + (void)memset(sb, 0, sizeof (sb)); + return (sb); +} + +/* + * Put a sbuf structure to the free list + */ +static void +freesb(sb) + sbuf_t *sb; +{ + sb->sb_nxt = sbfrlst; + sbfrlst = sb; +} + +/* + * Read a character and ensure that it is positive (except EOF). + * Increment line count(s) if necessary. + */ +static int +inpc() +{ + int c; + + if ((c = input()) != EOF && (c &= CHAR_MASK) == '\n') + incline(); + return (c); +} + +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 % HSHSIZ1); +} + +/* + * Lex has found a letter followed by zero or more letters or digits. + * It looks for a symbol in the symbol table with the same name. This + * symbol must either be a keyword or a symbol of the type required by + * symtyp (label, member, tag, ...). + * + * If it is a keyword, the token is returned. In some cases it is described + * more deeply by data written to yylval. + * + * If it is a symbol, T_NAME is returned and the pointer to a sbuf struct + * is stored in yylval. This struct contains the name of the symbol, it's + * length and hash value. If there is already a symbol of the same name + * and type in the symbol table, the sbuf struct also contains a pointer + * to the symbol table entry. + */ +static int +name() +{ + char *s; + sbuf_t *sb; + sym_t *sym; + int tok; + + sb = allocsb(); + sb->sb_name = yytext; + sb->sb_len = yyleng; + sb->sb_hash = hash(yytext); + + if ((sym = search(sb)) != NULL && sym->s_keyw) { + freesb(sb); + return (keyw(sym)); + } + + sb->sb_sym = sym; + + if (sym != NULL) { + if (blklev < sym->s_blklev) + lerror("name() 1"); + sb->sb_name = sym->s_name; + sb->sb_len = strlen(sym->s_name); + tok = sym->s_scl == TYPEDEF ? T_TYPENAME : T_NAME; + } else { + s = getblk(yyleng + 1); + (void)memcpy(s, yytext, yyleng + 1); + sb->sb_name = s; + sb->sb_len = yyleng; + tok = T_NAME; + } + + yylval.y_sb = sb; + return (tok); +} + +static sym_t * +search(sb) + sbuf_t *sb; +{ + sym_t *sym; + + for (sym = symtab[sb->sb_hash]; sym != NULL; sym = sym->s_link) { + if (strcmp(sym->s_name, sb->sb_name) == 0) { + if (sym->s_keyw || sym->s_kind == symtyp) + return (sym); + } + } + + return (NULL); +} + +static int +keyw(sym) + sym_t *sym; +{ + int t; + + if ((t = (int)sym->s_value.v_quad) == T_SCLASS) { + yylval.y_scl = sym->s_scl; + } else if (t == T_TYPE || t == T_SOU) { + yylval.y_tspec = sym->s_tspec; + } else if (t == T_QUAL) { + yylval.y_tqual = sym->s_tqual; + } + return (t); +} + +/* + * Convert a string representing an integer into internal representation. + * The value is returned in yylval. icon() (and yylex()) returns T_CON. + */ +static int +icon(base) + int base; +{ + int l_suffix, u_suffix; + int len; + const char *cp; + char c, *eptr; + tspec_t typ; + u_long ul; + u_quad_t uq; + int ansiu; + static tspec_t contypes[2][3] = { + { INT, LONG, QUAD }, + { UINT, ULONG, UQUAD } + }; + + cp = yytext; + len = yyleng; + + /* skip 0x */ + if (base == 16) { + cp += 2; + len -= 2; + } + + /* read suffixes */ + l_suffix = u_suffix = 0; + for ( ; ; ) { + if ((c = cp[len - 1]) == 'l' || c == 'L') { + l_suffix++; + } else if (c == 'u' || c == 'U') { + u_suffix++; + } else { + break; + } + len--; + } + if (l_suffix > 2 || u_suffix > 1) { + /* malformed integer constant */ + warning(251); + if (l_suffix > 2) + l_suffix = 2; + if (u_suffix > 1) + u_suffix = 1; + } + if (tflag && u_suffix != 0) { + /* suffix U is illegal in traditional C */ + warning(97); + } + typ = contypes[u_suffix][l_suffix]; + + errno = 0; + if (l_suffix < 2) { + ul = strtoul(cp, &eptr, base); + } else { + uq = strtouq(cp, &eptr, base); + } + if (eptr != cp + len) + lerror("icon() 1"); + if (errno != 0) + /* integer constant out of range */ + warning(252); + + /* + * If the value is to big for the current type, we must choose + * another type. + */ + ansiu = 0; + switch (typ) { + case INT: + if (ul <= INT_MAX) { + /* ok */ + } else if (ul <= (unsigned)UINT_MAX && base != 10) { + typ = UINT; + } else if (ul <= LONG_MAX) { + typ = LONG; + } else { + typ = ULONG; + } + if (typ == UINT || typ == ULONG) { + if (tflag) { + typ = LONG; + } else if (!sflag) { + /* + * Remember that the constant is unsigned + * only in ANSI C + */ + ansiu = 1; + } + } + break; + case UINT: + if (ul > (u_int)UINT_MAX) + typ = ULONG; + break; + case LONG: + if (ul > LONG_MAX && !tflag) { + typ = ULONG; + if (!sflag) + ansiu = 1; + } + break; + case QUAD: + if (uq > QUAD_MAX && !tflag) { + typ = UQUAD; + if (!sflag) + ansiu = 1; + } + break; + /* LINTED (enumeration values not handled in switch) */ + } + + if (typ != QUAD && typ != UQUAD) { + if (isutyp(typ)) { + uq = ul; + } else { + uq = (quad_t)(long)ul; + } + } + + uq = (u_quad_t)xsign((quad_t)uq, typ, -1); + + (yylval.y_val = xcalloc(1, sizeof (val_t)))->v_tspec = typ; + yylval.y_val->v_ansiu = ansiu; + yylval.y_val->v_quad = (quad_t)uq; + + return (T_CON); +} + +/* + * Returns 1 if t is a signed type and the value is negative. + * + * len is the number of significant bits. If len is -1, len is set + * to the width of type t. + */ +int +sign(q, t, len) + quad_t q; + tspec_t t; + int len; +{ + if (t == PTR || isutyp(t)) + return (0); + return (msb(q, t, len)); +} + +int +msb(q, t, len) + quad_t q; + tspec_t t; + int len; +{ + if (len <= 0) + len = size(t); + return ((q & qbmasks[len - 1]) != 0); +} + +/* + * Extends the sign of q. + */ +quad_t +xsign(q, t, len) + quad_t q; + tspec_t t; + int len; +{ + if (len <= 0) + len = size(t); + + if (t == PTR || isutyp(t) || !sign(q, t, len)) { + q &= qlmasks[len]; + } else { + q |= qumasks[len]; + } + return (q); +} + +/* + * Convert a string representing a floating point value into its interal + * representation. Type and value are returned in yylval. fcon() + * (and yylex()) returns T_CON. + * XXX Currently it is not possible to convert constants of type + * long double which are greater then DBL_MAX. + */ +static int +fcon() +{ + const char *cp; + int len; + tspec_t typ; + char c, *eptr; + double d; + float f; + + cp = yytext; + len = yyleng; + + if ((c = cp[len - 1]) == 'f' || c == 'F') { + typ = FLOAT; + len--; + } else if (c == 'l' || c == 'L') { + typ = LDOUBLE; + len--; + } else { + typ = DOUBLE; + } + + if (tflag && typ != DOUBLE) { + /* suffixes F and L are illegal in traditional C */ + warning(98); + } + + errno = 0; + d = strtod(cp, &eptr); + if (eptr != cp + len) + lerror("fcon() 1"); + if (errno != 0) + /* floating-point constant out of range */ + warning(248); + + if (typ == FLOAT) { + f = (float)d; + if (isinf(f)) { + /* floating-point constant out of range */ + warning(248); + f = f > 0 ? FLT_MAX : -FLT_MAX; + } + } + + (yylval.y_val = xcalloc(1, sizeof (val_t)))->v_tspec = typ; + if (typ == FLOAT) { + yylval.y_val->v_ldbl = f; + } else { + yylval.y_val->v_ldbl = d; + } + + return (T_CON); +} + +static int +operator(t, o) + int t; + op_t o; +{ + yylval.y_op = o; + return (t); +} + +/* + * Called if lex found a leading \'. + */ +static int +ccon() +{ + int n, val, c; + char cv; + + n = 0; + val = 0; + while ((c = getescc('\'')) >= 0) { + val = (val << CHAR_BIT) + c; + n++; + } + if (c == -2) { + /* unterminated character constant */ + error(253); + } else { + if (n > sizeof (int) || (n > 1 && (pflag || hflag))) { + /* too many characters in character constant */ + error(71); + } else if (n > 1) { + /* multi-character character constant */ + warning(294); + } else if (n == 0) { + /* empty character constant */ + error(73); + } + } + if (n == 1) { + cv = (char)val; + val = cv; + } + + yylval.y_val = xcalloc(1, sizeof (val_t)); + yylval.y_val->v_tspec = INT; + yylval.y_val->v_quad = val; + + return (T_CON); +} + +/* + * Called if lex found a leading L\' + */ +static int +wccon() +{ + static char buf[MB_LEN_MAX + 1]; + int i, c; + wchar_t wc; + + i = 0; + while ((c = getescc('\'')) >= 0) { + if (i < MB_CUR_MAX) + buf[i] = (char)c; + i++; + } + + wc = 0; + + if (c == -2) { + /* unterminated character constant */ + error(253); + } else if (c == 0) { + /* empty character constant */ + error(73); + } else { + if (i > MB_CUR_MAX) { + i = MB_CUR_MAX; + /* too many characters in character constant */ + error(71); + } else { + buf[i] = '\0'; + (void)mbtowc(NULL, NULL, 0); + if (mbtowc(&wc, buf, MB_CUR_MAX) < 0) + /* invalid multibyte character */ + error(291); + } + } + + yylval.y_val = xcalloc(1, sizeof (val_t)); + yylval.y_val->v_tspec = WCHAR; + yylval.y_val->v_quad = wc; + + return (T_CON); +} + +/* + * Read a character which is part of a character constant or of a string + * and handle escapes. + * + * The Argument is the character which delimits the character constant or + * string. + * + * Returns -1 if the end of the character constant or string is reached, + * -2 if the EOF is reached, and the charachter otherwise. + */ +static int +getescc(d) + int d; +{ + static int pbc = -1; + int n, c, v; + + if (pbc == -1) { + c = inpc(); + } else { + c = pbc; + pbc = -1; + } + if (c == d) + return (-1); + switch (c) { + case '\n': + /* newline in string or char constant */ + error(254); + return (-2); + case EOF: + return (-2); + case '\\': + switch (c = inpc()) { + case '"': + if (tflag && d == '\'') + /* \" inside character constant undef. ... */ + warning(262); + return ('"'); + case '\'': + return ('\''); + case '?': + if (tflag) + /* \? undefined in traditional C */ + warning(263); + return ('?'); + case '\\': + return ('\\'); + case 'a': + if (tflag) + /* \a undefined in traditional C */ + warning(81); +#ifdef __STDC__ + return ('\a'); +#else + return ('\007'); +#endif + case 'b': + return ('\b'); + case 'f': + return ('\f'); + case 'n': + return ('\n'); + case 'r': + return ('\r'); + case 't': + return ('\t'); + case 'v': + if (tflag) + /* \v undefined in traditional C */ + warning(264); +#ifdef __STDC__ + return ('\v'); +#else + return ('\013'); +#endif + case '8': case '9': + /* bad octal digit %c */ + warning(77, c); + /* FALLTHROUGH */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = 3; + v = 0; + do { + v = (v << 3) + (c - '0'); + c = inpc(); + } while (--n && isdigit(c) && (tflag || c <= '7')); + if (tflag && n > 0 && isdigit(c)) + /* bad octal digit %c */ + warning(77, c); + pbc = c; + if (v > UCHAR_MAX) { + /* character escape does not fit in char. */ + warning(76); + v &= CHAR_MASK; + } + return (v); + case 'x': + if (tflag) + /* \x undefined in traditional C */ + warning(82); + v = 0; + n = 0; + while ((c = inpc()) >= 0 && isxdigit(c)) { + c = isdigit(c) ? + c - '0' : toupper(c) - 'A' + 10; + v = (v << 4) + c; + if (n >= 0) { + if ((v & ~CHAR_MASK) != 0) { + /* overflow in hex escape */ + warning(75); + n = -1; + } else { + n++; + } + } + } + pbc = c; + if (n == 0) { + /* no hex digits follow \x */ + error(74); + } if (n == -1) { + v &= CHAR_MASK; + } + return (v); + case '\n': + return (getescc(d)); + case EOF: + return (-2); + default: + if (isprint(c)) { + /* dubious escape \%c */ + warning(79, c); + } else { + /* dubious escape \%o */ + warning(80, c); + } + } + } + return (c); +} + +/* + * Called for preprocessor directives. Currently implemented are: + * # lineno + * # lineno "filename" + */ +static void +directive() +{ + const char *cp, *fn; + char c, *eptr; + size_t fnl; + long ln; + static int first = 1; + + /* Go to first non-whitespace after # */ + for (cp = yytext + 1; (c = *cp) == ' ' || c == '\t'; cp++) ; + + if (!isdigit(c)) { + error: + /* undefined or invalid # directive */ + warning(255); + return; + } + ln = strtol(--cp, &eptr, 10); + if (cp == eptr) + goto error; + if ((c = *(cp = eptr)) != ' ' && c != '\t' && c != '\0') + goto error; + while ((c = *cp++) == ' ' || c == '\t') ; + if (c != '\0') { + if (c != '"') + goto error; + fn = cp; + while ((c = *cp) != '"' && c != '\0') + cp++; + if (c != '"') + goto error; + if ((fnl = cp++ - fn) > PATH_MAX) + goto error; + while ((c = *cp++) == ' ' || c == '\t') ; +#if 0 + if (c != '\0') + warning("extra character(s) after directive"); +#endif + curr_pos.p_file = fnnalloc(fn, fnl); + /* + * If this is the first directive, the name is the name + * of the C source file as specified at the command line. + * It is written to the output file. + */ + if (first) { + csrc_pos.p_file = curr_pos.p_file; + outsrc(curr_pos.p_file); + first = 0; + } + } + curr_pos.p_line = (int)ln - 1; + if (curr_pos.p_file == csrc_pos.p_file) + csrc_pos.p_line = (int)ln - 1; +} + +/* + * Handle lint comments. Following comments are currently understood: + * ARGSUSEDn + * CONSTCOND CONSTANTCOND CONSTANTCONDITION + * FALLTHRU FALLTHROUGH + * LINTLIBRARY + * LINTED NOSTRICT + * LONGLONG + * NOTREACHED + * PRINTFLIKEn + * PROTOLIB + * SCANFLIKEn + * VARARGSn + * If one of this comments is recognized, the arguments, if any, are + * parsed and a function which handles this comment is called. + */ +static void +comment() +{ + int c, lc; + static struct { + const char *keywd; + int arg; + void (*func) __P((int)); + } keywtab[] = { + { "ARGSUSED", 1, argsused }, + { "CONSTCOND", 0, constcond }, + { "CONSTANTCOND", 0, constcond }, + { "CONSTANTCONDITION", 0, constcond }, + { "FALLTHRU", 0, fallthru }, + { "FALLTHROUGH", 0, fallthru }, + { "LINTLIBRARY", 0, lintlib }, + { "LINTED", 0, linted }, + { "LONGLONG", 0, longlong }, + { "NOSTRICT", 0, linted }, + { "NOTREACHED", 0, notreach }, + { "PRINTFLIKE", 1, printflike }, + { "PROTOLIB", 1, protolib }, + { "SCANFLIKE", 1, scanflike }, + { "VARARGS", 1, varargs }, + }; + char keywd[32]; + char arg[32]; + int l, i, a; + int eoc; + + eoc = 0; + + /* Skip white spaces after the start of the comment */ + while ((c = inpc()) != EOF && isspace(c)) ; + + /* Read the potential keyword to keywd */ + l = 0; + while (c != EOF && isupper(c) && l < sizeof (keywd) - 1) { + keywd[l++] = (char)c; + c = inpc(); + } + keywd[l] = '\0'; + + /* look for the keyword */ + for (i = 0; i < sizeof (keywtab) / sizeof (keywtab[0]); i++) { + if (strcmp(keywtab[i].keywd, keywd) == 0) + break; + } + if (i == sizeof (keywtab) / sizeof (keywtab[0])) + goto skip_rest; + + /* skip white spaces after the keyword */ + while (c != EOF && isspace(c)) + c = inpc(); + + /* read the argument, if the keyword accepts one and there is one */ + l = 0; + if (keywtab[i].arg) { + while (c != EOF && isdigit(c) && l < sizeof (arg) - 1) { + arg[l++] = (char)c; + c = inpc(); + } + } + arg[l] = '\0'; + a = l != 0 ? atoi(arg) : -1; + + /* skip white spaces after the argument */ + while (c != EOF && isspace(c)) + c = inpc(); + + if (c != '*' || (c = inpc()) != '/') { + if (keywtab[i].func != linted) + /* extra characters in lint comment */ + warning(257); + } else { + /* + * remember that we have already found the end of the + * comment + */ + eoc = 1; + } + + if (keywtab[i].func != NULL) + (*keywtab[i].func)(a); + + skip_rest: + while (!eoc) { + lc = c; + if ((c = inpc()) == EOF) { + /* unterminated comment */ + error(256); + break; + } + if (lc == '*' && c == '/') + eoc = 1; + } +} + +/* + * Clear flags for lint comments LINTED, LONGLONG and CONSTCOND. + * clrwflgs() is called after function definitions and global and + * local declarations and definitions. It is also called between + * the controlling expression and the body of control statements + * (if, switch, for, while). + */ +void +clrwflgs() +{ + nowarn = 0; + quadflg = 0; + ccflg = 0; +} + +/* + * Strings are stored in a dynamically alloceted buffer and passed + * in yylval.y_xstrg to the parser. The parser or the routines called + * by the parser are responsible for freeing this buffer. + */ +static int +string() +{ + u_char *s; + int c; + size_t len, max; + strg_t *strg; + + s = xmalloc(max = 64); + + len = 0; + while ((c = getescc('"')) >= 0) { + /* +1 to reserve space for a trailing NUL character */ + if (len + 1 == max) + s = xrealloc(s, max *= 2); + s[len++] = (char)c; + } + s[len] = '\0'; + if (c == -2) + /* unterminated string constant */ + error(258); + + strg = xcalloc(1, sizeof (strg_t)); + strg->st_tspec = CHAR; + strg->st_len = len; + strg->st_cp = s; + + yylval.y_strg = strg; + return (T_STRING); +} + +static int +wcstrg() +{ + char *s; + int c, i, n, wi; + size_t len, max, wlen; + wchar_t *ws; + strg_t *strg; + + s = xmalloc(max = 64); + len = 0; + while ((c = getescc('"')) >= 0) { + /* +1 to save space for a trailing NUL character */ + if (len + 1 >= max) + s = xrealloc(s, max *= 2); + s[len++] = (char)c; + } + s[len] = '\0'; + if (c == -2) + /* unterminated string constant */ + error(258); + + /* get length of wide character string */ + (void)mblen(NULL, 0); + for (i = 0, wlen = 0; i < len; i += n, wlen++) { + if ((n = mblen(&s[i], MB_CUR_MAX)) == -1) { + /* invalid multibyte character */ + error(291); + break; + } + if (n == 0) + n = 1; + } + + ws = xmalloc((wlen + 1) * sizeof (wchar_t)); + + /* convert from multibyte to wide char */ + (void)mbtowc(NULL, NULL, 0); + for (i = 0, wi = 0; i < len; i += n, wi++) { + if ((n = mbtowc(&ws[wi], &s[i], MB_CUR_MAX)) == -1) + break; + if (n == 0) + n = 1; + } + ws[wi] = 0; + free(s); + + strg = xcalloc(1, sizeof (strg_t)); + strg->st_tspec = WCHAR; + strg->st_len = wlen; + strg->st_wcp = ws; + + yylval.y_strg = strg; + return (T_STRING); +} + +/* + * As noted above the scanner does not create new symbol table entries + * for symbols it cannot find in the symbol table. This is to avoid + * putting undeclared symbols into the symbol table if a syntax error + * occurs. + * + * getsym() is called as soon as it is probably ok to put the symbol to + * the symbol table. This does not mean that it is not possible that + * symbols are put to the symbol table which are than not completely + * declared due to syntax errors. To avoid too many problems in this + * case symbols get type int in getsym(). + * + * XXX calls to getsym() should be delayed until decl1*() is called + */ +sym_t * +getsym(sb) + sbuf_t *sb; +{ + dinfo_t *di; + char *s; + sym_t *sym; + + sym = sb->sb_sym; + + /* + * During member declaration it is possible that name() looked + * for symbols of type FVFT, although it should have looked for + * symbols of type FTAG. Same can happen for labels. Both cases + * are compensated here. + */ + if (symtyp == FMOS || symtyp == FLAB) { + if (sym == NULL || sym->s_kind == FVFT) + sym = search(sb); + } + + if (sym != NULL) { + if (sym->s_kind != symtyp) + lerror("storesym() 1"); + symtyp = FVFT; + freesb(sb); + return (sym); + } + + /* create a new symbol table entry */ + + /* labels must always be allocated at level 1 (outhermost block) */ + if (symtyp == FLAB) { + sym = getlblk(1, sizeof (sym_t)); + s = getlblk(1, sb->sb_len + 1); + (void)memcpy(s, sb->sb_name, sb->sb_len + 1); + sym->s_name = s; + sym->s_blklev = 1; + di = dcs; + while (di->d_nxt != NULL && di->d_nxt->d_nxt != NULL) + di = di->d_nxt; + if (di->d_ctx != AUTO) + lerror("storesym() 2"); + } else { + sym = getblk(sizeof (sym_t)); + sym->s_name = sb->sb_name; + sym->s_blklev = blklev; + di = dcs; + } + + STRUCT_ASSIGN(sym->s_dpos, curr_pos); + if ((sym->s_kind = symtyp) != FLAB) + sym->s_type = gettyp(INT); + + symtyp = FVFT; + + if ((sym->s_link = symtab[sb->sb_hash]) != NULL) + symtab[sb->sb_hash]->s_rlink = &sym->s_link; + (symtab[sb->sb_hash] = sym)->s_rlink = &symtab[sb->sb_hash]; + + *di->d_ldlsym = sym; + di->d_ldlsym = &sym->s_dlnxt; + + freesb(sb); + return (sym); +} + +/* + * Remove a symbol forever from the symbol table. s_blklev + * is set to -1 to avoid that the symbol will later be put + * back to the symbol table. + */ +void +rmsym(sym) + sym_t *sym; +{ + if ((*sym->s_rlink = sym->s_link) != NULL) + sym->s_link->s_rlink = sym->s_rlink; + sym->s_blklev = -1; + sym->s_link = NULL; +} + +/* + * Remove a list of symbols declared at one level from the symbol + * table. + */ +void +rmsyms(syms) + sym_t *syms; +{ + sym_t *sym; + + for (sym = syms; sym != NULL; sym = sym->s_dlnxt) { + if (sym->s_blklev != -1) { + if ((*sym->s_rlink = sym->s_link) != NULL) + sym->s_link->s_rlink = sym->s_rlink; + sym->s_link = NULL; + sym->s_rlink = NULL; + } + } +} + +/* + * Put a symbol into the symbol table + */ +void +inssym(bl, sym) + int bl; + sym_t *sym; +{ + int h; + + h = hash(sym->s_name); + if ((sym->s_link = symtab[h]) != NULL) + symtab[h]->s_rlink = &sym->s_link; + (symtab[h] = sym)->s_rlink = &symtab[h]; + sym->s_blklev = bl; + if (sym->s_link != NULL && sym->s_blklev < sym->s_link->s_blklev) + lerror("inssym()"); +} + +/* + * Called at level 0 after syntax errors + * Removes all symbols which are not declared at level 0 from the + * symbol table. Also frees all memory which is not associated with + * level 0. + */ +void +cleanup() +{ + sym_t *sym, *nsym; + int i; + + for (i = 0; i < HSHSIZ1; i++) { + for (sym = symtab[i]; sym != NULL; sym = nsym) { + nsym = sym->s_link; + if (sym->s_blklev >= 1) { + if ((*sym->s_rlink = nsym) != NULL) + nsym->s_rlink = sym->s_rlink; + } + } + } + + for (i = mblklev; i > 0; i--) + freelblk(i); +} + +/* + * Create a new symbol with the name of an existing symbol. + */ +sym_t * +pushdown(sym) + sym_t *sym; +{ + int h; + sym_t *nsym; + + h = hash(sym->s_name); + nsym = getblk(sizeof (sym_t)); + if (sym->s_blklev > blklev) + lerror("pushdown()"); + nsym->s_name = sym->s_name; + STRUCT_ASSIGN(nsym->s_dpos, curr_pos); + nsym->s_kind = sym->s_kind; + nsym->s_blklev = blklev; + + if ((nsym->s_link = symtab[h]) != NULL) + symtab[h]->s_rlink = &nsym->s_link; + (symtab[h] = nsym)->s_rlink = &symtab[h]; + + *dcs->d_ldlsym = nsym; + dcs->d_ldlsym = &nsym->s_dlnxt; + + return (nsym); +} + +/* + * Free any dynamically allocated memory referenced by + * the value stack or yylval. + * The type of information in yylval is described by tok. + */ +void +freeyyv(sp, tok) + void *sp; + int tok; +{ + if (tok == T_NAME || tok == T_TYPENAME) { + sbuf_t *sb = *(sbuf_t **)sp; + freesb(sb); + } else if (tok == T_CON) { + val_t *val = *(val_t **)sp; + free(val); + } else if (tok == T_STRING) { + strg_t *strg = *(strg_t **)sp; + if (strg->st_tspec == CHAR) { + free(strg->st_cp); + } else if (strg->st_tspec == WCHAR) { + free(strg->st_wcp); + } else { + lerror("fryylv() 1"); + } + free(strg); + } +} diff --git a/usr.bin/xlint/lint1/tree.c b/usr.bin/xlint/lint1/tree.c new file mode 100644 index 0000000..525e407 --- /dev/null +++ b/usr.bin/xlint/lint1/tree.c @@ -0,0 +1,3924 @@ +/* $NetBSD: tree.c,v 1.12 1995/10/02 17:37:57 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: tree.c,v 1.12 1995/10/02 17:37:57 jpo Exp $"; +#endif + +#include +#include +#include +#include +#include + +#include "lint1.h" +#include "y.tab.h" + +/* Various flags for each operator. */ +static mod_t modtab[NOPS]; + +static tnode_t *getinode __P((tspec_t, quad_t)); +static void ptrcmpok __P((op_t, tnode_t *, tnode_t *)); +static int asgntypok __P((op_t, int, tnode_t *, tnode_t *)); +static void chkbeop __P((op_t, tnode_t *, tnode_t *)); +static void chkeop2 __P((op_t, int, tnode_t *, tnode_t *)); +static void chkeop1 __P((op_t, int, tnode_t *, tnode_t *)); +static tnode_t *mktnode __P((op_t, type_t *, tnode_t *, tnode_t *)); +static void balance __P((op_t, tnode_t **, tnode_t **)); +static void incompat __P((op_t, tspec_t, tspec_t)); +static void illptrc __P((mod_t *, type_t *, type_t *)); +static void mrgqual __P((type_t **, type_t *, type_t *)); +static int conmemb __P((type_t *)); +static void ptconv __P((int, tspec_t, tspec_t, type_t *, tnode_t *)); +static void iiconv __P((op_t, int, tspec_t, tspec_t, type_t *, tnode_t *)); +static void piconv __P((op_t, tspec_t, type_t *, tnode_t *)); +static void ppconv __P((op_t, tnode_t *, type_t *)); +static tnode_t *bldstr __P((op_t, tnode_t *, tnode_t *)); +static tnode_t *bldincdec __P((op_t, tnode_t *)); +static tnode_t *bldamper __P((tnode_t *, int)); +static tnode_t *bldplmi __P((op_t, tnode_t *, tnode_t *)); +static tnode_t *bldshft __P((op_t, tnode_t *, tnode_t *)); +static tnode_t *bldcol __P((tnode_t *, tnode_t *)); +static tnode_t *bldasgn __P((op_t, tnode_t *, tnode_t *)); +static tnode_t *plength __P((type_t *)); +static tnode_t *fold __P((tnode_t *)); +static tnode_t *foldtst __P((tnode_t *)); +static tnode_t *foldflt __P((tnode_t *)); +static tnode_t *chkfarg __P((type_t *, tnode_t *)); +static tnode_t *parg __P((int, type_t *, tnode_t *)); +static void nulleff __P((tnode_t *)); +static void displexpr __P((tnode_t *, int)); +static void chkaidx __P((tnode_t *, int)); +static void chkcomp __P((op_t, tnode_t *, tnode_t *)); +static void precconf __P((tnode_t *)); + +/* + * Initialize mods of operators. + */ +void +initmtab() +{ + static struct { + op_t op; + mod_t m; + } imods[] = { + { ARROW, { 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + "->" } }, + { POINT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "." } }, + { NOT, { 0,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0, + "!" } }, + { COMPL, { 0,0,1,0,0,1,1,0,0,0,0,0,0,0,0,1,1, + "~" } }, + { INCBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "prefix++" } }, + { DECBEF, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "prefix--" } }, + { INCAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "postfix++" } }, + { DECAFT, { 0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "postfix--" } }, + { UPLUS, { 0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,1, + "unary +" } }, + { UMINUS, { 0,0,0,0,1,1,1,0,0,0,1,0,0,0,0,1,1, + "unary -" } }, + { STAR, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + "unary *" } }, + { AMPER, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "unary &" } }, + { MULT, { 1,0,0,0,1,1,1,0,1,0,0,1,0,0,0,1,1, + "*" } }, + { DIV, { 1,0,0,0,1,1,1,0,1,0,1,1,0,0,0,1,1, + "/" } }, + { MOD, { 1,0,1,0,0,1,1,0,1,0,1,1,0,0,0,1,1, + "%" } }, + { PLUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0, + "+" } }, + { MINUS, { 1,0,0,1,0,1,1,0,1,0,0,0,0,0,0,1,0, + "-" } }, + { SHL, { 1,0,1,0,0,1,1,0,0,0,0,0,1,0,0,1,1, + "<<" } }, + { SHR, { 1,0,1,0,0,1,1,0,0,0,1,0,1,0,0,1,1, + ">>" } }, + { LT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, + "<" } }, + { LE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, + "<=" } }, + { GT, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, + ">" } }, + { GE, { 1,1,0,1,0,1,1,0,1,0,1,1,0,1,1,0,1, + ">=" } }, + { EQ, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1, + "==" } }, + { NE, { 1,1,0,1,0,1,1,0,1,0,0,0,0,1,1,0,1, + "!=" } }, + { AND, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, + "&" } }, + { XOR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, + "^" } }, + { OR, { 1,0,1,0,0,1,1,0,1,0,0,0,1,0,0,1,0, + "|" } }, + { LOGAND, { 1,1,0,1,0,1,0,1,0,0,0,0,0,0,0,1,0, + "&&" } }, + { LOGOR, { 1,1,0,1,0,1,0,1,0,0,0,0,1,0,0,1,0, + "||" } }, + { QUEST, { 1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0, + "?" } }, + { COLON, { 1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0, + ":" } }, + { ASSIGN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, + "=" } }, + { MULASS, { 1,0,0,0,1,0,0,0,0,1,0,0,0,0,0,1,0, + "*=" } }, + { DIVASS, { 1,0,0,0,1,0,0,0,0,1,0,1,0,0,0,1,0, + "/=" } }, + { MODASS, { 1,0,1,0,0,0,0,0,0,1,0,1,0,0,0,1,0, + "%=" } }, + { ADDASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "+=" } }, + { SUBASS, { 1,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0, + "-=" } }, + { SHLASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, + "<<=" } }, + { SHRASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, + ">>=" } }, + { ANDASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, + "&=" } }, + { XORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, + "^=" } }, + { ORASS, { 1,0,1,0,0,0,0,0,0,1,0,0,0,0,0,1,0, + "|=" } }, + { NAME, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "NAME" } }, + { CON, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "CON" } }, + { STRING, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "STRING" } }, + { FSEL, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "FSEL" } }, + { CALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, + "CALL" } }, + { COMMA, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1, + "," } }, + { CVT, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + "CVT" } }, + { ICALL, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0, + "ICALL" } }, + { LOAD, { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + "LOAD" } }, + { PUSH, { 0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0, + "PUSH" } }, + { RETURN, { 1,0,0,0,0,0,0,0,0,1,0,0,0,0,1,0,0, + "RETURN" } }, + { INIT, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, + "INIT" } }, + { FARG, { 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0, + "FARG" } }, + { NOOP } + }; + int i; + + for (i = 0; imods[i].op != NOOP; i++) + STRUCT_ASSIGN(modtab[imods[i].op], imods[i].m); +} + +/* + * Increase degree of reference. + * This is most often used to change type "T" in type "pointer to T". + */ +type_t * +incref(tp, t) + type_t *tp; + tspec_t t; +{ + type_t *tp2; + + tp2 = getblk(sizeof (type_t)); + tp2->t_tspec = t; + tp2->t_subt = tp; + return (tp2); +} + +/* + * same for use in expressions + */ +type_t * +tincref(tp, t) + type_t *tp; + tspec_t t; +{ + type_t *tp2; + + tp2 = tgetblk(sizeof (type_t)); + tp2->t_tspec = t; + tp2->t_subt = tp; + return (tp2); +} + +/* + * Create a node for a constant. + */ +tnode_t * +getcnode(tp, v) + type_t *tp; + val_t *v; +{ + tnode_t *n; + + n = getnode(); + n->tn_op = CON; + n->tn_type = tp; + n->tn_val = tgetblk(sizeof (val_t)); + n->tn_val->v_tspec = tp->t_tspec; + n->tn_val->v_ansiu = v->v_ansiu; + n->tn_val->v_u = v->v_u; + free(v); + return (n); +} + +/* + * Create a node for a integer constant. + */ +static tnode_t * +getinode(t, q) + tspec_t t; + quad_t q; +{ + tnode_t *n; + + n = getnode(); + n->tn_op = CON; + n->tn_type = gettyp(t); + n->tn_val = tgetblk(sizeof (val_t)); + n->tn_val->v_tspec = t; + n->tn_val->v_quad = q; + return (n); +} + +/* + * Create a node for a name (symbol table entry). + * ntok is the token which follows the name. + */ +tnode_t * +getnnode(sym, ntok) + sym_t *sym; + int ntok; +{ + tnode_t *n; + + if (sym->s_scl == NOSCL) { + sym->s_scl = EXTERN; + sym->s_def = DECL; + if (ntok == T_LPARN) { + if (sflag) { + /* function implicitly declared to ... */ + warning(215); + } + /* + * XXX if tflag is set the symbol should be + * exported to level 0 + */ + sym->s_type = incref(sym->s_type, FUNC); + } else { + /* %s undefined */ + error(99, sym->s_name); + } + } + + if (sym->s_kind != FVFT && sym->s_kind != FMOS) + lerror("getnnode() 1"); + + n = getnode(); + n->tn_type = sym->s_type; + if (sym->s_scl != ENUMCON) { + n->tn_op = NAME; + n->tn_sym = sym; + if (sym->s_kind == FVFT && sym->s_type->t_tspec != FUNC) + n->tn_lvalue = 1; + } else { + n->tn_op = CON; + n->tn_val = tgetblk(sizeof (val_t)); + *n->tn_val = sym->s_value; + } + + return (n); +} + +/* + * Create a node for a string. + */ +tnode_t * +getsnode(strg) + strg_t *strg; +{ + size_t len; + tnode_t *n; + + len = strg->st_len; + + n = getnode(); + + n->tn_op = STRING; + n->tn_type = tincref(gettyp(strg->st_tspec), ARRAY); + n->tn_type->t_dim = len + 1; + n->tn_lvalue = 1; + + n->tn_strg = tgetblk(sizeof (strg_t)); + n->tn_strg->st_tspec = strg->st_tspec; + n->tn_strg->st_len = len; + + if (strg->st_tspec == CHAR) { + n->tn_strg->st_cp = tgetblk(len + 1); + (void)memcpy(n->tn_strg->st_cp, strg->st_cp, len + 1); + free(strg->st_cp); + } else { + n->tn_strg->st_wcp = tgetblk((len + 1) * sizeof (wchar_t)); + (void)memcpy(n->tn_strg->st_wcp, strg->st_wcp, + (len + 1) * sizeof (wchar_t)); + free(strg->st_wcp); + } + free(strg); + + return (n); +} + +/* + * Returns a symbol which has the same name as the msym argument and is a + * member of the struct or union specified by the tn argument. + */ +sym_t * +strmemb(tn, op, msym) + tnode_t *tn; + op_t op; + sym_t *msym; +{ + str_t *str; + type_t *tp; + sym_t *sym, *csym; + int eq; + tspec_t t; + + /* + * Remove the member if it was unknown until now (Which means + * that no defined struct or union has a member with the same name). + */ + if (msym->s_scl == NOSCL) { + /* undefined struct/union member: %s */ + error(101, msym->s_name); + rmsym(msym); + msym->s_kind = FMOS; + msym->s_scl = MOS; + msym->s_styp = tgetblk(sizeof (str_t)); + msym->s_styp->stag = tgetblk(sizeof (sym_t)); + msym->s_styp->stag->s_name = unnamed; + msym->s_value.v_tspec = INT; + return (msym); + } + + /* Set str to the tag of which msym is expected to be a member. */ + str = NULL; + t = (tp = tn->tn_type)->t_tspec; + if (op == POINT) { + if (t == STRUCT || t == UNION) + str = tp->t_str; + } else if (op == ARROW && t == PTR) { + t = (tp = tp->t_subt)->t_tspec; + if (t == STRUCT || t == UNION) + str = tp->t_str; + } + + /* + * If this struct/union has a member with the name of msym, return + * return this it. + */ + if (str != NULL) { + for (sym = msym; sym != NULL; sym = sym->s_link) { + if (sym->s_scl != MOS && sym->s_scl != MOU) + continue; + if (sym->s_styp != str) + continue; + if (strcmp(sym->s_name, msym->s_name) != 0) + continue; + return (sym); + } + } + + /* + * Set eq to 0 if there are struct/union members with the same name + * and different types and/or offsets. + */ + eq = 1; + for (csym = msym; csym != NULL; csym = csym->s_link) { + if (csym->s_scl != MOS && csym->s_scl != MOU) + continue; + if (strcmp(msym->s_name, csym->s_name) != 0) + continue; + for (sym = csym->s_link ; sym != NULL; sym = sym->s_link) { + int w; + + if (sym->s_scl != MOS && sym->s_scl != MOU) + continue; + if (strcmp(csym->s_name, sym->s_name) != 0) + continue; + if (csym->s_value.v_quad != sym->s_value.v_quad) { + eq = 0; + break; + } + w = 0; + eq = eqtype(csym->s_type, sym->s_type, 0, 0, &w) && !w; + if (!eq) + break; + if (csym->s_field != sym->s_field) { + eq = 0; + break; + } + if (csym->s_field) { + type_t *tp1, *tp2; + + tp1 = csym->s_type; + tp2 = sym->s_type; + if (tp1->t_flen != tp2->t_flen) { + eq = 0; + break; + } + if (tp1->t_foffs != tp2->t_foffs) { + eq = 0; + break; + } + } + } + if (!eq) + break; + } + + /* + * Now handle the case in which the left operand refers really + * to a struct/union, but the right operand is not member of it. + */ + if (str != NULL) { + /* illegal member use: %s */ + if (eq && tflag) { + warning(102, msym->s_name); + } else { + error(102, msym->s_name); + } + return (msym); + } + + /* + * Now the left operand of ARROW does not point to a struct/union + * or the left operand of POINT is no struct/union. + */ + if (eq) { + if (op == POINT) { + /* left operand of "." must be struct/union object */ + if (tflag) { + warning(103); + } else { + error(103); + } + } else { + /* left operand of "->" must be pointer to ... */ + if (tflag && tn->tn_type->t_tspec == PTR) { + warning(104); + } else { + error(104); + } + } + } else { + if (tflag) { + /* non-unique member requires struct/union %s */ + error(105, op == POINT ? "object" : "pointer"); + } else { + /* unacceptable operand of %s */ + error(111, modtab[op].m_name); + } + } + + return (msym); +} + +/* + * Create a tree node. Called for most operands except function calls, + * sizeof and casts. + * + * op operator + * ln left operand + * rn if not NULL, right operand + */ +tnode_t * +build(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + mod_t *mp; + tnode_t *ntn; + type_t *rtp; + + mp = &modtab[op]; + + /* If there was an error in one of the operands, return. */ + if (ln == NULL || (mp->m_binary && rn == NULL)) + return (NULL); + + /* + * Apply class conversions to the left operand, but only if its + * value is needed or it is compaired with null. + */ + if (mp->m_vctx || mp->m_tctx) + ln = cconv(ln); + /* + * The right operand is almost always in a test or value context, + * except if it is a struct or union member. + */ + if (mp->m_binary && op != ARROW && op != POINT) + rn = cconv(rn); + + /* + * Print some warnings for comparisions of unsigned values with + * constants lower than or equal to null. This must be done + * before promote() because otherwise unsigned char and unsigned + * short would be promoted to int. Also types are tested to be + * CHAR, which would also become int. + */ + if (mp->m_comp) + chkcomp(op, ln, rn); + + /* + * Promote the left operand if it is in a test or value context + */ + if (mp->m_vctx || mp->m_tctx) + ln = promote(op, 0, ln); + /* + * Promote the right operand, but only if it is no struct or + * union member, or if it is not to be assigned to the left operand + */ + if (mp->m_binary && op != ARROW && op != POINT && + op != ASSIGN && op != RETURN) { + rn = promote(op, 0, rn); + } + + /* + * If the result of the operation is different for signed or + * unsigned operands and one of the operands is signed only in + * ANSI C, print a warning. + */ + if (mp->m_tlansiu && ln->tn_op == CON && ln->tn_val->v_ansiu) { + /* ANSI C treats constant as unsigned, op %s */ + warning(218, mp->m_name); + ln->tn_val->v_ansiu = 0; + } + if (mp->m_transiu && rn->tn_op == CON && rn->tn_val->v_ansiu) { + /* ANSI C treats constant as unsigned, op %s */ + warning(218, mp->m_name); + rn->tn_val->v_ansiu = 0; + } + + /* Make sure both operands are of the same type */ + if (mp->m_balance || (tflag && (op == SHL || op == SHR))) + balance(op, &ln, &rn); + + /* + * Check types for compatibility with the operation and mutual + * compatibility. Return if there are serios problems. + */ + if (!typeok(op, 0, ln, rn)) + return (NULL); + + /* And now create the node. */ + switch (op) { + case POINT: + case ARROW: + ntn = bldstr(op, ln, rn); + break; + case INCAFT: + case DECAFT: + case INCBEF: + case DECBEF: + ntn = bldincdec(op, ln); + break; + case AMPER: + ntn = bldamper(ln, 0); + break; + case STAR: + ntn = mktnode(STAR, ln->tn_type->t_subt, ln, NULL); + break; + case PLUS: + case MINUS: + ntn = bldplmi(op, ln, rn); + break; + case SHL: + case SHR: + ntn = bldshft(op, ln, rn); + break; + case COLON: + ntn = bldcol(ln, rn); + break; + case ASSIGN: + case MULASS: + case DIVASS: + case MODASS: + case ADDASS: + case SUBASS: + case SHLASS: + case SHRASS: + case ANDASS: + case XORASS: + case ORASS: + case RETURN: + ntn = bldasgn(op, ln, rn); + break; + case COMMA: + case QUEST: + ntn = mktnode(op, rn->tn_type, ln, rn); + break; + default: + rtp = mp->m_logop ? gettyp(INT) : ln->tn_type; + if (!mp->m_binary && rn != NULL) + lerror("build() 1"); + ntn = mktnode(op, rtp, ln, rn); + break; + } + + /* Return if an error occured. */ + if (ntn == NULL) + return (NULL); + + /* Print a warning if precedence confusion is possible */ + if (mp->m_tpconf) + precconf(ntn); + + /* + * Print a warning if one of the operands is in a context where + * it is compared with null and if this operand is a constant. + */ + if (mp->m_tctx) { + if (ln->tn_op == CON || + ((mp->m_binary && op != QUEST) && rn->tn_op == CON)) { + if (hflag && !ccflg) + /* constant in conditional context */ + warning(161); + } + } + + /* Fold if the operator requires it */ + if (mp->m_fold) { + if (ln->tn_op == CON && (!mp->m_binary || rn->tn_op == CON)) { + if (mp->m_tctx) { + ntn = foldtst(ntn); + } else if (isftyp(ntn->tn_type->t_tspec)) { + ntn = foldflt(ntn); + } else { + ntn = fold(ntn); + } + } else if (op == QUEST && ln->tn_op == CON) { + ntn = ln->tn_val->v_quad ? rn->tn_left : rn->tn_right; + } + } + + return (ntn); +} + +/* + * Perform class conversions. + * + * Arrays of type T are converted into pointers to type T. + * Functions are converted to pointers to functions. + * Lvalues are converted to rvalues. + */ +tnode_t * +cconv(tn) + tnode_t *tn; +{ + type_t *tp; + + /* + * Array-lvalue (array of type T) is converted into rvalue + * (pointer to type T) + */ + if (tn->tn_type->t_tspec == ARRAY) { + if (!tn->tn_lvalue) { + /* %soperand of '%s' must be lvalue */ + /* XXX print correct operator */ + (void)gnuism(114, "", modtab[AMPER].m_name); + } + tn = mktnode(AMPER, tincref(tn->tn_type->t_subt, PTR), + tn, NULL); + } + + /* + * Expression of type function (function with return value of type T) + * in rvalue-expression (pointer to function with return value + * of type T) + */ + if (tn->tn_type->t_tspec == FUNC) + tn = bldamper(tn, 1); + + /* lvalue to rvalue */ + if (tn->tn_lvalue) { + tp = tduptyp(tn->tn_type); + tp->t_const = tp->t_volatile = 0; + tn = mktnode(LOAD, tp, tn, NULL); + } + + return (tn); +} + +/* + * Perform most type checks. First the types are checked using + * informations from modtab[]. After that it is done by hand for + * more complicated operators and type combinations. + * + * If the types are ok, typeok() returns 1, otherwise 0. + */ +int +typeok(op, arg, ln, rn) + op_t op; + int arg; + tnode_t *ln, *rn; +{ + mod_t *mp; + tspec_t lt, rt, lst, rst, olt, ort; + type_t *ltp, *rtp, *lstp, *rstp; + tnode_t *tn; + + mp = &modtab[op]; + + if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR) + lst = (lstp = ltp->t_subt)->t_tspec; + if (mp->m_binary) { + if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR) + rst = (rstp = rtp->t_subt)->t_tspec; + } + + if (mp->m_rqint) { + /* integertypes required */ + if (!isityp(lt) || (mp->m_binary && !isityp(rt))) { + incompat(op, lt, rt); + return (0); + } + } else if (mp->m_rqsclt) { + /* scalar types required */ + if (!issclt(lt) || (mp->m_binary && !issclt(rt))) { + incompat(op, lt, rt); + return (0); + } + } else if (mp->m_rqatyp) { + /* arithmetic types required */ + if (!isatyp(lt) || (mp->m_binary && !isatyp(rt))) { + incompat(op, lt, rt); + return (0); + } + } + + if (op == SHL || op == SHR || op == SHLASS || op == SHRASS) { + /* + * For these operations we need the types before promotion + * and balancing. + */ + for (tn=ln; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) ; + olt = tn->tn_type->t_tspec; + for (tn=rn; tn->tn_op==CVT && !tn->tn_cast; tn=tn->tn_left) ; + ort = tn->tn_type->t_tspec; + } + + switch (op) { + case POINT: + /* + * Most errors required by ANSI C are reported in strmemb(). + * Here we only must check for totaly wrong things. + */ + if (lt == FUNC || lt == VOID || ltp->t_isfield || + ((lt != STRUCT && lt != UNION) && !ln->tn_lvalue)) { + /* Without tflag we got already an error */ + if (tflag) + /* unacceptable operand of %s */ + error(111, mp->m_name); + return (0); + } + /* Now we have an object we can create a pointer to */ + break; + case ARROW: + if (lt != PTR && !(tflag && isityp(lt))) { + /* Without tflag we got already an error */ + if (tflag) + /* unacceptabel operand of %s */ + error(111, mp->m_name); + return (0); + } + break; + case INCAFT: + case DECAFT: + case INCBEF: + case DECBEF: + /* operands have scalar types (checked above) */ + if (!ln->tn_lvalue) { + if (ln->tn_op == CVT && ln->tn_cast && + ln->tn_left->tn_op == LOAD) { + /* a cast does not yield an lvalue */ + error(163); + } + /* %soperand of %s must be lvalue */ + error(114, "", mp->m_name); + return (0); + } else if (ltp->t_const) { + /* %soperand of %s must be modifiable lvalue */ + if (!tflag) + warning(115, "", mp->m_name); + } + break; + case AMPER: + if (lt == ARRAY || lt == FUNC) { + /* ok, a warning comes later (in bldamper()) */ + } else if (!ln->tn_lvalue) { + if (ln->tn_op == CVT && ln->tn_cast && + ln->tn_left->tn_op == LOAD) { + /* a cast does not yield an lvalue */ + error(163); + } + /* %soperand of %s must be lvalue */ + error(114, "", mp->m_name); + return (0); + } else if (issclt(lt)) { + if (ltp->t_isfield) { + /* cannot take address of bit-field */ + error(112); + return (0); + } + } else if (lt != STRUCT && lt != UNION) { + /* unacceptable operand of %s */ + error(111, mp->m_name); + return (0); + } + if (ln->tn_op == NAME && ln->tn_sym->s_reg) { + /* cannot take address of register %s */ + error(113, ln->tn_sym->s_name); + return (0); + } + break; + case STAR: + /* until now there were no type checks for this operator */ + if (lt != PTR) { + /* cannot dereference non-pointer type */ + error(96); + return (0); + } + break; + case PLUS: + /* operands have scalar types (checked above) */ + if ((lt == PTR && !isityp(rt)) || (rt == PTR && !isityp(lt))) { + incompat(op, lt, rt); + return (0); + } + break; + case MINUS: + /* operands have scalar types (checked above) */ + if (lt == PTR && (!isityp(rt) && rt != PTR)) { + incompat(op, lt, rt); + return (0); + } else if (rt == PTR && lt != PTR) { + incompat(op, lt, rt); + return (0); + } + if (lt == PTR && rt == PTR) { + if (!eqtype(lstp, rstp, 1, 0, NULL)) { + /* illegal pointer subtraction */ + error(116); + } + } + break; + case SHR: + /* operands have integer types (checked above) */ + if (pflag && !isutyp(lt)) { + /* + * The left operand is signed. This means that + * the operation is (possibly) nonportable. + */ + /* bitwise operation on signed value nonportable */ + if (ln->tn_op != CON) { + /* possibly nonportable */ + warning(117); + } else if (ln->tn_val->v_quad < 0) { + warning(120); + } + } else if (!tflag && !sflag && !isutyp(olt) && isutyp(ort)) { + /* + * The left operand would become unsigned in + * traditional C. + */ + if (hflag && + (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { + /* semantics of %s change in ANSI C; use ... */ + warning(118, mp->m_name); + } + } else if (!tflag && !sflag && !isutyp(olt) && !isutyp(ort) && + psize(lt) < psize(rt)) { + /* + * In traditional C the left operand would be extended, + * possibly with 1, and then shifted. + */ + if (hflag && + (ln->tn_op != CON || ln->tn_val->v_quad < 0)) { + /* semantics of %s change in ANSI C; use ... */ + warning(118, mp->m_name); + } + } + goto shift; + case SHL: + /* + * ANSI C does not perform balancing for shift operations, + * but traditional C does. If the width of the right operand + * is greather than the width of the left operand, than in + * traditional C the left operand would be extendet to the + * width of the right operand. For SHL this may result in + * different results. + */ + if (psize(lt) < psize(rt)) { + /* + * XXX If both operands are constant make sure + * that there is really a differencs between + * ANSI C and traditional C. + */ + if (hflag) + /* semantics of %s change in ANSI C; use ... */ + warning(118, mp->m_name); + } + shift: + if (rn->tn_op == CON) { + if (!isutyp(rt) && rn->tn_val->v_quad < 0) { + /* negative shift */ + warning(121); + } else if ((u_quad_t)rn->tn_val->v_quad == size(lt)) { + /* shift equal to size fo object */ + warning(267); + } else if ((u_quad_t)rn->tn_val->v_quad > size(lt)) { + /* shift greater than size of object */ + warning(122); + } + } + break; + case EQ: + case NE: + /* + * Accept some things which are allowed with EQ and NE, + * but not with ordered comparisions. + */ + if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { + if (rn->tn_op == CON && rn->tn_val->v_quad == 0) + break; + } + if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) { + if (ln->tn_op == CON && ln->tn_val->v_quad == 0) + break; + } + /* FALLTHROUGH */ + case LT: + case GT: + case LE: + case GE: + if ((lt == PTR || rt == PTR) && lt != rt) { + if (isityp(lt) || isityp(rt)) { + /* illegal comb. of pointer and int., op %s */ + warning(123, mp->m_name); + } else { + incompat(op, lt, rt); + return (0); + } + } else if (lt == PTR && rt == PTR) { + ptrcmpok(op, ln, rn); + } + break; + case QUEST: + if (!issclt(lt)) { + /* first operand must have scalar type, op ? : */ + error(170); + return (0); + } + if (rn->tn_op != COLON) + lerror("typeok() 2"); + break; + case COLON: + + if (isatyp(lt) && isatyp(rt)) + break; + + if (lt == STRUCT && rt == STRUCT && ltp->t_str == rtp->t_str) + break; + if (lt == UNION && rt == UNION && ltp->t_str == rtp->t_str) + break; + + /* combination of any pointer and 0, 0L or (void *)0 is ok */ + if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { + if (rn->tn_op == CON && rn->tn_val->v_quad == 0) + break; + } + if (rt == PTR && ((lt == PTR && lst == VOID) || isityp(lt))) { + if (ln->tn_op == CON && ln->tn_val->v_quad == 0) + break; + } + + if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) { + /* illegal comb. of ptr. and int., op %s */ + warning(123, mp->m_name); + break; + } + + if (lt == VOID || rt == VOID) { + if (lt != VOID || rt != VOID) + /* incompatible types in conditional */ + warning(126); + break; + } + + if (lt == PTR && rt == PTR && ((lst == VOID && rst == FUNC) || + (lst == FUNC && rst == VOID))) { + /* (void *)0 handled above */ + if (sflag) + /* ANSI C forbids conv. of %s to %s, op %s */ + warning(305, "function pointer", "'void *'", + mp->m_name); + break; + } + + if (rt == PTR && lt == PTR) { + if (!eqtype(lstp, rstp, 1, 0, NULL)) + illptrc(mp, ltp, rtp); + break; + } + + /* incompatible types in conditional */ + error(126); + return (0); + + case ASSIGN: + case INIT: + case FARG: + case RETURN: + if (!asgntypok(op, arg, ln, rn)) + return (0); + goto assign; + case MULASS: + case DIVASS: + case MODASS: + goto assign; + case ADDASS: + case SUBASS: + /* operands have scalar types (checked above) */ + if ((lt == PTR && !isityp(rt)) || rt == PTR) { + incompat(op, lt, rt); + return (0); + } + goto assign; + case SHLASS: + goto assign; + case SHRASS: + if (pflag && !isutyp(lt) && !(tflag && isutyp(rt))) { + /* bitwise operation on s.v. possibly nonportabel */ + warning(117); + } + goto assign; + case ANDASS: + case XORASS: + case ORASS: + goto assign; + assign: + if (!ln->tn_lvalue) { + if (ln->tn_op == CVT && ln->tn_cast && + ln->tn_left->tn_op == LOAD) { + /* a cast does not yield an lvalue */ + error(163); + } + /* %soperand of %s must be lvalue */ + error(114, "left ", mp->m_name); + return (0); + } else if (ltp->t_const || ((lt == STRUCT || lt == UNION) && + conmemb(ltp))) { + /* %soperand of %s must be modifiable lvalue */ + if (!tflag) + warning(115, "left ", mp->m_name); + } + break; + case COMMA: + if (!modtab[ln->tn_op].m_sideeff) + nulleff(ln); + break; + /* LINTED (enumeration values not handled in switch) */ + } + + if (mp->m_badeop && + (ltp->t_isenum || (mp->m_binary && rtp->t_isenum))) { + chkbeop(op, ln, rn); + } else if (mp->m_enumop && (ltp->t_isenum && rtp->t_isenum)) { + chkeop2(op, arg, ln, rn); + } else if (mp->m_enumop && (ltp->t_isenum || rtp->t_isenum)) { + chkeop1(op, arg, ln, rn); + } + + return (1); +} + +static void +ptrcmpok(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + type_t *ltp, *rtp; + tspec_t lt, rt; + const char *lts, *rts; + + lt = (ltp = ln->tn_type)->t_subt->t_tspec; + rt = (rtp = rn->tn_type)->t_subt->t_tspec; + + if (lt == VOID || rt == VOID) { + if (sflag && (lt == FUNC || rt == FUNC)) { + /* (void *)0 already handled in typeok() */ + *(lt == FUNC ? <s : &rts) = "function pointer"; + *(lt == VOID ? <s : &rts) = "'void *'"; + /* ANSI C forbids comparision of %s with %s */ + warning(274, lts, rts); + } + return; + } + + if (!eqtype(ltp->t_subt, rtp->t_subt, 1, 0, NULL)) { + illptrc(&modtab[op], ltp, rtp); + return; + } + + if (lt == FUNC && rt == FUNC) { + if (sflag && op != EQ && op != NE) + /* ANSI C forbids ordered comp. of func ptr */ + warning(125); + } +} + +/* + * Checks type compatibility for ASSIGN, INIT, FARG and RETURN + * and prints warnings/errors if necessary. + * If the types are (almost) compatible, 1 is returned, otherwise 0. + */ +static int +asgntypok(op, arg, ln, rn) + op_t op; + int arg; + tnode_t *ln, *rn; +{ + tspec_t lt, rt, lst, rst; + type_t *ltp, *rtp, *lstp, *rstp; + mod_t *mp; + const char *lts, *rts; + + if ((lt = (ltp = ln->tn_type)->t_tspec) == PTR) + lst = (lstp = ltp->t_subt)->t_tspec; + if ((rt = (rtp = rn->tn_type)->t_tspec) == PTR) + rst = (rstp = rtp->t_subt)->t_tspec; + mp = &modtab[op]; + + if (isatyp(lt) && isatyp(rt)) + return (1); + + if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) + /* both are struct or union */ + return (ltp->t_str == rtp->t_str); + + /* 0, 0L and (void *)0 may be assigned to any pointer */ + if (lt == PTR && ((rt == PTR && rst == VOID) || isityp(rt))) { + if (rn->tn_op == CON && rn->tn_val->v_quad == 0) + return (1); + } + + if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID)) { + /* two pointers, at least one pointer to void */ + if (sflag && (lst == FUNC || rst == FUNC)) { + /* comb. of ptr to func and ptr to void */ + *(lst == FUNC ? <s : &rts) = "function pointer"; + *(lst == VOID ? <s : &rts) = "'void *'"; + switch (op) { + case INIT: + case RETURN: + /* ANSI C forbids conversion of %s to %s */ + warning(303, rts, lts); + break; + case FARG: + /* ANSI C forbids conv. of %s to %s, arg #%d */ + warning(304, rts, lts, arg); + break; + default: + /* ANSI C forbids conv. of %s to %s, op %s */ + warning(305, rts, lts, mp->m_name); + break; + } + } + } + + if (lt == PTR && rt == PTR && (lst == VOID || rst == VOID || + eqtype(lstp, rstp, 1, 0, NULL))) { + /* compatible pointer types (qualifiers ignored) */ + if (!tflag && + ((!lstp->t_const && rstp->t_const) || + (!lstp->t_volatile && rstp->t_volatile))) { + /* left side has not all qualifiers of right */ + switch (op) { + case INIT: + case RETURN: + /* incompatible pointer types */ + warning(182); + break; + case FARG: + /* argument has incompat. ptr. type, arg #%d */ + warning(153, arg); + break; + default: + /* operands have incompat. ptr. types, op %s */ + warning(128, mp->m_name); + break; + } + } + return (1); + } + + if ((lt == PTR && isityp(rt)) || (isityp(lt) && rt == PTR)) { + switch (op) { + case INIT: + case RETURN: + /* illegal combination of pointer and integer */ + warning(183); + break; + case FARG: + /* illegal comb. of ptr. and int., arg #%d */ + warning(154, arg); + break; + default: + /* illegal comb. of ptr. and int., op %s */ + warning(123, mp->m_name); + break; + } + return (1); + } + + if (lt == PTR && rt == PTR) { + switch (op) { + case INIT: + case RETURN: + illptrc(NULL, ltp, rtp); + break; + case FARG: + /* argument has incompatible pointer type, arg #%d */ + warning(153, arg); + break; + default: + illptrc(mp, ltp, rtp); + break; + } + return (1); + } + + switch (op) { + case INIT: + /* initialisation type mismatch */ + error(185); + break; + case RETURN: + /* return value type mismatch */ + error(211); + break; + case FARG: + /* argument is incompatible with prototype, arg #%d */ + warning(155, arg); + break; + default: + incompat(op, lt, rt); + break; + } + + return (0); +} + +/* + * Prints a warning if an operator, which should be senseless for an + * enum type, is applied to an enum type. + */ +static void +chkbeop(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + mod_t *mp; + + if (!eflag) + return; + + mp = &modtab[op]; + + if (!(ln->tn_type->t_isenum || + (mp->m_binary && rn->tn_type->t_isenum))) { + return; + } + + /* + * Enum as offset to a pointer is an exception (otherwise enums + * could not be used as array indizes). + */ + if (op == PLUS && + ((ln->tn_type->t_isenum && rn->tn_type->t_tspec == PTR) || + (rn->tn_type->t_isenum && ln->tn_type->t_tspec == PTR))) { + return; + } + + /* dubious operation on enum, op %s */ + warning(241, mp->m_name); + +} + +/* + * Prints a warning if an operator is applied to two different enum types. + */ +static void +chkeop2(op, arg, ln, rn) + op_t op; + int arg; + tnode_t *ln, *rn; +{ + mod_t *mp; + + mp = &modtab[op]; + + if (ln->tn_type->t_enum != rn->tn_type->t_enum) { + switch (op) { + case INIT: + /* enum type mismatch in initialisation */ + warning(210); + break; + case FARG: + /* enum type mismatch, arg #%d */ + warning(156, arg); + break; + case RETURN: + /* return value type mismatch */ + warning(211); + break; + default: + /* enum type mismatch, op %s */ + warning(130, mp->m_name); + break; + } +#if 0 + } else if (mp->m_comp && op != EQ && op != NE) { + if (eflag) + /* dubious comparisions of enums */ + warning(243, mp->m_name); +#endif + } +} + +/* + * Prints a warning if an operator has both enum end other integer + * types. + */ +static void +chkeop1(op, arg, ln, rn) + op_t op; + int arg; + tnode_t *ln, *rn; +{ + if (!eflag) + return; + + switch (op) { + case INIT: + /* + * Initializations with 0 should be allowed. Otherwise, + * we should complain about all uninitialized enums, + * consequently. + */ + if (!rn->tn_type->t_isenum && rn->tn_op == CON && + isityp(rn->tn_type->t_tspec) && rn->tn_val->v_quad == 0) { + return; + } + /* initialisation of '%s' with '%s' */ + warning(277, tyname(ln->tn_type), tyname(rn->tn_type)); + break; + case FARG: + /* combination of '%s' and '%s', arg #%d */ + warning(278, tyname(ln->tn_type), tyname(rn->tn_type), arg); + break; + case RETURN: + /* combination of '%s' and '%s' in return */ + warning(279, tyname(ln->tn_type), tyname(rn->tn_type)); + break; + default: + /* combination of '%s' and %s, op %s */ + warning(242, tyname(ln->tn_type), tyname(rn->tn_type), + modtab[op].m_name); + break; + } +} + +/* + * Build and initialize a new node. + */ +static tnode_t * +mktnode(op, type, ln, rn) + op_t op; + type_t *type; + tnode_t *ln, *rn; +{ + tnode_t *ntn; + tspec_t t; + + ntn = getnode(); + + ntn->tn_op = op; + ntn->tn_type = type; + ntn->tn_left = ln; + ntn->tn_right = rn; + + if (op == STAR || op == FSEL) { + if (ln->tn_type->t_tspec == PTR) { + t = ln->tn_type->t_subt->t_tspec; + if (t != FUNC && t != VOID) + ntn->tn_lvalue = 1; + } else { + lerror("mktnode() 2"); + } + } + + return (ntn); +} + +/* + * Performs usual conversion of operands to (unsigned) int. + * + * If tflag is set or the operand is a function argument with no + * type information (no prototype or variable # of args), convert + * float to double. + */ +tnode_t * +promote(op, farg, tn) + op_t op; + int farg; + tnode_t *tn; +{ + tspec_t t; + type_t *ntp; + int len; + + t = tn->tn_type->t_tspec; + + if (!isatyp(t)) + return (tn); + + if (!tflag) { + /* + * ANSI C requires that the result is always of type INT + * if INT can represent all possible values of the previous + * type. + */ + if (tn->tn_type->t_isfield) { + len = tn->tn_type->t_flen; + if (size(INT) > len) { + t = INT; + } else { + if (size(INT) != len) + lerror("promote() 1"); + if (isutyp(t)) { + t = UINT; + } else { + t = INT; + } + } + } else if (t == CHAR || t == UCHAR || t == SCHAR) { + t = (size(CHAR) < size(INT) || t != UCHAR) ? + INT : UINT; + } else if (t == SHORT || t == USHORT) { + t = (size(SHORT) < size(INT) || t == SHORT) ? + INT : UINT; + } else if (t == ENUM) { + t = INT; + } else if (farg && t == FLOAT) { + t = DOUBLE; + } + } else { + /* + * In traditional C, keep unsigned and promote FLOAT + * to DOUBLE. + */ + if (t == UCHAR || t == USHORT) { + t = UINT; + } else if (t == CHAR || t == SCHAR || t == SHORT) { + t = INT; + } else if (t == FLOAT) { + t = DOUBLE; + } else if (t == ENUM) { + t = INT; + } + } + + if (t != tn->tn_type->t_tspec) { + ntp = tduptyp(tn->tn_type); + ntp->t_tspec = t; + /* + * Keep t_isenum so we are later able to check compatibility + * of enum types. + */ + tn = convert(op, 0, ntp, tn); + } + + return (tn); +} + +/* + * Insert conversions which are necessary to give both operands the same + * type. This is done in different ways for traditional C and ANIS C. + */ +static void +balance(op, lnp, rnp) + op_t op; + tnode_t **lnp, **rnp; +{ + tspec_t lt, rt, t; + int i, u; + type_t *ntp; + static tspec_t tl[] = { + LDOUBLE, DOUBLE, FLOAT, UQUAD, QUAD, ULONG, LONG, UINT, INT, + }; + + lt = (*lnp)->tn_type->t_tspec; + rt = (*rnp)->tn_type->t_tspec; + + if (!isatyp(lt) || !isatyp(rt)) + return; + + if (!tflag) { + if (lt == rt) { + t = lt; + } else if (lt == LDOUBLE || rt == LDOUBLE) { + t = LDOUBLE; + } else if (lt == DOUBLE || rt == DOUBLE) { + t = DOUBLE; + } else if (lt == FLOAT || rt == FLOAT) { + t = FLOAT; + } else { + /* + * If type A has more bits than type B it should + * be able to hold all possible values of type B. + */ + if (size(lt) > size(rt)) { + t = lt; + } else if (size(lt) < size(rt)) { + t = rt; + } else { + for (i = 3; tl[i] != INT; i++) { + if (tl[i] == lt || tl[i] == rt) + break; + } + if ((isutyp(lt) || isutyp(rt)) && + !isutyp(tl[i])) { + i--; + } + t = tl[i]; + } + } + } else { + /* Keep unsigned in traditional C */ + u = isutyp(lt) || isutyp(rt); + for (i = 0; tl[i] != INT; i++) { + if (lt == tl[i] || rt == tl[i]) + break; + } + t = tl[i]; + if (u && isityp(t) && !isutyp(t)) + t = utyp(t); + } + + if (t != lt) { + ntp = tduptyp((*lnp)->tn_type); + ntp->t_tspec = t; + *lnp = convert(op, 0, ntp, *lnp); + } + if (t != rt) { + ntp = tduptyp((*rnp)->tn_type); + ntp->t_tspec = t; + *rnp = convert(op, 0, ntp, *rnp); + } +} + +/* + * Insert a conversion operator, which converts the type of the node + * to another given type. + * If op is FARG, arg is the number of the argument (used for warnings). + */ +tnode_t * +convert(op, arg, tp, tn) + op_t op; + int arg; + type_t *tp; + tnode_t *tn; +{ + tnode_t *ntn; + tspec_t nt, ot, ost; + + if (tn->tn_lvalue) + lerror("convert() 1"); + + nt = tp->t_tspec; + if ((ot = tn->tn_type->t_tspec) == PTR) + ost = tn->tn_type->t_subt->t_tspec; + + if (!tflag && !sflag && op == FARG) + ptconv(arg, nt, ot, tp, tn); + if (isityp(nt) && isityp(ot)) { + iiconv(op, arg, nt, ot, tp, tn); + } else if (nt == PTR && ((ot == PTR && ost == VOID) || isityp(ot)) && + tn->tn_op == CON && tn->tn_val->v_quad == 0) { + /* 0, 0L and (void *)0 may be assigned to any pointer. */ + } else if (isityp(nt) && ot == PTR) { + piconv(op, nt, tp, tn); + } else if (nt == PTR && ot == PTR) { + ppconv(op, tn, tp); + } + + ntn = getnode(); + ntn->tn_op = CVT; + ntn->tn_type = tp; + ntn->tn_cast = op == CVT; + if (tn->tn_op != CON || nt == VOID) { + ntn->tn_left = tn; + } else { + ntn->tn_op = CON; + ntn->tn_val = tgetblk(sizeof (val_t)); + cvtcon(op, arg, ntn->tn_type, ntn->tn_val, tn->tn_val); + } + + return (ntn); +} + +/* + * Print a warning if a prototype causes a type conversion that is + * different from what would happen to the same argument in the + * absence of a prototype. + * + * Errors/Warnings about illegal type combinations are already printed + * in asgntypok(). + */ +static void +ptconv(arg, nt, ot, tp, tn) + int arg; + tspec_t nt, ot; + type_t *tp; + tnode_t *tn; +{ + tnode_t *ptn; + + if (!isatyp(nt) || !isatyp(ot)) + return; + + /* + * If the type of the formal parameter is char/short, a warning + * would be useless, because functions declared the old style + * can't expect char/short arguments. + */ + if (nt == CHAR || nt == UCHAR || nt == SHORT || nt == USHORT) + return; + + /* get default promotion */ + ptn = promote(NOOP, 1, tn); + ot = ptn->tn_type->t_tspec; + + /* return if types are the same with and without prototype */ + if (nt == ot || (nt == ENUM && ot == INT)) + return; + + if (isftyp(nt) != isftyp(ot) || psize(nt) != psize(ot)) { + /* representation and/or width change */ + if (styp(nt) != SHORT || !isityp(ot) || psize(ot) > psize(INT)) + /* conversion to '%s' due to prototype, arg #%d */ + warning(259, tyname(tp), arg); + } else if (hflag) { + /* + * they differ in sign or base type (char, short, int, + * long, long long, float, double, long double) + * + * if they differ only in sign and the argument is a constant + * and the msb of the argument is not set, print no warning + */ + if (ptn->tn_op == CON && isityp(nt) && styp(nt) == styp(ot) && + msb(ptn->tn_val->v_quad, ot, -1) == 0) { + /* ok */ + } else { + /* conversion to '%s' due to prototype, arg #%d */ + warning(259, tyname(tp), arg); + } + } +} + +/* + * Print warnings for conversions of integer types which my cause + * problems. + */ +/* ARGSUSED */ +static void +iiconv(op, arg, nt, ot, tp, tn) + op_t op; + int arg; + tspec_t nt, ot; + type_t *tp; + tnode_t *tn; +{ + if (tn->tn_op == CON) + return; + + if (op == CVT) + return; + +#if 0 + if (psize(nt) > psize(ot) && isutyp(nt) != isutyp(ot)) { + /* conversion to %s may sign-extend incorrectly (, arg #%d) */ + if (aflag && pflag) { + if (op == FARG) { + warning(297, tyname(tp), arg); + } else { + warning(131, tyname(tp)); + } + } + } +#endif + + if (psize(nt) < psize(ot) && + (ot == LONG || ot == ULONG || ot == QUAD || ot == UQUAD || + aflag > 1)) { + /* conversion from '%s' may lose accuracy */ + if (aflag) { + if (op == FARG) { + warning(298, tyname(tn->tn_type), arg); + } else { + warning(132, tyname(tn->tn_type)); + } + } + } +} + +/* + * Print warnings for dubious conversions of pointer to integer. + */ +static void +piconv(op, nt, tp, tn) + op_t op; + tspec_t nt; + type_t *tp; + tnode_t *tn; +{ + if (tn->tn_op == CON) + return; + + if (op != CVT) { + /* We got already an error. */ + return; + } + + if (psize(nt) < psize(PTR)) { + if (pflag && size(nt) >= size(PTR)) { + /* conv. of pointer to %s may lose bits */ + warning(134, tyname(tp)); + } else { + /* conv. of pointer to %s loses bits */ + warning(133, tyname(tp)); + } + } +} + +/* + * Print warnings for questionable pointer conversions. + */ +static void +ppconv(op, tn, tp) + op_t op; + tnode_t *tn; + type_t *tp; +{ + tspec_t nt, ot; + const char *nts, *ots; + + /* + * We got already an error (pointers of different types + * without a cast) or we will not get a warning. + */ + if (op != CVT) + return; + + nt = tp->t_subt->t_tspec; + ot = tn->tn_type->t_subt->t_tspec; + + if (nt == VOID || ot == VOID) { + if (sflag && (nt == FUNC || ot == FUNC)) { + /* (void *)0 already handled in convert() */ + *(nt == FUNC ? &nts : &ots) = "function pointer"; + *(nt == VOID ? &nts : &ots) = "'void *'"; + /* ANSI C forbids conversion of %s to %s */ + warning(303, ots, nts); + } + return; + } else if (nt == FUNC && ot == FUNC) { + return; + } else if (nt == FUNC || ot == FUNC) { + /* questionable conversion of function pointer */ + warning(229); + return; + } + + if (getbound(tp->t_subt) > getbound(tn->tn_type->t_subt)) { + if (hflag) + /* possible pointer alignment problem */ + warning(135); + } + if (((nt == STRUCT || nt == UNION) && + tp->t_subt->t_str != tn->tn_type->t_subt->t_str) || + psize(nt) != psize(ot)) { + if (cflag) { + /* pointer casts may be troublesome */ + warning(247); + } + } +} + +/* + * Converts a typed constant in a constant of another type. + * + * op operator which requires conversion + * arg if op is FARG, # of argument + * tp type in which to convert the constant + * nv new constant + * v old constant + */ +void +cvtcon(op, arg, tp, nv, v) + op_t op; + int arg; + type_t *tp; + val_t *nv, *v; +{ + tspec_t ot, nt; + ldbl_t max, min; + int sz, rchk; + quad_t xmask, xmsk1; + int osz, nsz; + + ot = v->v_tspec; + nt = nv->v_tspec = tp->t_tspec; + rchk = 0; + + if (ot == FLOAT || ot == DOUBLE || ot == LDOUBLE) { + switch (nt) { + case CHAR: + max = CHAR_MAX; min = CHAR_MIN; break; + case UCHAR: + max = UCHAR_MAX; min = 0; break; + case SCHAR: + max = SCHAR_MAX; min = SCHAR_MIN; break; + case SHORT: + max = SHRT_MAX; min = SHRT_MIN; break; + case USHORT: + max = USHRT_MAX; min = 0; break; + case ENUM: + case INT: + max = INT_MAX; min = INT_MIN; break; + case UINT: + max = (u_int)UINT_MAX; min = 0; break; + case LONG: + max = LONG_MAX; min = LONG_MIN; break; + case ULONG: + max = (u_long)ULONG_MAX; min = 0; break; + case QUAD: + max = QUAD_MAX; min = QUAD_MIN; break; + case UQUAD: + max = (u_quad_t)UQUAD_MAX; min = 0; break; + case FLOAT: + max = FLT_MAX; min = -FLT_MAX; break; + case DOUBLE: + max = DBL_MAX; min = -DBL_MAX; break; + case PTR: + /* Got already an error because of float --> ptr */ + case LDOUBLE: + max = LDBL_MAX; min = -LDBL_MAX; break; + default: + lerror("cvtcon() 1"); + } + if (v->v_ldbl > max || v->v_ldbl < min) { + if (nt == LDOUBLE) + lerror("cvtcon() 2"); + if (op == FARG) { + /* conv. of %s to %s is out of rng., arg #%d */ + warning(295, tyname(gettyp(ot)), tyname(tp), + arg); + } else { + /* conversion of %s to %s is out of range */ + warning(119, tyname(gettyp(ot)), tyname(tp)); + } + v->v_ldbl = v->v_ldbl > 0 ? max : min; + } + if (nt == FLOAT) { + nv->v_ldbl = (float)v->v_ldbl; + } else if (nt == DOUBLE) { + nv->v_ldbl = (double)v->v_ldbl; + } else if (nt == LDOUBLE) { + nv->v_ldbl = v->v_ldbl; + } else { + nv->v_quad = (nt == PTR || isutyp(nt)) ? + (u_quad_t)v->v_ldbl : (quad_t)v->v_ldbl; + } + } else { + if (nt == FLOAT) { + nv->v_ldbl = (ot == PTR || isutyp(ot)) ? + (float)(u_quad_t)v->v_quad : (float)v->v_quad; + } else if (nt == DOUBLE) { + nv->v_ldbl = (ot == PTR || isutyp(ot)) ? + (double)(u_quad_t)v->v_quad : (double)v->v_quad; + } else if (nt == LDOUBLE) { + nv->v_ldbl = (ot == PTR || isutyp(ot)) ? + (ldbl_t)(u_quad_t)v->v_quad : (ldbl_t)v->v_quad; + } else { + rchk = 1; /* Check for lost precision. */ + nv->v_quad = v->v_quad; + } + } + + if (v->v_ansiu && isftyp(nt)) { + /* ANSI C treats constant as unsigned */ + warning(157); + v->v_ansiu = 0; + } else if (v->v_ansiu && (isityp(nt) && !isutyp(nt) && + psize(nt) > psize(ot))) { + /* ANSI C treats constant as unsigned */ + warning(157); + v->v_ansiu = 0; + } + + if (nt != FLOAT && nt != DOUBLE && nt != LDOUBLE) { + sz = tp->t_isfield ? tp->t_flen : size(nt); + nv->v_quad = xsign(nv->v_quad, nt, sz); + } + + if (rchk && op != CVT) { + osz = size(ot); + nsz = tp->t_isfield ? tp->t_flen : size(nt); + xmask = qlmasks[nsz] ^ qlmasks[osz]; + xmsk1 = qlmasks[nsz] ^ qlmasks[osz - 1]; + /* + * For bitwise operations we are not interested in the + * value, but in the bits itself. + */ + if (op == ORASS || op == OR || op == XOR) { + /* + * Print a warning if bits which were set are + * lost due to the conversion. + * This can happen with operator ORASS only. + */ + if (nsz < osz && (v->v_quad & xmask) != 0) { + /* constant truncated by conv., op %s */ + warning(306, modtab[op].m_name); + } + } else if (op == ANDASS || op == AND) { + /* + * Print a warning if additional bits are not all 1 + * and the most significant bit of the old value is 1, + * or if at least one (but not all) removed bit was 0. + */ + if (nsz > osz && + (nv->v_quad & qbmasks[osz - 1]) != 0 && + (nv->v_quad & xmask) != xmask) { + /* + * extra bits set to 0 in conversion + * of '%s' to '%s', op %s + */ + warning(309, tyname(gettyp(ot)), + tyname(tp), modtab[op].m_name); + } else if (nsz < osz && + (v->v_quad & xmask) != xmask && + (v->v_quad & xmask) != 0) { + /* const. truncated by conv., op %s */ + warning(306, modtab[op].m_name); + } + } else if ((nt != PTR && isutyp(nt)) && + (ot != PTR && !isutyp(ot)) && v->v_quad < 0) { + if (op == ASSIGN) { + /* assignment of negative constant to ... */ + warning(164); + } else if (op == INIT) { + /* initialisation of unsigned with neg. ... */ + warning(221); + } else if (op == FARG) { + /* conversion of neg. const. to ..., arg #%d */ + warning(296, arg); + } else if (modtab[op].m_comp) { + /* we get this warning already in chkcomp() */ + } else { + /* conversion of negative constant to ... */ + warning(222); + } + } else if (nv->v_quad != v->v_quad && nsz <= osz && + (v->v_quad & xmask) != 0 && + (isutyp(ot) || (v->v_quad & xmsk1) != xmsk1)) { + /* + * Loss of significant bit(s). All truncated bits + * of unsigned types or all truncated bits plus the + * msb of the target for signed types are considered + * to be significant bits. Loss of significant bits + * means that at least on of the bits was set in an + * unsigned type or that at least one, but not all of + * the bits was set in an signed type. + * Loss of significant bits means that it is not + * possible, also not with necessary casts, to convert + * back to the original type. A example for a + * necessary cast is: + * char c; int i; c = 128; + * i = c; ** yields -128 ** + * i = (unsigned char)c; ** yields 128 ** + */ + if (op == ASSIGN && tp->t_isfield) { + /* precision lost in bit-field assignment */ + warning(166); + } else if (op == ASSIGN) { + /* constant truncated by assignment */ + warning(165); + } else if (op == INIT && tp->t_isfield) { + /* bit-field initializer does not fit */ + warning(180); + } else if (op == INIT) { + /* initializer does not fit */ + warning(178); + } else if (op == CASE) { + /* case label affected by conversion */ + warning(196); + } else if (op == FARG) { + /* conv. of %s to %s is out of rng., arg #%d */ + warning(295, tyname(gettyp(ot)), tyname(tp), + arg); + } else { + /* conversion of %s to %s is out of range */ + warning(119, tyname(gettyp(ot)), tyname(tp)); + } + } else if (nv->v_quad != v->v_quad) { + if (op == ASSIGN && tp->t_isfield) { + /* precision lost in bit-field assignment */ + warning(166); + } else if (op == INIT && tp->t_isfield) { + /* bit-field initializer out of range */ + warning(11); + } else if (op == CASE) { + /* case label affected by conversion */ + warning(196); + } else if (op == FARG) { + /* conv. of %s to %s is out of rng., arg #%d */ + warning(295, tyname(gettyp(ot)), tyname(tp), + arg); + } else { + /* conversion of %s to %s is out of range */ + warning(119, tyname(gettyp(ot)), tyname(tp)); + } + } + } +} + +/* + * Called if incompatible types were detected. + * Prints a appropriate warning. + */ +static void +incompat(op, lt, rt) + op_t op; + tspec_t lt, rt; +{ + mod_t *mp; + + mp = &modtab[op]; + + if (lt == VOID || (mp->m_binary && rt == VOID)) { + /* void type illegal in expression */ + error(109); + } else if (op == ASSIGN) { + if ((lt == STRUCT || lt == UNION) && + (rt == STRUCT || rt == UNION)) { + /* assignment of different structures */ + error(240); + } else { + /* assignment type mismatch */ + error(171); + } + } else if (mp->m_binary) { + /* operands of %s have incompatible types */ + error(107, mp->m_name); + } else { + /* operand of %s has incompatible type */ + error(108, mp->m_name); + } +} + +/* + * Called if incompatible pointer types are detected. + * Print an appropriate warning. + */ +static void +illptrc(mp, ltp, rtp) + mod_t *mp; + type_t *ltp, *rtp; +{ + tspec_t lt, rt; + + if (ltp->t_tspec != PTR || rtp->t_tspec != PTR) + lerror("illptrc() 1"); + + lt = ltp->t_subt->t_tspec; + rt = rtp->t_subt->t_tspec; + + if ((lt == STRUCT || lt == UNION) && (rt == STRUCT || rt == UNION)) { + if (mp == NULL) { + /* illegal structure pointer combination */ + warning(244); + } else { + /* illegal structure pointer combination, op %s */ + warning(245, mp->m_name); + } + } else { + if (mp == NULL) { + /* illegal pointer combination */ + warning(184); + } else { + /* illegal pointer combination, op %s */ + warning(124, mp->m_name); + } + } +} + +/* + * Make sure type (*tpp)->t_subt has at least the qualifiers + * of tp1->t_subt and tp2->t_subt. + */ +static void +mrgqual(tpp, tp1, tp2) + type_t **tpp, *tp1, *tp2; +{ + if ((*tpp)->t_tspec != PTR || + tp1->t_tspec != PTR || tp2->t_tspec != PTR) { + lerror("mrgqual()"); + } + + if ((*tpp)->t_subt->t_const == + (tp1->t_subt->t_const | tp2->t_subt->t_const) && + (*tpp)->t_subt->t_volatile == + (tp1->t_subt->t_volatile | tp2->t_subt->t_volatile)) { + return; + } + + *tpp = tduptyp(*tpp); + (*tpp)->t_subt = tduptyp((*tpp)->t_subt); + (*tpp)->t_subt->t_const = + tp1->t_subt->t_const | tp2->t_subt->t_const; + (*tpp)->t_subt->t_volatile = + tp1->t_subt->t_volatile | tp2->t_subt->t_volatile; +} + +/* + * Returns 1 if the given structure or union has a constant member + * (maybe recursively). + */ +static int +conmemb(tp) + type_t *tp; +{ + sym_t *m; + tspec_t t; + + if ((t = tp->t_tspec) != STRUCT && t != UNION) + lerror("conmemb()"); + for (m = tp->t_str->memb; m != NULL; m = m->s_nxt) { + tp = m->s_type; + if (tp->t_const) + return (1); + if ((t = tp->t_tspec) == STRUCT || t == UNION) { + if (conmemb(m->s_type)) + return (1); + } + } + return (0); +} + +const char * +tyname(tp) + type_t *tp; +{ + tspec_t t; + const char *s; + + if ((t = tp->t_tspec) == INT && tp->t_isenum) + t = ENUM; + + switch (t) { + case CHAR: s = "char"; break; + case UCHAR: s = "unsigned char"; break; + case SCHAR: s = "signed char"; break; + case SHORT: s = "short"; break; + case USHORT: s = "unsigned short"; break; + case INT: s = "int"; break; + case UINT: s = "unsigned int"; break; + case LONG: s = "long"; break; + case ULONG: s = "unsigned long"; break; + case QUAD: s = "long long"; break; + case UQUAD: s = "unsigned long long"; break; + case FLOAT: s = "float"; break; + case DOUBLE: s = "double"; break; + case LDOUBLE: s = "long double"; break; + case PTR: s = "pointer"; break; + case ENUM: s = "enum"; break; + case STRUCT: s = "struct"; break; + case UNION: s = "union"; break; + case FUNC: s = "function"; break; + case ARRAY: s = "array"; break; + default: + lerror("tyname()"); + } + return (s); +} + +/* + * Create a new node for one of the operators POINT and ARROW. + */ +static tnode_t * +bldstr(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + tnode_t *ntn, *ctn; + int nolval; + + if (rn->tn_op != NAME) + lerror("bldstr() 1"); + if (rn->tn_sym->s_value.v_tspec != INT) + lerror("bldstr() 2"); + if (rn->tn_sym->s_scl != MOS && rn->tn_sym->s_scl != MOU) + lerror("bldstr() 3"); + + /* + * Remember if the left operand is an lvalue (structure members + * are lvalues if and only if the structure itself is an lvalue). + */ + nolval = op == POINT && !ln->tn_lvalue; + + if (op == POINT) { + ln = bldamper(ln, 1); + } else if (ln->tn_type->t_tspec != PTR) { + if (!tflag || !isityp(ln->tn_type->t_tspec)) + lerror("bldstr() 4"); + ln = convert(NOOP, 0, tincref(gettyp(VOID), PTR), ln); + } + +#if PTRDIFF_IS_LONG + ctn = getinode(LONG, rn->tn_sym->s_value.v_quad / CHAR_BIT); +#else + ctn = getinode(INT, rn->tn_sym->s_value.v_quad / CHAR_BIT); +#endif + + ntn = mktnode(PLUS, tincref(rn->tn_type, PTR), ln, ctn); + if (ln->tn_op == CON) + ntn = fold(ntn); + + if (rn->tn_type->t_isfield) { + ntn = mktnode(FSEL, ntn->tn_type->t_subt, ntn, NULL); + } else { + ntn = mktnode(STAR, ntn->tn_type->t_subt, ntn, NULL); + } + + if (nolval) + ntn->tn_lvalue = 0; + + return (ntn); +} + +/* + * Create a node for INCAFT, INCBEF, DECAFT and DECBEF. + */ +static tnode_t * +bldincdec(op, ln) + op_t op; + tnode_t *ln; +{ + tnode_t *cn, *ntn; + + if (ln == NULL) + lerror("bldincdec() 1"); + + if (ln->tn_type->t_tspec == PTR) { + cn = plength(ln->tn_type); + } else { + cn = getinode(INT, (quad_t)1); + } + ntn = mktnode(op, ln->tn_type, ln, cn); + + return (ntn); +} + +/* + * Create a tree node for the & operator + */ +static tnode_t * +bldamper(tn, noign) + tnode_t *tn; + int noign; +{ + tnode_t *ntn; + tspec_t t; + + if (!noign && ((t = tn->tn_type->t_tspec) == ARRAY || t == FUNC)) { + /* & before array or function: ignored */ + if (tflag) + warning(127); + return (tn); + } + + /* eliminate &* */ + if (tn->tn_op == STAR && + tn->tn_left->tn_type->t_tspec == PTR && + tn->tn_left->tn_type->t_subt == tn->tn_type) { + return (tn->tn_left); + } + + ntn = mktnode(AMPER, tincref(tn->tn_type, PTR), tn, NULL); + + return (ntn); +} + +/* + * Create a node for operators PLUS and MINUS. + */ +static tnode_t * +bldplmi(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + tnode_t *ntn, *ctn; + type_t *tp; + + /* If pointer and integer, then pointer to the lhs. */ + if (rn->tn_type->t_tspec == PTR && isityp(ln->tn_type->t_tspec)) { + ntn = ln; + ln = rn; + rn = ntn; + } + + if (ln->tn_type->t_tspec == PTR && rn->tn_type->t_tspec != PTR) { + + if (!isityp(rn->tn_type->t_tspec)) + lerror("bldplmi() 1"); + + ctn = plength(ln->tn_type); + if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec) + rn = convert(NOOP, 0, ctn->tn_type, rn); + rn = mktnode(MULT, rn->tn_type, rn, ctn); + if (rn->tn_left->tn_op == CON) + rn = fold(rn); + ntn = mktnode(op, ln->tn_type, ln, rn); + + } else if (rn->tn_type->t_tspec == PTR) { + + if (ln->tn_type->t_tspec != PTR || op != MINUS) + lerror("bldplmi() 2"); +#if PTRDIFF_IS_LONG + tp = gettyp(LONG); +#else + tp = gettyp(INT); +#endif + ntn = mktnode(op, tp, ln, rn); + if (ln->tn_op == CON && rn->tn_op == CON) + ntn = fold(ntn); + ctn = plength(ln->tn_type); + balance(NOOP, &ntn, &ctn); + ntn = mktnode(DIV, tp, ntn, ctn); + + } else { + + ntn = mktnode(op, ln->tn_type, ln, rn); + + } + return (ntn); +} + +/* + * Create a node for operators SHL and SHR. + */ +static tnode_t * +bldshft(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + tspec_t t; + tnode_t *ntn; + + if ((t = rn->tn_type->t_tspec) != INT && t != UINT) + rn = convert(CVT, 0, gettyp(INT), rn); + ntn = mktnode(op, ln->tn_type, ln, rn); + return (ntn); +} + +/* + * Create a node for COLON. + */ +static tnode_t * +bldcol(ln, rn) + tnode_t *ln, *rn; +{ + tspec_t lt, rt, pdt; + type_t *rtp; + tnode_t *ntn; + + lt = ln->tn_type->t_tspec; + rt = rn->tn_type->t_tspec; +#if PTRDIFF_IS_LONG + pdt = LONG; +#else + pdt = INT; +#endif + + /* + * Arithmetic types are balanced, all other type combinations + * still need to be handled. + */ + if (isatyp(lt) && isatyp(rt)) { + rtp = ln->tn_type; + } else if (lt == VOID || rt == VOID) { + rtp = gettyp(VOID); + } else if (lt == STRUCT || lt == UNION) { + /* Both types must be identical. */ + if (rt != STRUCT && rt != UNION) + lerror("bldcol() 1"); + if (ln->tn_type->t_str != rn->tn_type->t_str) + lerror("bldcol() 2"); + if (incompl(ln->tn_type)) { + /* unknown operand size, op %s */ + error(138, modtab[COLON].m_name); + return (NULL); + } + rtp = ln->tn_type; + } else if (lt == PTR && isityp(rt)) { + if (rt != pdt) { + rn = convert(NOOP, 0, gettyp(pdt), rn); + rt = pdt; + } + rtp = ln->tn_type; + } else if (rt == PTR && isityp(lt)) { + if (lt != pdt) { + ln = convert(NOOP, 0, gettyp(pdt), ln); + lt = pdt; + } + rtp = rn->tn_type; + } else if (lt == PTR && ln->tn_type->t_subt->t_tspec == VOID) { + if (rt != PTR) + lerror("bldcol() 4"); + rtp = ln->tn_type; + mrgqual(&rtp, ln->tn_type, rn->tn_type); + } else if (rt == PTR && rn->tn_type->t_subt->t_tspec == VOID) { + if (lt != PTR) + lerror("bldcol() 5"); + rtp = rn->tn_type; + mrgqual(&rtp, ln->tn_type, rn->tn_type); + } else { + if (lt != PTR || rt != PTR) + lerror("bldcol() 6"); + /* + * XXX For now we simply take the left type. This is + * probably wrong, if one type contains a functionprototype + * and the other one, at the same place, only an old style + * declaration. + */ + rtp = ln->tn_type; + mrgqual(&rtp, ln->tn_type, rn->tn_type); + } + + ntn = mktnode(COLON, rtp, ln, rn); + + return (ntn); +} + +/* + * Create a node for an assignment operator (both = and op= ). + */ +static tnode_t * +bldasgn(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + tspec_t lt, rt; + tnode_t *ntn, *ctn; + + if (ln == NULL || rn == NULL) + lerror("bldasgn() 1"); + + lt = ln->tn_type->t_tspec; + rt = rn->tn_type->t_tspec; + + if ((op == ADDASS || op == SUBASS) && lt == PTR) { + if (!isityp(rt)) + lerror("bldasgn() 2"); + ctn = plength(ln->tn_type); + if (rn->tn_type->t_tspec != ctn->tn_type->t_tspec) + rn = convert(NOOP, 0, ctn->tn_type, rn); + rn = mktnode(MULT, rn->tn_type, rn, ctn); + if (rn->tn_left->tn_op == CON) + rn = fold(rn); + } + + if ((op == ASSIGN || op == RETURN) && (lt == STRUCT || rt == STRUCT)) { + if (rt != lt || ln->tn_type->t_str != rn->tn_type->t_str) + lerror("bldasgn() 3"); + if (incompl(ln->tn_type)) { + if (op == RETURN) { + /* cannot return incomplete type */ + error(212); + } else { + /* unknown operand size, op %s */ + error(138, modtab[op].m_name); + } + return (NULL); + } + } + + if (op == SHLASS || op == SHRASS) { + if (rt != INT) { + rn = convert(NOOP, 0, gettyp(INT), rn); + rt = INT; + } + } else { + if (op == ASSIGN || lt != PTR) { + if (lt != rt || + (ln->tn_type->t_isfield && rn->tn_op == CON)) { + rn = convert(op, 0, ln->tn_type, rn); + rt = lt; + } + } + } + + ntn = mktnode(op, ln->tn_type, ln, rn); + + return (ntn); +} + +/* + * Get length of type tp->t_subt. + */ +static tnode_t * +plength(tp) + type_t *tp; +{ + int elem, elsz; + tspec_t st; + + if (tp->t_tspec != PTR) + lerror("plength() 1"); + tp = tp->t_subt; + + elem = 1; + elsz = 0; + + while (tp->t_tspec == ARRAY) { + elem *= tp->t_dim; + tp = tp->t_subt; + } + + switch (tp->t_tspec) { + case FUNC: + /* pointer to function is not allowed here */ + error(110); + break; + case VOID: + /* cannot do pointer arithmetic on operand of ... */ + (void)gnuism(136); + break; + case STRUCT: + case UNION: + if ((elsz = tp->t_str->size) == 0) + /* cannot do pointer arithmetic on operand of ... */ + error(136); + break; + case ENUM: + if (incompl(tp)) { + /* cannot do pointer arithmetic on operand of ... */ + warning(136); + } + /* FALLTHROUGH */ + default: + if ((elsz = size(tp->t_tspec)) == 0) { + /* cannot do pointer arithmetic on operand of ... */ + error(136); + } else if (elsz == -1) { + lerror("plength() 2"); + } + break; + } + + if (elem == 0 && elsz != 0) { + /* cannot do pointer arithmetic on operand of ... */ + error(136); + } + + if (elsz == 0) + elsz = CHAR_BIT; + +#if PTRDIFF_IS_LONG + st = LONG; +#else + st = INT; +#endif + + return (getinode(st, (quad_t)(elem * elsz / CHAR_BIT))); +} + +#ifdef XXX_BROKEN_GCC +static int +quad_t_eq(x, y) + quad_t x, y; +{ + return (x == y); +} + +static int +u_quad_t_eq(x, y) + u_quad_t x, y; +{ + return (x == y); +} +#endif + +/* + * Do only as much as necessary to compute constant expressions. + * Called only if the operator allows folding and (both) operands + * are constants. + */ +static tnode_t * +fold(tn) + tnode_t *tn; +{ + val_t *v; + tspec_t t; + int utyp, ovfl; + quad_t sl, sr, q, mask; + u_quad_t ul, ur; + tnode_t *cn; + + v = xcalloc(1, sizeof (val_t)); + v->v_tspec = t = tn->tn_type->t_tspec; + + utyp = t == PTR || isutyp(t); + ul = sl = tn->tn_left->tn_val->v_quad; + if (modtab[tn->tn_op].m_binary) + ur = sr = tn->tn_right->tn_val->v_quad; + + ovfl = 0; + + switch (tn->tn_op) { + case UPLUS: + q = sl; + break; + case UMINUS: + q = -sl; + if (msb(q, t, -1) == msb(sl, t, -1)) + ovfl = 1; + break; + case COMPL: + q = ~sl; + break; + case MULT: + q = utyp ? ul * ur : sl * sr; + if (msb(q, t, -1) != (msb(sl, t, -1) ^ msb(sr, t, -1))) + ovfl = 1; + break; + case DIV: + if (sr == 0) { + /* division by 0 */ + error(139); + q = utyp ? UQUAD_MAX : QUAD_MAX; + } else { + q = utyp ? ul / ur : sl / sr; + } + break; + case MOD: + if (sr == 0) { + /* modulus by 0 */ + error(140); + q = 0; + } else { + q = utyp ? ul % ur : sl % sr; + } + break; + case PLUS: + q = utyp ? ul + ur : sl + sr; + if (msb(sl, t, -1) != 0 && msb(sr, t, -1) != 0) { + if (msb(q, t, -1) == 0) + ovfl = 1; + } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) == 0) { + if (msb(q, t, -1) != 0) + ovfl = 1; + } + break; + case MINUS: + q = utyp ? ul - ur : sl - sr; + if (msb(sl, t, -1) != 0 && msb(sr, t, -1) == 0) { + if (msb(q, t, -1) == 0) + ovfl = 1; + } else if (msb(sl, t, -1) == 0 && msb(sr, t, -1) != 0) { + if (msb(q, t, -1) != 0) + ovfl = 1; + } + break; + case SHL: + q = utyp ? ul << sr : sl << sr; + break; + case SHR: + /* + * The sign must be explizitly extended because + * shifts of signed values are implementation dependent. + */ + q = ul >> sr; + q = xsign(q, t, size(t) - (int)sr); + break; + case LT: + q = utyp ? ul < ur : sl < sr; + break; + case LE: + q = utyp ? ul <= ur : sl <= sr; + break; + case GE: + q = utyp ? ul >= ur : sl >= sr; + break; + case GT: + q = utyp ? ul > ur : sl > sr; + break; + case EQ: +#ifdef XXX_BROKEN_GCC + q = utyp ? u_quad_t_eq(ul, ur) : quad_t_eq(sl, sr); +#else + q = utyp ? ul == ur : sl == sr; +#endif + break; + case NE: + q = utyp ? ul != ur : sl != sr; + break; + case AND: + q = utyp ? ul & ur : sl & sr; + break; + case XOR: + q = utyp ? ul ^ ur : sl ^ sr; + break; + case OR: + q = utyp ? ul | ur : sl | sr; + break; + default: + lerror("fold() 5"); + } + + mask = qlmasks[size(t)]; + + /* XXX does not work for quads. */ + if (ovfl || ((q | mask) != ~(u_quad_t)0 && (q & ~mask) != 0)) { + if (hflag) + /* integer overflow detected, op %s */ + warning(141, modtab[tn->tn_op].m_name); + } + + v->v_quad = xsign(q, t, -1); + + cn = getcnode(tn->tn_type, v); + + return (cn); +} + +#ifdef XXX_BROKEN_GCC +int +ldbl_t_neq(x, y) + ldbl_t x, y; +{ + return (x != y); +} +#endif + +/* + * Same for operators whose operands are compared with 0 (test context). + */ +static tnode_t * +foldtst(tn) + tnode_t *tn; +{ + int l, r; + val_t *v; + + v = xcalloc(1, sizeof (val_t)); + v->v_tspec = tn->tn_type->t_tspec; + if (tn->tn_type->t_tspec != INT) + lerror("foldtst() 1"); + + if (isftyp(tn->tn_left->tn_type->t_tspec)) { +#ifdef XXX_BROKEN_GCC + l = ldbl_t_neq(tn->tn_left->tn_val->v_ldbl, 0.0); +#else + l = tn->tn_left->tn_val->v_ldbl != 0.0; +#endif + } else { + l = tn->tn_left->tn_val->v_quad != 0; + } + + if (modtab[tn->tn_op].m_binary) { + if (isftyp(tn->tn_right->tn_type->t_tspec)) { +#ifdef XXX_BROKEN_GCC + r = ldbl_t_neq(tn->tn_right->tn_val->v_ldbl, 0.0); +#else + r = tn->tn_right->tn_val->v_ldbl != 0.0; +#endif + } else { + r = tn->tn_right->tn_val->v_quad != 0; + } + } + + switch (tn->tn_op) { + case NOT: + if (hflag) + /* constant argument to NOT */ + warning(239); + v->v_quad = !l; + break; + case LOGAND: + v->v_quad = l && r; + break; + case LOGOR: + v->v_quad = l || r; + break; + default: + lerror("foldtst() 1"); + } + + return (getcnode(tn->tn_type, v)); +} + +/* + * Same for operands with floating point type. + */ +static tnode_t * +foldflt(tn) + tnode_t *tn; +{ + val_t *v; + tspec_t t; + ldbl_t l, r; + + v = xcalloc(1, sizeof (val_t)); + v->v_tspec = t = tn->tn_type->t_tspec; + + if (!isftyp(t)) + lerror("foldflt() 1"); + + if (t != tn->tn_left->tn_type->t_tspec) + lerror("foldflt() 2"); + if (modtab[tn->tn_op].m_binary && t != tn->tn_right->tn_type->t_tspec) + lerror("foldflt() 3"); + + l = tn->tn_left->tn_val->v_ldbl; + if (modtab[tn->tn_op].m_binary) + r = tn->tn_right->tn_val->v_ldbl; + + switch (tn->tn_op) { + case UPLUS: + v->v_ldbl = l; + break; + case UMINUS: + v->v_ldbl = -l; + break; + case MULT: + v->v_ldbl = l * r; + break; + case DIV: + if (r == 0.0) { + /* division by 0 */ + error(139); + if (t == FLOAT) { + v->v_ldbl = l < 0 ? -FLT_MAX : FLT_MAX; + } else if (t == DOUBLE) { + v->v_ldbl = l < 0 ? -DBL_MAX : DBL_MAX; + } else { + v->v_ldbl = l < 0 ? -LDBL_MAX : LDBL_MAX; + } + } else { + v->v_ldbl = l / r; + } + break; + case PLUS: + v->v_ldbl = l + r; + break; + case MINUS: + v->v_ldbl = l - r; + break; + case LT: + v->v_quad = l < r; + break; + case LE: + v->v_quad = l <= r; + break; + case GE: + v->v_quad = l >= r; + break; + case GT: + v->v_quad = l > r; + break; + case EQ: + v->v_quad = l == r; + break; + case NE: + v->v_quad = l != r; + break; + default: + lerror("foldflt() 4"); + } + + if (isnan((double)v->v_ldbl)) + lerror("foldflt() 5"); + if (isinf((double)v->v_ldbl) || + (t == FLOAT && + (v->v_ldbl > FLT_MAX || v->v_ldbl < -FLT_MAX)) || + (t == DOUBLE && + (v->v_ldbl > DBL_MAX || v->v_ldbl < -DBL_MAX))) { + /* floating point overflow detected, op %s */ + warning(142, modtab[tn->tn_op].m_name); + if (t == FLOAT) { + v->v_ldbl = v->v_ldbl < 0 ? -FLT_MAX : FLT_MAX; + } else if (t == DOUBLE) { + v->v_ldbl = v->v_ldbl < 0 ? -DBL_MAX : DBL_MAX; + } else { + v->v_ldbl = v->v_ldbl < 0 ? -LDBL_MAX: LDBL_MAX; + } + } + + return (getcnode(tn->tn_type, v)); +} + +/* + * Create a constant node for sizeof. + */ +tnode_t * +bldszof(tp) + type_t *tp; +{ + int elem, elsz; + tspec_t st; + + elem = 1; + while (tp->t_tspec == ARRAY) { + elem *= tp->t_dim; + tp = tp->t_subt; + } + if (elem == 0) { + /* cannot take size of incomplete type */ + error(143); + elem = 1; + } + switch (tp->t_tspec) { + case FUNC: + /* cannot take size of function */ + error(144); + elsz = 1; + break; + case STRUCT: + case UNION: + if (incompl(tp)) { + /* cannot take size of incomplete type */ + error(143); + elsz = 1; + } else { + elsz = tp->t_str->size; + } + break; + case ENUM: + if (incompl(tp)) { + /* cannot take size of incomplete type */ + warning(143); + } + /* FALLTHROUGH */ + default: + if (tp->t_isfield) { + /* cannot take size of bit-field */ + error(145); + } + if (tp->t_tspec == VOID) { + /* cannot take size of void */ + error(146); + elsz = 1; + } else { + elsz = size(tp->t_tspec); + if (elsz <= 0) + lerror("bldszof() 1"); + } + break; + } + +#if SIZEOF_IS_ULONG + st = ULONG; +#else + st = UINT; +#endif + + return (getinode(st, (quad_t)(elem * elsz / CHAR_BIT))); +} + +/* + * Type casts. + */ +tnode_t * +cast(tn, tp) + tnode_t *tn; + type_t *tp; +{ + tspec_t nt, ot; + + if (tn == NULL) + return (NULL); + + tn = cconv(tn); + + nt = tp->t_tspec; + ot = tn->tn_type->t_tspec; + + if (nt == VOID) { + /* + * XXX ANSI C requires scalar types or void (Plauger&Brodie). + * But this seams really questionable. + */ + } else if (nt == STRUCT || nt == UNION || nt == ARRAY || nt == FUNC) { + /* invalid cast expression */ + error(147); + return (NULL); + } else if (ot == STRUCT || ot == UNION) { + /* invalid cast expression */ + error(147); + return (NULL); + } else if (ot == VOID) { + /* improper cast of void expression */ + error(148); + return (NULL); + } else if (isityp(nt) && issclt(ot)) { + /* ok */ + } else if (isftyp(nt) && isatyp(ot)) { + /* ok */ + } else if (nt == PTR && isityp(ot)) { + /* ok */ + } else if (nt == PTR && ot == PTR) { + if (!tp->t_subt->t_const && tn->tn_type->t_subt->t_const) { + if (hflag) + /* cast discards 'const' from ... */ + warning(275); + } + } else { + /* invalid cast expression */ + error(147); + return (NULL); + } + + tn = convert(CVT, 0, tp, tn); + tn->tn_cast = 1; + + return (tn); +} + +/* + * Create the node for a function argument. + * All necessary conversions and type checks are done in funccall(), because + * in funcarg() we have no information about expected argument types. + */ +tnode_t * +funcarg(args, arg) + tnode_t *args, *arg; +{ + tnode_t *ntn; + + /* + * If there was a serious error in the expression for the argument, + * create a dummy argument so the positions of the remaining arguments + * will not change. + */ + if (arg == NULL) + arg = getinode(INT, (quad_t)0); + + ntn = mktnode(PUSH, arg->tn_type, arg, args); + + return (ntn); +} + +/* + * Create the node for a function call. Also check types of + * function arguments and insert conversions, if necessary. + */ +tnode_t * +funccall(func, args) + tnode_t *func, *args; +{ + tnode_t *ntn; + op_t fcop; + + if (func == NULL) + return (NULL); + + if (func->tn_op == NAME && func->tn_type->t_tspec == FUNC) { + fcop = CALL; + } else { + fcop = ICALL; + } + + /* + * after cconv() func will always be a pointer to a function + * if it is a valid function designator. + */ + func = cconv(func); + + if (func->tn_type->t_tspec != PTR || + func->tn_type->t_subt->t_tspec != FUNC) { + /* illegal function */ + error(149); + return (NULL); + } + + args = chkfarg(func->tn_type->t_subt, args); + + ntn = mktnode(fcop, func->tn_type->t_subt->t_subt, func, args); + + return (ntn); +} + +/* + * Check types of all function arguments and insert conversions, + * if necessary. + */ +static tnode_t * +chkfarg(ftp, args) + type_t *ftp; /* type of called function */ + tnode_t *args; /* arguments */ +{ + tnode_t *arg; + sym_t *asym; + tspec_t at; + int narg, npar, n, i; + + /* get # of args in the prototype */ + npar = 0; + for (asym = ftp->t_args; asym != NULL; asym = asym->s_nxt) + npar++; + + /* get # of args in function call */ + narg = 0; + for (arg = args; arg != NULL; arg = arg->tn_right) + narg++; + + asym = ftp->t_args; + if (ftp->t_proto && npar != narg && !(ftp->t_vararg && npar < narg)) { + /* argument mismatch: %d arg%s passed, %d expected */ + error(150, narg, narg > 1 ? "s" : "", npar); + asym = NULL; + } + + for (n = 1; n <= narg; n++) { + + /* + * The rightmost argument is at the top of the argument + * subtree. + */ + for (i = narg, arg = args; i > n; i--, arg = arg->tn_right) ; + + /* some things which are always not allowd */ + if ((at = arg->tn_left->tn_type->t_tspec) == VOID) { + /* void expressions may not be arguments, arg #%d */ + error(151, n); + return (NULL); + } else if ((at == STRUCT || at == UNION) && + incompl(arg->tn_left->tn_type)) { + /* argument cannot have unknown size, arg #%d */ + error(152, n); + return (NULL); + } else if (isityp(at) && arg->tn_left->tn_type->t_isenum && + incompl(arg->tn_left->tn_type)) { + /* argument cannot have unknown size, arg #%d */ + warning(152, n); + } + + /* class conversions (arg in value context) */ + arg->tn_left = cconv(arg->tn_left); + + if (asym != NULL) { + arg->tn_left = parg(n, asym->s_type, arg->tn_left); + } else { + arg->tn_left = promote(NOOP, 1, arg->tn_left); + } + arg->tn_type = arg->tn_left->tn_type; + + if (asym != NULL) + asym = asym->s_nxt; + } + + return (args); +} + +/* + * Compare the type of an argument with the corresponding type of a + * prototype parameter. If it is a valid combination, but both types + * are not the same, insert a conversion to convert the argument into + * the type of the parameter. + */ +static tnode_t * +parg(n, tp, tn) + int n; /* pos of arg */ + type_t *tp; /* expected type (from prototype) */ + tnode_t *tn; /* argument */ +{ + tnode_t *ln; + int warn; + + ln = xcalloc(1, sizeof (tnode_t)); + ln->tn_type = tduptyp(tp); + ln->tn_type->t_const = 0; + ln->tn_lvalue = 1; + if (typeok(FARG, n, ln, tn)) { + if (!eqtype(tp, tn->tn_type, 1, 0, (warn = 0, &warn)) || warn) + tn = convert(FARG, n, tp, tn); + } + free(ln); + return (tn); +} + +/* + * Return the value of an integral constant expression. + * If the expression is not constant or its type is not an integer + * type, an error message is printed. + */ +val_t * +constant(tn) + tnode_t *tn; +{ + val_t *v; + + if (tn != NULL) + tn = cconv(tn); + if (tn != NULL) + tn = promote(NOOP, 0, tn); + + v = xcalloc(1, sizeof (val_t)); + + if (tn == NULL) { + if (nerr == 0) + lerror("constant() 1"); + v->v_tspec = INT; + v->v_quad = 1; + return (v); + } + + v->v_tspec = tn->tn_type->t_tspec; + + if (tn->tn_op == CON) { + if (tn->tn_type->t_tspec != tn->tn_val->v_tspec) + lerror("constant() 2"); + if (isityp(tn->tn_val->v_tspec)) { + v->v_ansiu = tn->tn_val->v_ansiu; + v->v_quad = tn->tn_val->v_quad; + return (v); + } + v->v_quad = tn->tn_val->v_ldbl; + } else { + v->v_quad = 1; + } + + /* integral constant expression expected */ + error(55); + + if (!isityp(v->v_tspec)) + v->v_tspec = INT; + + return (v); +} + +/* + * Perform some tests on expressions which can't be done in build() and + * functions called by build(). These tests must be done here because + * we need some information about the context in which the operations + * are performed. + * After all tests are performed, expr() frees the memory which is used + * for the expression. + */ +void +expr(tn, vctx, tctx) + tnode_t *tn; + int vctx, tctx; +{ + if (tn == NULL && nerr == 0) + lerror("expr() 1"); + + if (tn == NULL) { + tfreeblk(); + return; + } + + /* expr() is also called in global initialisations */ + if (dcs->d_ctx != EXTERN) + chkreach(); + + chkmisc(tn, vctx, tctx, !tctx, 0, 0, 0); + if (tn->tn_op == ASSIGN) { + if (hflag && tctx) + /* assignment in conditional context */ + warning(159); + } else if (tn->tn_op == CON) { + if (hflag && tctx && !ccflg) + /* constant in conditional context */ + warning(161); + } + if (!modtab[tn->tn_op].m_sideeff) { + /* + * for left operands of COMMA this warning is already + * printed + */ + if (tn->tn_op != COMMA && !vctx && !tctx) + nulleff(tn); + } + if (dflag) + displexpr(tn, 0); + + /* free the tree memory */ + tfreeblk(); +} + +static void +nulleff(tn) + tnode_t *tn; +{ + if (!hflag) + return; + + while (!modtab[tn->tn_op].m_sideeff) { + if (tn->tn_op == CVT && tn->tn_type->t_tspec == VOID) { + tn = tn->tn_left; + } else if (tn->tn_op == LOGAND || tn->tn_op == LOGOR) { + /* + * && and || have a side effect if the right operand + * has a side effect. + */ + tn = tn->tn_right; + } else if (tn->tn_op == QUEST) { + /* + * ? has a side effect if at least one of its right + * operands has a side effect + */ + tn = tn->tn_right; + } else if (tn->tn_op == COLON) { + /* + * : has a side effect if at least one of its operands + * has a side effect + */ + if (modtab[tn->tn_left->tn_op].m_sideeff) { + tn = tn->tn_left; + } else if (modtab[tn->tn_right->tn_op].m_sideeff) { + tn = tn->tn_right; + } else { + break; + } + } else { + break; + } + } + if (!modtab[tn->tn_op].m_sideeff) + /* expression has null effect */ + warning(129); +} + +/* + * Dump an expression to stdout + * only used for debugging + */ +static void +displexpr(tn, offs) + tnode_t *tn; + int offs; +{ + u_quad_t uq; + + if (tn == NULL) { + (void)printf("%*s%s\n", offs, "", "NULL"); + return; + } + (void)printf("%*sop %s ", offs, "", modtab[tn->tn_op].m_name); + + if (tn->tn_op == NAME) { + (void)printf("%s: %s ", + tn->tn_sym->s_name, scltoa(tn->tn_sym->s_scl)); + } else if (tn->tn_op == CON && isftyp(tn->tn_type->t_tspec)) { + (void)printf("%#g ", (double)tn->tn_val->v_ldbl); + } else if (tn->tn_op == CON && isityp(tn->tn_type->t_tspec)) { + uq = tn->tn_val->v_quad; + (void)printf("0x %08lx %08lx ", (long)(uq >> 32) & 0xffffffffl, + (long)uq & 0xffffffffl); + } else if (tn->tn_op == CON) { + if (tn->tn_type->t_tspec != PTR) + lerror("displexpr() 1"); + (void)printf("0x%0*lx ", (int)(sizeof (void *) * CHAR_BIT / 4), + (u_long)tn->tn_val->v_quad); + } else if (tn->tn_op == STRING) { + if (tn->tn_strg->st_tspec == CHAR) { + (void)printf("\"%s\"", tn->tn_strg->st_cp); + } else { + char *s; + size_t n; + n = MB_CUR_MAX * (tn->tn_strg->st_len + 1); + s = xmalloc(n); + (void)wcstombs(s, tn->tn_strg->st_wcp, n); + (void)printf("L\"%s\"", s); + free(s); + } + (void)printf(" "); + } else if (tn->tn_op == FSEL) { + (void)printf("o=%d, l=%d ", tn->tn_type->t_foffs, + tn->tn_type->t_flen); + } + (void)printf("%s\n", ttos(tn->tn_type)); + if (tn->tn_op == NAME || tn->tn_op == CON || tn->tn_op == STRING) + return; + displexpr(tn->tn_left, offs + 2); + if (modtab[tn->tn_op].m_binary || + (tn->tn_op == PUSH && tn->tn_right != NULL)) { + displexpr(tn->tn_right, offs + 2); + } +} + +/* + * Called by expr() to recursively perform some tests. + */ +/* ARGSUSED */ +void +chkmisc(tn, vctx, tctx, eqwarn, fcall, rvdisc, szof) + tnode_t *tn; + int vctx, tctx, eqwarn, fcall, rvdisc, szof; +{ + tnode_t *ln, *rn; + mod_t *mp; + int nrvdisc, cvctx, ctctx; + op_t op; + scl_t sc; + dinfo_t *di; + + if (tn == NULL) + return; + + ln = tn->tn_left; + rn = tn->tn_right; + mp = &modtab[op = tn->tn_op]; + + switch (op) { + case AMPER: + if (ln->tn_op == NAME && (reached || rchflg)) { + if (!szof) + setsflg(ln->tn_sym); + setuflg(ln->tn_sym, fcall, szof); + } + if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) + /* check the range of array indices */ + chkaidx(ln->tn_left, 1); + break; + case LOAD: + if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) + /* check the range of array indices */ + chkaidx(ln->tn_left, 0); + /* FALLTHROUGH */ + case PUSH: + case INCBEF: + case DECBEF: + case INCAFT: + case DECAFT: + case ADDASS: + case SUBASS: + case MULASS: + case DIVASS: + case MODASS: + case ANDASS: + case ORASS: + case XORASS: + case SHLASS: + case SHRASS: + if (ln->tn_op == NAME && (reached || rchflg)) { + sc = ln->tn_sym->s_scl; + /* + * Look if there was a asm statement in one of the + * compound statements we are in. If not, we don't + * print a warning. + */ + for (di = dcs; di != NULL; di = di->d_nxt) { + if (di->d_asm) + break; + } + if (sc != EXTERN && sc != STATIC && + !ln->tn_sym->s_set && !szof && di == NULL) { + /* %s may be used before set */ + warning(158, ln->tn_sym->s_name); + setsflg(ln->tn_sym); + } + setuflg(ln->tn_sym, 0, 0); + } + break; + case ASSIGN: + if (ln->tn_op == NAME && !szof && (reached || rchflg)) { + setsflg(ln->tn_sym); + if (ln->tn_sym->s_scl == EXTERN) + outusg(ln->tn_sym); + } + if (ln->tn_op == STAR && ln->tn_left->tn_op == PLUS) + /* check the range of array indices */ + chkaidx(ln->tn_left, 0); + break; + case CALL: + if (ln->tn_op != AMPER || ln->tn_left->tn_op != NAME) + lerror("chkmisc() 1"); + if (!szof) + outcall(tn, vctx || tctx, rvdisc); + break; + case EQ: + /* equality operator "==" found where "=" was exp. */ + if (hflag && eqwarn) + warning(160); + break; + case CON: + case NAME: + case STRING: + return; + /* LINTED (enumeration values not handled in switch) */ + } + + cvctx = mp->m_vctx; + ctctx = mp->m_tctx; + /* + * values of operands of ':' are not used if the type of at least + * one of the operands (for gcc compatibility) is void + * XXX test/value context of QUEST should probably be used as + * context for both operands of COLON + */ + if (op == COLON && tn->tn_type->t_tspec == VOID) + cvctx = ctctx = 0; + nrvdisc = op == CVT && tn->tn_type->t_tspec == VOID; + chkmisc(ln, cvctx, ctctx, mp->m_eqwarn, op == CALL, nrvdisc, szof); + + switch (op) { + case PUSH: + if (rn != NULL) + chkmisc(rn, 0, 0, mp->m_eqwarn, 0, 0, szof); + break; + case LOGAND: + case LOGOR: + chkmisc(rn, 0, 1, mp->m_eqwarn, 0, 0, szof); + break; + case COLON: + chkmisc(rn, cvctx, ctctx, mp->m_eqwarn, 0, 0, szof); + break; + default: + if (mp->m_binary) + chkmisc(rn, 1, 0, mp->m_eqwarn, 0, 0, szof); + break; + } + +} + +/* + * Checks the range of array indices, if possible. + * amper is set if only the address of the element is used. This + * means that the index is allowd to refere to the first element + * after the array. + */ +static void +chkaidx(tn, amper) + tnode_t *tn; + int amper; +{ + int dim; + tnode_t *ln, *rn; + int elsz; + quad_t con; + + ln = tn->tn_left; + rn = tn->tn_right; + + /* We can only check constant indices. */ + if (rn->tn_op != CON) + return; + + /* Return if the left node does not stem from an array. */ + if (ln->tn_op != AMPER) + return; + if (ln->tn_left->tn_op != STRING && ln->tn_left->tn_op != NAME) + return; + if (ln->tn_left->tn_type->t_tspec != ARRAY) + return; + + /* + * For incomplete array types, we can print a warning only if + * the index is negative. + */ + if (incompl(ln->tn_left->tn_type) && rn->tn_val->v_quad >= 0) + return; + + /* Get the size of one array element */ + if ((elsz = length(ln->tn_type->t_subt, NULL)) == 0) + return; + elsz /= CHAR_BIT; + + /* Change the unit of the index from bytes to element size. */ + if (isutyp(rn->tn_type->t_tspec)) { + con = (u_quad_t)rn->tn_val->v_quad / elsz; + } else { + con = rn->tn_val->v_quad / elsz; + } + + dim = ln->tn_left->tn_type->t_dim + (amper ? 1 : 0); + + if (!isutyp(rn->tn_type->t_tspec) && con < 0) { + /* array subscript cannot be negative: %ld */ + warning(167, (long)con); + } else if (dim > 0 && (u_quad_t)con >= dim) { + /* array subscript cannot be > %d: %ld */ + warning(168, dim - 1, (long)con); + } +} + +/* + * Check for ordered comparisions of unsigned values with 0. + */ +static void +chkcomp(op, ln, rn) + op_t op; + tnode_t *ln, *rn; +{ + tspec_t lt, rt; + mod_t *mp; + + lt = ln->tn_type->t_tspec; + rt = rn->tn_type->t_tspec; + mp = &modtab[op]; + + if (ln->tn_op != CON && rn->tn_op != CON) + return; + + if (!isityp(lt) || !isityp(rt)) + return; + + if ((hflag || pflag) && lt == CHAR && rn->tn_op == CON && + (rn->tn_val->v_quad < 0 || + rn->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) { + /* nonportable character comparision, op %s */ + warning(230, mp->m_name); + return; + } + if ((hflag || pflag) && rt == CHAR && ln->tn_op == CON && + (ln->tn_val->v_quad < 0 || + ln->tn_val->v_quad > ~(~0 << (CHAR_BIT - 1)))) { + /* nonportable character comparision, op %s */ + warning(230, mp->m_name); + return; + } + if (isutyp(lt) && !isutyp(rt) && + rn->tn_op == CON && rn->tn_val->v_quad <= 0) { + if (rn->tn_val->v_quad < 0) { + /* comparision of %s with %s, op %s */ + warning(162, tyname(ln->tn_type), "negative constant", + mp->m_name); + } else if (op == LT || op == GE || (hflag && op == LE)) { + /* comparision of %s with %s, op %s */ + warning(162, tyname(ln->tn_type), "0", mp->m_name); + } + return; + } + if (isutyp(rt) && !isutyp(lt) && + ln->tn_op == CON && ln->tn_val->v_quad <= 0) { + if (ln->tn_val->v_quad < 0) { + /* comparision of %s with %s, op %s */ + warning(162, "negative constant", tyname(rn->tn_type), + mp->m_name); + } else if (op == GT || op == LE || (hflag && op == GE)) { + /* comparision of %s with %s, op %s */ + warning(162, "0", tyname(rn->tn_type), mp->m_name); + } + return; + } +} + +/* + * Takes an expression an returns 0 if this expression can be used + * for static initialisation, otherwise -1. + * + * Constant initialisation expressions must be costant or an address + * of a static object with an optional offset. In the first case, + * the result is returned in *offsp. In the second case, the static + * object is returned in *symp and the offset in *offsp. + * + * The expression can consist of PLUS, MINUS, AMPER, NAME, STRING and + * CON. Type conversions are allowed if they do not change binary + * representation (including width). + */ +int +conaddr(tn, symp, offsp) + tnode_t *tn; + sym_t **symp; + ptrdiff_t *offsp; +{ + sym_t *sym; + ptrdiff_t offs1, offs2; + tspec_t t, ot; + + switch (tn->tn_op) { + case MINUS: + if (tn->tn_right->tn_op != CON) + return (-1); + /* FALLTHROUGH */ + case PLUS: + offs1 = offs2 = 0; + if (tn->tn_left->tn_op == CON) { + offs1 = (ptrdiff_t)tn->tn_left->tn_val->v_quad; + if (conaddr(tn->tn_right, &sym, &offs2) == -1) + return (-1); + } else if (tn->tn_right->tn_op == CON) { + offs2 = (ptrdiff_t)tn->tn_right->tn_val->v_quad; + if (tn->tn_op == MINUS) + offs2 = -offs2; + if (conaddr(tn->tn_left, &sym, &offs1) == -1) + return (-1); + } else { + return (-1); + } + *symp = sym; + *offsp = offs1 + offs2; + break; + case AMPER: + if (tn->tn_left->tn_op == NAME) { + *symp = tn->tn_left->tn_sym; + *offsp = 0; + } else if (tn->tn_left->tn_op == STRING) { + /* + * If this would be the front end of a compiler we + * would return a label instead of 0. + */ + *offsp = 0; + } + break; + case CVT: + t = tn->tn_type->t_tspec; + ot = tn->tn_left->tn_type->t_tspec; + if ((!isityp(t) && t != PTR) || (!isityp(ot) && ot != PTR)) { + return (-1); + } else if (psize(t) != psize(ot)) { + return (-1); + } + if (conaddr(tn->tn_left, symp, offsp) == -1) + return (-1); + break; + default: + return (-1); + } + return (0); +} + +/* + * Concatenate two string constants. + */ +strg_t * +catstrg(strg1, strg2) + strg_t *strg1, *strg2; +{ + size_t len1, len2, len; + + if (strg1->st_tspec != strg2->st_tspec) { + /* cannot concatenate wide and regular string literals */ + error(292); + return (strg1); + } + + len = (len1 = strg1->st_len) + (len2 = strg2->st_len); + + if (strg1->st_tspec == CHAR) { + strg1->st_cp = xrealloc(strg1->st_cp, len + 1); + (void)memcpy(strg1->st_cp + len1, strg2->st_cp, len2 + 1); + free(strg2->st_cp); + } else { + strg1->st_wcp = xrealloc(strg1->st_wcp, + (len + 1) * sizeof (wchar_t)); + (void)memcpy(strg1->st_wcp + len1, strg2->st_wcp, + (len2 + 1) * sizeof (wchar_t)); + free(strg2->st_wcp); + } + free(strg2); + + return (strg1); +} + +/* + * Print a warning if the given node has operands which should be + * parenthesized. + * + * XXX Does not work if an operand is a constant expression. Constant + * expressions are already folded. + */ +static void +precconf(tn) + tnode_t *tn; +{ + tnode_t *ln, *rn; + op_t lop, rop; + int lparn, rparn; + mod_t *mp; + int warn; + + if (!hflag) + return; + + mp = &modtab[tn->tn_op]; + + lparn = 0; + for (ln = tn->tn_left; ln->tn_op == CVT; ln = ln->tn_left) + lparn |= ln->tn_parn; + lparn |= ln->tn_parn; + lop = ln->tn_op; + + if (mp->m_binary) { + rparn = 0; + for (rn = tn->tn_right; tn->tn_op == CVT; rn = rn->tn_left) + rparn |= rn->tn_parn; + rparn |= rn->tn_parn; + rop = rn->tn_op; + } + + warn = 0; + + switch (tn->tn_op) { + case SHL: + case SHR: + if (!lparn && (lop == PLUS || lop == MINUS)) { + warn = 1; + } else if (!rparn && (rop == PLUS || rop == MINUS)) { + warn = 1; + } + break; + case LOGOR: + if (!lparn && lop == LOGAND) { + warn = 1; + } else if (!rparn && rop == LOGAND) { + warn = 1; + } + break; + case AND: + case XOR: + case OR: + if (!lparn && lop != tn->tn_op) { + if (lop == PLUS || lop == MINUS) { + warn = 1; + } else if (lop == AND || lop == XOR) { + warn = 1; + } + } + if (!warn && !rparn && rop != tn->tn_op) { + if (rop == PLUS || rop == MINUS) { + warn = 1; + } else if (rop == AND || rop == XOR) { + warn = 1; + } + } + break; + /* LINTED (enumeration values not handled in switch) */ + } + + if (warn) { + /* precedence confusion possible: parenthesize! */ + warning(169); + } + +} 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 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 +#include +#include +#include + +#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 + +#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 +#include +#include + +#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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 + +#include +#ifdef __STDC__ +#include +#else +#include +#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 +#include +#include +#include +#include +#include + +#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); +} diff --git a/usr.bin/xlint/llib/Makefile b/usr.bin/xlint/llib/Makefile new file mode 100644 index 0000000..2e24c31 --- /dev/null +++ b/usr.bin/xlint/llib/Makefile @@ -0,0 +1,20 @@ +# $NetBSD: Makefile,v 1.2 1995/07/03 21:25:05 cgd Exp $ + +LIBS= llib-lposix.ln llib-lstdc.ln + +all: ${LIBS} + +install: + install ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NONBINMODE} \ + ${LIBS} ${DESTDIR}${LINTLIBDIR} + +clean cleanall: + rm -f ${LIBS} + +llib-lposix.ln: llib-lposix + lint -Cposix ${.ALLSRC} + +llib-lstdc.ln: llib-lstdc + lint -Cstdc ${.ALLSRC} + +.include diff --git a/usr.bin/xlint/llib/llib-lposix b/usr.bin/xlint/llib/llib-lposix new file mode 100644 index 0000000..db1b3cf --- /dev/null +++ b/usr.bin/xlint/llib/llib-lposix @@ -0,0 +1,311 @@ +/* $NetBSD: llib-lposix,v 1.2 1995/07/03 21:25:09 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. + */ + +/* LINTLIBRARY */ + +#define _POSIX_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* PROTOLIB1 */ + + +void (abort)(void); +int (abs)(int j); +int (access)(const char *path, int amode); +double (acos)(double x); +unsigned (alarm)(unsigned seconds); +char *(asctime)(const struct tm *timeptr); +double (asin)(double x); +void (__assert)(const char *expression, int line, const char *file); +double (atan)(double x); +double (atan2)(double y, double x); +int (atexit)(void (*func)(void)); +double (atof)(const char *nptr); +int (atoi)(const char *nptr); +long (atol)(const char *nptr); +void *(bsearch)(const void *key, const void *base, size_t nmemb, + size_t size, int (*compar)(const void *, const void *)); +void *(calloc)(size_t nmemb, size_t size); +double (ceil)(double x); +speed_t (cfgetispeed)(const struct termios *p); +speed_t (cfgetospeed)(const struct termios *p); +int (cfsetispeed)(struct termios *p, speed_t speed); +int (cfsetospeed)(struct termios *p, speed_t speed); +int (chdir)(const char *path); +int (chmod)(const char *path, mode_t mode); +int (chown)(const char *path, uid_t owner, gid_t group); +void (clearerr)(FILE *stream); +clock_t (clock)(void); +int (close)(int fildes); +int (closedir)(DIR *dirp); +double (cos)(double x); +double (cosh)(double x); +int (creat)(const char *path, mode_t mode); +char *(ctermid)(char *s); +char *(ctime)(const time_t *timer); +char *(cuserid)(char *s); +double (difftime)(time_t time1, time_t time0); +div_t (div)(int numer, int denom); +int (dup)(int fildes); +int (dup2)(int fildes, int fildes2); +int (errno); +int (execl)(const char *path, const char *arg, ...); +int (execle)(const char *path, const char *arg, ...); +int (execlp)(const char *file, const char *arg, ...); +int (execv)(const char *path, char *const argv[]); +int (execve)(const char *path, char *const argv[], char *const *envp); +int (execvp)(const char *file, char *const argv[]); +void (exit)(int status); +void (_exit)(int status); +double (exp)(double x); +double (fabs)(double x); +int (fclose)(FILE *stream); +int (fcntl)(int fildes, int cmd, ...); +FILE *(fdopen)(int fildes, const char *type); +int (feof)(FILE *stream); +int (ferror)(FILE *stream); +int (fflush)(FILE *stream); +int (fgetc)(FILE *stream); +int (fgetpos)(FILE *stream, fpos_t *pos); +char *(fgets)(char *s, int n, FILE *stream); +int (fileno)(FILE *stream); +double (floor)(double x); +double (fmod)(double x, double y); +FILE *(fopen)(const char *filename, const char *mode); +pid_t (fork)(void); +long (fpathconf)(int fildes, int name); +/* PRINTFLIKE2 */ +int (fprintf)(FILE *stream, const char *format, ...); +int (fputc)(int c, FILE *stream); +int (fputs)(const char *s, FILE *stream); +size_t (fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); +void (free)(void *ptr); +FILE *(freopen)(const char *filename, const char *mode, FILE *stream); +double (frepx)(double value, int *exp); +/* SCANFLIKE2 */ +int (fscanf)(FILE *stream, const char *format, ...); +int (fseek)(FILE *stream, long int offset, int whence); +int (fsetpos)(FILE *stream, const fpos_t *pos); +int (fstat)(int fildes, struct stat *buf); +long (ftell)(FILE *stream); +size_t (fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int (getc)(FILE *stream); +int (getchar)(void); +char *(getcwd)(char *buf, size_t size); +gid_t (getegid)(void); +char *(getenv)(const char *name); +uid_t (geteuid)(void); +gid_t (getgid)(void); +struct group *(getgrgid)(gid_t gid); +struct group *(getgrnam)(const char *name); +int (getgroups)(int gidsetsize, gid_t grouplist[]); +char *(getlogin)(void); +pid_t (getpgrp)(void); +pid_t (getpid)(void); +pid_t (getppid)(void); +struct passwd *(getpwnam)(const char *name); +struct passwd *(getpwuid)(uid_t uid); +char *(gets)(char *s); +uid_t (getuid)(void); +struct tm *(gmtime)(const time_t *timer); +int (isalnum)(int c); +int (isalpha)(int c); +int (isatty)(int fildes); +int (iscntrl)(int c); +int (isdigit)(int c); +int (isgraph)(int c); +int (islower)(int c); +int (isprint)(int c); +int (ispunct)(int c); +int (isspace)(int c); +int (isupper)(int c); +int (isxdigit)(int c); +int (kill)(pid_t pid, int sig); +long (labs)(long j); +double (ldexp)(double x, int exp); +ldiv_t (ldiv)(long numer, long denom); +int (link)(const char *existing, const char *new); +struct lconv *(localeconv)(void); +struct tm *(localtime)(const time_t *timer); +double (log)(double x); +double (log10)(double x); +void (longjmp)(jmp_buf env, int val); +off_t (lseek)(int fildes, off_t offset, int whence); +void *(malloc)(size_t size); +int (mblen)(const char *s, size_t n); +size_t (mbstowcs)(wchar_t *pwcs, const char *s, size_t n); +int (mbtowc)(wchar_t *pwc, const char *s, size_t n); +void *(memchr)(const void *s, int c, size_t n); +int (memcmp)(const void *s1, const void *s2, size_t n); +void *(memcpy)(void *s1, const void *s2, size_t n); +void *(memmove)(void *s1, const void *s2, size_t n); +void *(memset)(void *s, int c, size_t n); +int (mkdir)(const char *path, mode_t mode); +int (mkfifo)(const char *path, mode_t mode); +time_t (mktime)(struct tm *timeptr); +double (modf)(double value, double *iptr); +int (open)(const char *path, int oflag, ...); +DIR *(opendir)(const char *dirname); +long (pathconf)(const char *path, int name); +int (pause)(void); +void (perror)(const char *s); +int (pipe)(int fildes[2]); +double (pow)(double x, double y); +/* PRINTFLIKE1 */ +int (printf)(const char *format, ...); +int (putc)(int c, FILE *stream); +int (putchar)(int c); +int (puts)(const char *s); +void (qsort)(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); +int (raise)(int sig); +int (rand)(void); +ssize_t (read)(int fildes, void *buf, size_t nbyte); +struct dirent *(readdir)(DIR *dirp); +void *(realloc)(void *ptr, size_t size); +int (remove)(const char *filename); +int (rename)(const char *old, const char *new); +void (rewind)(FILE *stream); +void (rewinddir)(DIR *dirp); +int (rmdir)(const char *path); +/* SCANFLIKE1 */ +int (scanf)(const char *format, ...); +void (setbuf)(FILE *stream, char *buf); +int (setgid)(gid_t gid); +int (setjmp)(jmp_buf env); +char *(setlocale)(int category, const char *locale); +int (setpgid)(pid_t pid, pid_t pgid); +pid_t (setsid)(void); +int (setuid)(uid_t uid); +int (setvbuf)(FILE *stream, char *buf, int mode, size_t size); +int (sigaction)(int sig, const struct sigaction *act, + struct sigaction *oact); +int (sigaddset)(sigset_t *set, int signo); +int (sigdelset)(sigset_t *set, int signo); +int (sigemptyset)(sigset_t *set); +int (sigfillset)(sigset_t *set); +int (sigismember)(const sigset_t *set, int signo); +void (siglongjmp)(sigjmp_buf env, int val); +void (*(signal)(int sig, void (*func)(int)))(int); +int (sigpending)(sigset_t *set); +int (sigprocmask)(int how, const sigset_t *set, sigset_t *oset); +int (sigsetjmp)(sigjmp_buf env, int savemask); +int (sigsuspend)(const sigset_t *sigmask); +double (sin)(double x); +double (sinh)(double x); +unsigned (sleep)(unsigned seconds); +/* PRINTFLIKE2 */ +int (sprintf)(char *s, const char *format, ...); +double (sqrt)(double x); +void (srand)(unsigned seed); +/* SCANFLIKE2 */ +int (sscanf)(const char *s, const char *format, ...); +int (stat)(const char *path, struct stat *buf); +char *(strcat)(char *s1, const char *s2); +char *(strchr)(const char *s, int c); +int (strcmp)(const char *s1, const char *s2); +int (strcoll)(const char *s1, const char *s2); +char *(strcpy)(char *s1, const char *s2); +size_t (strcspn)(const char *s1, const char *s2); +char *(strerror)(int errnum); +size_t (strftime)(char *s, size_t maxsize, const char *format, + const struct tm *timeptr); +size_t (strlen)(const char *s); +char *(strncat)(char *s1, const char *s2, size_t n); +int (strncmp)(const char *s1, const char *s2, size_t n); +char *(strncpy)(char *s1, const char *s2, size_t n); +char *(strpbrk)(const char *s1, const char *s2); +char *(strrchr)(const char *s, int c); +size_t (strspn)(const char *s1, const char *s2); +char *(strstr)(const char *s1, const char *s2); +double (strtod)(const char *nptr, char **endptr); +char *(strtok)(char *s1, const char *s2); +long (strtol)(const char *nptr, char **endptr, int base); +unsigned long (strtoul)(const char *nptr, char **endptr, int base); +size_t (strxfrm)(char *s1, const char *s2, size_t n); +long (sysconf)(int name); +int (system)(const char *string); +double (tan)(double x); +double (tanh)(double x); +int (tcdrain)(int fildes); +int (tcflow)(int fildes, int action); +int (tcflush)(int fildes, int queue_selector); +int (tcgetattr)(int fildes, struct termios *tp); +pid_t (tcgetpgrp)(int fildes); +int (tcsendbreak)(int fildes, int duration); +int (tcsetattr)(int fildes, int options, const struct termios *tp); +int (tcsetpgrp)(int fildes, pid_t pgrpid); +time_t (time)(time_t *timer); +clock_t (times)(struct tms *buffer); +FILE *(tmpfile)(void); +char *(tmpnam)(char *s); +int (tolower)(int c); +int (toupper)(int c); +char *(ttyname)(int filedes); +void (tzset)(void); +mode_t (umask)(mode_t cmask); +int (uname)(struct utsname *name); +int (ungetc)(int c, FILE *stream); +int (unlink)(const char *path); +int (utime)(const char *path, const struct utimbuf *times); +int (vfprintf)(FILE *stream, const char *format, va_list arg); +int (vprintf)(const char *format, va_list arg); +int (vsprintf)(char *s, const char *format, va_list arg); +pid_t (wait)(int *statloc); +pid_t (waitpid)(pid_t pid, int *stat_loc, int options); +size_t (wcstombs)(char *s, const wchar_t *pwcs, size_t n); +int (wctomb)(char *s, wchar_t wchar); +ssize_t (write)(int fildes, const void *buf, size_t nbyte); diff --git a/usr.bin/xlint/llib/llib-lstdc b/usr.bin/xlint/llib/llib-lstdc new file mode 100644 index 0000000..f2b33da --- /dev/null +++ b/usr.bin/xlint/llib/llib-lstdc @@ -0,0 +1,252 @@ +/* $NetBSD: llib-lstdc,v 1.3 1995/07/03 21:39:28 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. + */ + +/* LINTLIBRARY */ + +#define _ANSI_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* PROTOLIB1 */ + +/* + * assert.h + */ +#ifdef __NetBSD__ +void (__assert)(const char *expression, int line, const char *file); +#else +void (assert)(int expression); +#endif + +/* + * ctype.h + */ +int (isalnum)(int c); +int (isalpha)(int c); +int (iscntrl)(int c); +int (isdigit)(int c); +int (isgraph)(int c); +int (islower)(int c); +int (isprint)(int c); +int (ispunct)(int c); +int (isspace)(int c); +int (isupper)(int c); +int (isxdigit)(int c); +int (tolower)(int c); +int (toupper)(int c); + +/* + * errno.h + */ +int (errno); + +/* + * locale.h + */ +char *(setlocale)(int category, const char *locale); +struct lconv *(localeconv)(void); + +/* + * math.h + */ +double (acos)(double x); +double (asin)(double x); +double (atan)(double x); +double (atan2)(double y, double x); +double (cos)(double x); +double (sin)(double x); +double (tan)(double x); +double (cosh)(double x); +double (sinh)(double x); +double (tanh)(double x); +double (exp)(double x); +double (frexp)(double value, int *exp); +double (ldexp)(double x, int exp); +double (log)(double x); +double (log10)(double x); +double (modf)(double value, double *iptr); +double (pow)(double x, double y); +double (sqrt)(double x); +double (ceil)(double x); +double (fabs)(double x); +double (floor)(double x); +double (fmod)(double x, double y); + +/* + * setjmp.h + */ +int (setjmp)(jmp_buf env); +void (longjmp)(jmp_buf env, int val); + +/* + * signal.h + */ +void (*(signal)(int sig, void (*func)(int)))(int); +int (raise)(int sig); + +/* + * stdio.h + */ +int (remove)(const char *filename); +int (rename)(const char *old, const char *new); +FILE *(tmpfile)(void); +char *(tmpnam)(char *s); +int (fclose)(FILE *stream); +int (fflush)(FILE *stream); +FILE *(fopen)(const char *filename, const char *mode); +FILE *(freopen)(const char *filename, const char *mode, FILE *stream); +void (setbuf)(FILE *stream, char *buf); +int (setvbuf)(FILE *stream, char *buf, int mode, size_t size); +/* PRINTFLIKE2 */ +int (fprintf)(FILE *stream, const char *format, ...); +/* SCANFLIKE2 */ +int (fscanf)(FILE *stream, const char *format, ...); +/* PRINTFLIKE1 */ +int (printf)(const char *format, ...); +/* SCANFLIKE1 */ +int (scanf)(const char *format, ...); +/* PRINTFLIKE2 */ +int (sprintf)(char *s, const char *format, ...); +/* SCANFLIKE2 */ +int (sscanf)(const char *s, const char *format, ...); +int (vfprintf)(FILE *stream, const char *format, va_list arg); +int (vprintf)(const char *format, va_list arg); +int (vsprintf)(char *s, const char *format, va_list arg); +int (fgetc)(FILE *stream); +char *(fgets)(char *s, int n, FILE *stream); +int (fputc)(int c, FILE *stream); +int (fputs)(const char *s, FILE *stream); +int (getc)(FILE *stream); +int (getchar)(void); +char *(gets)(char *s); +int (putc)(int c, FILE *stream); +int (putchar)(int c); +int (puts)(const char *s); +int (ungetc)(int c, FILE *stream); +size_t (fread)(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t (fwrite)(const void *ptr, size_t size, size_t nmemb, FILE *stream); +int (fgetpos)(FILE *stream, fpos_t *pos); +int (fseek)(FILE *stream, long offset, int whence); +int (fsetpos)(FILE *stream, const fpos_t *pos); +long (ftell)(FILE *stream); +void (rewind)(FILE *stream); +void (clearerr)(FILE *stream); +int (feof)(FILE *stream); +int (ferror)(FILE *stream); +void (perror)(const char *s); + +/* + * stdlib.h + */ +double (atof)(const char *nptr); +int (atoi)(const char *nptr); +long (atol)(const char *nptr); +double (strtod)(const char *nptr, char **endptr); +long (strtol)(const char *nptr, char **endptr, int base); +unsigned long (strtoul)(const char *nptr, char **endptr, int base); +int (rand)(void); +void (srand)(unsigned seed); +void *(calloc)(size_t nmemb, size_t size); +void (free)(void *ptr); +void *(malloc)(size_t size); +void *(realloc)(void *ptr, size_t size); +void (abort)(void); +int (atexit)(void (*func)(void)); +void (exit)(int status); +char *(getenv)(const char *name); +int (system)(const char *string); +void *(bsearch)(const void *key, const void *base, size_t nmemb, + size_t size, int (*compar)(const void *, const void *)); +void (qsort)(void *base, size_t nmemb, size_t size, + int (*compar)(const void *, const void *)); +int (abs)(int j); +div_t (div)(int numer, int denom); +long (labs)(long j); +ldiv_t (ldiv)(long numer, long denom); +int (mblen)(const char *s, size_t n); +int (mbtowc)(wchar_t *PWC, const char *s, size_t n); +int (wctomb)(char *s, wchar_t wchar); +size_t (mbstowcs)(wchar_t *pwcs, const char *s, size_t n); +size_t (wcstombs)(char *s, const wchar_t *pwcs, size_t n); + +/* + * string.h + */ +void *(memcpy)(void *s1, const void *s2, size_t n); +void *(memmove)(void *s1, const void *s2, size_t n); +char *(strcpy)(char *s1, const char *s2); +char *(strncpy)(char *s1, const char *s2, size_t n); +char *(strcat)(char *s1, const char *s2); +char *(strncat)(char *s1, const char *s2, size_t n); +int (memcmp)(const void *s1, const void *s2, size_t n); +int (strcmp)(const char *s1, const char *s2); +int (strcoll)(const char *s1, const char *s2); +int (strncmp)(const char *s1, const char *s2, size_t n); +size_t (strxfrm)(char *s1, const char *s2, size_t n); +void *(memchr)(const void *s, int c, size_t n); +char *(strchr)(const char *s, int c); +size_t (strcspn)(const char *s1, const char *s2); +char *(strpbrk)(const char *s1, const char *s2); +char *(strrchr)(const char *s1, int c); +size_t (strspn)(const char *s1, const char *s2); +char *(strstr)(const char *s1, const char *s2); +char *(strtok)(char *s1, const char *s2); +void *(memset)(void *s, int c, size_t n); +char *(strerror)(int errnom); +size_t (strlen)(const char *s); + +/* + * time.h + */ +clock_t (clock)(void); +double (difftime)(time_t time1, time_t time2); +time_t (mktime)(struct tm *timeptr); +time_t (time)(time_t *timer); +char *(asctime)(const struct tm *timeptr); +char *(ctime)(const time_t *timer); +struct tm *(gmtime)(const time_t *timer); +struct tm *(localtime)(const time_t *timer); +size_t (strftime)(char *s, size_t maxsize, const char *format, + const struct tm *timeptr); diff --git a/usr.bin/xlint/xlint/Makefile b/usr.bin/xlint/xlint/Makefile new file mode 100644 index 0000000..41c833c --- /dev/null +++ b/usr.bin/xlint/xlint/Makefile @@ -0,0 +1,17 @@ +# $NetBSD: Makefile,v 1.2 1995/07/03 21:25:14 cgd Exp $ + +.PATH: ${.CURDIR}/../lint1 + +PROG= xlint +SRCS= xlint.c mem.c +MAN= lint.1 + +CFLAGS+=-I${.CURDIR}/../lint1 + +realinstall: + install ${COPY} ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ + ${PROG} ${DESTDIR}${BINDIR}/lint + + +.include "${.CURDIR}/../../Makefile.inc" +.include diff --git a/usr.bin/xlint/xlint/lint.1 b/usr.bin/xlint/xlint/lint.1 new file mode 100644 index 0000000..99d459c --- /dev/null +++ b/usr.bin/xlint/xlint/lint.1 @@ -0,0 +1,509 @@ +.\" $NetBSD: lint.1,v 1.3 1995/10/23 13:45:31 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. +.\" +.Dd August 28, 1994 +.Dt LINT 1 +.Os NetBSD +.Sh NAME +.Nm lint +.Nd a C program verifier. +.Sh SYNOPSIS +.Nm lint +.Op Fl abceghprvxzHFV +.Op Fl s Ns | Ns Fl t +.Op Fl i Ns | Ns Fl nu +.Op Fl D Ns Ar name Ns Op =def +.Op Fl U Ns Ar name +.Op Fl I Ns Ar directory +.Op Fl L Ns Ar directory +.Op Fl l Ns Ar library +.Op Fl o Ns Ar outputfile +.Ar +.Nm lint +.Op Fl abceghprvzHFV +.Op Fl s Ns | Ns Fl t +.Fl C Ns Ar library +.Op Fl D Ns Ar name Ns Op =def +.Op Fl I Ns Ar directory +.Op Fl U Ns Ar name +.Ar +.Sh DESCRIPTION +.Nm +attempts to detect features of the named C program files +that are likely to be bugs, to be non-portable, or to be +wasteful. It also performs stricter type checking then does +the C compiler. +.Nm +runs the C preprocessor as its first phase, with the +preprocessor symbol +.Sy lint +defined to allow certain questionable code to be altered +or skipped by +.Nm lint . +Therefore, this symbol should be thought of as a reserved +word for all code that is to be checked by +.Nm lint . +.Pp +Among the possible problems that are currently noted are +unreachable statements, loops not entered at the top, +variables declared and not used, and logical expressions +with constant values. Function calls are checked for +inconsistencies, such as calls to functions that return +values in some places and not in others, functions called +with varying numbers of arguments, function calls that +pass arguments of a type other than the type the function +expects to receive, functions whose values are not used, +and calls to functions not returning values that use +the non-existent return value of the function. +.Pp +Filename arguments ending with +.Pa \&.c +are taken to be C source files. Filename arguments with +names ending with +.Pa \&.ln +are taken to be the result of an earlier invocation of +.Nm lint , +with either the +.Fl i , +.Fl o +or +.Fl C +option in effect. The +.Pa \&.ln +files are analogous to the +.Pa \&.o +(object) files produced by +.Xr cc 1 +from +.Pa \&.c +files. +.Nm +also accepts special libraries specified with the +.Fl l +option, which contain definitions of library routines and +variables. +.Pp +.Nm +takes all the +.Pa \&.c , \&.ln , +and +.Pa llib-l Ns Ar library Ns Pa \&.ln +(lint library) files and processes them in command-line order. +By default, +.Nm +appends the standard C lint library +.Pq Pa llib-lc.ln +to the end of the list of files. When the +.Fl i +option is used, the +.Pa \&.ln +files are ignored. +Also, when the +.Fl o +or +.Fl i +options are used, the +.Pa llib-l Ns Ar library Ns Pa \&.ln +files are ignored. When the +.Fl i +option is +.Em omitted +the second pass of +.Nm +checks this list of files for mutual compatibility. At this point, +if a complaint stems not from a given source file, but from one of +its included files, the source filename will be printed followed by +a question mark. +.Pp +.Sy Options +.Bl -tag -width Fl +.It Fl a +Report assignments of +.Sy long +values to variables that are not +.Sy long . +.It Fl aa +Additional to +.Fl a , +report +.Em all +assignments of integer values to other integer values which +cause implicit narrowing conversion. +.It Fl b +Report +.Sy break +statements that cannot be reached. This is not the default +because, unfortunately, most +.Xr lex 1 +and many +.Xr yacc 1 +outputs produce many such complaints. +.It Fl c +Complain about casts which have questionable portability. +.It Fl e +Complain about unusual operations on +.Sy enum Ns -Types +and combinations of +.Sy enum Ns - +and +.Sy integer Ns -Types. +.It Fl g +Don't print warnings for some extensions of +.Xr gcc 1 +to the C language. Currently these are nonconstant initializers in +automatic aggregate initializations, arithmetic on pointer to void, +zero sized structures, subscripting of non-lvalue arrays, prototypes +overriding old style function declarations and long long +integer types. The +.Fl g +flag also turns on the keywords +.Sy asm +and +.Sy inline +(alternate keywords with leading underscores for both +.Sy asm +and +.Sy inline +are always available). +.It Fl h +Apply a number of heuristic tests to attempt to intuit +bugs, improve style, and reduce waste. +.It Fl i +Produce a +.Pa \&.ln +file for every +.Pa \&.c +file on the command line. These +.Pa \&.ln +files are the product of +.Nm lint Ns 's +first pass only, and are not checked for compatibility +between functions. +.It Fl n +Do not check compatibility against the standard library. +.It Fl p +Attempt to check portability of code to other dialects of C. +.It Fl r +In case of redeclarations report the position of the +previous declaration. +.It Fl s +Strict ANSI C mode. Issue warnings and errors required by ANSI C. +Also do not produce warnings for constructs which behave +differently in traditional C and ANSI C. With the +.Fl s +flag, +.Li __STRICT_ANSI__ +is a predefined preprocessor macro. +.It Fl t +Traditional C mode. +.Li __STDC__ +is not predefined in this mode. Warnings are printed for constructs +not allowed in traditional C. Warnings for constructs which behave +differently in traditional C and ANSI C are suppressed. Preprocessor +macros describing the machine type (e.g. +.Li sun3 Ns ) +and machine architecture (e.g. +.Li m68k Ns ) +are defined without leading and trailing underscores. The keywords +.Sy const Ns , +.Sy volatile +and +.Sy signed +are not available in traditional C mode (although the alternate +keywords with leading underscores still are). +.It Fl u +Do not complain about functions and external variables used +and not defined, or defined and not used (this is suitable +for running +.Nm +on a subset of files comprising part of a larger program). +.It Fl v +Suppress complaints about unused arguments in functions. +.It Fl x +Report variables refferd to by +.Sy extern +declarations, but never used. +.It Fl z +Do not complain about structures that are never defined +(for example, using a structure pointer without knowing +its contents). +.It Fl C Ns Ar library +Create a +.Nm +library with the name +.Pa llib-l Ns Ar library Ns Pa .ln . +This library is built from all +.Pa \&.c +and +.Pa \&.ln +input files. After all global definitions of functions and +variables in these files are written to the newly created library, +.Nm +checks all input files, including libraries specified with the +.Fl l +option, for mutual compatibility. +.It Fl D Ns Ar name Ns Op =def +Define +.Ar name +for +.Xr cpp 1 , +as if by a +.Li #define +directive. If no definition is given, +.Ar name +is defined as 1. +.It Fl I Ns Ar directory +Add +.Ar directory +to the list of directories in which to search for include files. +.It Fl l Ns Ar library +Include the lint library +.Pa llib-l Ns Ar library Ns Pa \&.ln . +.It Fl L Ns Ar directory +Search for lint libraries in +.Ar directory +and +.Ar directory Ns Pa /lint +before searching the standard place. +.It Fl F +Print pathnames of files. +.Nm +normally prints the filename without the path. +.It Fl H +If a complaint stems from an included file +.Nm +prints the name of the included file instead of the source file name +followed by a question mark. +.It Fl o Ns Ar outputfile +Name the output file +.Ar outputfile . +The output file produced is the input that is given to +.Nm lint Ns 's +second pass. The +.Fl o +option simply saves this file in the named output file. If the +.Fl i +option is also used the files are not checked for compatibility. +To produce a +.Pa llib-l Ns Ar library Ns Pa \&.ln +without extraneous messages, use of the +.Fl u +option is suggested. The +.Fl v +option is useful if the source file(s) for the lint library +are just external interfaces. +.It Fl U Ns Ar name +Remove any initial definition of +.Ar name +for the preprocessor. +.It Fl V +Print the command lines constructed by the controller program to +run the C preprocessor and +.Nm lint Ns 's +first and second pass. +.El +.Pp +.Sy Input Grammar +.Pp +.Nm lint Ns 's +first pass reads standard C source files. +.Nm +recognizes the following C comments as commands. +.Bl -tag -width Fl +.It Li /* ARGSUSED Ns Ar n Li */ +makes +.Nm +check only the first +.Ar n +arguments for usage; a missing +.Ar n +is taken to be 0 (this option acts like the +.Fl v +option for the next function). +.It Li /* CONSTCOND */ No or Xo +.Li /* CONSTANTCOND */ No or +.Li /* CONSTANTCONDITION */ +.Xc +suppress complaints about constant operands for the next expression. +.It Li /*\ FALLTHRU\ */ No or Xo +.Li /* FALLTHROUGH */ +.Xc +suppress complaints about fall through to a +.Sy case +or +.Sy default +labelled statement. This directive should be placed immediately +preceding the label. +.It Li /* LINTLIBRARY */ +At the beginning of a file, mark all functions and variables defined +in this file as +.Em used . +Also shut off complaints about unused function arguments. +.It Li /* LINTED Xo +.Op Ar comment +.Li */ No or +.Li /* NOSTRICT +.Op Ar comment +.Li */ +.Xc +Suppresses any intra-file warning except those dealing with +unused variables or functions. This directive should be placed +on the line immediately preceding where the lint warning occured. +.It Li /* LONGLONG */ +Suppress complaints about use of long long integer types. +.It Li /* NOTREACHED */ +At appropriate points, inhibit complaints about unreachable code. +(This comment is typically placed just after calls to functions +like +.Xr exit 2 ). +.It Li /* PRINTFLIKE Ns Ar n Li */ +makes +.Nm +check the first +.Pq Ar n Ns No -1 +arguments as usual. The +.Ar n Ns No -th +argument is interpreted as a +.Sy printf +format string that is used to check the remaining arguments. +.It Li /* PROTOLIB Ns Ar n Li */ +causes +.Nm +to treat function declaration prototypes as function definitions +if +.Ar n +is non-zero. This directive can only be used in conjunction with +the +.Li /* LINTLIBRARY */ +directive. If +.Ar n +is zero, function prototypes will be treated normally. +.It Li /* SCANFLIKE Ns Ar n Li */ +makes +.Nm +check the first +.Pq Ar n Ns No -1 +arguments as usual. The +.Ar n Ns No -th +argument is interpreted as a +.Sy scanf +format string that is used to check the remaining arguments. +.It Li /* VARARGS Ns Ar n Li */ +Suppress the usual checking for variable numbers of arguments in +the following function declaration. The data types of the first +.Ar n +arguments are checked; a missing +.Ar n +is taken to be 0. +.El +.Pp +The behavior of the +.Fl i +and the +.Fl o +options allows for incremental use of +.Nm +on a set of C source files. Generally, one invokes +.Nm +once for each source file with the +.Fl i +option. Each of these invocations produces a +.Pa \&.ln +file that corresponds to the +.Pa \&.c +file, and prints all messages that are about just that +source file. After all the source files have been separetely +run through +.Nm lint , +it is invoked once more (without the +.Fl i +option), listing all the +.Pa \&.ln +files with the needed +.Fl l Ns Ar library +options. this will print all the inter-file inconsistencies. This +scheme works well with +.Xr make 1 ; +it allows +.Xr make 1 +to be used to +.Nm +only the source files that have been modified since the last +time the set of source files were +.Nm lint Ns No ed . +.Sh ENVIRONMENT +.Bl -tag -width Fl +.It Ev LIBDIR +the directory where the lint libraries specified by the +.Fl l Ns Ar library +option must exist. If this environment variable is undefined, +then the default path +.Pa /usr/libdata/lint +will be used to search for the libraries. +.It Ev TMPDIR +usually the path for temporary files can be redefined by setting +this environment variable. +.El +.Sh FILES +.Bl -tag -width /usr/libdata/lint/llib-lc.ln -compact +.It Pa /usr/libexec/lint Ns Bq 12 +programs +.It Pa /usr/libdata/lint/llib-l*.ln +various prebuilt lint libraries +.It Pa /tmp/lint* +temporaries +.Sh SEE ALSO +.Xr cc 1 , +.Xr cpp 1 , +.Xr make 1 +.Sh AUTHORS +Jochen Pohl +.Sh BUGS +The routines +.Xr exit 2 , +.Xr longjmp 3 +and other functions that do not return are not understood; this +causes various incorrect diagnostics. +.Pp +Static functions which are used only before their first +extern declaration are reported as unused. +.Pp +Libraries created by the +.Fl o +option will, when used in later +.Nm +runs, cause certain errors that were reported when the libraries +were created to be reported again, and cause line numbers and file +names from the original source used to create those libraries +to be reported in error messages. For these reasons, it is recommended +to use the +.Fl C +option to create lint libraries. diff --git a/usr.bin/xlint/xlint/pathnames.h b/usr.bin/xlint/xlint/pathnames.h new file mode 100644 index 0000000..d03845d --- /dev/null +++ b/usr.bin/xlint/xlint/pathnames.h @@ -0,0 +1,38 @@ +/* $NetBSD: pathnames.h,v 1.2 1995/07/03 21:25:20 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. + */ + +/* directory where lint1 and lint2 reside */ +#define PATH_LIBEXEC "/usr/libexec" + +/* default library search path */ +#define PATH_LINTLIB "/usr/libdata/lint" diff --git a/usr.bin/xlint/xlint/xlint.c b/usr.bin/xlint/xlint/xlint.c new file mode 100644 index 0000000..3e57797 --- /dev/null +++ b/usr.bin/xlint/xlint/xlint.c @@ -0,0 +1,761 @@ +/* $NetBSD: xlint.c,v 1.3 1995/10/23 14:29:30 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: xlint.c,v 1.3 1995/10/23 14:29:30 jpo Exp $"; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "lint.h" +#include "pathnames.h" + +/* directory for temporary files */ +static const char *tmpdir; + +/* path name for cpp output */ +static char *cppout; + +/* files created by 1st pass */ +static char **p1out; + +/* input files for 2nd pass (without libraries) */ +static char **p2in; + +/* library which will be created by 2nd pass */ +static char *p2out; + +/* flags always passed to cpp */ +static char **cppflags; + +/* flags for cpp, controled by sflag/tflag */ +static char **lcppflgs; + +/* flags for lint1 */ +static char **l1flags; + +/* flags for lint2 */ +static char **l2flags; + +/* libraries for lint2 */ +static char **l2libs; + +/* default libraries */ +static char **deflibs; + +/* additional libraries */ +static char **libs; + +/* search path for libraries */ +static char **libsrchpath; + +/* flags */ +static int iflag, oflag, Cflag, sflag, tflag, Fflag; + +/* print the commands executed to run the stages of compilation */ +static int Vflag; + +/* filename for oflag */ +static char *outputfn; + +/* reset after first .c source has been processed */ +static int first = 1; + +/* + * name of a file which is currently written by a child and should + * be removed after abnormal termination of the child + */ +static const char *currfn; + + +static void appstrg __P((char ***, char *)); +static void appcstrg __P((char ***, const char *)); +static void applst __P((char ***, char *const *)); +static void freelst __P((char ***)); +static char *concat2 __P((const char *, const char *)); +static char *concat3 __P((const char *, const char *, const char *)); +static void terminate __P((int)); +static const char *basename __P((const char *, int)); +static void appdef __P((char ***, const char *)); +static void usage __P((void)); +static void fname __P((const char *, int)); +static void runchild __P((const char *, char *const *, const char *)); +static void findlibs __P((char *const *)); +static int rdok __P((const char *)); +static void lint2 __P((void)); +static void cat __P((char *const *, const char *)); + +/* + * Some functions to deal with lists of strings. + * Take care that we get no surprises in case of asyncron signals. + */ +static void +appstrg(lstp, s) + char ***lstp, *s; +{ + char **lst, **olst; + int i; + + olst = *lstp; + for (i = 0; olst[i] != NULL; i++) ; + lst = xmalloc((i + 2) * sizeof (char *)); + (void)memcpy(lst, olst, i * sizeof (char *)); + lst[i] = s; + lst[i + 1] = NULL; + *lstp = lst; +} + +static void +appcstrg(lstp, s) + char ***lstp; + const char *s; +{ + appstrg(lstp, xstrdup(s)); +} + +static void +applst(destp, src) + char ***destp; + char *const *src; +{ + int i, k; + char **dest, **odest; + + odest = *destp; + for (i = 0; odest[i] != NULL; i++) ; + for (k = 0; src[k] != NULL; k++) ; + dest = xmalloc((i + k + 1) * sizeof (char *)); + (void)memcpy(dest, odest, i * sizeof (char *)); + for (k = 0; src[k] != NULL; k++) + dest[i + k] = xstrdup(src[k]); + dest[i + k] = NULL; + *destp = dest; + free(odest); +} + +static void +freelst(lstp) + char ***lstp; +{ + char *s; + int i; + + for (i = 0; (*lstp)[i] != NULL; i++) ; + while (i-- > 0) { + s = (*lstp)[i]; + (*lstp)[i] = NULL; + free(s); + } +} + +static char * +concat2(s1, s2) + const char *s1, *s2; +{ + char *s; + + s = xmalloc(strlen(s1) + strlen(s2) + 1); + (void)strcpy(s, s1); + (void)strcat(s, s2); + + return (s); +} + +static char * +concat3(s1, s2, s3) + const char *s1, *s2, *s3; +{ + char *s; + + s = xmalloc(strlen(s1) + strlen(s2) + strlen(s3) + 1); + (void)strcpy(s, s1); + (void)strcat(s, s2); + (void)strcat(s, s3); + + return (s); +} + +/* + * Clean up after a signal. + */ +static void +terminate(signo) + int signo; +{ + int i; + + if (cppout != NULL) + (void)remove(cppout); + + if (p1out != NULL) { + for (i = 0; p1out[i] != NULL; i++) + (void)remove(p1out[i]); + } + + if (p2out != NULL) + (void)remove(p2out); + + if (currfn != NULL) + (void)remove(currfn); + + exit(signo != 0 ? 1 : 0); +} + +/* + * Returns a pointer to the last component of strg after delim. + * Returns strg if the string does not contain delim. + */ +static const char * +basename(strg, delim) + const char *strg; + int delim; +{ + const char *cp, *cp1, *cp2; + + cp = cp1 = cp2 = strg; + while (*cp != '\0') { + if (*cp++ == delim) { + cp2 = cp1; + cp1 = cp; + } + } + return (*cp1 == '\0' ? cp2 : cp1); +} + +static void +appdef(lstp, def) + char ***lstp; + const char *def; +{ + appstrg(lstp, concat2("-D__", def)); + appstrg(lstp, concat3("-D__", def, "__")); +} + +static void +usage() +{ + (void)printf("lint [-abceghprvxzHF] [-s|-t] [-i|-nu] [-Dname[=def]] [-Uname]\n"); + (void)printf(" [-Idirectory] [-Ldirectory] [-llibrary] [-ooutputfile] file ...\n"); + (void)printf("\n"); + (void)printf("lint [-abceghprvzHF] [-s|-t] -Clibrary [-Dname[=def]]\n"); + (void)printf(" [-Idirectory] [-Uname] file ...\n"); + terminate(-1); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int c; + char flgbuf[3], *tmp, *s; + size_t len; + struct utsname un; + + if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) { + tmpdir = xstrdup(_PATH_TMP); + } else { + s = xmalloc(len + 2); + (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/"); + tmpdir = s; + } + + cppout = xmalloc(strlen(tmpdir) + sizeof ("lint0.XXXXXX")); + (void)sprintf(cppout, "%slint0.XXXXXX", tmpdir); + if (mktemp(cppout) == NULL) { + warn("can't make temp"); + terminate(-1); + } + + p1out = xcalloc(1, sizeof (char *)); + p2in = xcalloc(1, sizeof (char *)); + cppflags = xcalloc(1, sizeof (char *)); + lcppflgs = xcalloc(1, sizeof (char *)); + l1flags = xcalloc(1, sizeof (char *)); + l2flags = xcalloc(1, sizeof (char *)); + l2libs = xcalloc(1, sizeof (char *)); + deflibs = xcalloc(1, sizeof (char *)); + libs = xcalloc(1, sizeof (char *)); + libsrchpath = xcalloc(1, sizeof (char *)); + + appcstrg(&cppflags, "-lang-c"); + appcstrg(&cppflags, "-undef"); + appcstrg(&cppflags, "-$"); + appcstrg(&cppflags, "-C"); + appcstrg(&cppflags, "-Wcomment"); + appcstrg(&cppflags, "-D__NetBSD__"); + appcstrg(&cppflags, "-Dlint"); /* XXX don't def. with -s */ + appdef(&cppflags, "lint"); + appdef(&cppflags, "unix"); + + appcstrg(&lcppflgs, "-Wtraditional"); + + if (uname(&un) == -1) + err(1, "uname"); + appdef(&cppflags, un.machine); + appstrg(&lcppflgs, concat2("-D", un.machine)); + +#ifdef MACHINE_ARCH + if (strcmp(un.machine, MACHINE_ARCH) != 0) { + appdef(&cppflags, MACHINE_ARCH); + appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH)); + } +#endif + + appcstrg(&deflibs, "c"); + + if (signal(SIGHUP, terminate) == SIG_IGN) + (void)signal(SIGHUP, SIG_IGN); + (void)signal(SIGINT, terminate); + (void)signal(SIGQUIT, terminate); + (void)signal(SIGTERM, terminate); + + while (argc > optind) { + + argc -= optind; + argv += optind; + optind = 0; + + c = getopt(argc, argv, "abceghil:no:prstuvxzC:D:FHI:L:U:V"); + + switch (c) { + + case 'a': + case 'b': + case 'c': + case 'e': + case 'g': + case 'r': + case 'v': + case 'z': + (void)sprintf(flgbuf, "-%c", c); + appcstrg(&l1flags, flgbuf); + break; + + case 'F': + Fflag = 1; + /* FALLTHROUGH */ + case 'u': + case 'h': + (void)sprintf(flgbuf, "-%c", c); + appcstrg(&l1flags, flgbuf); + appcstrg(&l2flags, flgbuf); + break; + + case 'i': + if (Cflag) + usage(); + iflag = 1; + break; + + case 'n': + freelst(&deflibs); + break; + + case 'p': + appcstrg(&l1flags, "-p"); + appcstrg(&l2flags, "-p"); + if (*deflibs != NULL) { + freelst(&deflibs); + appcstrg(&deflibs, "c"); + } + break; + + case 's': + if (tflag) + usage(); + freelst(&lcppflgs); + appcstrg(&lcppflgs, "-trigraphs"); + appcstrg(&lcppflgs, "-Wtrigraphs"); + appcstrg(&lcppflgs, "-pedantic"); + appcstrg(&lcppflgs, "-D__STRICT_ANSI__"); + appcstrg(&l1flags, "-s"); + appcstrg(&l2flags, "-s"); + sflag = 1; + break; + + case 't': + if (sflag) + usage(); + freelst(&lcppflgs); + appcstrg(&lcppflgs, "-traditional"); + appstrg(&lcppflgs, concat2("-D", MACHINE)); + appstrg(&lcppflgs, concat2("-D", MACHINE_ARCH)); + appcstrg(&l1flags, "-t"); + appcstrg(&l2flags, "-t"); + tflag = 1; + break; + + case 'x': + appcstrg(&l2flags, "-x"); + break; + + case 'C': + if (Cflag || oflag || iflag) + usage(); + Cflag = 1; + appstrg(&l2flags, concat2("-C", optarg)); + p2out = xmalloc(sizeof ("llib-l.ln") + strlen(optarg)); + (void)sprintf(p2out, "llib-l%s.ln", optarg); + freelst(&deflibs); + break; + + case 'D': + case 'I': + case 'U': + (void)sprintf(flgbuf, "-%c", c); + appstrg(&cppflags, concat2(flgbuf, optarg)); + break; + + case 'l': + appcstrg(&libs, optarg); + break; + + case 'o': + if (Cflag || oflag) + usage(); + oflag = 1; + outputfn = xstrdup(optarg); + break; + + case 'L': + appcstrg(&libsrchpath, optarg); + break; + + case 'H': + appcstrg(&l2flags, "-H"); + break; + + case 'V': + Vflag = 1; + break; + + case '?': + usage(); + /* NOTREACHED */ + + case -1: + /* filename */ + fname(argv[0], argc == 1); + first = 0; + optind = 1; + } + + } + + if (first) + usage(); + + if (iflag) + terminate(0); + + if (!oflag) { + if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0) + s = PATH_LINTLIB; + appcstrg(&libsrchpath, s); + findlibs(libs); + findlibs(deflibs); + } + + (void)printf("Lint pass2:\n"); + lint2(); + + if (oflag) + cat(p2in, outputfn); + + if (Cflag) + p2out = NULL; + + terminate(0); + /* NOTREACHED */ +} + +/* + * Read a file name from the command line + * and pass it through lint1 if it is a C source. + */ +static void +fname(name, last) + const char *name; + int last; +{ + const char *bn, *suff; + char **args, *ofn, *path; + size_t len; + + bn = basename(name, '/'); + suff = basename(bn, '.'); + + if (strcmp(suff, "ln") == 0) { + /* only for lint2 */ + if (!iflag) + appcstrg(&p2in, name); + return; + } + + if (strcmp(suff, "c") != 0 && + (strncmp(bn, "llib-l", 6) != 0 || bn != suff)) { + warnx("unknown file type: %s\n", name); + return; + } + + if (!iflag || !first || !last) + (void)printf("%s:\n", Fflag ? name : bn); + + /* build the name of the output file of lint1 */ + if (oflag) { + ofn = outputfn; + outputfn = NULL; + oflag = 0; + } else if (iflag) { + ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2)); + len = bn == suff ? strlen(bn) : (suff - 1) - bn; + (void)sprintf(ofn, "%.*s", (int)len, bn); + (void)strcat(ofn, ".ln"); + } else { + ofn = xmalloc(strlen(tmpdir) + sizeof ("lint1.XXXXXX")); + (void)sprintf(ofn, "%slint1.XXXXXX", tmpdir); + if (mktemp(ofn) == NULL) { + warn("can't make temp"); + terminate(-1); + } + } + if (!iflag) + appcstrg(&p1out, ofn); + + args = xcalloc(1, sizeof (char *)); + + /* run cpp */ + + path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/cpp")); + (void)sprintf(path, "%s/cpp", PATH_LIBEXEC); + + appcstrg(&args, path); + applst(&args, cppflags); + applst(&args, lcppflgs); + appcstrg(&args, name); + appcstrg(&args, cppout); + + runchild(path, args, cppout); + free(path); + freelst(&args); + + /* run lint1 */ + + path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint1")); + (void)sprintf(path, "%s/lint1", PATH_LIBEXEC); + + appcstrg(&args, path); + applst(&args, l1flags); + appcstrg(&args, cppout); + appcstrg(&args, ofn); + + runchild(path, args, ofn); + free(path); + freelst(&args); + + appcstrg(&p2in, ofn); + free(ofn); + + free(args); +} + +static void +runchild(path, args, crfn) + const char *path, *crfn; + char *const *args; +{ + int status, rv, signo, i; + + if (Vflag) { + for (i = 0; args[i] != NULL; i++) + (void)printf("%s ", args[i]); + (void)printf("\n"); + } + + currfn = crfn; + + (void)fflush(stdout); + + switch (fork()) { + case -1: + warn("cannot fork"); + terminate(-1); + /* NOTREACHED */ + default: + /* parent */ + break; + case 0: + /* child */ + (void)execv(path, args); + warn("cannot exec %s", path); + exit(1); + /* NOTREACHED */ + } + + while ((rv = wait(&status)) == -1 && errno == EINTR) ; + if (rv == -1) { + warn("wait"); + terminate(-1); + } + if (WIFSIGNALED(status)) { + signo = WTERMSIG(status); + warnx("%s got SIG%s", path, sys_signame[signo]); + terminate(-1); + } + if (WEXITSTATUS(status) != 0) + terminate(-1); + currfn = NULL; +} + +static void +findlibs(liblst) + char *const *liblst; +{ + int i, k; + const char *lib, *path; + char *lfn; + size_t len; + + lfn = NULL; + + for (i = 0; (lib = liblst[i]) != NULL; i++) { + for (k = 0; (path = libsrchpath[k]) != NULL; k++) { + len = strlen(path) + strlen(lib); + lfn = xrealloc(lfn, len + sizeof ("/llib-l.ln")); + (void)sprintf(lfn, "%s/llib-l%s.ln", path, lib); + if (rdok(lfn)) + break; + lfn = xrealloc(lfn, len + sizeof ("/lint/llib-l.ln")); + (void)sprintf(lfn, "%s/lint/llib-l%s.ln", path, lib); + if (rdok(lfn)) + break; + } + if (path != NULL) { + appstrg(&l2libs, concat2("-l", lfn)); + } else { + warnx("cannot find llib-l%s.ln", lib); + } + } + + free(lfn); +} + +static int +rdok(path) + const char *path; +{ + struct stat sbuf; + + if (stat(path, &sbuf) == -1) + return (0); + if ((sbuf.st_mode & S_IFMT) != S_IFREG) + return (0); + if (access(path, R_OK) == -1) + return (0); + return (1); +} + +static void +lint2() +{ + char *path, **args; + + args = xcalloc(1, sizeof (char *)); + + path = xmalloc(strlen(PATH_LIBEXEC) + sizeof ("/lint2")); + (void)sprintf(path, "%s/lint2", PATH_LIBEXEC); + + appcstrg(&args, path); + applst(&args, l2flags); + applst(&args, l2libs); + applst(&args, p2in); + + runchild(path, args, p2out); + free(path); + freelst(&args); + free(args); +} + +static void +cat(srcs, dest) + char *const *srcs; + const char *dest; +{ + int ifd, ofd, i; + char *src, *buf; + ssize_t rlen; + + if ((ofd = open(dest, O_WRONLY | O_CREAT | O_TRUNC, 0666)) == -1) { + warn("cannot open %s", dest); + terminate(-1); + } + + buf = xmalloc(MBLKSIZ); + + for (i = 0; (src = srcs[i]) != NULL; i++) { + if ((ifd = open(src, O_RDONLY)) == -1) { + free(buf); + warn("cannot open %s", src); + terminate(-1); + } + do { + if ((rlen = read(ifd, buf, MBLKSIZ)) == -1) { + free(buf); + warn("read error on %s", src); + terminate(-1); + } + if (write(ofd, buf, (size_t)rlen) == -1) { + free(buf); + warn("write error on %s", dest); + terminate(-1); + } + } while (rlen == MBLKSIZ); + (void)close(ifd); + } + (void)close(ofd); + free(buf); +} + -- cgit v1.1