Skip to content
Snippets Groups Projects
Name Last commit Last update
README.md
cscan

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:

  1. the filename and source line
  2. the type of expression being cast
  3. 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.