OS-9/OSK Answers! by Joel Mathew Hegberg
December 1993 issue


Lately, there has been a lot of discussion on Delphi regarding simple yet helpful subroutines. During the discussions, Ted Jaeger mentioned he had written an "intelligent entry" subroutine for OS-9 BASIC. Essentially, you can specify what characters a user is allowed to enter, and any special characters, that may be required, such as the hyphen in a nine-digit zip code (30125-5114). This type of a routine is extremely useful for data entry applications, as it helps ensure valid data is being typed in.

I decided to take his OS-9 BASIC source and port it over to C to allow wider use of the routine. Listing #1, "entry.bas", is the commented OS-9 BASIC source for the Intelligent Entry subroutine. Listing #2 is the commented source for "testentry.bas", which is a simple program to demonstrate how to use the entry routines.

For C programmers, due to limited space, I've not included Ted's comments within the C source, but you can simply look to the OS-9 BASIC source if you have questions about the code. Listing #3, "entry.c", is the C source which can easily be compiled down to a relocateable '.r' file. If you're using the Microware C Compiler, you simply type "cc entry.c -r=<dir>" where <dir> is the directory you want the '.r' file to be placed in. After you have the relocateable file, any programs you compile in the future that use the entry routine simply have to be "linked" to 'entry.r' by the compiler. This also is fairly easy. For instance, to compile listing #4, "testentry.c", using the Microware C Compiler, you may use "cc testentry.c -l=entry.r". ( The previous assumes that entry.r is in the current data directory. If not, the entire pathlist to entry.r would need to be specified.)

As always, anyone else with helpful OS-9 subroutines, please feel free to send them in!

