* * * * *

      99 ways to program a hex, Part 6: C89, “splint -strict” compliant

Back in the K&R days [1], C code tended to play rather loose with the rules.
As a result, some pretty subtle bugs would go undetected, such as passing the
wrong number of parameters to a function, the wrong type of parameters to a
function, and ignoring the results of a function. Because of these types of
errors, a program called lint [2] was developed that could detect them, as
well as other commonly made mistakes. In fact, lint was very fussy about the
code it was given.

But it was a popular tool (I remember the ads for PC Lint [3] that would show
a snippit of C code that had a subtle bug that PC Lint could detect. I got
good enough to spot the errors shown in the ads) and one could always tell
code that's been through lint because of code like:

> (void)printf("hello world\n");
>

The standard these days seems to be a program called splint [4] and man, is
it picky; just getting code to pass through splint is hard enough, but then
there's the -strict option:

> -strict

>         Absurdly strict checking. All checking done by checks, plus
>         modifications and global variables used in unspecified functions,
>         strict standard library, and strict typing of C operators. A
>         special reward will be presented to the first person to produce a
>         real program that produces no errors with strict checking.
>

Which brings us to today's code:

> /*************************************************************************
> *
> * Copyright 2012 by Sean Conner.  All Rights Reserved.
> *
> * This program is free software; you can redistribute it and/or
> * modify it under the terms of the GNU General Public License
> * as published by the Free Software Foundation; either version 2
> * of the License, or (at your option) any later version.
> *
> * This program is distributed in the hope that it will be useful,
> * but WITHOUT ANY WARRANTY; without even the implied warranty of
> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> * GNU General Public License for more details.
> *
> * You should have received a copy of the GNU General Public License
> * along with this program; if not, write to the Free Software
> * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
> *
> * Comments, questions and criticisms can be sent to: [email protected]
> *
> *************************************************************************/
>
> /* Style: C89, "splint -strict" compliant */
>
> #ifndef S_SPLINT_S
> #  include <stdio.h>
> #  include <ctype.h>
> #  include <string.h>
> #  include <stdlib.h>
> #endif
>
> #define LINESIZE      ((size_t)16)
>
> /*@-protoparamname@*/
> static void do_dump(FILE *fpin,FILE *fpout)
>       /*@globals fileSystem @*/
>       /*@modifies *fpin, *fpout, fileSystem @*/
>       ;
> /*@+protoparamname@*/
>
> /****************************************************************/
>
> int main(int argc,char *argv[])
> /*@globals  fileSystem, stdin, stdout@*/
> /*@modifies fileSystem, stdin, stdout@*/
> {
>   if (argc == 1)
>   {
>     do_dump(stdin,stdout);
>   }
>   else
>   {
>     int i;
>
>     for (i = 1 ; i < argc ; i++)
>     {
>       FILE *fp;
>
>       /*@-boundsread@*/
>       fp = fopen(argv[i],"rb");
>       /*@+boundsread@*/
>
>       if (fp == NULL)
>       {
>         perror(argv[i]);
>         continue;
>       }
>
>       printf("-----%s-----\n",argv[i]);
>       do_dump(fp,stdout);
>       if (fclose(fp) == EOF)
>       {
>         perror(argv[i]);
>       }
>     }
>   }
>
>   return EXIT_SUCCESS;
> }
>
> /******************************************************************/
>
> static void do_dump(FILE *fpin,FILE *fpout)
> /*@globals fileSystem @*/
> /*@modifies *fpin, *fpout, fileSystem@*/
> {
>   unsigned char  buffer[BUFSIZ];
>   unsigned char *pbyte;
>   size_t         offset;
>   size_t         bread;
>   size_t         j;
>   char           ascii[LINESIZE + 1];
>
>   offset = 0;
>
>   while((bread = fread(buffer,(size_t)1,BUFSIZ,fpin)) > 0)
>   {
>     pbyte = buffer;
>     while (bread > 0)
>     {
>       fprintf(fpout,"%08lX: ",(unsigned long)offset);
>       j = 0;
>       do
>       {
>         fprintf(fpout,"%02X ",(unsigned int)*pbyte);
>         if (isprint(*pbyte))
>         {
>           ascii [j] = (char)*pbyte;
>         }
>         else
>         {
>           ascii [j] = '.';
>         }
>         pbyte  ++;
>         offset ++;
>         j      ++;
>         bread  --;
>       } while ((j < LINESIZE) && (bread > 0));
>       ascii [j] = '\0';
>       if (j < LINESIZE)
>       {
>       size_t i;
>
>       for (i = j ; i < LINESIZE ; i++)
>       {
>         fprintf(fpout,"   ");
>       }
>       }
>       fprintf(fpout,"%s\n",ascii);
>     }
>
>     if (fflush(fpout) == EOF)
>     {
>       perror("output");
>       exit(EXIT_FAILURE);
>     }
>   }
> }
>
> /***************************************************************/
>

I'm actually surprised at just how few splint directives I needed (they're
those funny looking comments like /*@-frobnitz@*/) to get this code through
splint -strict. The only hard part was the function prototype—it didn't
matter if I included the parameter names:

> Splint 3.1.2 --- 07 Dec 2009
>
> 06.c:34:27: Declaration parameter has name: fpin
>   A parameter in a function prototype has a name.  This is dangerous, since a
>   macro definition could be visible here. (Use either -protoparamname or
>   -namechecks to inhibit warning)
> 06.c:34:38: Declaration parameter has name: fpout
>   A parameter in a function prototype has a name.  This is dangerous, since a
>   macro definition could be visible here. (Use either -protoparamname or
>   -namechecks to inhibit warning)
>
> Finished checking --- 2 code warnings
>

or not:

> Splint 3.1.2 --- 07 Dec 2009
>
> 06.c:36:15: Unrecognized identifier in modifies comment: fpin
>   Identifier used in code has not been declared. (Use -unrecog to inhibit
>   warning)
> 06.c:36:22: Unrecognized identifier in modifies comment: fpout
> sRef.c:1369: at source point
> 06.c:47:26: *** Internal Bug at sRef.c:1369: llassert failed:
>                sRef_isReasonable (s) [errno: 25]
>      *** Please report bug to [email protected] ***
>        (attempting to continue, results may be incorrect)
> *** Segmentation Violation
> *** Location (not trusted): 06.c:47:26
> *** Last code point: exprNode.c:3046
> *** Previous code point: exprNode.c:10317
> *** Please report bug to [email protected]
> *** A useful bug report should include everything we need to reproduce the bug.
>

(and it crashes! Woot!)

splint bitched about the prototype. I could have rearranged the code so the
prototype was unnecessary, but I decided to shut that particular error up
with the /*@-protoparamname@*/ ... /*@+protoparamname@*/ directives. But
really, other than that and one other minor bitch, the code passed splint -
strict rather easily.

I wonder if I can claim that prize, or is the program too simple?

* Part 5: C99 in K&R style [5]
* Part 7: C89, const correctness [6]

[1] http://en.wikipedia.org/wiki/K&R_C
[2] http://en.wikipedia.org/wiki/Lint_(software)
[3] http://en.wikipedia.org/wiki/PC-Lint
[4] http://www.splint.org/
[5] gopher://gopher.conman.org/0Phlog:2012/01/13.1
[6] gopher://gopher.conman.org/0Phlog:2012/01/15.1

Email author at [email protected]