diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..beed6a08064358035345beb04b30c6bfe3e76e95 --- /dev/null +++ b/README.md @@ -0,0 +1,110 @@ +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](https://github.com/eliben/pycparser) module by Eli +Bendersky, based on [Ply](http://www.dabeaz.com/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 +============= + +- [pycparser](https://github.com/eliben/pycparser) +- [Ply](http://www.dabeaz.com/ply/) + +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. +