Error trapping

PowerBASIC has two broad classes of errors, compile-time and run-time. It catches as many compile-time errors as it can, while it is compiling your source code. Some errors will not show up until you run it, either in the IDE, or under DOS, as an .EXE file, so these are called run-time errors.

Error trapping catches the run-time errors before PowerBASIC does, and lets you keep control over what happens next, instead of your program "bombing out".

Before any filename-related or pathname-related operations, and before any device input or output operation, it is a good idea to designate an error-recovery routine, by use of the ON ERROR command.

ON ERROR GOTO Label.

ON ERROR GOTO LineNumber

ON LOCAL ERROR GOTO Label

ON LOCAL ERROR GOTO LineNumber

ON ERROR RESUME NEXT

ON ERROR STATEMENT

ON ERROR, and ON LOCAL ERROR, are statements that turn on user-defined error checking, and link it to a particular error handling routine.

Once user-defined error handling has been turned on with this statement, instead of displaying an error message and terminating execution, i.e., "bombing out", all run-time errors will result in a jump to your own error-handling code.

There can be local and global error handlers.

Global error handlers are located in the mainline level of your program. They are turned on by the ON ERROR GOTO statement.

Local error handlers are located inside a function or subroutine. They are turned on by

the ON LOCAL ERROR GOTO statement.

When you encounter a local error, then execution will jump to that local error handler, where you can keep better control over execution flow after that.

You can have many small, focused, specific error handlers, inside your risky functions and subroutines, and a large, catch-all, general error handler, in your mainline level program module.

Example

rem Try this to see what happens.

on error goto errorexit

dummy=FNHiThere

dummy=FNTryLocal1

dummy=FNTryLocal2

error 6

input "out of here";x$

system

errorexit:

print"err=";err,"eradr=";eradr

input"at error exit global ";x$

system

def FNHiThere

on local error goto gotcha

exit def

gotcha:

print"err=";err

input"at gotcha local";x$

end def

def FNTryLocal1

error 8

end def

def FNTryLocal2

error 12

end def

ERROR-HANDLING ROUTINES

Inside your error handling routine, you need to check ERR first of all, and find out what kind of error it was. You can also check ERADR to get the address of the error.

Since the system variables err, erl, eradr, erdev, and erdev$ are directly accessible by the programmer, you can find out where the error occurred with eradr, and what kind of error it was, with err, and then use {AltC,FindError} command to get the PowerBASIC IDE to point right to it, in a few seconds.

Then you can see what your mistake was, or where it needs extra error-recovery to anticipate unforeseen circumstances.

If an error is found outside the IDE, under DOS, PowerBASIC code in your .EXE file will display the memory address, and you can get back into the IDE, and also use the {AltC,FindError} command to get PowerBASIC to find it for you.

Example

rem you can use these in any error handler

dummy=FNSaveTheErrorNumbers

dummy=FNDisplayTheSavedErrorNumbers

def FNSaveTheErrorNumbers

rem save these before the RESUME

saveerr=err

saveeradr=eradr

saveerdev=erdev

saverdev$=erdev$

end def

def FNDisplayTheSavedErrorNumbers

rem these can be sure to be there after the RESUME

print"err=";saveerr,"eradr=";saveeradr

print"erdev=";saveerdev,"erdev$=";saverdev$

input x$

end def

Example

GetFileName:

Input"Type in file name to retrieve ";FileName$

ON LOCAL ERROR GOTO LocalErrorHandler

open FileName$ for input as #1

if saveerr>0 then goto GetFileName

...

...

LocalErrorHandler:

dummy=FNSaveTheErrorNumbers

dummy=FNDisplayTheSavedErrorNumbers

select case err

case %FileNotFound

print"File not found"

case %FileAlreadyOpen

print"File already open"

case %PathNotFound

print"Path doesn't exist"

end select

input"Type ENTER to continue ";x$

RESUME NEXT ' will take you back to the next line

Checking ERR for its value will not reset it to zero, until you execute a RESUME, but checking it with ERRTEST will reset ERR and ERRTEST to zero.

ERRTEST is reset to zero after you reference it.

ERR is not reset to zero until you do a RESUME.

Don't try to call another subroutine or function from your error handling routine, before you do a RESUME.

'try this example

ON ERROR RESUME NEXT

OPEN "nothere" FOR INPUT AS #1

print"err=";err,"errtest=";errtest

rem as soon as errtest was accessed, both err and errtest were reset

print"err=";err,"errtest=";errtest

IF ERRTEST <> 0 THEN PRINT "something is wrong"

