Metropoli BBS
VIEWER: cl-print.asm MODE: TEXT (ASCII)
;             CL-Print.asm;
CR             Equ  13
LF             Equ  10
BELL           Equ  7
TAB            Equ  9
SWITCH         Equ  '/'

CODE SEGMENT                           ;*************************
ASSUME CS:CODE,DS:CODE                 ;*                       *
               Org 100H                ;*  REMEMBER TO EXE2BIN  *
                                       ;*                       *
Start:         Jmp    Beginning        ;*************************


;              Data Area
;              ---------

Copyright$     DB  CR,LF,'CL-Print Ver 2.40 - '
               DB  'Copyright 1991,1992 Gary Meeker',CR,LF,'"'
Comment$       DB     62 DUP (' '), CR, LF, 1AH
Quote2$        DB  '"=>$'
File_Start     DW  ?                            ;Points to Asciiz Filename
Commands       DB     'BQN*Z'
BiosDos        DB  0FFH                         ;0=Bios, -1=DOS Time/Date
Quiet          DB  0FFH                         ;0=Quiet, -1=Copyright Display
NoAppend       DB  0FFH                         ;0=No Append, -1=Append
Close_It       DB  0FFH                         ;0=write 62 *'s
ASCIIZ$        DB  ' '                          ;0=ASCIIZ, Space
Usage_Msg      DB  BELL,CR,LF
               DB  'Usage:  CL-PRINT filespec "logstring"',CR,LF,LF
               DB  ' String must be in quotes or decimal ASCII - '
               DB  'Seperate multiple codes by commas.',CR,LF,LF
;      DB  ' e.g. CL-PRINT C:\PCB\CALLER /S,"Scheduled Event Ended /A"',CR,LF,LF
               DB  TAB,'/A = " at MM-DD-YY HH:MM:SS"',CR,LF
               DB  TAB,'/D = "MM-DD-YY"',CR,LF
               DB  TAB,'/L = "MM-DD-YY (HH:MM)"',CR,LF
               DB  TAB,'/S = 6 spaces',CR,LF
               DB  TAB,'/T = "HH:MM"',CR,LF
               DB  TAB,'/* = row of *',CR,LF
               DB  TAB,'/Q = No console output',CR,LF
               DB  TAB,'/N = Create NEW file'
               DB  TAB,'/B = use BIOS Date/Time Services '
CrLf$          DB  CR,LF,'$'
NoUser$        DB  BELL,CR,LF,'No User Online!',CR,LF,'$'

PCBDrive       DB  'PCBDRIVE='
LenPCBDrive    EQU $ - PCBDrive

PCBDir         DB  'PCBDIR='
LenPCBDir      EQU $ - PCBDir

PCBoardSys$    DB  'PCBOARD.SYS',0
PCBoardDat$    DB  'PCBOARD.DAT',0

Not_Found2     DB  ' - File not found! ',CR,LF,'$'
Not_Found3     DB  ' - Path not found! ',CR,LF,'$'
No_Handle      DB  ' - No handles left!',CR,LF,'$'
No_Access      DB  ' - Access Denied!',CR,LF,'$'
Unknown        DB  ' - Unknown Error!',CR,LF,'$'
ReadError$     DB  ' - Error reading file!',CR,LF,'$'

;              Code Area
;              ---------

;-------------------------------------------------------;
; First we will parse the filename and the string.      ;
;-------------------------------------------------------;

Beginning:     Mov    DX,OFFSET Usage_Msg    ;Point to syntax message.
               Cld                           ;Move in forward direction.
               Mov    AX,DS                  ;Point ES to DS
               Mov    ES,AX
               Mov    SI,80H                 ;Point to parameters.
               Mov    DI,81H                 ;Point DI for SCASB
               Mov    AL,'@'                 ;See if any @ Variables exist
               Xor    CH,CH                  ;Get Command Line
               Mov    CL,[SI]                ;  Length in CX
               Jcxz   ErrorExit              ;Nevermind if no parameters
               Repne  Scasb                  ;Do we have any @ variables
               Jne    No_Read                ;Nope, None found
               Call   ReadFile               ;Yep, Read PCBoard files into
               Jc     ErrorExit              ;  buffers, oops Error occured
No_Read:       Mov    DI,OFFSET Comment$     ;Point to comment string storage.
               Xor    CX,CX                  ;Set string counter to zero.
               Call   Space                  ;Parse leading spaces.
               Mov    File_Start,SI          ;We now point to filename.

File_Byte:     Cmp    BYTE PTR [SI],CR       ;Is it a carriage return?
               Jz     ErrorExit              ;If yes, exit with syntax error.
               Cmp    BYTE PTR [SI],' '      ;Is it a space?
               Jz     Asciiz                 ;If yes, end of filename
               Inc    SI                     ; else, point to next byte
               Jmp    File_Byte              ; and check it.

OpenError:     Call   File_Error             ;Show what type of Error
ErrorExit:     Call   Display
               Mov    AX,4C01H
               Int    21H                    ; and terminate.

Asciiz:        Mov    BYTE PTR [SI],0        ;Make filename into ASCIIZ.

               Call   Space                  ;Parse the spaces.
String_Byte:   Call   String                 ;Get string.
               Cmp    BYTE PTR [SI],CR       ;Are we now pointing to CR?
               Jz     Open_File              ;If yes, we are done here
               Inc    SI                     ; else point to next byte
               Cmp    CX,62                  ;Maximum Length?
               Jl     String_Byte            ; No, get rest of string.

;------------------------------------------;
; OK, Open file and seek to the end of it. ;
;------------------------------------------;

Open_File:     Push   CX                     ;Save length
               Mov    DX,File_Start          ;Point to ASCIIZ filename
               Cmp    NoAppend, 0            ;Is it NoAppend mode?
               Je     Create_File            ;Yes, Just create it!
               Mov    AX,3D41H               ; Open file for writing.
               Int    21H
               Jnc    Open2                  ;OK if no error
               Cmp    AX,2                   ;Was it NOT FOUND?
               Jne    OpenError              ;  No, Report the error
Create_File:   Mov    AH,3CH                 ;  Yes, Create File
               Xor    CX,CX                  ;       Normal Attribute
               Int    21H
               Jc     OpenError              ;Report the error

