* * * * * 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: sean@conman.org > * > *************************************************************************/ > > /* Style: C89, "splint -strict" compliant */ > > #ifndef S_SPLINT_S > # include > # include > # include > # include > #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? * 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 Sean Conner at sean@conman.org .