PRINT "Now the value of ERRTEST is";ERRTEST

input x$

RESUME

After a runtime error, your error-recovery code will be turned off until you do a RESUME. Any subsequent errors will trigger PowerBASIC's error-handling, which usually will bomb out your program.

Use the RESUME statement to continue execution, or STOP or END to terminate the program.

RESUME

RESUME NEXT

RESUME { Label | LineNumber }

Remarks

ON ERROR RESUME NEXT causes PowerBASIC to always execute the next statement following a run-time error. It is a shorthand version of

ON ERROR GOTO Label

IF ERRTEST<>0 THEN print"We have a problem ": goto errorexit

...

...

Label:

...

RESUME NEXT

If you use this one, you need to have the next line of instructions check to see if there is an error.

DISABLE ERROR TRAPPING

To disable error trapping, use

ON ERROR GOTO 0, in global error handlers, and

ON LOCAL ERROR GOTO 0, in local error handlers.

PRE-DEFINED CONSTANTS FOR ALL

TRAPPABLE RUN-TIME ERRORS

Programs can be made more readable, if you use pre-defined constants instead of numbers, when you check for a certain kind of error. This can be important when you pick up an old project after six months, or if someone else takes over your work. It will look a lot less like gobbledygook. The less you strain your human memory banks, the more reserve energy you will have.

Example

' Predefined equates for all trappable run-time errors. Programs are more

' readable if you reference symbols instead of literal numbers.

%SyntaxError=2: %ReturnWithoutGOSUB=3: %OutOfData=4

%IllegalFunctionCall=5: %OverFlow=6: %OutOfMemory=7

%SubscriptOutOfRange=9: %DuplicateDefinition=10: %DivisionByZero=11

%TypeMismatch=13: %OutOfStringSpace=14: %StringTooLong=15

%NoRESUME=19: %ResumeWithoutError=20: %DeviceTimeOut=24

%DeviceFault=25: %OutOfPaper=27: %FieldOverflow=50

%InternalError=51: %BadFileNumber=52: %FileNotFound=53

%BadFileMode=54: %FileAlreadyOpen=55: %DeviceIOError=57

%FileAlreadyExists=58: %BadRecordLength=59: %DiskFull=61

%InputPastEnd=62: %BadRecordNumber=63: %BadFileName=64

%TooManyFiles=67: %DeviceUnavailable=68: %ComBufferOverflow=69

%PermissionDenied=70: %DiskNotReady=71: %DiskMediaError=72

%FeatureUnavailable=73: %RenameAcrossDisks=74: %PathFileAccessError=75

%PathNotFound=76: %OutOfStackSpace=201: %MismatchedCommons=203

%MismatchedOptions=204: %MismatchedRevisions=205: %InvalidProgramFile=206

%ArrayIsStatic=207: %InvalidStringHandle=208: %IncompatibleMouse=209

HELP SYSTEM

The new version 3.0c help system {F1,F1}, has many errors explained, and it is faster than getting out the PowerBASIC Reference Guide, and looking in the back.

BULLET-PROOFING YOUR PROGRAM

Some errors will not immediately cause your program to abort, but will contribute to more and further errors.

If you are developing commercial software or software for others to use, at least half of your time will be devoted to building in error-recovery routines, to catch all possible human operator errors, so that the computer will not hang up because a new user does not know all your commands yet.

The more that you can cover for, provide error-recovery routines for, all possible user errors, the more "user-friendly" your programs will be.

One way to cover for operator error is to use SELECT CASE to list all the possible inputs that the operator can use, and also a CASE ELSE, which will catch all the other possible keys that a novice might try, or accidentally touch.

Example

ChooseNow:

print"Choose your vacation destination"

input"A=California, B=Hawaii, C=NewYork, D=Paris ";TempChar$

TempChar$=lcase$(TempChar$)

SELECT CASE TempChar$

CASE "a"

AirTime$="1 hour"

CASE "b"

AirTime$="4 hours"

CASE "c"

AirTime$="8 hours"

CASE "d"

AirTime$="17 hours"

CASE ELSE

goto ChooseNow

END SELECT

print"Flight time is ";AirTime$

Remarks

If you did not include the CASE ELSE lines, any stray response would fall right thru and AirTime$ would be blank.

CATCHING UNEXPECTED ERRORS SOONER

It is a good idea to put a function call to check ERR in places that your program cycles thru on a regular basis, to catch any hidden or unresumed errors, before they can do any real damage.

This will help you to find all the weak areas in your programs, before your customers do, so that you can put in extra error-recovery code.