Open2:         Mov    BX,AX                  ;File Handle to BX
               Mov    AX,4202H               ;No, Move File Pointer
               Xor    CX,CX                  ; Zero offset
               Xor    DX,DX                  ;  from end of file (CX:DX = 0)
               Int    21H
               Pop    CX

;------------------------------------------------;
; We are ready to write the comment to the file. ;
;------------------------------------------------;

Write:         Jcxz   Skip_Write
               Mov    DX,OFFSET Comment$     ;Point to COMMENT$
               Jmp    SHORT Write_Close      ;Write the comment
Make_Close:    Call   Do_Close               ;Fill the Buffer
Write_Close:   Mov    CX,64                  ;64 Bytes to write
               Mov    AH,40H                 ; and write comment.
               Int    21H
Skip_Write:    Cmp    Close_It,0FFH          ;Do we need a row of *,s
               Jne    Make_Close             ;yes
               Mov    AH,3EH                 ;No,close file.
               Int    21H
               Cmp    Quiet,0FFh             ;Quiet Mode
               Jne    QExit                  ;Yes, So Exit
               Mov    Comment$+62,'$'        ;No, prepare the Comment
               Mov    DX,OFFSET Copyright$   ;Point to Copyright message
               Call   Display
               Mov    DX,OFFSET Quote2$      ;Print trailing Quote & Arrow
               Call   Display
               Mov    SI,File_Start          ;Point to File Name
               Call   DisplayText

;------------------------------------------;
; The exit is placed in the middle of code ;
; so it can be reached by short jumps.     ;
;------------------------------------------;

Exit2:         Mov    DX,OFFSET CrLf$
Exit:          Call   Display
QExit:         Mov    AX,4C00H
               Int    21H                    ; and terminate.

File_Error:    Mov    DX,OFFSET Not_Found2
               Cmp    AX,2
               Je     Error_Ret
               Mov    DX,OFFSET Not_Found3
               Cmp    AX,3
               Je     Error_Ret
               Mov    DX,OFFSET No_Handle
               Cmp    AX,4
               Je     Error_Ret
               Mov    DX,OFFSET No_Access
               Cmp    AX,5
               Je     Error_Ret
               Mov    DX,OFFSET Unknown
Error_Ret:     Ret

;---------------------------------------;
; This subroutine will use DOS function ;
; to print string terminated by '$'     ;
;---------------------------------------;

Display:       Mov    AH,9                   ;Display message
               Int    21H                    ;
               Ret                           ;


;---------------------------------------------;
; This subroutine will get the quoted string  ;
; or convert the decimal ASCII to hexadecimal ;
; and store in the appropriate storage area.  ;
;---------------------------------------------;

String:        Mov    AH,[SI]                ;Get a character
               Cmp    AH,'"'                 ;Is it double quotes?
               Jz     Got_Quote              ;Yes -
               Cmp    AH,"'"                 ;No  - Is it single quotes?
               Jnz    Number                 ;If no, must be number
Got_Quote:     Inc    SI                     ; else, point to first string
Next_String:   Lodsb                         ; byte and retrieve.
               Cmp    AL,CR                  ;Is it carriage return?
               Jz     Exit                   ;If yes, syntax error; exit.
               Cmp    AL,SWITCH              ;Is it a Switch character
               Jz     Is_Switch              ;Yes
               Cmp    AL,'@'                 ;No, Is it a Var Subst Char?
               Jnz    Not_switch             ;No,
Is_Switch:     Call   Substitute             ;Yes, Substitute
               Jnc    Next_2                 ; If Not carry then continue
               Ret                           ; else done
Not_Switch:    Cmp    AL,AH                  ;Is it a matching quotes?
               Jnz    Store                  ;If no, store the byte
               Call   Delimiter              ; else, see if delimiter
               Jnc    Store                  ; and store the quote if part
               Ret                           ; of string else, we are done.

Store:         Stosb                         ;Store the byte
               Inc    CX                     ; increment byte count
Next_2:        Cmp    CX,62                  ;Maximum length?
               Jl     Next_String            ; no, get next string byte
               Ret                           ; yes, then return

;-------------------------------;
; This routine converts Decimal ;
; Ascii number to charcater and ;
; places it in Comment$         ;
;-------------------------------;

Number:        Xor    BL,BL                  ;Zero into hex counter.
               Cmp    AH,Switch              ;Was it a Switch character?
               Jz     Is_Switch2             ;Yes
               Cmp    AH,'@'                 ;No, Is it a Var Subst char?
               Jnz    Get_Number             ;No, must be a number
Is_Switch2:    Inc    SI                     ;Yes, Skip the Switch Char
               Mov    AL,AH                  ;Copy Switch to AL
               Jmp    SHORT Substitute       ;Do the substitute
Get_Number:    Call   Delimiter              ;Is it a delimiter?
               Jnc    Load_Number            ;If no, get next decimal number
               Mov    AL,BL                  ; get hex byte
               Stosb                         ; and store
               Inc    CX                     ; increment byte count
               Ret                           ; yes, then return
Jump2Exit:     Jmp    Exit

Load_Number:   Lodsb                         ;Get the decimal number
               Cmp    AL,'0'                 ;Is it between 0 and 9?
               Jb     Jump2Exit              ;If no, syntax error
               Cmp    AL,'9'
               Ja     Jump2Exit
               Sub    AL,30H                 ; else, convert to hex
               Mov    BH,AL                  ; and save
               Mov    AL,10                  ; multiply by ten
               Mul    BL                     ; to shift place left
               Mov    BL,AL
               Add    BL,BH                  ; add new number
               Jmp    Get_Number             ; and get next decimal number.

;------------------------------------------------;
; This subroutine converts '/' variables to      ;
; their values and stuffs Comment$               ;
;------------------------------------------------;

Substitute:    Push   AX                     ;Save Quote
               Mov    AH,AL                  ;Save the switch
               Lodsb                         ;Get the next Character
               Cmp    AL,'a'                 ;Lower Case?
               Jb     S1                     ;No,
               Cmp    AL,'z'                 ;Maybe, let's see
               Ja     S1                     ;No,
               Sub    AL,20h                 ;Yes, convert to Upper Case
S1:            Cmp    AH,Switch              ;Is it a switch or Var Subst?
               Je     Do_Switch              ;It was a Switch?
               Jmp    VarSub                 ;No, It was a Var Subst?
