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:

/*************************************************************************
*************************************************************************/
/* 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 splint-bug@splint.org ***
(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 splint-bug@splint.org
*** 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?

=> [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] /boston/2012/01/13.1 | [6] /boston/2012/01/15.1

=> Gemini Mention this post | Contact the author

Proxy Information
Original URL
gemini://gemini.conman.org/boston/2012/01/14.1
Status Code
Success (20)
Meta
text/gemini
Capsule Response Time
557.600271 milliseconds
Gemini-to-HTML Time
7.542978 milliseconds

This content has been proxied by September (ba2dc).