Example

err=FNTrapUnresumedErrors

def FNTrapUnresumedErrors

if err>0 then

print"Further Error Recovery Required At

print"err=";err,"eradr=";eradr

print"erdev=";erdev,"erdev$=";erdev$

print"in TrapUnresumedErrors

input"Type Control-Break to exit to system ";x$

end if

FNTrapUnresumedErrors=err

end def

Remarks

You can also use ERRTEST for this same checking.

TESTING YOUR ERROR HANDLING ROUTINES

You can also simulate each type of error by using the ERROR n command in with your code, guarded by a conditional compilation flag.

$if %testing

ERROR 53

$endif

COMPILER ERROR-CHECKING CODE GENERATION

The $ERROR commands give you the power to control whether and where PowerBASIC will generate different types of runtime error-checking code, to minimize the size of the compiled .EXE file output.

$ERROR ALL { ON | OFF | + | - }

These are your choices.

1. Error checking code all thru your program.

$ERROR ALL ON

2. No error checking code anywhere in your program.

$ERROR ALL OFF

3. Error checking code only thru really risky portions of your program.

$ERROR ALL OFF

...

...

$ERROR ALL ON

on error goto errorexit

open "tempfile." for input as #3

line input#3,InputLineString$

...

close#3

$ERROR ALL OFF

...

...

You can even control four types of error checking with even more explicit

$ERROR commands.

$ERROR BOUNDS { ON | OFF | + | - }

$ERROR NUMERIC { ON | OFF | + | - }

$ERROR OVERFLOW { ON | OFF | + | - }

$ERROR STACK { ON | OFF | + | - }

One option is to have PowerBASIC generate the error-checking code during testing, and then when you are satisfied with the program, you can take it out, and make the program smaller and faster.

$ERROR BOUNDS metastatement

When toggled On, this option causes the compiler to generate code that checks array subscripts. Subscript-checking code ensures that a subscript is appropriate, given the size of the array it is about to be used on.

It also checks to see that an array being referenced has been dimensioned first.

With Bounds Test toggled On, you'll see

"Subscript range error" (error 9)

whenever your program attempts to use a subscript too large or too small for a given array.

Note that the compiler always reports constant out-of-range subscripts.

$ERROR NUMERIC metastatement

Numeric error checking traps errors in numeric operations, such as divide-by-zero, or assignment overflow. When this option is disabled, the generated code will be faster and

smaller, but will be unpredictable if any numeric errors occur.

$ERROR OVERFLOW metastatement

Overflow occurs when the result of an arithmetic operation is so large or small that the sign of the result is incorrectly changed. Sometimes, overflow is harmless and self-correcting:

x%= 32000 + 1000 - 500

PowerBASIC evaluates the above expression in 16-bit cpu registers. So, the first operation, 32000 + 1000, would yield an overflow with an internal representation of -32536. That's a quirk of two's-complement arithmetic, as 33000 is beyond the range of an integer. Luckily, the second operation, subtracting 500, brings the result back into valid integer range at 32500. Other cases may not be so fortunate.

If overflow checking is enabled, PowerBASIC validates every arithmetic operation for

overflow, and generates an error 6 (overflow) if it should occur. This should not be confused with assignment overflow, the condition which occurs when you attempt to assign a value to a variable that is too large or small to be represented correctly:

x%= 44000

Even though this condition also generates an error 6 (overflow), the check for it is governed by the Numeric test toggle. Even though it is a litlle confusing to have two conditions generate the same error number, we chose to do so in order to maintain compatibility with other forms of the Basic language.

$ERROR STACK metastatement

When Stack Test is toggled On, PowerBASIC generates code that checks for stack collisions upon entry to each subroutine, function, or procedure. If you suspect that your programs are running out of stack space, compile with this switch turned on.

A typical reason for running out of stack space is performing many nested recursive procedure and function calls.

To allocate more stack space, use the $STACK metastatement in your program.

ERROR IN A DEVICE DRIVER

If an error occurs in a device driver, PowerBASIC will pass you the name of the device in

ERDEV$, and the error code returned by DOS, in ERDEV.

YOUR OWN CUSTOM ERROR CODES

You can define your own custom error codes, from numbers not used by PowerBASIC, by use of the ERROR errcode statement, and your own special error handlers for those numbers. This is another way that PowerBASIC gives you a way to extend it, and do it

your own way.

This file has been created with the evaluation or unregistered copy of
EasyHelp from Eon Solutions
(Tel: UK 0973 209667, Email: eon@cix.compulink.co.uk)