Valid

First not submitted for publication in Quanta in March 1992

Having followed the "debate" on input validation in the pages of this magazine for some time, I though it about time I did my bit. The starting point for my VALID keyword below is: let QDOS do the work! Obviously, QDOS accepts inputs that we may not always find logical eg x%='1e700' is a valid integer (x%=1!) in QDOS' eyes though it won't do the same for x (overflow), however the main point is often just to check whether a user input conforms with certain restrictions so that our carefully crafted programs don't ignominously fall over due some unintended input. VALID, together with other useful routines can be found in my programming toolkit, PTOOL, available to members from the library.

VALID: It's task in life is merely to check a number or variable to see whether is conforms to the internal QDOS format.

RETurn VALID(expected_type,variable)

where expected_type is:

0 = long integer, 1 = string, 2 = floating point, 3 = integer.

A further type exists: -1 = query

If the variable, literal or expression does not conform to the expected type the function returns zero, otherwise it returns non-zero. An additional bonus of this function is that it returns zero if the variable is unset ie, has not been given any value. (This is not true for SBASIC though. But then it doesn't fall over when it hits an unset variable either). This makes it possible to write procedures in SuperBasic with optional parameters, eg

DEFine PROCedure Test(a$,b,c%)
IF NOT VALID(3,c%):c%=default%
PRINT a$,b,c%
END DEFine

Set default% to whatever you like and try Test '1',2 or Test 1,2,3 - or Test '1',2,'rubbish'. (SBASIC requires a slight modification).

A variation on VALID is query:

RETurn VALID(-1,variable)

returns the type of the parameter variable as an integer. It does not tell you whether the contents of a variable are valid or not eg whether a$='.-3' is a valid number, as you can using the other type specifications above, but it will tell you the basic type of the variable in question. These are the types that can be tested (values in hex and decimal):

$0001 (1) unset str   $0201 (513) set str   $0301 (769) str array
$0002 (2) unset fp    $0202 (514) set fp    $0302 (770) fp  array
$0003 (3) unset int   $0203 (515) set int   $0303 (771) int array

also:

$0602 (1538) REPeat loop index   $0702 (1794) FOR loop index
$0300 (0768) Sub string

You can also tell which separator (ie , ; ! \ TO) variable is followed by, or whether it is preceded by a hash -

PRINT VALID(-1,#2;)

but what's the point?


        section code

* SuperBasic VALID function     ©PWITTE 1992

        lea.l table,a1          ;point to keyword definitions
        move.w $110,a2          ;bp.init
        jmp (a2)                ;link to SuperBasic

table
        dc.w 0,0                ;no procs, endfor procs
        dc.w 1                  ;one function
        dc.w VALID-*            ;offset to routine
        dc.b 5,'VALID'          ;name of keyword
        dc.w 0                  ;endfor fns

v_err_bp
        moveq #-15,d0           ;error "bad parameter"

v_err
        rts

VALID
        lea.l 8*2(a3),a4        ;check for exactly two parameters
        cmpa.l a4,a5
        bne.s v_err_bp          ;if <> 2 return bad parameter error
        lea.l 8(a3),a5          ;hide last parameter to get first only
        move.w $112,a2          ;ca.gint
        jsr (a2)                ;get an integer
        bne.s v_err             ;something's gone wrong, quit

        move.l a5,a3            ;point to next parameter
        addq.l #8,a5            ; which is the final parameter
        move.w 0(a6,a1.l),d7    ;d7 contains first parameter
        bmi.s v_query           ;if negative then it is a query

        cmpi.w #3,d7            ;key range is 0..3
        bgt.s v_err_bp          ; number too big, quit

        movea.l $58(a6),a4      ;copy of (almost) empty stack pointer
        addq.l #2,$58(a6)       ;reset stack
        add.w d7,d7             ;convert key to offset
        movea.w d7,a2           ;a2 = offset to fetch routine vector
        moveq   #1,d7           ;assume valid
        movea.w $112(a2),a2     ;attempt to get second parameter
        jsr (a2)                ; according to specified type
        beq.s v_return          ;if valid return TRUE

        moveq #0,d7             ;not valid, FALSE

v_return
        movea.l a4,a1           ;reset stack leaving room for integer
        move.l a1,$58(a6)       ;set bv.rip

v_ret
        move.w d7,0(a6,a1.l)    ;put return value on stack
        moveq #3,d4             ;tell BASIC: integer return
        moveq #0,d0             ;no errors
        rts                     ;all done

v_query
        move.w 0(a6,a3.l),d7    ;query. d7 contains parameter
        bra.s v_ret             ; information to return
*

        end

HTML generated by the amazing asm2htm !
2004 Nov 09

A (very) simple input validation routine could then look something like this:

DEFine FuNction get_integer%
REPeat input_loop
 INPUT "Enter integer"!n$
 IF VALID(3,n$):EXIT input_loop
 PRINT "Not an integer!"
END REPeat input_loop
RETurn n$
END DEFine get_integer%

You can download the ready-to-run toolkit from here