Do_Switch:     Mov    BX,5                   ;5 commands to check
Next_Cmp:      Cmp    AL,Commands-1[BX]      ;Is this the one?
               Je     Set_Mode               ;Yes
               Dec    BX                     ;No, Decrement BX
               Jnz    Next_Cmp               ;Loop until Done
;---------------------------------------------------------------------------
;               Cmp    AL,'Q'                 ;Is it QUIET Switch?
; code          Je     Set_Quiet              ;Yes (Carry is Cleared)
; replaced      Cmp    AL,'N'                 ;Is it NoAppend mode?
; by 5          Je     Set_NoAppend           ;Yes, (Carry is Cleared)
; lines         Cmp    AL,'B'                 ;Is it BIOS services mode?
; above         Je     Set_TimeDate           ;Yes, (Carry is Cleared)
;                Cmp    AL,'*'                 ;No, Is it CLOSED Var?
;               Je     Set_Closed             ;Yes (Carry is Cleared)
;               Cmp    AL,'Z'                 ;Is it a ASCIIZ var?
;               Je     Set_ASCIIZ             ;Yes, Carry is Cleared
;---------------------------------------------------------------------------
               Cmp    AL,CR                  ;Is it CR?
               Je     Jump2Exit              ;If yes, syntax error; exit.
               Mov    BX,DI                  ;Save COMMENT$ Pointer
               Mov    DI,OFFSET DatBuffer    ;Point to Buffer
               Cmp    AL,'T'                 ;Is it TIME var?
               Je     Stuff_Time             ;Yes, and Carry is Clear
               Cmp    AL,'D'                 ;Is it DATE Var?
               Je     Stuff_Date             ;No,
               Cmp    AL,'A'                 ;Maybe, let's see
               Jnz    S2                     ;No,
               Mov    AX,'a '                ;Yes, Set ' at ' in Buffer
               Stosw                         ;
               Mov    AX,' t'                ;
               Stosw                         ;
               Call   Do_Date                ;add Date to Buffer
               Mov    AL,' '                 ;add ' '
               Stosb                         ;
               Stc                           ;We want seconds too
Stuff_Time:    Call   DO_Time                ;add Time to Buffer
               Jmp    SHORT Stuff_It
S2:            Cmp    AL,'L'                 ;Is it LOGON Var?
               Jne    S3                     ;No,
               Call   Do_Date                ;Yes, Set Date in Buffer
               Mov    AX,'( '                ; add ' ('
               Stosw                         ;
               Clc                           ;We don't want seconds
               Call   Do_Time                ;add Time to Buffer
               Mov    AL,')'                 ; add ')'
               Stosb                         ;
               Jmp    SHORT Stuff_It
S3:            Cmp    AL,'S'                 ;Is it SPACES var?
               Jne    S4                     ;No,
               Mov    AX,'  '                ;Yes, Set six spaces in Buffer
               Stosw                         ;
               Stosw                         ;
               Stosw                         ;
               Jmp    SHORT Stuff_It
S4:            Mov    DI,BX                  ;restore Comment$ pointer
               Stosb                         ;just store it
               Inc    CX                     ;count it
               Clc                           ;
               Pop    AX                     ;Restore Quote
               Ret
Set_Mode:      Mov    BiosDOS-1[BX],BH       ;Handles all 5 Set variables, BH=0
               Pop    AX                     ;Restore Quote
               Ret                           ;Done - Carry Clear

;---------------------------------------------------------------------------
;Set_ASCIIZ:    Mov    BYTE PTR ASCIIZ$,0     ;Set 0 in ASCIIZ$
;               Pop    AX                     ;Restore Quote
;               Ret                           ;Done - Carry Clear
;Set_Quiet:     Mov    BYTE PTR Quiet,0       ;Show Quiet mode
;               Pop    AX                     ;Restore Quote
;               Ret                           ;Done - Carry Clear
;Set_NoAppend:  Mov    BYTE PTR NoAppend,0    ;Show NoAppend mode
;               Pop    AX                     ;Restore Quote
;               Ret                           ;Done - Carry Clear
;Set_TimeDate:  Mov    BYTE PTR BiosDos,0     ;Show BIOS Services used
;               Pop    AX                     ;Restore Quote
;               Ret                           ;Done - Carry Clear
;Set_Closed:    Mov    BYTE PTR Close_It,0    ;Show Closed mode
;               Pop    AX                     ;Restore Quote
;               Ret                           ;Done - Carry Clear
;---------------------------------------------------------------------------


Stuff_Date:    Call   Do_Date                ;Yes, Set Date in Buffer
Stuff_It:      Xor    AL,AL                  ;Zero marks end of data
               Stosb                         ;
               Mov    DI,BX                  ;resore Comment$ pointer
               Mov    BX,SI                  ;Save Command Line pointer
               Mov    SI,OFFSET DatBuffer    ;Point to Buffer
Stuff_More:    Lodsb                         ;Get character from Buffer
               Or     AL,AL                  ;End of data
               Jz     Done_Stuff             ;Yes,
               Stosb                         ;Copy to Comment$
               Inc    CX                     ;Count it
               Cmp    CX,62                  ;Maximum Length?
               Jl     Stuff_More             ;No - Continue
               Stc                           ;Yes - Indicate that
Done_Stuff:    Mov    SI,BX                  ;Restore Command Line pointer
               Pop    AX                     ;Restore Quote
               Ret                           ;All done

;------------------------------------------;
; This subroutine Reads Date and stuffs    ;
; buffer with information (uses part of    ;
; Do_Time routine)                         ;
;------------------------------------------;

Do_Date:       Push   BX             ;Save BX
               Push   CX             ;Save Length
               Push   DX             ;Save Message Pointer
               Test   BiosDOS,0      ;Get flag
               Jne    DosDate        ;We use DOS
               Mov    AH,04H         ;Get date service
               Int    1AH            ;Call BIOS - return codes as follows:
                                     ;CH = Century (19-20)  CL = Year (00-99)
                                     ;DH = Month   (1-12)   DL = Day  (00-31)
               Jmp    SHORT Do_Date1
DosDate:       Mov    AH,2AH         ;Get System Date
               Int    21H            ;DOS Call
               Sub    CX,1900        ;Remove Century
