MASKED INPUT BUGS

{************************************************************************
***********************MASK INPUT UNIT PROBLEM**************************
************************************************************************

THIS UNIT IS HAS BUG THAT I CANNOT FIND
WILL SOMEONE PLEASE HELP ME TO FIX THEM WITHOUT RENDERING
THE UNIT TOO MUCH SINCE MOST OF THE FEATURES ARE THERE
FOR A SPECIFIC REASON.

IF YOU WANT TO COMPILE THIS YOU ALSO NEED TO COMPILE
UNIT SCRN(I WILL POST THIS IN ANOTHER MESSAGE).

THE BUGS I HAVE FOUND WAS:
*THE TEST PROGRAM INDICATES THAT THE CURSOR IS NOT ALWAYS AT THE
RIGHT POSITION.
*IF YOU INITIALLY START READING THE CURSOR IS ONE POSITION LEFT
OF WHERE IT SHOULD BE(NOTE THAT THIS ONLY HAPPENS IN SECTION 1)
*IF YOU FILL THE WHOLE TEXT AREA THE CURSOR WILL JUMP TO THE FIRST
POSITION(DON'T ASK ME WHY).

Quote
}

UNIT Inputs;

INTERFACE
CONST ESC = #27; CR = #13; TAB = #9; BSP = #8;
TYPE MaskType = record
                x,y,max:byte;
                prompt,
                mask,
                sIn, sOut : String;
                fillChar : Char;
                end;

PROCEDURE SetReadMask(var
id:Masktype;x,y:byte;prompt,mask,sIn:String;fillChar:Char);

PROCEDURE ReadMask(var id:MaskType;var sOut:String);

IMPLEMENTATION

USES Crt, Scrn;

PROCEDURE WriteMask(id:MaskType);
var ix,add:byte;
begin
   ix:=0;
   add:=0;

   repeat
     inc(ix);
     if (id.mask[ix] IN ['@', '#', '_']) then
        begin
        inc(add);
        if length(id.sOut)+1>add then
PutChar(id.x+length(id.Prompt)+ix,id.y,id.sOut[add])
        else PutChar(id.x+length(id.Prompt)+ix,id.y,id.fillChar);
        end
     else PutChar(id.x+length(id.Prompt)+ix,id.y,id.mask[ix]);
   until (ix = length(id.mask));
end;

PROCEDURE SetReadMask(var
id:Masktype;x,y:byte;prompt,mask,sIn:String;fillChar:Char);
var ix,add:byte;
begin
   id.x:=x;
   id.y:=y;
   id.prompt:=prompt+' ';
   id.mask:=mask;
   id.fillChar:=fillChar;
   for add := 1 to length(sIn) do id.sIn[add]:=Upcase(sIn[add]);
   for add := 1 to length(sIn) do id.sOut[add]:=Upcase(sIn[add]);
   id.sIn[0]:=chr(length(sIn));
   id.sOut[0]:=chr(length(sIn));
   id.max:=0;
   for ix := 1 to length(id.mask) do
      if (id.mask[ix] IN ['@','#','_']) then
         inc(id.max);

   PutStr(x,y,Prompt);

   WriteMask(id);
end;

PROCEDURE ReadMask(var id:MaskType;var sOut:String);
var ix,pos,i:byte;
    ch:Char;
procedure init;
begin
   ix:=0;
   pos:=1;
   repeat
     inc(ix);
     if (id.mask[ix] IN ['@','#','_'])AND(length(id.sOut)+1>pos) then
inc(pos);
   until (pos>length(id.sOut))AND(id.mask[ix] IN ['@','#','_']);
end;

begin
   showcursor;
   init;

   with id do
   begin
      repeat
         if (mask[ix] IN ['@', '#', '_']) then
            begin
               gotoxy(id.x+ix+length(id.prompt),id.y);
               WriteMask(id);
               ch:=Upcase(Readkey);
               if length(id.sOut)<max+1 then
               case mask[ix] of
                  '@':if (ch IN ['A'..'Z', ' ', '.', '-']) then
                     begin
                     if (pos=1+length(id.sOut)) then id.sOut:=id.sOut+ch
                     else id.sOut[pos]:=ch;
                     inc(pos);
                     inc(ix);
                     end;
                  '#':if (ch IN ['0'..'9', '.']) then
                     begin
                     if (pos=1+length(id.sOut)) then id.sOut:=id.sOut+ch
                     else id.sOut[pos]:=ch;
                     inc(pos);
                     inc(ix);
                     end;
                  '_':if NOT(ch IN [CR, TAB, BSP, ESC]) then
                     begin
                     if (pos=1+length(id.sOut)) then id.sOut:=id.sOut+ch
                     else id.sOut[pos]:=ch;
                     inc(pos);
                     inc(ix);
                     end;
               end;
               case ch of
                  CR:id.sOut[length(id.sOut)+1]:=ch;
                  TAB:id.sOut[length(id.sOut)+1]:=ch;
                  ESC:
                     begin
                     id.sOut:=id.sIn;

SetReadMask(id,id.x,id.y,id.prompt,id.mask,id.sIn,id.fillChar);
                     init;
                     end;
                  BSP:if not(pos=1) then
                     begin
                     for i := pos-1 to length(id.sOut)-1 do
                        id.sOut[i]:=id.sOut[i+1];
                     id.sOut[0]:=chr(ord(id.sOut[0])-1);
                     dec(pos);
                     dec(ix);
                     end;
               end;
            end
         else inc(ix);
      until (ch IN [CR,TAB]);
   end;
   sOut:=id.sOut;
end;

BEGIN

END.

{-------------------test program-----------------------------}
PROGRAM test;
USES crt,inputs;
VAR mask:MaskType;
    output:String;

BEGIN

clrscr;
SetReadMask(mask,3,3,'Name:','__________________________________________','0
1234567abcdefgh','_');
ReadMask(mask,output);
gotoxy(10,5);write(output);
readln;

clrscr;
SetReadMask(mask,3,3,'Name:','@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@','a
bcdefgh','_');
ReadMask(mask,output);
gotoxy(10,5);write(output);
readln;

clrscr;
SetReadMask(mask,3,3,'Name:','##########################################','0
1234567','_');
ReadMask(mask,output);
gotoxy(10,5);write(output);
readln;

clrscr;
SetReadMask(mask,3,3,'Name:','(@@@) @@@-@@@@/(###) ###-####/(___)
___-____','','_');
ReadMask(mask,output);
gotoxy(10,5);write(output);
readln;

clrscr;
SetReadMask(mask,3,3,'Name:','((((@@@) @@@-@@@@/(###) ###-####/(___)
___-____','','_');
ReadMask(mask,output);
gotoxy(10,5);write(output);
readln;

END.