cscan reports the use of a cast as an error message. It can be used in place of a C compiler in familiar tools that let you jump to the line in a source file where the "error" occurs. It is intended as an aid to review C source code for unnecessary (or perhaps harmful) casts.
cscan is a Python script. More than a fancy grep(1), it uses the pycparser module by Eli Bendersky, based on Ply, David Beazley's excellent Lex/Yacc implementation. cscan uses pycparser to parse the C code, and responds to recognized casts.
Example output:
$ cscan somefile.c
scanning "../libcob/somefile.c"
../lib/gettext.h:224: FuncCall: (char*) (malloc(msgctxt_len + msgid_len))
../lib/gettext.h:272: FuncCall: (char*) (malloc(msgctxt_len + msgid_len))
somefile.c:48: StructRef: (void*) (ff->fcd->_fnamePtr.ptr_name)
somefile.c:49: StructRef: (void*) (ff->fcd)
somefile.c:51: StructRef: (void*) (ff->f)
somefile.c:53: ID: (void*) (ff)
somefile.c:164: StructRef: (void*) (fcd->_fnamePtr.ptr_name)
somefile.c:214: BinaryOp: (EXTKEY*) ((char*) ((char*) (kdb)) + keypos)
somefile.c:221: StructRef: (unsigned char) (f->keys[idx].char_suppress)
Each output line has 3 parts:
- the filename and source line
- the type of expression being cast
- the cast and a rendering of the expression in the form
(cast)(expression)
The expression approximately matches the source code. Sometimes it's a little mangled, and sometimes extra type information is included in an effort to be helpful. Comments and whitespace are obviously missing, because it is reconstructed from the parse tree.
Prerequisites
If these aren't supplied by your favorite package manager, both packages can be downloaded from GitHub. Each includes simple installation instructions.
Installation
Put cscan somewhere on your PATH
for your convenience.
Invocation
cscan [-E cpp] filename ...
The -E
option lets you specify the preprocessor you wish to use,
overriding the default.
Environment
- CSCAN_FLAGS are passed to the preprocessor.
PLEASE NOTE:
Individual options in CSCAN_FLAGS must be separated by at least 2 spaces or tabs. For reasons too tedious to go into, cscan must break up the string into individual options, and it seemed simplest to use extra whitespace to delineate them.
It can be handy to keep a script to be "sourced" as needed, along the lines of:
if [ -z "$FAKE_INCLUDES" ]
then
FAKE_INCLUDES=$HOME/projects/3rd/pycparser/utils/fake_libc_include
fi
CSCAN_FLAGS="-undef \
-I$FAKE_INCLUDES -I.. -I. -Ilib \
-I/usr/local/include"
export CSCAN_FLAGS
Implementation Notes
Preprocesser
While pycparser recognizes C99 code, it does not include its own C
preprocessor. Instead, it invokes cpp(1) and processes the
preprocessor output. Which preprocessor is used can be controlled by
the cscan -E
option.
cscan (because pycparser) will not parse C extensions, notably GNU gcc
extensions. Consequently it will choke on many system header
files. To avoid problems with files you probably don't want to examine
anyway, pycparser includes a set of fake standard C header files. The
basic trick is to put that directory at the front of the include
path by making it the first -I
option in CSCAN_FLAGS. See the
pycparser documentation for details.