Do_Date1:      Mov    AL,DH          ;Month
               Call   ASCII          ;Convert byte to ASCII digits
               Mov    AL,'-'         ;
               Stosb                 ;
               Mov    AL,DL          ;Day
               Call   ASCII          ;Convert byte to ASCII digits
               Mov    AL,'-'         ;
               Stosb                 ;
               Mov    AL,CL          ;Year
               Jmp    SHORT Skip_Seconds

;------------------------------------------;
; This subroutine Reads Time and stuffs    ;
; buffer with information                  ;
;------------------------------------------;

Do_Time:       Push   BX             ;Save BX
               Push   CX             ;Save Length
               Push   DX             ;Save Message Pointer
               PushF                 ;Save Flags
               Test   BiosDOS,0      ;Get flag
               Jne    DosTime        ;We use DOS
               Mov    AH,02H         ;Get time service
               Int    1AH            ;Call BIOS - return codes as follows:
                                     ;CH = Hours   (0-23)   CL = Minutes (0-59)
                                     ;DH = Seconds (0-59)
               Jmp    SHORT Do_Time1
DosTime:       Mov    AH,2CH         ;Get System Time
               Int    21H            ;DOS Call
Do_Time1:      Mov    AL,CH          ;Hours
               Call   ASCII          ;Convert byte to ASCII digits
               Mov    AL,':'         ;
               Stosb                 ;
               Mov    AL,CL          ;Minutes
               PopF                  ;Restore Flags
               Jnc    Skip_Seconds   ;Skip the Seconds if No Carry
               Call   ASCII          ;Convert byte to ASCII digits
               Mov    AL,':'         ;
               Stosb                 ;
               Mov    AL,DH          ;Seconds
Skip_Seconds:  Pop    DX             ;Restore Message Pointer
               Pop    CX             ;Restore Length
               Pop    BX             ;Restore BX
ASCII:         Test   BiosDos,0      ;Do we use DOS or BIOS
               Jne    DosASCII       ;We use DOS
               Mov    AH,AL          ;Need to get BCD Digits
               Shr    AL,1           ; MSD in AL
               Shr    AL,1           ;  lower
               Shr    AL,1           ;    4
               Shr    AL,1           ;    Bits
               And    AH,0FH         ;And LSD in AH cleanly
PutAscii:      Or     AX,3030H       ;Convert to ASCII
               Stosw                 ;Stuff Digits into Buffer (in reverse)
               Ret                   ;SI Points to String Data
DosASCII:      Mov    BL,10          ;Divide by 10
               Xor    AH,AH          ;Change AL to AX
               Div    BL             ;Do the Division
               Jmp    PutAscii       ;and write the digits

;--------------------------------------------;
; This subroutine will translate @ variables ;
; to data read from PCBOARD.SYS & USERS file ;
;--------------------------------------------;

VarSub:        Push   DI                     ;Save Comment$ pointer
               Mov    BX,OFFSET SUB_TABLE    ;Point to Variable List
               Mov    DI,OFFSET DatBuffer    ;Point to Buffer
NextVar:       Mov    AH,[BX]                ;Get a letter from table
               Or     AH,AH                  ;End of table?
               Jz     NoSub                  ;Yes, no match then
               Cmp    AH,AL                  ;No,  Is this our Letter?
               Jz     GotIt                  ;     Yes,
               Add    BX,4                   ;     No, Bump the pointer
               Jmp    NextVar                ;         Next Variable
GotIt:         Mov    AX,[BX][1]             ;Get Offset value from table
               Mov    CL,[BX][3]             ;Get Length value from table
               Xor    CH,CH                  ;Convert to word
               Mov    BX,SI                  ;Save Command line pointer
               Mov    SI,OFFSET SysBuffer    ;Point to PCBOARD.SYS Buffer
               Add    SI,AX                  ;Add Offset
VarCopy:       Lodsb                         ;Get Character
               Or     AL,AL                  ;Is it a Zero
               Jnz    CopyIt                 ;No,
               Or     AL,ASCIIZ$             ;Yes, Is ASCIIZ$ = 0
               Jz     CopyDone               ;     Yes, Done
CopyIt:        Stosb                         ;     No,  Copy it
               Loop   VarCopy                ;Loop till Done
;               Rep    Movsb                  ;Copy Variable to Buffer
CopyDone:      Mov    SI,BX                  ;Restore Command line pointer
               Pop    BX                     ;Restore Comment$ Pointer to BX
               Jmp    Stuff_It               ; Now stuff the data in Comment$
NoSub:         Pop    BX                     ;Restore Comment$ Pointer to BX
               Jmp    S4                     ;Just store the character

;--------------------------------;
; This subroutine will parse     ;
; leading and delimiting spaces. ;
;--------------------------------;

Space:         Inc    SI                     ;Point to next byte
               Cmp    BYTE PTR [SI],' '      ;Is it space?
               Jz     Space                  ;If no, get next byte
               Ret                           ; else, return.

;----------------------------;
; This subroutine will check ;
; for delimiter characters.  ;
;----------------------------;

Delimiter:     Push   AX
               Mov    AL,[SI]
               Clc                           ;Assume not delimiter.
               Cmp    AL,' '                 ;Is it space
               Jz     Set_Carry
               Cmp    AL,CR                  ; or carriage return
               Jz     Set_Carry
               Cmp    AL,','                 ; or comma?
               Jz     Set_Carry
               Cmp    AL,';'                 ; or semi-colon?
               Jz     Set_Carry
               Cmp    AL,Switch              ; or Switch?
               Jnz    Return                 ;If no, return else, indicate
Set_Carry:     Stc                           ; by setting carry flag
Return:        Pop    AX
               Ret                           ; and return.

;----------------------------;
; This subroutine will fill  ;
; the buffer with *'s.       ;
;----------------------------;

Do_Close:      Mov    DI,Offset DatBuffer    ;Point to Buffer for this
               Mov    DX,DI                  ;DX also for Write
               Mov    AL,'*'                 ;Make a line of *'s
               Mov    CX,62                  ;Need 62 of them
               Rep    Stosb                  ;
               Mov    AL,CR                  ;Add a Carriage Return
               Mov    AH,LF                  ;  and Line Feed
               Stosw
               Mov    Close_It,0FFh          ;Turn Off flag or we will be here
               Ret                           ;  forever - and Return

