1988-09-20 16:44:27 +00:00
|
|
|
/*
|
|
|
|
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
|
|
|
|
* See the copyright notice in the ACK home directory, in the file "Copyright".
|
|
|
|
*/
|
|
|
|
/* $Header$ */
|
|
|
|
/* Lint status checking */
|
|
|
|
|
|
|
|
#include "lint.h"
|
|
|
|
|
|
|
|
#ifdef LINT
|
|
|
|
|
|
|
|
#include <alloc.h> /* for st_free */
|
|
|
|
#include "assert.h"
|
|
|
|
#include "arith.h" /* definition arith */
|
|
|
|
#include "label.h" /* definition label */
|
|
|
|
#include "expr.h"
|
|
|
|
#include "idf.h"
|
|
|
|
#include "def.h"
|
|
|
|
#include "code.h" /* RVAL etc */
|
|
|
|
#include "LLlex.h"
|
|
|
|
#include "Lpars.h"
|
|
|
|
#include "stack.h"
|
|
|
|
#include "type.h"
|
|
|
|
#include "level.h"
|
|
|
|
#include "nofloat.h"
|
|
|
|
#include "l_lint.h"
|
|
|
|
#include "l_brace.h"
|
|
|
|
#include "l_state.h"
|
1988-10-12 16:05:17 +00:00
|
|
|
#include "l_comment.h"
|
1988-09-20 16:44:27 +00:00
|
|
|
#include "l_outdef.h"
|
|
|
|
|
|
|
|
#define min(a, b) ((a) < (b) ? (a) : (b))
|
|
|
|
|
|
|
|
extern char *symbol2str();
|
|
|
|
extern char *func_name;
|
|
|
|
extern struct type *func_type;
|
|
|
|
extern int func_notypegiven;
|
|
|
|
extern char loptions[];
|
|
|
|
|
|
|
|
/* global variables for the lint_stack */
|
|
|
|
struct lint_stack_entry stack_bottom;
|
|
|
|
struct lint_stack_entry *top_ls = &stack_bottom;
|
|
|
|
|
|
|
|
/* global variables for the brace stack */
|
|
|
|
int brace_count;
|
|
|
|
struct brace brace_bottom;
|
|
|
|
struct brace *top_br = &brace_bottom;
|
|
|
|
|
|
|
|
static print_autos();
|
|
|
|
|
1988-11-03 15:18:46 +00:00
|
|
|
lint_init_stack()
|
|
|
|
{
|
|
|
|
/* Allocate some memory for the global stack_bottom
|
|
|
|
*/
|
|
|
|
stack_bottom.ls_current = new_state();
|
|
|
|
}
|
|
|
|
|
1988-09-20 16:44:27 +00:00
|
|
|
lint_start_local()
|
|
|
|
{
|
|
|
|
register struct brace *br = new_brace();
|
|
|
|
|
|
|
|
brace_count++;
|
|
|
|
br->br_count = brace_count;
|
|
|
|
br->br_level = level;
|
|
|
|
br->next = top_br;
|
|
|
|
top_br = br;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_local_level(stl)
|
|
|
|
struct stack_level *stl;
|
|
|
|
{
|
|
|
|
if (s_NOTREACHED) {
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
s_NOTREACHED = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top_ls->ls_class == CASE && level == top_ls->ls_level) {
|
|
|
|
lint_break_stmt();
|
|
|
|
/* To prevent a warning for the case
|
|
|
|
* switch (cond) {
|
|
|
|
* int i;
|
|
|
|
* case 0:
|
|
|
|
* i = 0;
|
|
|
|
* use(i);
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
check_autos();
|
|
|
|
end_brace(stl);
|
|
|
|
}
|
|
|
|
|
|
|
|
end_brace(stl)
|
|
|
|
struct stack_level *stl;
|
|
|
|
{
|
|
|
|
/* Check if static variables and labels are used and/or set.
|
|
|
|
*/
|
|
|
|
register struct stack_entry *se = stl->sl_entry;
|
|
|
|
register struct brace *br;
|
|
|
|
|
|
|
|
while (se) {
|
|
|
|
register struct idf *idf = se->se_idf;
|
|
|
|
register struct def *def = idf->id_def;
|
|
|
|
|
|
|
|
if (def) {
|
|
|
|
lint_1_local(idf, def);
|
|
|
|
}
|
|
|
|
se = se->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
br = top_br;
|
|
|
|
top_br = br->next;
|
|
|
|
free_brace(br);
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_1_local(idf, def)
|
|
|
|
struct idf *idf;
|
|
|
|
struct def *def;
|
|
|
|
{
|
|
|
|
register int sc = def->df_sc;
|
|
|
|
|
|
|
|
if ( (sc == STATIC || sc == LABEL)
|
|
|
|
&& !def->df_used
|
|
|
|
) {
|
|
|
|
def_warning(def, "%s %s declared but not used in function %s",
|
|
|
|
symbol2str(sc), idf->id_text, func_name);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( loptions['h']
|
|
|
|
&& sc == AUTO
|
|
|
|
&& !def->df_initialized
|
|
|
|
&& def->df_firstbrace != 0
|
|
|
|
&& def->df_minlevel != level
|
|
|
|
) {
|
|
|
|
register int diff = def->df_minlevel - level;
|
|
|
|
|
|
|
|
def_warning(def,
|
|
|
|
"local %s could be declared %d level%s deeper",
|
|
|
|
idf->id_text, diff, (diff == 1 ? "" : "s")
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_global_level(stl)
|
|
|
|
struct stack_level *stl;
|
|
|
|
{
|
|
|
|
register struct stack_entry *se = stl->sl_entry;
|
|
|
|
|
|
|
|
ASSERT(level == L_GLOBAL);
|
|
|
|
while (se) {
|
|
|
|
register struct idf *idf = se->se_idf;
|
|
|
|
register struct def *def = idf->id_def;
|
|
|
|
|
|
|
|
if (def) {
|
|
|
|
lint_1_global(idf, def);
|
|
|
|
}
|
|
|
|
se = se->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_1_global(idf, def)
|
|
|
|
struct idf *idf;
|
|
|
|
struct def *def;
|
|
|
|
{
|
|
|
|
register int sc = def->df_sc;
|
|
|
|
register int fund = def->df_type->tp_fund;
|
|
|
|
|
|
|
|
switch (sc) {
|
|
|
|
case STATIC:
|
|
|
|
case EXTERN:
|
|
|
|
case GLOBAL:
|
|
|
|
case IMPLICIT:
|
|
|
|
if (fund == ERRONEOUS)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (def->df_set || def->df_used) {
|
|
|
|
/* Output a line to the intermediate file for
|
|
|
|
* used external variables (including functions)
|
|
|
|
*/
|
|
|
|
output_use(idf);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (sc == STATIC) {
|
|
|
|
if (def->df_set) {
|
|
|
|
def_warning(def,
|
|
|
|
"%s %s %s set but not used",
|
|
|
|
symbol2str(sc),
|
|
|
|
symbol2str(fund),
|
|
|
|
idf->id_text);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
def_warning(def,
|
|
|
|
"%s %s %s not used anywhere",
|
|
|
|
symbol2str(sc),
|
|
|
|
symbol2str(fund),
|
|
|
|
idf->id_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (loptions['x']) {
|
|
|
|
register char *fn = def->df_file;
|
|
|
|
|
|
|
|
if ( (sc == EXTERN || sc == GLOBAL)
|
|
|
|
&& def->df_alloc == 0
|
|
|
|
&& !def->df_set
|
|
|
|
&& !def->df_initialized
|
|
|
|
&& strcmp(&fn[strlen(fn)-2], ".c") == 0
|
|
|
|
) {
|
|
|
|
def_warning(def,
|
|
|
|
"%s %s %s not used anywhere",
|
|
|
|
symbol2str(sc),
|
|
|
|
symbol2str(fund),
|
|
|
|
idf->id_text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
change_state(idf, to_state)
|
|
|
|
struct idf *idf;
|
|
|
|
{
|
|
|
|
/* Changes the state of the variable identified by idf in the current state
|
|
|
|
* on top of the stack.
|
|
|
|
* For non-automatic variables, the fields in the def-descriptor are set too.
|
|
|
|
*/
|
|
|
|
register struct def *def = idf->id_def;
|
|
|
|
register struct auto_def *a = top_ls->ls_current->st_auto_list;
|
|
|
|
|
|
|
|
if (def) {
|
|
|
|
if (to_state == SET)
|
|
|
|
def->df_set = 1;
|
|
|
|
else
|
|
|
|
def->df_used = 1;
|
|
|
|
|
|
|
|
if (def->df_firstbrace == 0) {
|
|
|
|
def->df_firstbrace = brace_count;
|
|
|
|
def->df_minlevel = level;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
register struct brace *br = top_br;
|
|
|
|
|
|
|
|
/* find the smallest brace range from which
|
|
|
|
firstbrace is visible
|
|
|
|
*/
|
|
|
|
while (br && br->br_count > def->df_firstbrace) {
|
|
|
|
br = br->next;
|
|
|
|
}
|
|
|
|
ASSERT(br && def->df_minlevel >= br->br_level);
|
|
|
|
def->df_minlevel = br->br_level;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
while(a && a->ad_idf != idf)
|
|
|
|
a = a->next;
|
|
|
|
if (a == 0) /* identifier not in list */
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (to_state == SET) {
|
|
|
|
a->ad_maybe_set = 0;
|
|
|
|
a->ad_set = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/* else to_state == USED */
|
|
|
|
if (!a->ad_set) {
|
|
|
|
warning("%s%s uninitialized", idf->id_text,
|
|
|
|
(a->ad_maybe_set ? " possibly" : "")
|
|
|
|
);
|
|
|
|
a->ad_maybe_set = 0;
|
|
|
|
a->ad_set = 1; /* one warning */
|
|
|
|
}
|
|
|
|
a->ad_used = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern struct stack_level *local_level;
|
|
|
|
|
|
|
|
add_auto(idf) /* to current state on top of lint_stack */
|
|
|
|
struct idf *idf;
|
|
|
|
{
|
|
|
|
/* Check if idf's definition is really an auto (or register).
|
|
|
|
* It could be a static or extern too.
|
|
|
|
* Watch out for register formal parameters.
|
|
|
|
*/
|
|
|
|
register struct def *def = idf->id_def;
|
|
|
|
register struct auto_def *a;
|
|
|
|
|
|
|
|
if (!def)
|
|
|
|
return;
|
|
|
|
switch (def->df_sc) {
|
|
|
|
case AUTO:
|
|
|
|
case REGISTER:
|
|
|
|
if (def->df_level < L_LOCAL)
|
|
|
|
return; /* a register formal */
|
|
|
|
a = new_auto_def();
|
|
|
|
a->ad_idf = idf;
|
|
|
|
a->ad_def = idf->id_def;
|
|
|
|
a->ad_used = def->df_used;
|
|
|
|
a->ad_set = def->df_set;
|
|
|
|
a->next = top_ls->ls_current->st_auto_list;
|
|
|
|
top_ls->ls_current->st_auto_list = a;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
check_autos()
|
|
|
|
{
|
|
|
|
/* Before leaving a block remove the auto_defs of the automatic
|
|
|
|
* variables on this level and check if they are used
|
|
|
|
*/
|
|
|
|
register struct auto_def *a1 = top_ls->ls_current->st_auto_list;
|
|
|
|
register struct auto_def *a2;
|
|
|
|
|
1988-09-26 11:37:38 +00:00
|
|
|
ASSERT(!(a1 && a1->ad_def->df_level > level));
|
1988-09-20 16:44:27 +00:00
|
|
|
while (a1 && a1->ad_def->df_level == level) {
|
|
|
|
a2 = a1;
|
|
|
|
a1 = a1->next;
|
|
|
|
if (!a2->ad_used) {
|
|
|
|
if (a2->ad_set || a2->ad_maybe_set) {
|
|
|
|
def_warning(a2->ad_def,
|
|
|
|
"%s set but not used in function %s",
|
|
|
|
a2->ad_idf->id_text, func_name);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
def_warning(a2->ad_def,
|
|
|
|
"%s neither set nor used in function %s",
|
|
|
|
a2->ad_idf->id_text, func_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_auto_def(a2);
|
|
|
|
}
|
|
|
|
top_ls->ls_current->st_auto_list = a1;
|
|
|
|
}
|
|
|
|
|
|
|
|
check_args_used()
|
|
|
|
{
|
|
|
|
register struct stack_entry *se = local_level->sl_entry;
|
|
|
|
|
1988-09-26 11:37:38 +00:00
|
|
|
ASSERT(level == L_FORMAL1);
|
1988-09-20 16:44:27 +00:00
|
|
|
while (se) {
|
|
|
|
register struct def *def = se->se_idf->id_def;
|
|
|
|
|
1988-10-12 16:05:17 +00:00
|
|
|
if ( (def && !def->df_used)
|
|
|
|
&& !(f_ARGSUSED || LINTLIB)
|
|
|
|
) {
|
1988-09-20 16:44:27 +00:00
|
|
|
def_warning(def, "argument %s not used in function %s",
|
|
|
|
se->se_idf->id_text, func_name);
|
|
|
|
}
|
|
|
|
se = se->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct auto_def *
|
|
|
|
copy_st_auto_list(from_al, lvl)
|
|
|
|
struct auto_def *from_al;
|
|
|
|
{
|
|
|
|
struct auto_def *start = 0;
|
|
|
|
register struct auto_def **hook = &start;
|
|
|
|
|
|
|
|
while (from_al && from_al->ad_def->df_level > lvl) {
|
|
|
|
from_al = from_al->next;
|
|
|
|
}
|
|
|
|
while (from_al) {
|
|
|
|
register struct auto_def *a = new_auto_def();
|
|
|
|
|
|
|
|
*hook = a;
|
|
|
|
*a = *from_al;
|
|
|
|
hook = &a->next;
|
|
|
|
from_al = from_al->next;
|
|
|
|
}
|
|
|
|
|
1988-09-25 17:29:37 +00:00
|
|
|
return start;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
free_st_auto_list(au)
|
|
|
|
register struct auto_def *au;
|
|
|
|
{
|
|
|
|
register struct auto_def *a;
|
|
|
|
|
|
|
|
while (au) {
|
|
|
|
a = au;
|
|
|
|
au = au->next;
|
|
|
|
free_auto_def(a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct state *
|
|
|
|
copy_state(from_st, lvl)
|
|
|
|
struct state *from_st;
|
|
|
|
{
|
|
|
|
/* Memory for the struct state and the struct auto_defs is allocated
|
|
|
|
* by this function
|
|
|
|
*/
|
|
|
|
register struct state *st = new_state();
|
|
|
|
|
|
|
|
st->st_auto_list = copy_st_auto_list(from_st->st_auto_list, lvl);
|
|
|
|
st->st_notreached = from_st->st_notreached;
|
|
|
|
st->st_warned = from_st->st_warned;
|
1988-09-25 17:29:37 +00:00
|
|
|
return st;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
Free_state(stp)
|
|
|
|
struct state **stp;
|
|
|
|
{
|
|
|
|
/* This function also frees the list of auto_defs
|
|
|
|
*/
|
|
|
|
free_st_auto_list((*stp)->st_auto_list);
|
|
|
|
free_state(*stp);
|
|
|
|
*stp = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_settings(state, lvl)
|
|
|
|
struct state *state;
|
|
|
|
{
|
|
|
|
/* The state of all variables on this level are set to 'not set' and
|
|
|
|
* 'not maybe set'. (I think you have to read this twice.)
|
|
|
|
*/
|
|
|
|
register struct auto_def *a = state->st_auto_list;
|
|
|
|
|
|
|
|
while (a && a->ad_def->df_level == lvl) {
|
|
|
|
a->ad_set = a->ad_maybe_set = 0;
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******** M E R G E ********/
|
|
|
|
|
|
|
|
/* modes for merging */
|
|
|
|
#define NORMAL 0
|
|
|
|
#define CASE_BREAK 1
|
|
|
|
#define USE_ONLY 2
|
|
|
|
|
|
|
|
struct auto_def *
|
|
|
|
merge_autos(a1, a2, lvl, mode)
|
|
|
|
struct auto_def *a1, *a2;
|
|
|
|
int mode;
|
|
|
|
{
|
|
|
|
/* Returns a pointer to the result.
|
|
|
|
* a1 is left unchanged.
|
|
|
|
* a2 is used to create this result.
|
|
|
|
* The fields are set as follows:
|
|
|
|
* a1_set + a2_set -> set
|
|
|
|
* + a?_maybe_set -> maybe set
|
|
|
|
* ELSE -> NOT set && NOT maybe set
|
|
|
|
* * + a?_used -> used
|
|
|
|
*
|
|
|
|
* For mode == CASE_BREAK:
|
|
|
|
* First a2 is taken as the result, then
|
|
|
|
* variables NOT set in a2 and set or maybe set in a1 become 'maybe set'
|
|
|
|
*
|
|
|
|
* For mode == USE_ONLY:
|
|
|
|
* Start with a2 as the result.
|
|
|
|
* Variables used in a1 become used in a2.
|
|
|
|
* The rest of the result is not changed.
|
|
|
|
*/
|
|
|
|
register struct auto_def *a;
|
|
|
|
|
|
|
|
while (a1 && a1->ad_def->df_level > lvl) {
|
|
|
|
a1 = a1->next;
|
|
|
|
}
|
|
|
|
while (a2 && a2->ad_def->df_level > lvl) {
|
|
|
|
a = a2;
|
|
|
|
a2 = a2->next;
|
|
|
|
free_auto_def(a);
|
|
|
|
}
|
|
|
|
a = a2; /* pointer to the result */
|
|
|
|
while (a1) {
|
1988-09-26 11:37:38 +00:00
|
|
|
ASSERT(a2);
|
|
|
|
ASSERT(a1->ad_idf == a2->ad_idf);
|
1988-09-20 16:44:27 +00:00
|
|
|
if (a1->ad_used)
|
|
|
|
a2->ad_used = 1;
|
|
|
|
|
|
|
|
if (mode != USE_ONLY) {
|
|
|
|
if ( ( !a2->ad_set
|
|
|
|
&& (a1->ad_set || a1->ad_maybe_set)
|
|
|
|
)
|
|
|
|
|| ( mode == NORMAL
|
|
|
|
&& !a1->ad_set
|
|
|
|
&& (a2->ad_set || a2->ad_maybe_set)
|
|
|
|
)
|
|
|
|
) {
|
|
|
|
a2->ad_set = 0;
|
|
|
|
a2->ad_maybe_set = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
a1 = a1->next;
|
|
|
|
a2 = a2->next;
|
|
|
|
}
|
1988-09-26 11:37:38 +00:00
|
|
|
ASSERT(!a2);
|
1988-09-25 17:29:37 +00:00
|
|
|
return a;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
merge_states(st1, st2, lvl, mode)
|
|
|
|
struct state *st1, *st2;
|
|
|
|
int mode;
|
|
|
|
{
|
|
|
|
/* st2 becomes the result.
|
|
|
|
* st1 is left unchanged.
|
|
|
|
* The resulting state is the state the program gets in if st1 OR st2
|
|
|
|
* becomes the state. (E.g. the states at the end of an if-part and an
|
|
|
|
* end-part are merged by this function.)
|
|
|
|
*/
|
|
|
|
if (st1->st_notreached) {
|
|
|
|
if (mode == NORMAL || st2->st_notreached) {
|
|
|
|
st2->st_auto_list =
|
|
|
|
merge_autos(st1->st_auto_list,
|
|
|
|
st2->st_auto_list, lvl, USE_ONLY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (st2->st_notreached) {
|
|
|
|
register struct auto_def *tmp = st2->st_auto_list;
|
|
|
|
|
|
|
|
st2->st_auto_list = copy_st_auto_list(st1->st_auto_list, lvl);
|
|
|
|
st2->st_notreached = 0;
|
|
|
|
st2->st_warned = 0;
|
|
|
|
st2->st_auto_list = merge_autos(tmp, st2->st_auto_list,
|
|
|
|
lvl, USE_ONLY);
|
|
|
|
free_st_auto_list(tmp);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
st2->st_auto_list =
|
|
|
|
merge_autos(st1->st_auto_list, st2->st_auto_list,
|
|
|
|
lvl, mode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******** L I N T S T A C K S E A R C H I N G ********/
|
|
|
|
|
|
|
|
/* The next four find-functions search the lint_stack for an entry.
|
|
|
|
* The letters mean : w: WHILE; d: DO; f: FOR; s: SWITCH; c: CASE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct lint_stack_entry *
|
|
|
|
find_wdf()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = top_ls;
|
|
|
|
|
|
|
|
while (lse != &stack_bottom) {
|
1988-09-25 17:29:37 +00:00
|
|
|
switch (lse->ls_class) {
|
1988-09-20 16:44:27 +00:00
|
|
|
case WHILE:
|
|
|
|
case DO:
|
|
|
|
case FOR:
|
1988-09-25 17:29:37 +00:00
|
|
|
return lse;
|
|
|
|
}
|
1988-09-20 16:44:27 +00:00
|
|
|
lse = lse->ls_previous;
|
|
|
|
}
|
1988-09-25 17:29:37 +00:00
|
|
|
return 0;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct lint_stack_entry *
|
|
|
|
find_wdfc()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = top_ls;
|
|
|
|
|
|
|
|
while (lse != &stack_bottom) {
|
1988-09-25 17:29:37 +00:00
|
|
|
switch (lse->ls_class) {
|
1988-09-20 16:44:27 +00:00
|
|
|
case WHILE:
|
|
|
|
case DO:
|
|
|
|
case FOR:
|
|
|
|
case CASE:
|
1988-09-25 17:29:37 +00:00
|
|
|
return lse;
|
|
|
|
}
|
1988-09-20 16:44:27 +00:00
|
|
|
lse = lse->ls_previous;
|
|
|
|
}
|
1988-09-25 17:29:37 +00:00
|
|
|
return 0;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct lint_stack_entry *
|
|
|
|
find_cs()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = top_ls;
|
|
|
|
|
|
|
|
while (lse != &stack_bottom) {
|
1988-09-25 17:29:37 +00:00
|
|
|
switch (lse->ls_class) {
|
1988-09-20 16:44:27 +00:00
|
|
|
case CASE:
|
|
|
|
case SWITCH:
|
1988-09-25 17:29:37 +00:00
|
|
|
return lse;
|
|
|
|
}
|
1988-09-20 16:44:27 +00:00
|
|
|
lse = lse->ls_previous;
|
|
|
|
}
|
1988-09-25 17:29:37 +00:00
|
|
|
return 0;
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/******** A C T I O N S ********/
|
|
|
|
|
1988-09-25 17:29:37 +00:00
|
|
|
start_if_part(const)
|
1988-09-20 16:44:27 +00:00
|
|
|
{
|
|
|
|
/* Push a new stack entry on the lint_stack with class == IF
|
|
|
|
* copy the ls_current to the top of this stack
|
|
|
|
*/
|
|
|
|
register struct lint_stack_entry *lse = new_lint_stack_entry();
|
|
|
|
|
1988-09-25 17:29:37 +00:00
|
|
|
if (const)
|
|
|
|
hwarning("condition in if statement is constant");
|
|
|
|
|
1988-09-20 16:44:27 +00:00
|
|
|
lse->ls_class = IF;
|
|
|
|
lse->ls_current = copy_state(top_ls->ls_current, level);
|
|
|
|
lse->ls_level = level;
|
|
|
|
lint_push(lse);
|
|
|
|
}
|
|
|
|
|
|
|
|
start_else_part()
|
|
|
|
{
|
|
|
|
/* Move ls_current to LS_IF_STATE
|
|
|
|
* ls_current of the stack entry one below is copied to ls_current.
|
|
|
|
*/
|
|
|
|
if (s_NOTREACHED) {
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
s_NOTREACHED = 0;
|
|
|
|
}
|
|
|
|
top_ls->LS_IF_STATE = top_ls->ls_current;
|
|
|
|
/* this is the reason why ls_current is a pointer */
|
|
|
|
top_ls->ls_current = copy_state(top_ls->ls_previous->ls_current,
|
|
|
|
level);
|
|
|
|
top_ls->ls_level = level;
|
|
|
|
}
|
|
|
|
|
|
|
|
end_if_else_stmt()
|
|
|
|
{
|
|
|
|
Free_state(&top_ls->ls_previous->ls_current);
|
|
|
|
merge_states(top_ls->LS_IF_STATE, top_ls->ls_current,
|
|
|
|
top_ls->ls_level, NORMAL);
|
|
|
|
Free_state(&top_ls->LS_IF_STATE);
|
|
|
|
top_ls->ls_previous->ls_current = top_ls->ls_current;
|
|
|
|
lint_pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
end_if_stmt()
|
|
|
|
{
|
|
|
|
/* No else-part met; merge ls_current with ls_current of previous
|
|
|
|
* stack entry
|
|
|
|
*/
|
|
|
|
merge_states(top_ls->ls_current, top_ls->ls_previous->ls_current,
|
|
|
|
top_ls->ls_level, NORMAL);
|
|
|
|
Free_state(&top_ls->ls_current);
|
|
|
|
lint_pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
start_loop_stmt(looptype, const, cond)
|
|
|
|
{
|
|
|
|
/* If const, the condition is constant and given in cond */
|
|
|
|
register struct lint_stack_entry *lse = new_lint_stack_entry();
|
|
|
|
|
|
|
|
lse->ls_class = looptype;
|
|
|
|
lse->ls_current = copy_state(top_ls->ls_current, level);
|
|
|
|
lse->ls_level = level;
|
|
|
|
if (const && !cond) {
|
|
|
|
/* while (0) | for (;0;) */
|
|
|
|
hwarning("condition in %s statement is constant",
|
|
|
|
symbol2str(looptype));
|
|
|
|
lse->ls_current->st_notreached = 1;
|
|
|
|
}
|
|
|
|
if (const && cond) {
|
|
|
|
/* while (1) | for (;;) | do */
|
|
|
|
/* omitting the copy for LS_END will force this loop
|
|
|
|
to be treated as a do loop
|
|
|
|
*/
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lse->LS_END = copy_state(top_ls->ls_current, level);
|
|
|
|
}
|
|
|
|
lint_push(lse);
|
|
|
|
}
|
|
|
|
|
|
|
|
end_loop_stmt()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *prev_ls = top_ls->ls_previous;
|
|
|
|
|
|
|
|
lint_continue_stmt();
|
|
|
|
top_ls->LS_END->st_notreached = prev_ls->ls_current->st_notreached;
|
|
|
|
top_ls->LS_END->st_warned = prev_ls->ls_current->st_warned;
|
|
|
|
Free_state(&top_ls->ls_current);
|
|
|
|
Free_state(&prev_ls->ls_current);
|
|
|
|
prev_ls->ls_current = top_ls->LS_END;
|
|
|
|
lint_pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
end_do_stmt(const, cond)
|
|
|
|
{
|
|
|
|
end_loop_stmt();
|
|
|
|
if (const)
|
1988-09-25 17:29:37 +00:00
|
|
|
hwarning("condition in do-while statement is constant");
|
1988-09-20 16:44:27 +00:00
|
|
|
if (const && cond && top_ls->ls_current->st_notreached) {
|
|
|
|
/* no break met; this is really an endless loop */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
top_ls->ls_current->st_notreached = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cont_break_merge(lse)
|
|
|
|
struct lint_stack_entry *lse;
|
|
|
|
{
|
|
|
|
/* merge for continue and break statements */
|
|
|
|
if (lse->LS_END) {
|
|
|
|
merge_states(top_ls->ls_current, lse->LS_END,
|
|
|
|
lse->ls_level, NORMAL);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lse->LS_END = copy_state(top_ls->ls_current, lse->ls_level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_continue_stmt()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = find_wdf();
|
|
|
|
|
|
|
|
if (!lse)
|
|
|
|
return; /* not inside a loop statement */
|
|
|
|
|
|
|
|
cont_break_merge(lse);
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
}
|
|
|
|
|
1988-09-25 17:29:37 +00:00
|
|
|
start_switch_part(expr)
|
|
|
|
struct expr *expr;
|
1988-09-20 16:44:27 +00:00
|
|
|
{
|
|
|
|
/* ls_current of a SWITCH entry has different meaning from ls_current of
|
|
|
|
* other entries. It keeps track of which variables are used in all
|
|
|
|
* following case parts. (Needed for variables declared in a compound
|
|
|
|
* switch-block.)
|
|
|
|
*/
|
|
|
|
register struct lint_stack_entry *lse = new_lint_stack_entry();
|
|
|
|
|
1988-09-25 17:29:37 +00:00
|
|
|
if (is_cp_cst(expr))
|
|
|
|
hwarning("value in switch statement is constant");
|
|
|
|
|
1988-09-20 16:44:27 +00:00
|
|
|
lse->ls_class = SWITCH;
|
|
|
|
lse->ls_current = copy_state(top_ls->ls_current, level);
|
|
|
|
lse->ls_level = level;
|
|
|
|
lse->LS_CASE = copy_state(top_ls->ls_current, level);
|
|
|
|
lse->ls_current->st_notreached = 1;
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
lint_push(lse);
|
|
|
|
}
|
|
|
|
|
|
|
|
end_switch_stmt()
|
|
|
|
{
|
|
|
|
if (top_ls->ls_class == CASE) {
|
|
|
|
/* no break after last case or default */
|
|
|
|
lint_break_stmt(); /* introduce break */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!top_ls->LS_DEFAULT_MET) {
|
|
|
|
top_ls->ls_current->st_notreached = 0;
|
|
|
|
if (top_ls->LS_BREAK) {
|
|
|
|
merge_states(top_ls->ls_current, top_ls->LS_BREAK,
|
|
|
|
top_ls->ls_level, NORMAL);
|
|
|
|
Free_state(&top_ls->ls_current);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
top_ls->LS_BREAK = top_ls->ls_current;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Free_state(&top_ls->ls_current);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (top_ls->LS_BREAK) {
|
|
|
|
merge_states(top_ls->LS_CASE, top_ls->LS_BREAK,
|
|
|
|
top_ls->ls_level, CASE_BREAK);
|
|
|
|
Free_state(&top_ls->LS_CASE);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
top_ls->LS_BREAK = top_ls->LS_CASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
top_ls->LS_BREAK->st_notreached =
|
|
|
|
top_ls->ls_previous->ls_current->st_notreached;
|
|
|
|
/* yack */
|
|
|
|
Free_state(&top_ls->ls_previous->ls_current);
|
|
|
|
|
|
|
|
if (!top_ls->LS_DEFAULT_MET)
|
|
|
|
top_ls->LS_BREAK->st_notreached = 0;
|
|
|
|
top_ls->ls_previous->ls_current = top_ls->LS_BREAK;
|
|
|
|
|
|
|
|
lint_pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_case_stmt(dflt)
|
|
|
|
{
|
|
|
|
/* A default statement is just a special case statement */
|
|
|
|
|
|
|
|
register struct lint_stack_entry *lse;
|
|
|
|
register struct lint_stack_entry *cs_entry = find_cs();
|
|
|
|
|
|
|
|
if (!cs_entry)
|
|
|
|
return; /* not inside switch */
|
|
|
|
if (cs_entry != top_ls) {
|
|
|
|
warning("%s statement in strange context",
|
|
|
|
dflt ? "default" : "case");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cs_entry->ls_class == SWITCH) {
|
|
|
|
if (dflt) {
|
|
|
|
cs_entry->LS_DEFAULT_MET = 1;
|
|
|
|
}
|
|
|
|
lse = new_lint_stack_entry();
|
|
|
|
lse->ls_class = CASE;
|
|
|
|
lse->ls_current = copy_state(top_ls->ls_current, level);
|
|
|
|
remove_settings(lse->ls_current, level);
|
|
|
|
lse->ls_level = level;
|
|
|
|
lint_push(lse);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ASSERT(cs_entry->ls_class == CASE);
|
|
|
|
if (dflt) {
|
|
|
|
cs_entry->ls_previous->LS_DEFAULT_MET = 1;
|
|
|
|
}
|
|
|
|
merge_states(top_ls->ls_current, top_ls->ls_previous->LS_CASE,
|
|
|
|
top_ls->ls_previous->ls_level, NORMAL);
|
|
|
|
merge_states(top_ls->ls_current,
|
|
|
|
top_ls->ls_previous->ls_current,
|
|
|
|
top_ls->ls_previous->ls_level, NORMAL);
|
|
|
|
Free_state(&top_ls->ls_current);
|
|
|
|
top_ls->ls_current =
|
|
|
|
copy_state(top_ls->ls_previous->ls_current,
|
|
|
|
top_ls->ls_previous->ls_level);
|
|
|
|
remove_settings(top_ls->ls_current, top_ls->ls_level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_break_stmt()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = find_wdfc();
|
|
|
|
|
|
|
|
if (!lse)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (lse->ls_class) {
|
|
|
|
case WHILE:
|
|
|
|
case FOR:
|
|
|
|
case DO:
|
|
|
|
/* loop break */
|
|
|
|
lse->ls_previous->ls_current->st_notreached = 0;
|
|
|
|
cont_break_merge(lse);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CASE:
|
|
|
|
/* case break */
|
|
|
|
if (!top_ls->ls_current->st_notreached) {
|
|
|
|
lse->ls_previous->ls_previous->ls_current->st_notreached = 0;
|
|
|
|
}
|
|
|
|
merge_states(lse->ls_current, lse->ls_previous->ls_current,
|
|
|
|
lse->ls_previous->ls_level, NORMAL);
|
|
|
|
if (lse->ls_previous->LS_BREAK) {
|
|
|
|
merge_states(top_ls->ls_current, lse->ls_previous->LS_BREAK,
|
|
|
|
lse->ls_previous->ls_level, NORMAL);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
lse->ls_previous->LS_BREAK = copy_state(top_ls->ls_current,
|
|
|
|
lse->ls_previous->ls_level);
|
|
|
|
}
|
|
|
|
if (lse == top_ls) {
|
|
|
|
Free_state(&lse->ls_current);
|
|
|
|
lint_pop();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
1988-09-26 11:37:38 +00:00
|
|
|
NOTREACHED();
|
1988-09-20 16:44:27 +00:00
|
|
|
/*NOTREACHED*/
|
|
|
|
}
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_start_function()
|
|
|
|
{
|
|
|
|
lint_return_stmt(-1); /* initialization */
|
1988-10-12 16:05:17 +00:00
|
|
|
lint_comment_function();
|
1988-09-20 16:44:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
lint_end_function()
|
|
|
|
{
|
|
|
|
extern struct outdef OutDef;
|
|
|
|
register int fund = func_type->tp_fund;
|
|
|
|
|
|
|
|
if ( OutDef.od_valreturned == NOVALRETURNED
|
|
|
|
&& !func_notypegiven
|
|
|
|
&& fund != VOID
|
|
|
|
) {
|
|
|
|
warning("function %s declared %s%s but no value returned",
|
|
|
|
func_name,
|
|
|
|
(func_type->tp_unsigned && fund != POINTER) ?
|
|
|
|
"unsigned " : "",
|
|
|
|
symbol2str(fund)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
/* write the function definition record */
|
|
|
|
outdef();
|
|
|
|
|
|
|
|
/* At this stage it is possible that stack_bottom.ls_current is
|
|
|
|
* pointing to a state with a list of auto_defs.
|
|
|
|
* These auto_defs must be freed and the state must be filled
|
|
|
|
* with zeros.
|
|
|
|
*/
|
1988-09-26 11:37:38 +00:00
|
|
|
ASSERT(top_ls == &stack_bottom);
|
1988-09-20 16:44:27 +00:00
|
|
|
if (top_ls->ls_current->st_auto_list != 0)
|
|
|
|
free_st_auto_list(top_ls->ls_current->st_auto_list);
|
|
|
|
top_ls->ls_current->st_auto_list = 0;
|
|
|
|
top_ls->ls_current->st_notreached = 0;
|
|
|
|
top_ls->ls_current->st_warned = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_return_stmt(e)
|
|
|
|
int e;
|
|
|
|
{
|
|
|
|
/* The statics of this function are initialized by calling it with e = -1. */
|
|
|
|
|
|
|
|
static int ret_e;
|
|
|
|
/*-1 no return met yet
|
|
|
|
* 0 return; met
|
|
|
|
* 1 return with expression met
|
|
|
|
*/
|
|
|
|
static int warned;
|
|
|
|
|
|
|
|
switch (e) {
|
|
|
|
case -1:
|
|
|
|
ret_e = -1;
|
|
|
|
warned = 0;
|
|
|
|
return;
|
|
|
|
case 0:
|
|
|
|
if (top_ls->ls_current->st_notreached)
|
|
|
|
break;
|
|
|
|
if (ret_e == 1 && !warned) {
|
|
|
|
warning("function %s does not always return a value",
|
|
|
|
func_name);
|
|
|
|
warned = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret_e = 0;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (top_ls->ls_current->st_notreached)
|
|
|
|
break;
|
|
|
|
if (ret_e == 0 && !warned) {
|
|
|
|
warning("function %s does not always return a value",
|
|
|
|
func_name);
|
|
|
|
warned = 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ret_e = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (!top_ls->ls_current->st_notreached)
|
|
|
|
set_od_valreturned(e);
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_jump_stmt(idf)
|
|
|
|
struct idf *idf;
|
|
|
|
{
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
if (!idf->id_def)
|
|
|
|
return;
|
|
|
|
idf->id_def->df_used = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_label()
|
|
|
|
{
|
|
|
|
/* When meeting a label, we should take the intersection of all
|
|
|
|
settings at all goto's leading this way, but this cannot reasonably
|
|
|
|
be done. So we assume that the user knows what he is doing and set
|
|
|
|
all automatic variables to set.
|
|
|
|
*/
|
|
|
|
register struct auto_def *a = top_ls->ls_current->st_auto_list;
|
|
|
|
|
|
|
|
while (a) {
|
|
|
|
a->ad_maybe_set = 0;
|
|
|
|
a->ad_set = 1;
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_statement()
|
|
|
|
{
|
|
|
|
/* Check if this statement can be reached
|
|
|
|
*/
|
|
|
|
if (s_NOTREACHED) {
|
|
|
|
top_ls->ls_current->st_notreached = 1;
|
|
|
|
s_NOTREACHED = 0;
|
|
|
|
}
|
|
|
|
if (DOT == '{' || DOT == ';')
|
|
|
|
return;
|
|
|
|
if (top_ls->ls_current->st_warned)
|
|
|
|
return;
|
|
|
|
if (top_ls->ls_current->st_notreached) {
|
|
|
|
if (DOT != CASE && DOT != DEFAULT && AHEAD != ':') {
|
|
|
|
if (DOT != BREAK || !loptions['b'])
|
|
|
|
warning("statement cannot be reached");
|
|
|
|
top_ls->ls_current->st_warned = 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
top_ls->ls_current->st_notreached = 0;
|
|
|
|
top_ls->ls_current->st_warned = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_push(lse)
|
|
|
|
struct lint_stack_entry *lse;
|
|
|
|
{
|
|
|
|
lse->ls_previous = top_ls;
|
|
|
|
top_ls->next = lse;
|
|
|
|
top_ls = lse;
|
|
|
|
}
|
|
|
|
|
|
|
|
lint_pop()
|
|
|
|
{
|
|
|
|
top_ls = top_ls->ls_previous;
|
|
|
|
free_lint_stack_entry(top_ls->next);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* FOR DEBUGGING */
|
|
|
|
|
|
|
|
print_lint_stack()
|
|
|
|
{
|
|
|
|
register struct lint_stack_entry *lse = top_ls;
|
|
|
|
|
|
|
|
while (lse) {
|
|
|
|
print(" |-------------- level %d ------------\n",
|
|
|
|
lse->ls_level);
|
|
|
|
print(" |cur: ");
|
|
|
|
if (lse->ls_current) {
|
|
|
|
print_autos(lse->ls_current->st_auto_list);
|
|
|
|
print(" |st_notreached == %d\n",
|
|
|
|
lse->ls_current->st_notreached);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("\n");
|
|
|
|
print(" |class == %s\n",
|
|
|
|
lse->ls_class ? symbol2str(lse->ls_class) : "{");
|
|
|
|
switch (lse->ls_class) {
|
|
|
|
case SWITCH:
|
|
|
|
print(" |LS_BREAK: ");
|
|
|
|
if (lse->LS_BREAK) {
|
|
|
|
print_autos(lse->LS_BREAK->st_auto_list);
|
|
|
|
print(" |st_notreached == %d\n",
|
|
|
|
lse->LS_BREAK->st_notreached);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("\n");
|
|
|
|
print(" |LS_CASE: ");
|
|
|
|
if (lse->LS_CASE) {
|
|
|
|
print_autos(lse->LS_CASE->st_auto_list);
|
|
|
|
print(" |st_notreached == %d\n",
|
|
|
|
lse->LS_CASE->st_notreached);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("\n");
|
|
|
|
break;
|
|
|
|
case DO:
|
|
|
|
case WHILE:
|
|
|
|
case FOR:
|
|
|
|
print(" |LS_END: ");
|
|
|
|
if (lse->LS_END) {
|
|
|
|
print_autos(lse->LS_END->st_auto_list);
|
|
|
|
print(" |st_notreached == %d\n",
|
|
|
|
lse->LS_END->st_notreached);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("\n");
|
|
|
|
break;
|
|
|
|
case IF:
|
|
|
|
print(" |LS_IF_STATE: ");
|
|
|
|
if (lse->LS_IF_STATE) {
|
|
|
|
print_autos(lse->LS_IF_STATE->st_auto_list);
|
|
|
|
print(" |st_notreached == %d\n",
|
|
|
|
lse->LS_IF_STATE->st_notreached);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
print("\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lse = lse->ls_previous;
|
|
|
|
}
|
|
|
|
print(" |--------------\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
|
|
|
print_autos(a)
|
|
|
|
register struct auto_def *a;
|
|
|
|
{
|
|
|
|
while (a) {
|
|
|
|
print("%s", a->ad_idf->id_text);
|
|
|
|
print("(l=%d)", a->ad_def->df_level);
|
|
|
|
print("(U%dS%dM%d) ", a->ad_used, a->ad_set, a->ad_maybe_set);
|
|
|
|
a = a->next;
|
|
|
|
}
|
|
|
|
print("\n");
|
|
|
|
}
|
|
|
|
#endif LINT
|