Click for: WEBPAGE INDEX

web page updated 10.jun.2008,

Many people arrive at this webpage google searching for -dJPEGQ, for that visit:

Ghostscript -dJPEGQ usage explained

General topic: cryptic code hides bugs, dont compress lots of actions into one statement, instead break up the code into lots of actions with only one thing happening per statement. This generally exposes bugs.

Example from the -sDEVICE=jpeg source code, here is the code that reads the -dJPEG switch eg -dJPEGQ=80:

switch (code = param_read_int(plist, (param_name = "JPEGQ"), &jq)) {
	case 0:
	    if (jq < 0 || jq > 100)
		ecode = gs_error_limitcheck;
	    else
		break;
	    goto jqe;
	default:
	    ecode = code;
	  jqe:param_signal_error(plist, param_name, ecode);
	case 1:
	    break;
    }

After reading this web page look at the ascii version of the code transformation as HTML has a total disregard for spaces tabs and newlines. html was not designed by computer programmers I suspect. In fact it was not designed but appears to have been somehow hodge podged together (probably by physicists)

First point: most of programming time is spent reading programs and not writing them. When you program always try to make life easy for the reader *not* the writer. Badly written programs eat up huge amounts of debugging time. Those seconds that programmers save by cryptic tricks are offset by hours of debug time.

In this code fragment it is impossible to know what is going on, sometimes people do this deliberately to cover their tracks!

Lets look at the first line of the above: 3 different things are happening,

1. param_name is being set to the value "JPEGQ"

2. A function call is being made: param_read_int(...)

3. A switch(..) is being made on the return value.

This is very difficult to read in one scan of the fragment. As there are 3 things going on, why dont we break it up into 3 steps:

param_name = "JPEGQ" ;
 code = param_read_int( plist , param_name , &jq ) ;
switch(code)
	{
	 etc 

There, the program flow is now clear as glass.

What lies inside the switch statement is total chaos!

There are only 3 cases in the switch, yet it is totally knotty program flow with even a goto. And that goto is to 1 line of code. I can understand someone using a goto to save retyping a lot of lines but not one line! Please!!

Lets undo the goto in the case 0: fragment:

	case 0:
	    if (jq < 0 || jq > 100)
		{
		ecode = gs_error_limitcheck;
		param_signal_error(plist, param_name, ecode);
		}
	    break;

Thats a bit more readable and understandable. As a general rule c switch cases should always terminate with a break; unless you have a very good reason. Its a good idea also for the final case to end with break; : this makes the code more stable to cut-and-paste activities. Anyway as a result we can remove the label jqe: from the default statement which can become:

	default:
	  ecode = code;
	  param_signal_error(plist, param_name, ecode);
	break ;

Finally the case 1: fragment immediately goes to a break; ie it is being used as a trick to define the default case.

This does not look good, programming-by-exclusion is usually a bad idea: as a program expands the excluded set changes, this leads to bugs.

But how else can we do this?

The answer is that using switch for 3 cases where 1 is a default is a bit heavy handed, lets use if-else constructs instead. Switch statements are the correct approach when you have a lot of cases eg 10 or 100. So rewritten from scratch the new nice + readable version becomes:

param_name = "JPEGQ" ;
code = param_read_int( plist , param_name , &jq ) ;
if( code==0 )
	{
	if( jq < 0 || jq > 100 )
		{
		ecode = gs_error_limitcheck ;
		param_signal_error( plist , param_name , ecode ) ;
		}
	}
else if( code!=1 )
	{
	ecode = code;
	param_signal_error(plist, param_name, ecode);
	}

HTML sucks because it doesnt understand spaces, tabs and newlines, but apart from that glitch we at last can see whats going on! (much later on: I found how to do tabs etc with html, you use <pre>) Look at the non html text version, the above fragment is the second fragment in this non html. Looking at this version the if clause is reporting error if the parameter is out of range. The else if clause is clearly also reporting error. But now I detect a problem: the internal-GS8 convention is that errors are signified by return values < 0. If code is eg 2,3,4 etc it is positive therefore not an error. This code is wrong!!! It evidently doesnt matter as I have used -sDEVICE=jpeg -dJPEGQ=... many times. Notice also the severity of the response: it appears to be responding with a signal! This example shows how by clarifying our code bugs become much more visible. Well written code generally is bug free code, because it makes bugs stand out like a naked woman on the pavement! The possibilities for -dJPEGQ=... are:

1. The switch is absent: experiment reveals the value for code is then 1.

2. The code is an integer, here code is 0.

3. The switch is present but contains garbage: experiment reveals that either a negative value is returned eg -20 == "typecheck error" or the function never returns but the program quits! eg -dJPEGQ=x causes the program to quit saying -dvar=name is only allowed for name equal to null, true or false.

So finally we reach the correct version which is:

param_name = "JPEGQ" ;
code = param_read_int( plist , param_name , &jq ) ;
if( code==0 ) 
	{
	/* -dJPEGQ=valid integer */

	if( jq < 0 || jq > 100 )
		{
		ecode = gs_error_limitcheck ;
		param_signal_error( plist , param_name , ecode ) ;

		/* 
		how terrible, the int is out of range lets 
		quit work for today and generally give up 
		*/
		}
	}
else if( code < 0 ) /* -dJPEGQ=non-integer garbage */
	{
	ecode = code;
	param_signal_error(plist, param_name, ecode);

	/* :give up hope at the slightest excuse */
	}

Finally we have correct code, you can quickly look at this code and *know* it is correct, you dont even need to compare it with the original (wrong) fragment! Return and look at the original code, the author of it certainly didnt detect this bug! Neither did I until I "improved" the clarity + sequencing of the code.

Of course I wouldnt actually alter the source code of -sDEVICE=jpeg as it functions correctly. Personally I believe in the error-recovery concept: if the value is garbage issue an error message, ignore the garbage and continue without it! Again look at the non html text version to see the 3 phases of this transformation: code viewed via html always comes out as jibberish. If someone out there knows how to make html render multiple spaces and tabs correctly, email me!

The reason that I have used Memacs ever since I bought my A500 is because it understands + respects spaces tabs and newlines. In particular it really understands tabs the way I code.

You may be wondering why I was looking at this fragment! The reason is that for the new GS8 I am working on I need to be able to extract command line arguments. Integer arguments are quite important so I was looking to see how -sDEVICE=jpeg -dJPEGQ=... did it and ended up at the incomprehensible code fragment at the top of this web page. This needed the steps outlined above to reduce it to the last item in the non html version before I could understand it. With that I now understand how to get command line arguments. (there is no argc , argv available for this).
Get a GoStats hit counter