;---------------------------------------;
; This subroutine will use DOS function ;
; to print string terminated by Zero.   ;
;---------------------------------------;

DisplayText:   Lodsb                         ;get a character
               Or     AL,AL                  ;End of name
               Jz     DisplayDone            ;Yes, Done
               Mov    DL,AL                  ;No,Print it
               Mov    AH,2                   ;Character Output
               Int    21H
               Jmp    DisplayText            ;Loop until done
DisplayDone:   Ret

;---------------------------------------;
; This subroutine will read PCBOARD.SYS ;
; file into a buffer area.              ;
;---------------------------------------;

ReadFile:      Push   DI                     ;
               Push   SI                     ;
               Push   DX                     ;Save message pointer
               Xor    CX,CX              ;Zero Counter for test purposes
NextFlush:     Mov    BX,CX              ;Let's dup the Stdin handle
               Mov    AH,45h             ;service to duplicate a file handle
               Int    21h                ;call DOS to do it
               Jcxz   StartFlush         ;First Time thru
               Jmp    SHORT CloseIt
StartFlush:    Mov    CX,AX              ;copy to CX for counter
CloseIt:       Mov    BX,AX              ;put the new handle into BX
;               Mov    DX,AX              ;Copy Handle number
;               Add    DX,3030h           ;Make it Ascii
;               Mov    AH,2               ;Display Character
;               Int    21h                ;Let's display the number for testing
               Mov    AH,3Eh             ;and close the "alias" file
               Int    21h                ;call DOS to do it
               Dec    CX                 ;Next Handle
               Cmp    CX,4               ;Did we inherit any files?
               Ja     NextFlush          ;Yes, lets Flush them
                                         ;No, or we went thru them all!
DoneFlushing:  Mov    DI,OFFSET SysBuffer    ;Point to PCBoard.Sys Buffer area
               Push   DI
               Mov    CX,1024                ;Let's fill the whole buffer area
               Mov    AL,'.'                 ;  with '.' characters
               Rep    Stosb                  ;    and do it
               Mov    DX,OFFSET PCBoardSys$  ;Point to Asciiz filename
               Call   CheckFile              ;See if it's present
               Pop    DI
               Mov    CX,128                 ;128 bytes is all we need
               Xor    AX,AX                  ;No Record Seek
               Call   ReadBuffer             ;
               Jc     ReadErr_Hop            ;
               Mov    AL,SysBuffer + 84      ;Get the first character (UserName)
               Cmp    AL, ' '                ;Is it a space?
               Je     NoUser                 ;Yes
               Mov    DX,OFFSET PCBoardDat$  ;Point to Asciiz filename
               Call   CheckFile              ;See if it's present
               Mov    DI,OFFSET DatBuffer    ;Point to PCBoard.Dat Buffer area
               Mov    CX,8000                ;8000 bytes is more than enough
               Xor    AX,AX                  ;No Record Seek
               Call   ReadBuffer
ReadErr_Hop:   Jc     ReadError
               Mov    DI,OFFSET DatBuffer    ;Point to PCBoard.Dat Buffer area
               Mov    CX,8000
               Mov    BX,28                  ;Scan past 28 Lines
               Mov    AL,LF
NextLine:      Repne  Scasb
               Dec    BX
               Jnz    NextLine
               Mov    DX,DI                  ;DX points to filename
               Repne  Scasb                  ;Find End of line
               Dec    DI                     ;back up
               Dec    DI                     ;back up
               Mov    Byte Ptr [DI],0        ;Make ASCIIZ
;               Push   DX
;               Mov    SI,DX
;               Call   DisplayText            ;Lets see that name
;               Pop    DX
               Mov    DI,OFFSET UserBuffer   ;Point to User File Buffer
               Mov    CX,400                 ;400 Bytes in User Record
               Mov    AX,Word Ptr SYSBuffer+23 ;Get User Record Number
               Or     AX,AX                  ;Test the Record Number
               Jz     NoUser                 ;None present, error
               Push   AX                     ;Save the record Number
               Call   ReadBuffer
               Pop    CX
               Jc     ReadError
               Mov    AH,3EH                 ;Close file
               Int    21H
               Mov    DI,OFFSET UserBuffer+229 ;Point to LastMsgConf
               Mov    SI,OFFSET UserBuffer+87  ;Point SI to Last Date On
               Mov    AX,CX                  ;Get Record Number
               Call   TransferInt2           ;Copy Integer
               Call   TransferDate           ;Copy Date (DI=DI+8, SI=SI+6)
               Add    SI,8                   ;Point to Last Scan Date
               Call   TransferDate           ;Copy Date (DI=DI+8, SI=SI+6)
               Call   TransferChr            ;Copy Chr$(Seclevel)
               Call   TransferInt            ;Copy Integer (TimesOn)
               Add    SI,73                  ;Point to Minutes On
               Call   TransferInt            ;Copy Integer (Minutes On)
               Call   TransferDate           ;Copy Date (DI=DI+8, SI=SI+6)
               Call   TransferChr            ;Copy Chr$(ExpSecLevel)
               Mov    AX,Word Ptr SYSBuffer+109 ;Get Time Remaining
               Call   TransferInt2           ;Copy Integer
               Mov    SI,OFFSET UserBuffer   ;Point SI to UserName
               Call   TransferFile           ;Make it a FileName
               Pop    DX                     ;Restore message pointer
               Clc                           ;Indicate No Error
               Jmp    SHORT ReadError2       ;
NoUser:        Mov    DX, OFFSET NoUser$     ;No user Online!
               Stc
ReadError:     Pop    AX                     ;Forget message pointer
ReadError2:    Pop    SI
               Pop    DI
               Ret

;---------------------------------------;
; Transfer UserName to FileName         ;
;---------------------------------------;

TransferFile:  Mov    BL,'.'      ; We need a '.', then a 0h
               Mov    BH,' '      ; First we need to find space
               Mov    AH,254      ; Flag shows we are in FileName, then Ext.
               Mov    CX,8        ; Eight Characters Max
               Mov    DX,25       ; UserName is 25 characters long
Next_Char:     Lodsb              ; Read a Char
               Dec    DX          ; Count it
               Call   CheckChar   ; Check for bad characters
               Cmp    AL,BH       ; Is it a Space?
               Jne    Not_Space   ; No, continue then
               Inc    AH          ; Yes, Show we found a Space