LISTING #1: "entry.bas"
=======================
PROCEDURE entry
(* Nov 5, 1993
(* Intelligent entry routine
(* allows predefined characters in a string
(* and restriction of user input to
(* programmer specified characters
(* Cursor selection and input
(* positioning on screen also included
(* Inspired by a similar GWBASIC program
(* written by Paul Lepato and published in
(* November PCM

(* return string to calling routine
(* adjust string length as needed
PARAM result:STRING[5]
(* initial value of input string
(* string length should match length of "result" string
PARAM fields:STRING[5]
(* string of characters identified as acceptable
(* to this routine
(* again, adjust string length as needed
PARAM allow:STRING[15]
(* asc specification of cursor character
PARAM cursor:INTEGER
(* location on screen where input will occur
PARAM row,col:INTEGER

DIM char:STRING[1]
DIM offset:INTEGER
DIM errnum:INTEGER
DIM ok:BOOLEAN
DIM k:INTEGER

(* check for legitimate values of row and col
IF row>24 OR col>76 THEN
PRINT CHR$(7);
END
ENDIF

(* add BACKSPACE to allowable characters
allow=allow+CHR$(8)

offset=1
ok=FALSE

ON ERROR GOTO 100

SHELL "tmode noecho"
(* cursor off
PRINT CHR$($05); CHR$($20)
PRINT CHR$(2); CHR$(31+col); CHR$(31+row); fields
PRINT CHR$(2); CHR$(31+col); CHR$(31+row); CHR$(cursor);

WHILE offset<=LEN(fields) DO
IF MID$(fields,offset,1)=" " THEN
GET #0,char
(* is it allowable character?
GOSUB 10

IF ok=TRUE THEN
(* go process the letter
GOSUB 20
ELSE
(* beep at user
PRINT CHR$(7);
ENDIF

ELSE
(* skip predefined characters
result=result+MID$(fields,offset,1)
PRINT CHR$(2); CHR$(31+col+offset); CHR$(31+row); CHR$(cursor);
offset=offset+1
ENDIF

ok=FALSE
ENDWHILE
END

10 (* see if allowable character was entered
k=1
WHILE k<=LEN(allow) DO
IF MID$(allow,k,1)=char THEN
ok=TRUE
ENDIF
k=k+1
ENDWHILE
RETURN

20 (* determine letter and take action
IF char<>CHR$(8) THEN
PRINT CHR$(2); CHR$(31+col+offset-1); CHR$(31+row); char;
result=result+char
offset=offset+1
ELSE
(* erase cursor
PRINT CHR$(2); CHR$(31+col+offset-1); CHR$(31+row); " ";
(* find fields field
WHILE offset>1 DO
offset=offset-1
EXITIF MID$(fields,offset,1)=" " THEN
ENDEXIT
ENDWHILE
PRINT CHR$(2); CHR$(31+col+offset); CHR$(31+row); MID$(fields,offset+1,1);
result=LEFT$(result,offset-1)
ENDIF

(* display cursor
IF MID$(fields,offset,1)=" " THEN
PRINT CHR$(2); CHR$(31+col+offset-1); CHR$(31+row); CHR$(cursor);
ENDIF
RETURN

100 errnum=ERR
(* user struck [ESC] key
IF errnum=211 THEN
END
ENDIF


LISTING #2: "testentry.bas"
===========================
PROCEDURE testentry
DIM result:STRING[5]
DIM allow:STRING[15]
DIM fields:STRING[5]
DIM cursor:INTEGER
DIM row,col:INTEGER

DIM sub:STRING[5]
sub="entry"

(* allow user to enter only numbers
allow="1234567890"
(* define original fields string
(* with predefined colon
fields="  :  "
(* pick a cursor
(* examples from MM1 ascii codes
(* 63= question mark;  254=small block; 95=underline
(* 219=large block; dollar sign=36; number sign=35
cursor=219
(* define position on screen
col=50
row=10
(* initialize return string
result=""
RUN sub(result,fields,allow,cursor,row,col)
PRINT
PRINT result; " returned"
SHELL "tmode echo"
(* turn cursor back on
PRINT CHR$($05); CHR$($21);


LISTING #3: "entry.c"
=====================
#include <stdio.h>

#define FALSE 0
#define TRUE  1
#define STDIN 0

int entry(result, fields, allow, cursor, row, col)
char *result, *fields, *allow;
int  cursor, row, col;
{
    char chr[2];
    int  offset, errnum, ok;
    
    if (row > 24 || col > 76)
    {
        putchar(7);
        return(0);
    }
    
    offset = 1;
    ok = FALSE;
    *result = 0x00;
    chr[1] = 0x00;
    
    system("tmode noecho\n");
    printf("\x05\x20");
    printf("\x02%c%c%s", 31 + col, 31 + row, fields);
    printf("\x02%c%c%c", 31 + col, 31 + row, cursor);
    fflush(stdout);
    while(offset <= strlen(fields))
    {
        if (*(fields + offset - 1) == 0x20)
        {
            if (read(STDIN, chr, 1) != 1) return(FALSE);
            ok = check_valid_char(chr[0], allow);
            if (ok) process_char(chr, fields, result, &offset, cursor, row, col);
            else putchar(7);
        }
        else
        {
            chr[0] = *(fields + offset - 1);
            strcat(result, chr);
            printf("\x02%c%c%c", 31 + col + offset, 31 + row, cursor);
            offset++;
        }
        fflush(stdout);
        ok = FALSE;
    }
    return(TRUE);
}

int check_valid_char(chr, allow)
char chr, *allow;
{
    int k;
    
    if (chr == 0x08) return(TRUE);
    k = 1;
    for (k = 1; k <= strlen(allow); k++)
    {
        if (*(allow + k - 1) == chr) return(TRUE);
    }
    return(FALSE);
}

process_char(chr, fields, result, offset, cursor, row, col)
char *chr, *fields, *result;
int  *offset, cursor, row, col;
{
    if (*chr != 0x08)
    {
        printf("\x02%c%c%c", 30 + col + *offset, 31 + row, *chr);
        strcat(result, chr);
        *offset += 1;
    }
    else
    {
        printf("\x02%c%c%c", 30 + col + *offset, 31 + row, 0x20);
        while(*offset > 1)
        {
            *offset -= 1;
            if (*(fields + *offset - 1) == 0x20) break;
        }
        printf("\x02%c%c%c", 31 + col + *offset, 31 + row, *(fields + *offset));
        *(result + strlen(result) - 1) = 0x00;
    }
    
    if (*(fields + *offset - 1) == 0x20)
        printf("\x02%c%c%c", 30 + col + *offset, 31 + row, cursor);
    return;
}


LISTING #4: "testentry.c"
========================
#include <stdio.h>

main()
{
    char result[6], fields[6], allow[15];
    int  cursor, row, col, ok;
    
    strcpy(allow, "1234567890");
    strcpy(fields, "  :  ");
    cursor = 219;
    col = 50; row = 10;
    result[0] = 0x00;
    ok = entry(result, fields, allow, cursor, row, col);
    if (ok) printf("\n%s returned.\n", result);
    else printf("\nError occured while reading keyboard.\n");
    system("tmode echo\n");
    printf("\x05\x21");
}

* THE END *