Yeah, I know Esdger Dijkstra said you shouldn't use goto
, but he's not God. I think it's fine to use an unconditional branch as long as you don't overdo it. You're just as likely to create unreadable code by overusing inheritance as you are by overusing branch statements.
Anyway, now that I'm done with that preemptive rebuttal, here's the program I was writing. It's supposed to read code from a file (eventually I want it to read HTML code, but I'm using my own simplified markup language for now because it's easier) and convert it to a format that can be used as input to the enscript
program.
#include <stdio.h>
#include <stdlib.h>
#define INPUT_ERROR 1
void writecolor( FILE *, float, float, float );
unsigned short hextonum( char );
char escape = '\0'; // Default for enscript
int main( int argc, char **argv ){
FILE *in;
FILE *out;
if( argv[1] ){
in = fopen( argv[1], "r" );
}
else{
in = stdin;
}
if( argv[2] ){
out = fopen( argv[2], "w" );
}
else{
out = stdout;
}
char c; // Input character
float red; // Red value from hex code
float green; // Green value from hex code
float blue; // Blue value from hex code
int line = 1; // Current line number, used for error reporting
while( (c = fgetc( in )) != EOF ){
if( c == '\\' ){
if( fgetc( in ) == '#' ){
red = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
green = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
blue = (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.1 + (float) hextonum( fgetc( in ) ) * 10 / 16 * 0.01;
writecolor( out, red, green, blue );
}
}
else{
fputc( c, out );
}
if( c == '\n' ){
line++;
}
}
fclose( in );
fclose( out );
return 0;
:infile_error // XXX goto in hextonum branches here
fprintf( stderr, "%s: Error on line %d of input file.\n", argv[0], line );
return INPUT_ERROR;
}
// Converts a color code in the markup to a color code
// recognized by enscript
void writecolor( FILE *fp, float red, float green, float blue ){
fwrite( &escape, 1, 1, fp );
fprintf( fp, "color{%f %f %f}", red, green, blue );
}
// Converts a hex digit to its corresponding value
unsigned short hextonum( char hex ){
if( hex >= '0' && hex <= '9' ){
return atoi( hex );
}
switch( hex ){
case( 'a' ) : case( 'A' ) : return 0xa;
case( 'b' ) : case( 'B' ) : return 0xb;
case( 'c' ) : case( 'C' ) : return 0xc;
case( 'd' ) : case( 'D' ) : return 0xd;
case( 'e' ) : case( 'E' ) : return 0xe;
case( 'f' ) : case( 'F' ) : return 0xf;
}
// Okay, I think rather than putting an if statement
// around every piece of code that uses this function,
// I'm just going to jump to an error code in the
// main function.
goto infile_error;
}
It is very much a work in progress and is far from perfect or even functional at this point. I'm just wondering why I keep getting the following error:
html2enscript.c:50:2: error: expected expression
:infile_error // XXX goto in hextonum branches here
^
Is this some kind of compiler-enforced good practice rule here, or did I actually do something wrong (and by wrong I mean syntactically incorrect, not just bad programming style)?
Additional information:
Here is my gcc
version information:
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.3.0 (clang-703.0.31)
Target: x86_64-apple-darwin15.0.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
Two issues here. The first is that your label for goto
is incorrectly specified.
You have this:
:infile_error
Where it should be this:
infile_error:
The second issue, which is the larger one, is that you're attempting to use goto
to jump to a different function. This is not allowed.
It seems you're attempting to implement some kind of exception mechanism. This is not supported in C however.
The proper way to do this is to have the function return some value that specifies an error then check the value when the function exits.
If you really want to do a non-local goto
, you can do this with setjmp
and longjmp
. This is as close as you'll get to exception handling in C.
jmp_buf hextonum_err;
int main( int argc, char **argv ){
...
if (setjmp(hextonum_err) != 0) {
goto infile_error;
}
while( (c = fgetc( in )) != EOF ){
...
}
unsigned short hextonum( char hex ){
if( hex >= '0' && hex <= '9' ){
// don't use atoi here as that expects a string
return hex - '0';
}
switch( hex ){
case( 'a' ) : case( 'A' ) : return 0xa;
case( 'b' ) : case( 'B' ) : return 0xb;
case( 'c' ) : case( 'C' ) : return 0xc;
case( 'd' ) : case( 'D' ) : return 0xd;
case( 'e' ) : case( 'E' ) : return 0xe;
case( 'f' ) : case( 'F' ) : return 0xf;
}
longjmp(hextonum_err, 1);
// never reached, but put here because compiler will warn on non returning a value
return 0;
}
In general, I wouldn't recommend writing code like this.