Do_Dot:        Mov    AL,BL       ; Yes, Time for the '.' or 0h
Not_Space:     Stosb              ; Store the character
               Cmp    AL,BL       ; Was it a '.'?
               Je     Extension   ; Yes
               Loop   Next_Char   ; No, then loop till all 8
               Jmp    Do_Dot      ; And now we need a Dot
Extension:     Or     BL,BL       ; Are we Done (BL = 0)?
               Jz     FileDone    ; Yes, Bye-bye
               Xor    BL,BL       ; Zero to BL
               Inc    AH          ; No, Did we find a Space already?
               Jz     Skip_Scan   ; Yes, then No need for scan!
               Mov    CX,DX       ; No, Get Characters remaining in UserName
               Xchg   SI,DI       ; Scasb needs ES:DI (but we want to scan SI)
               Mov    AL,BH       ; Scan for a Space
               Repne  Scasb       ; Do the scan
               Xchg   SI,DI       ; Restore SI & DI (SI past the SPACE!)
               Jne    End_File    ; Sorry, no Space found, end the name
               Inc    AH          ; AH = 0 (else we exit!)
Skip_Scan:     Mov    CX,3        ; Three characters MAX for extension
               Jz     Next_Char   ; Back we go for the extension
End_File:      Mov    [DI],BL     ; There isn't one, end the FileName here
FileDone:      Ret

CheckChar:     Cmp    AL,BH       ; Space is OK
               Je     Char_Ok
               Cmp    AL,BH       ; '.' or 0h is OK
               Je     Char_Ok
               Cmp    AL,'0'      ; Ctrl-Chars !"#$%'&()*+,-./ are no good
               Jb     Bad_Char
               Cmp    AL,'9'      ; 0-9 are OK
               Jbe    Char_Ok
               Cmp    AL,'A'      ; ;:<=>? are no good
               Jb     Bad_Char
               Cmp    AL,'Z'      ; A-Z are OK
               Jbe    Char_Ok     ; Anything else is no good
Bad_Char:      Mov    AL,'$'      ; Replace all bad characters with '$'
Char_Ok:       Ret

;---------------------------------------;
; Transfer Date from yymmdd to mm-dd-yy ;
;---------------------------------------;

TransferDate:  Mov    BL,'-'
               Lodsw              ; Read Year
               Movsw              ; Copy Month
               Mov    [DI],BL     ; Write '-'
               Inc    DI
               Movsw              ; Copy Day
               Mov    [DI],BL     ; Write '-'
               Inc    DI
               Stosw              ; Write year
               Ret

;------------------------------------------;
; This subroutine will converts a byte     ;
; into ascii text                          ;
;------------------------------------------;

TransferChr:   Lodsb                  ;Read the Chr
               Xor    AH,AH           ;force upper byte to zero
               Mov    DX,3            ;We want 3 characters
               Jmp    SHORT TI2

;------------------------------------------;
; This subroutine will converts an integer ;
; into ascii text                          ;
;------------------------------------------;

TransferInt:   Lodsw                  ;Read the integer
TransferInt2:  Mov    DX,5            ;We want 5 characters
TI2:           Push   BX              ;Save register
               Mov    BL,10           ;for division by 10
               Xor    CX,CX           ;set count to zero
Divide:        Push   DX              ;save register
               Xor    DX,DX           ;perform word divide
               Xor    BH,BH           ;  to avoid divide overflow
               Div    BX              ;return quotient in AX
               Mov    BH,DL           ;return remainder in BH
               Pop    DX
               Push   BX              ;build our number on the stack
               Inc    CX              ;keep count of them
               Or     AX,AX           ;All done? (remainder = 0)
               Jnz    Divide          ;No, Keep dividing
               Mov    AH,"0"          ;prepare for ASCII translation
Write_it:      Pop    BX              ;get a remainder off the stack
               Mov    AL,BH           ;put remainder in AL
               Add    AL,AH           ;convert to ASCII
               Stosb                  ;write digit to end of string
               Dec    DX              ;decrement desired count
               Loop   Write_it        ;pop all of them off of stack
               Mov    CX,DX           ;Remaining Count to CX
               Pop    BX              ;Retore register

               Jcxz   PadDone         ;No padding needed
               Xor    AL,AL           ;Load Zero
               Rep    Stosb           ;Pad remainder
PadDone:       Ret                    ;All Done!


;-------------------------------------------;
; DX Points to ASCIIZ Filename              ;
;                                           ;
; Check to see if file in current directory ;
; or at %PCBDRIVE%%PCBDIR% from environment ;
;-------------------------------------------;

CheckFile:     Push   DX              ;Save filename pointer
               Mov    AX,3D40H        ;Open file for reading.
               Int    21H
               Jc     CheckError      ;Error if Carry Set
               Mov    AX,BX           ;Copy handle of open file
               Mov    AH,3Eh          ;and close it.
               Int    21H
               Jnc    CheckDone       ;OK, File exists
CheckError:    Cmp    AX,2            ;File Not Found?
               Jne    CheckDone       ;Exit if anything else
               Mov    BX,OFFSET UserBuffer
               Pop    SI              ;Get filename pointer
               Push   BX              ;Save Buffer Address instead
               Push   SI              ;Now save filename pointer
               Mov    DX,OFFSET PCBDrive
               Mov    BP,LenPCBDrive
               Call   Copy_Env        ;Findit & Copy it
               Mov    DX,OFFSET PCBDir
               Mov    BP,LenPCBDir
               Call   Copy_Env        ;Findit & Copy it
               Pop    SI              ;Get back filename pointer
               Mov    AL,'\'          ;Append a '\' (if needed)
               Cmp    [DI-1],AL       ;Is there one already?
               Je     No_Slash        ;Yes,
               Stosb                  ;No,  so add one.
No_Slash:      Call   Copy_ASCIIZ     ;And Copy it
               Stosb                  ; And terminate it with a Zero
;               Pop    SI              ;Get the buffer address
;               Push   SI              ; put it back on the stack
;               Call   DisplayText     ; Now, Lets see that name!
CheckDone:     Pop    DX              ;Get Filename pointer or buffer address.
               Ret

