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.
+