;------------------------------------------------------;
; On Entry:                                            ;
;    BX Points to Destination Buffer                   ;
;    DX points to Target String to find in Environment ;
;    BP Contains length of Target String               ;
; On Exit:                                             ;
;    BX adjusted to next character to write in Buffer  ;
;    DI is same as BX (All other Regs altered)         ;
;------------------------------------------------------;

Copy_Env:      Mov    AX,CS:[2Ch]       ;Point ES to Environment
               Mov    ES,AX             ;
               Xor    DI,DI             ;Start of Environment (ES:DI)
               Xor    AX,AX             ;
Next_Env:      Mov    CX,BP             ;Get length.
               Mov    SI,DX             ;Get Target pointer
               Rep    Cmpsb             ;Do we have a match? (DS:SI) = (ES:DI)
               Je     FoundMatch        ;Yes, Found it
               Dec    DI                ;No,  It didn't match, back up one
               Mov    CX,-1             ;
               Repne  Scasb             ;Find the end of this one (ES:DI)
               Cmp    ES:[DI],AL        ;Is this the last one?
               Jne    Next_Env          ;No, Do the next one.
               Mov    DI,BX             ;Point DI to Buffer also
               Je     Copy_Exit         ;Yes, No Copy

FoundMatch:    Mov    AX,ES             ;Point DS to Env Segment
               Mov    DS,AX             ;
               Mov    AX,CS             ; and ES back to Program Data Segment
               Mov    ES,AX             ;
               Mov    SI,DI             ;Point SI to variable value
               Mov    DI,BX             ;Point DI to Buffer

Copy_ASCIIZ:   Xor    AL,AL             ;We need to copy up to the 0h
Copy_Next:     Cmp    [SI],AL           ;Is this the end?
               Je     Copy_Done         ;Yes,
               Movsb                    ;No, Copy it (ES:DI) <= (DS:SI)
               Jne    Copy_Next         ;Keep going
Copy_Done:     Mov    BX,DI             ;Update BX
Copy_Exit:     Mov    DX,CS             ;Make sure DS points to
               Mov    DS,DX             ;  Code Segment again.
               Mov    ES,DX             ;  and ES too!
               Ret                      ;

;--------------------------------;
; DX Points to ASCIIZ Filename   ;
; DI Points to Buffer Area       ;
; CX Contains Length to read     ;
; AX Contains Record Number or 0 ;
; Carry set if error             ;
;--------------------------------;

ReadBuffer:    Mov    SI,DX                  ;Save filename pointer (for disp)
               Push   AX                     ;Save Record Number (if any)
               Mov    AX,3D40H               ;Open file for reading.
               Int    21H
               Pop    BX                     ;Restore Record Number (to BX)
               Jc     FileError              ;Error if Carry Set
               Xchg   AX,BX                  ;Copy handle of open file
               Or     AX,AX                  ;Random Record Seek?
               Jz     No_Seek                ;No,
               Push   CX
               Dec    AX
               Mul    CX                     ;Yes, CX=Length, AX=Record Number
                                             ;Now DX:AX = Seek (Save Length)
               Mov    CX,AX                  ;Now DX:CX
               Xchg   CX,DX                  ;Now CX:DX
               Mov    AX,4200H               ;Seek
               Int    21H
               Pop    CX                     ;Restore Length
               Jc     FileError              ;Error if Carry Set
No_Seek:       Mov    DX,DI                  ;Point to buffer
               Mov    AH,3FH                 ;Read from handle
               Int    21H
               Jc     FileError              ;Error if Carry Set
               Mov    AH,3EH                 ;Close file
               Int    21H
               Jc     FileError
               Ret                           ;All done
FileError:     Push   AX                     ;Save error code
               Call   DisplayText            ;Display the filename

;For trouble shooting error codes use the following

;               Mov    DL,' '
;               Mov    AH,2                   ;Character Output
;               Int    21H
;               Mov    DL,'('
;               Mov    AH,2                   ;Character Output
;               Int    21H
;               Mov    DL,BL
;               Add    DL,30h
;               Mov    AH,2                   ;Character Output
;               Int    21H
;               Mov    DL,')'
;               Mov    AH,2                   ;Character Output
;               Int    21H
               Pop     AX                    ;Get error code
               Call    File_Error            ;Translate it
               Stc                           ;Indicate the error
               Ret

SUB_TABLE              LABEL BYTE
               DB     'F',  25, 0, 15          ; First Name (Proper Case)
               DB     '?',  40, 0, 12          ; Password
               DB     'G',  56, 0,  5          ; Time User logged On
               DB     'Z',  80, 0,  4          ; Language Extension
               DB     'N',  84, 0, 25          ; User Full Name
               DB     '*', 146, 1,  5          ; Time Remaining (from 109)
               DB     'V', 112, 0,  5          ; Event Time
               DB     'C', 153, 0, 24          ; City, State
               DB     'B', 189, 0, 13          ; Bus/Data Phone
               DB     'H', 202, 0, 13          ; Home/Voice Phone
               DB     'D', 106, 1,  8          ; Last Date On (from 215)
               DB     'T', 221, 0,  5          ; Last Time On
               DB     '!', 226, 0,  1          ; Expert Mode (Y or N)
               DB     'P', 227, 0,  1          ; Default Protocol
               DB     'L', 114, 1,  8          ; Last Dir Scan (from 229)
               DB     'A', 122, 1,  3          ; Sec Level Access (from 235)
               DB     '#', 125, 1,  5          ; Number of Times On (from 236)
               DB     'M', 130, 1,  5          ; TimeSpent last call (from 311)
               DB     'E', 135, 1,  8          ; Expiration Date  (from 313)
               DB     'X', 143, 1,  3          ; Expiration Sec Level (from 319)
               DB     'U', 251, 0, 30          ; User Maintained Comment
               DB     'S',  25, 1, 30          ; Sysop Maintained Comment
               DB     'R', 101, 1,  5          ; Record Number
               DB     '=', 151, 1, 13          ; First.Last
               DB     '$', 101, 1, 63          ; Translated Data

Table_End      DB     0    ; 528 DUP ('.')     ;PCBoard.Sys reads in here
SysBuffer      EQU    Table_End + 1

;    0 Display AS STRING * 2   'Display On/Off ("-1" = On, " 0" = Off)
;    2 Printer AS STRING * 2   'Printer On/Off ("-1" = On, " 0" = Off)
;    4 PageBell AS STRING * 2  'Page Bell On/Off ("-1" = On, " 0" = Off)
;    6 CallAlarm AS STRING * 2 'Caller Alarm On/Off ("-1" = On, " 0" = Off)
;    8 SysopFlag AS STRING * 1 'Sysop Flag (" ", "N"=sysop next, "X"=exit dos)
;    9 ErrCorr AS STRING * 2   'Error Corrected ("-1" = On, " 0" = Off)
;   11 Graphics AS STRING * 1  'Graphics Mode ('Y'=yes, 'N'=no, '7'=7E1)
;   12 NodeChat AS STRING * 1  'Node Chat Status ('A'=available, 'U'=unavailable)
;   13 DTEPort  AS STRING * 5  'DTE Port Speed (PC to Modem speed)
;   18 Connect  AS STRING * 5  'Connect Speed shown to caller or "Local"
;|  23 RecNum   AS INTEGER     'User's Record Number in the USERS file
;|  25 FirstName AS STRING * 15 'User's First Name (padded to 15 characters)
;|  40 Password AS STRING * 12 'User's Password (padded to 12 characters)
;   52 TimeOn   AS INTEGER     'Time User Logged On (in minutes since midnight)
;   54 TimeUsed AS INTEGER     'Time used so far today (negative number of minutes)
;|  56 TimeOnF  AS STRING * 5  'Time User Logged On (in "HH:MM" format)
;   61 DayTime  AS INTEGER     'Time Allowed On (from PWRD file)
;   63 DLKbytes AS INTEGER     'Allowed K-Bytes for Download
;   65 ConfArea AS STRING * 1  'Conference Area user was in (if <= 255)
;   66 ConfJoined AS STRING * 5 'Conference Areas the user has joined this session
;   71 ConfScaned AS STRING * 5 'Conference Areas the user has scanned this session
;   76 ConfAddTime AS INTEGER  'Conference Add Time in minutes
;   78 CreditTime AS INTEGER   'Upload/Sysop CHAT Credit Minutes
;|  80 LangExt AS STRING * 4   'Language Extension
;|  84 UserName AS STRING * 25 'User's Full Name (padded to 25 characters)
;| 109 MinRemain AS INTEGER    'Calculated Minutes Remaining
;  111 NodeNum AS STRING * 1   'Node Number (or ' ' if no network)     CHR$()
;| 112 EventTime AS STRING * 5 'Scheduled Event Time (in "HH:MM" format)
;  117 EventOn AS STRING * 2   'Is Event Active ("-1" = On, " 0" = Off)
;  119 EventSlide AS STRING * 2 'Slide Event ("-1" = On, " 0" = Off)
;  121 MemMesg AS STRING * 4   'Memorized Message Number               MKSMBF$()
;  125 ComPort AS STRING * 1   'Comm Port Number (0=none, 1-8)
;  126 Reserved1 AS STRING * 1 'Reserved for PCBoard
;  127 Reserved2 AS STRING * 1 'Reserved for PCBoard

UserBuffer     EQU    SysBuffer+128            ;Users record reads in here

;| 128 UserName AS STRING * 25      'First name and Last name
;| 153 CityState AS STRING * 24     'City and State
;  177 Password AS STRING * 12      'Password
;| 189 BusPhone AS STRING * 13      'Business Phone Number
;| 202 HomePhone AS STRING * 13     'Home Phone Number
;| 215 LastOnDate AS STRING * 6     'Last Date on system (in yymmdd format)
;| 221 LastOnTime AS STRING * 5     'Last Time on system (in hh:mm format)
;| 226 Expert AS STRING * 1         'Expert Mode - "Y" or "N"
;| 227 Protocol AS STRING * 1       'Protocol
;  228 PackedFlags AS STRING * 1    'Dirty, ClrScrn, HasMail, DontAsk, Editor
;| 229 LastDirScan AS STRING * 6    'Last date looked at directory (in yymmdd format)
;| 235 SecLevel AS STRING * 1       'Security Level CHR$(X)
;| 236 TimesOn AS INTEGER           'Number of times on the system
;  238 PageLength AS STRING * 1     'Page Length CHR$(X)
;  239 Uploads AS INTEGER           'Number of Uploads
;  241 Downloads AS INTEGER         'Number of Downloads
;  243 DailyDlBytes AS STRING * 8   'Daily Download Bytes so far
;| 251 UserComment AS STRING * 30   'User Maintained Comment
;| 281 SysopComment AS STRING * 30  'Sysop Maintained Comment
;| 311 TimeSpent AS INTEGER         'Elapsed Time on system on last date called
;| 313 SubDate AS STRING * 6        'Subscription Registration Date (yymmdd format)
;| 319 ExpSecLevel AS STRING * 1    'Subscription Expired Security Level CHR$(X)
;  320 LastConference AS STRING * 1 'Last Conference left - CHR$(X) - 255 = Ext.
;  321 AreaReg AS STRING * 5        'Area Registration Info (1-39) BitMap
;  326 ExpAreaReg AS STRING * 5     'Expired Area Registration Info (1-39) BitMap
;  331 UserScanArea AS STRING * 5   'User Message Scan Areas (1-39) BitMap
;  336 TotalDownloads AS STRING * 8 'Total Bytes Download - all calls
;  344 TotalUploads AS STRING * 8   'Total Bytes Upload - all calls
;  352 Delete AS STRING * 1         'Positive Delete Flag to Delete User (Repack)
;  353 LastMsgMain AS STRING * 4    'Last message read in Main Board     MKSMBF$()
;  357 LastMsgConf AS STRING * 156  'Last message read in Conferences 39 * 4  "
;                                   ' (Gets overwritten by 3 date conversions)
;                                   ' ( & numeric conversions)
;  513 InfPointer AS LONG           'Pointer into USERS.INF record
;  517 Space2 AS STRING * 9         'Reserved
;  526 ExtLastConference AS INTEGER 'Last Conference Left (If LastConf.$ = 255)

DatBuffer      EQU    UserBuffer+400           ;PCBoard.Dat read in here
                                               ;and we buffer or conversions.

CODE ENDS
END  Start
[ RETURN TO DIRECTORY ]