home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS - Coast to Coast
/
simteldosarchivecoasttocoast2.iso
/
waffle
/
aser28.zip
/
UUD20.ASM
< prev
next >
Wrap
Assembly Source File
|
1991-03-12
|
22KB
|
746 lines
name UUD
page 55,132
title 'UUDECODE.ASM'
;
; UUDECODE.ASM -- UUDecodes a UUEncoded Binary File
;
; Copyright (C) 1988, by Theodore A. Kaldis
;
; To assemble and link this program into the executable UUDECODE.COM:
; (It will NOT run assembled as an .EXE program!)
; MASM UUD;
; LINK UUD;
; (If you just have EXE2BIN:
; EXE2BIN UUD
; REN UUD.BIN UUDECODE.COM (or whatever)
; (If you have Public Domain EXE2COM or equivalent:
; EXE2COM UUD
; REN UUD.COM UUDECODE.COM (or whatever)
; (Delete the .OBJ file)
; See version 1.1 for .EXE equivalent.
Comment ~
v2.0, 10 Mar 91
- Keith Petersen says we must handle "begin 0600 foobar.arc" type
headers .. which we surprisingly can't!
He found the bug:
|David, I found the bug in TOADUU19's uudecode. I added one line to the
|source code and changed a comment which said that AH already had a
|space in it.
|
|Here's the fix:
|
|;Put a space into AH for fast comparisons
| mov ah,' '
|Strip_Spc:
- He also wanted file overwrite protection like TOADXX, so I added that
as well. New command line parm (/o or /O) to force overwrite if
file exists. Else abort without overwrite.
- He then found a bug in the "no filename" handling. I added usage
before the filename prompt.
- While I'm here, tightened up error msg writing a little (preceding
and following all msgs with CR/LF, etc.)
v1.8, 5 Jul 89
Thanks to Karl-L. Noell <NOELL%DWIFH1.BITNET@CUNYVM.CUNY.EDU>
for a bug report:
| When I call up UUE12 or UUD17 without filename, a prompt appears:
| Input path/file:
| but answering to that prompt always yields the message
| "Input file error."
Karl-L's right .. stupid .. donno how I missed it. Fixed.
Added a little logo at the end (overrun by buffers) just to identify
the program.
Toad Hall
~
;v1.7, 9 Nov 88
; - Versions since 1.3 BROKE (a new Truncated line test).
; Trying again.
; - Handling headers, tailers, truncated lines ok.
; - Moving some start-up data and code down into buffer space
; just as an exercise.
; - Previously cut down input buffer size to limit buffer requirements.
; However, since DOS needs 64Kb to load a .COM file anyway,
; might as well use the entire 64Kb segment allocated us.
;(notes on versions 1.4 thru 1.6 discarded.)
;v1.3, 7 Sep 88
; - Tightened up seeking for 'begin' and 'end'.
; - Added 'start' error.
; - Tightened up line reading, uudecoding a little.
; - made console/error msgs REALLY go to standard ErrOut device (2)
; - Renamed to UUD13.ASM (differenciate from UUE, UU).
;V1.2, 6 Sep 88
; - Changing to .COM format.
; - moved a bunch of buffers (file names, input and output buffers)
; to be just pointers at code end (not taking up file space).
; - Reduced max file read from 0FE00H to 0F000H (to stay within 64Kb
; for program and buffers).
;V1.1, 6 Sep 88
; Toad Hall Tweak
; - General tightening.
; - Added comments.
;
;
; David Kirschbaum
; Toad Hall
; kirsch@usasoc.soc.mil
;-------------------------------------------
CR EQU 0DH
LF EQU 0AH
SPC EQU 20H
FALSE EQU 0
TRUE EQU NOT FALSE
;-------------------------------------------
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG,DS:CSEG, ES:CSEG
org 0
cseg_ofs label byte ;fake out MASM v1.9
org 80H
cmd_tail label byte ;v1.8
org 100H
;-------------------------------------------
Uudecode PROC near
jmp Start ;jump over data
;This data will be used during the uudecode run.
err_inp DB 'Input file error.'
ERR_INP_LEN EQU $-err_inp
err_out DB 'Output file error.'
ERR_OUT_LEN EQU $-err_out
err_begin db 'start not found.'
ERR_START_LEN EQU $-err_begin
err_end DB 'End not found.'
ERR_END_LEN EQU $-err_end
exist$ db ' exists. Aborting!' ;v2.0
EXIST$_LEN equ $ - exist$ ;v2.0
inp_handle DW 0
out_handle DW 0
uuptr DW UUBUFF ;points at next byte in uuencoded
; input buffer UUBUFF
uuEndptr DW UUBUFF ;points beyond uuencoded data
; (e.g., buffer end)
outptr DW OUT_BUF ;pointer to binary output buffer
; (where to stuff NEXT uudecoded byte)
overwrite db FALSE ;is set to true for overwriting v2.0
;-------------------------------------------
Start:
call Init ;cmd line parsing, file opening
;v1.7 If we make it back, all's ok.
;We should have input file opened.
call Read_File ;do the initial read
Get_Out_Fil:
mov di,offset OUT_BUF ;clear DI
call Get_Line ;read a line of input
;Look for uuencoded file's 'begin'
;(Don't like this very much .. could be stuck here until we've gone
;through the entire doggone file! Oh, well ..it works anyway..
;(Also gobbles up any headers or other garbage at file start.)
;Tried a 'CMPSB' or 'SCASB' of the input buffer against a 'begin'
;in our own code space, but it was a mess!
lodsw ;snarf 2 plaintext chars
cmp ax,'eb' ;'be'?
jne Get_Out_Fil ;nope, next line
lodsw ;next 2
cmp ax,'ig' ;'gi'?
jne Get_Out_Fil ;nope, next line
lodsw ;next 2
cmp ax,' n' ;'n '?
jne Get_Out_Fil ;nope, next line
mov di,offset OUT_FIL ;to output buffer
;Put a space into AH for fast comparisons
mov ah,' '
Strip_Spc:
;Gobble spaces until we hit the mode number after 'begin'
lodsb
cmp al,ah ;space?
jbe Strip_Spc ;gobble spaces, tabs, ctrl chars
Strip_Num:
;Hit the number, now gobble until the space after number
lodsb
cmp al,ah ;space?
jne Strip_Num ;gobble until space
;v1.7 now gobble any spaces between number and name ... sigh ...
Strip_Spc2:
lodsb
cmp al,ah ;space?
jbe Strip_Spc2 ;gobble until real char
;We should now be at the first char of the original target's filename.
;Move output filename from LINE_IN buffer to our output filename buffer.
;Since LINE_IN has been padded with spaces, a space will indicate
;name end.
Get_Out:
cmp al,ah ;space means done
je Out_Fin ;yep, name end
stosb ;input byte > OUT_FIL filename buff
lodsb ;next filename char
jmp Get_Out ;and keep going
;-------------------------------------------
Out_Fin:
mov dx,offset OUT_FIL ;output filename buffer
xor cx,cx ;normal file attribs (R/W)
mov [di],cl ;AsciiZ output filename
;v2.0 User requested overwrite protection for the output filename.
; Ok .. let's check for output filename existence before we
; do the create.
cmp overwrite,TRUE ;"-o" commandline switch?
jz Out_Create ;yep, no checking
mov ah,4EH ;find first
int 21H
cmp al,2 ;file not found?
jz Out_Create ;fine, doesn't exist
cmp al,18 ;no more files to be found?
jz Out_Create ;fine
;v2.0 File exists. Error message, die.
; DI -> filename AsciiZ 0
; DX -> filename 1st char
mov cx,di ;last char
sub cx,dx ;- first char = chars to write
call Say_Error ;display filename
mov dx,OFFSET exist$ ;'File exists'
mov cx,EXIST$_LEN ;msg length
mov al,5 ;fake "Access denied" errorlevel
jmp Fatal_Error ;display, terminate
Out_Create: ;v2.0
;End of v2.0 change
mov ah,3Ch ;create output file
int 21h
jnc Out_Open ;went ok
jmp Out_Err ;output file create error
;-------------------------------------------
Out_Open:
mov out_handle,ax ;remember output file handle
mov di,offset OUT_BUF ;prepare to clear outptr
New_Line:
call Get_Line ;read in uuencoded line
;First char is nr of binary bytes used to produce this line. (Asciified)
;If there's an empty last line, it'll just be a space or '`'
lodsb ;snarf uuencoded binary byte count
or al,al ;empty line?
jz Prog_End ;yep
mov bx,2020H ;a handy constant
sub al,bl ;20H ;Deasciify
or al,al ;null?
je Prog_End ;yep, must be done
;v1.7 keeping line's binary byte count in BP
xor ah,ah ;clear msb
mov bp,ax ;BP=binary byte count
;uuencoded stuff (input) is in 'quads' (4-char packets),
;binary output is in 'hunks' (3 byte packets)
;4 chars = 3 bytes
NN_0:
mov cx,0604H ;handy constant
;CL=4, CH=6
lodsb ;quad[1]
mov ah,al ;AH=quad[1]
lodsb ;AL=quad[2]
mov dl,al ;save quad[2]
sub ax,bx ;remove standard offset
shl ah,1 ;quad[1] SHL 2
shl ah,1 ;(faster this way)
shr al,cl ;quad[2] SHR 4
or al,ah ;shifted quad[2] OR shifted quad[1]
stosb ;=hunk[1], stuff in OUT_BUF
dec bp ;decr binary byte ctr
jz New_Line ;uuencoded line is done
mov ah,dl ;AH=unshifted quad[2]
lodsb ;AL=quad[3]
mov dl,al ;save quad[3]
sub ax,bx ;remove standard offset
shl ah,cl ;quad[2] SHL 4
shr al,1 ;quad[2] SHR 2
shr al,1 ;(faster this way)
or al,ah ;shifted quad[3] OR shifted quad[2]
stosb ;=hunk[2], stuff in OUT_BUF
dec bp ;decr binary byte ctr
jz New_Line ;uuencoded line is done
mov ah,dl ;AH=unshifted quad[3]
lodsb ;AL=quad[4]
sub ax,bx ;remove standard offset
mov cl,ch ;6
shl ah,cl ;quad[3] SHL 6
or al,ah ;shifted quad[4] OR shifted quad[3]
stosb ;=hunk[3], stuff in OUT_BUF
dec bp ;decr binary byte ctr
jnz NN_0 ;uuencoded line not done
jmp short New_Line ;line done, get new line
;-------------------------------------------
;Now we look for the uuencoded file's 'end'.
;We'll write out what we've got, even if we don't find the 'end'.
Prog_End:
call Get_Line ;should be file's last line
lodsw ;load next 2 chars
cmp ax,'ne' ;'en'?
jne End_Err_C ;'No end found'
lodsb
cmp al,'d'
je File_End ;ok, got the 'end'
End_Err_C:
call End_Err ;say we had an end error
File_End:
call Write_File ;do our final output write
;Seems there's no need to close files .. DOS must do it!
File_End_X:
mov ah,4Ch ;terminate, ERRORLEVEL ?
int 21h
Uudecode endp
;-------------------------------------------
;Reads in a line of uuencoded text
;Didn't like the original logic here (how it physically looks for a CR (0DH)
;as an End of Line indicator (EOL), then gobbles until a LF (0DH).
;Unix text and uuencoded files only have LFs as EOL,
;MACs only have CR EOLs (I think).
;Well, we're handling CR/LF and LF EOLs, and that'll do for now.
;The first char of a uuencoded line is usually an 'M' (ASCII 77). This is
;the nr of binary bytes used to create this uuencoded line (Asciified).
Get_Line PROC NEAR
mov si,uuptr ;current raw input buffer psn
;This is looped to (from below) if the line was garbage (e.g., some sort
;of file header. It refreshes the outbuf pointer to buffer start,
;and sets up the uuencoded line buffer LINE_IN anew.
Flush_Lin:
mov outptr,di ;save outbuf ptr in outptr
;for Write_File test
;BP holds the constant 'maximum line length'.
;I never heard of a uuencode that used lines longer than 60 bytes,
;plus length byte and CR/LF.
mov bp,80 ;max allowable uuencoded line len
;Prepare our uuencoded line buffer LINE_IN for transfer of a line.
;Remember, this same sequence is used to get the uuencode protocol's
;first line ('begin 6xx filename.typ'),normal uuencoded lines,
;and the last uuencode protocol line ('end').
;We're preparing an 80-char line (maximum allowed).
mov di,offset LINE_IN ;uuencoded line buffer start
xor ax,ax
stosw ;clear first 2 bytes
mov cx,(78/2) ;clear remaining 78 bytes v1.9
mov ax,2020H ;fill with spaces
rep stosw
mov di,offset LINE_IN ;uuencoded line buffer start
Next_Chr:
cmp si,uuEndptr ;hit end yet?
jb Not_Mt ;not empty yet
call Write_File ;write our binary output (if any)
call Read_File ;read more uuencoded input
Not_Mt:
lodsb ;snarf uuencoded line char
cmp al,96 ;special '`' in place of spaces
jne Not_Hi ;wasn't a space substitute
mov al,' ' ;replace with space
jmp short Was_Space ;and skip the next test
;We check for both CR End-Of_Line (EOL) and LF EOL. v1.9
;(This handles DOS and Unix files.)
Not_Hi: cmp al,CR ;CR means line end
je Eol_CR ;end of line, got line
cmp al,LF ;How about Unix EOL?
je Eol_LF ;Yep, was Unix EOL, got line
Was_Space:
stosb ;stuff uuencoded line char
dec bp ;count down allowable length
jnz Next_Chr ;Ok, not yet
;This line is longer than 80 chars, so it CAN'T be a uuencoded line.
;Continue to gobble it up, throwing it away, until EOL,
;then flush and continue.
;This only happens for headers. We never hit trailers at all.
Strip_Head:
cmp si,uuEndptr ;beyond uubuf data?
jb Strip_NoRead ;nope
call Read_File ;refill the uubuf
Strip_NoRead:
lodsb ;look for a LF
cmp al,LF ;LF yet?
jne Strip_Head ;nope, keep going w/this line
mov di,offset OUT_BUF ;clear DI to clear outptr
jmp Flush_Lin ;EOL, keep working thru raw
;input buffer
;-------------------------------------------
;Hit CR, got a uuencoded line (DOS files).
;SI -> LF just past the CR.
Eol_CR:
inc si ;bump raw input buffer ptr past LF
;Hit LF EOL (Unix files).
;SI -> next raw buff char.
Eol_LF:
mov uuptr,si ;remember current raw buff ptr
mov di,outptr ;restore binary output ptr
mov si,offset LINE_IN ;ptr to inbuffer start
ret ;done
Get_Line ENDP
;-------------------------------------------
;Write a chunk of uudecoded data (if any) to output file.
Write_File PROC NEAR
mov dx,offset OUT_BUF ;output binary buffer start
mov cx,dx ;prepare to reinit outptr
xchg cx,outptr ;outptr=output buffer start,
;CX=outptr
sub cx,dx ;-buffer start=buffer byte count
;IF CX=0, there's no uudecoded data to write, just exit.
;If CX went below 0 (e.g., outptr pointed BELOW OUT_BUF start),
;we have an error of some sort (my code logic?).
;Ignore that also. It'll get cleaned up later.
jbe Write_Good ;error
mov bx,out_handle ;output file handle
mov ah,40h ;write to file/device
int 21h
jb Out_Err ;write error
Write_Good:
ret ;write done
;Output file write error
Out_Err:
mov dx,OFFSET err_out ;'Output file write error'
mov cx,ERR_OUT_LEN ;msg length
jmp short Fatal_Error ;common code, error value in AL
Write_File ENDP
;-------------------------------------------
;Read a chunk of input (uuencoded) data into our uuencode buffer.
;I don't think we'll EVER try to read past EOF (since the 'end' line
;should've been detected and program terminated).
;Let's assume that 'read past EOF' is an error of some sort and die.
;('Coding by trial and error')
Read_File PROC NEAR
mov dx,offset UUBUFF ;read into uuencode input buffer
mov cx,offset UU200 ;code space - 200H stack bytes
;v1.9 not cx ;= remaining segment memory
mov bx,inp_handle ;input file handle
mov ah,3Fh ;read from file/device
int 21h
jb Inp_Err ;failed
or ax,ax ;read anything?
jz Read_EOF ;nope, EOF
mov si,dx ;point to input UUBUFF start
add ax,si ;chars read+UUBUFF start
mov uuEndptr,ax ;points beyond last UUBUFF char
ret ;read done
Read_EOF:
;-------------------------------------------
;Input file read error. Error value in AL
Inp_Err:
mov dx,OFFSET err_inp ;'Input file error'
mov cx,ERR_INP_LEN ;msg length
;Common code added here
Fatal_Error:
push ax ;save error in AL
call Say_Error ;common code
pop ax ;restore error in AL
jmp File_End_X ;terminate
Read_File ENDP
;-------------------------------------------
;Unexpected End of File. (No 'end')
End_Err PROC NEAR
mov dx,OFFSET err_end ;'End not found'
mov cx,ERR_END_LEN ;msg length
Say_Error: ;common code
push dx ;v2.0
push cx
mov dx,offset crlf ;Precede all err msgs w/CR/LF v2.0
mov cx,CRLF_LEN
call ErrOut ;display CR/LF
pop cx ;restore orig msg len v2.0
pop dx ;ptr to msg v2.0
call ErrOut ;display it
mov dx,offset crlf ;Follow all err msgs w/CR/LF v2.0
mov cx,CRLF_LEN
ErrOut:
mov bx,2 ;Std ErrOut handle
mov ah,40h ;write to file/device
int 21h
ret
End_Err ENDP
;using pointers beyond runtime code and/or code end for various buffers.
;This does NOT take up any space in our program.
EVEN
;v1.7 LINE_IN is not used until after Init,
;so it can overwrite this code and data.
LINE_IN equ $ ;80 bytes long
;Some start-up data moved down here to minimize memory requirements.
msg_v1 DB CR,LF,'This Program Requires DOS Version 2.0 '
DB 'or higher.'
crlf db CR,LF,'$'
CRLF_LEN EQU 2 ;v2.0
pr_inp DB CR,LF,'Input path/file: '
PR_INP_LEN EQU $-pr_inp
no_action db 'No action'
NO_ACTION_LEN equ $-no_action
;v1.7 Some start-up code.
Init proc near
;First make sure we have DOS 2.0 or higher, or handles won't work.
mov ah,30h ;get DOS version
int 21h
cmp al,2 ;2.0 or above?
jae Chk_CmdLine ;yep, ok
mov dx,OFFSET msg_v1 ;'DOS 2.0 or above'
Msg_Die:
mov ah,9 ;display string
int 21h
mov ax,4C01H ;terminate, ERRORLEVEL 1
int 21H
;Now check cmd line for uuencoded source file.
Chk_CmdLine:
call Parse_CmdLine ;get cmdline target filename v1.8
jnc Open_Inp_Fil ;fine, got one v1.8
;DI -> AsciiZed filename
;0 terminator.
;Let's be nice and prompt the user for an input filename
;v1.8 using normal buffered keyboard input:
;v2.0 First, display usage to the dummy.
mov dx,offset usage$ ;'Usage: '
mov cx,USAGE_LEN ;nr chars to display
call Say_Error ;display to STDERR
mov dx,OFFSET pr_inp ;'Input/file name:' prompt
mov cx,PR_INP_LEN ;nr chars to display
mov bx,2 ;Std ErrOut handle (always to con)
mov ah,40h ;write to file or device
int 21h
;Now get the user kbd input:
mov di,offset cmd_tail-1 ;1 byte before PSP cmdline v1.8
mov byte ptr [di],80 ;tell DOS max of 80 chars v1.8
mov dx,di ;DX -> buffer v1.8
mov ah,0AH ;buffered kbd input svc v1.8
int 21H
call Parse_CmdLine ;parse the input v1.8
jnc Open_Inp_Fil ;got input (already AsciiZed) v1.8
;try to open v1.8
;DI -> AsciiZ 0 v1.8
;Okay, he doesn't wanna play...
mov dx,offset no_action ;'No action' v2.0
mov cx,NO_ACTION_LEN ;length v2.0
mov al,1 ;ERRORLEVEL 1 v2.0
jmp Fatal_Error ;display msg, die v2.0
Open_Inp_Fil:
mov dx,offset INP_FIL ;input file name buffer
mov ax,3D00h ;open file
int 21h
jb Open_Die ;failed, terminate w/error
mov inp_handle,ax ;save input file handle
ret ;go uudecode
Open_Die:
jmp Inp_Err ;input file open error, die
Init endp
;v1.8 Command line processing subroutine
Parse_CmdLine proc near
mov si,offset cmd_tail ;move cmd line parm
mov di,offset INP_FIL ;to our filename buffer
cld ;insure fwd
lodsb ;cmd line length byte
or al,al ;nothing there?
jz PC_NoInput ;yep, nothing there
mov ah,20H ;get a handy space
Strip_Ct:
lodsb ;next cmd line char
cmp al,ah ;gobble leading spaces,tabs, etc.
jbe Strip_Ct
Ct_Char:
cmp al,ah ;ctrl char? (e.g., CR)
jbe PC_Input ;yep, done
;v2.0 Check for a "-o" switch on command line
cmp al,'/' ;this kind of switch?
jz GotSwitch ;yep
cmp al,'-' ;switch?
jnz NotSwitch ;nope, must be name char
GotSwitch:
mov dx,ax ;save this char a second
mov ax,[si] ;snarf next 2 chars
cmp al,'?' ;-? or /? v2.0
jz JustHelp ;yep, help and die v2.0
and al,5FH ;mask 1st char to uppercase
cmp ax,' O' ;O and space means switch
mov ax,dx ;restore in case not
jnz NotSwitch ;nope, must be name
;v2.0 We have the overwrite switch
not overwrite ;toggle flag to TRUE
inc si ;bump past 'o'
inc si ;and past space
lodsb ;next char should be name
NotSwitch:
;end of v2.0 changes
stosb ;stuff filename byte
lodsb ;snarf next cmdline char
jmp short Ct_Char ;and loop
PC_NoInput:
stc ;no input, return CF set
ret
PC_Input:
mov byte ptr [di],0 ;Asciize filename input
clc ;got input, return CF clear
ret
JustHelp:
mov dx,offset usage$
jmp Msg_Die ;display, terminate
Parse_CmdLine endp
EVEN
;INP_FIL is used by Init startup, so it must sit below the code.
INP_FIL equ $ ;80 bytes long,
; input filename buffer.
; Overwrites OUT_FIL buffer
; and uuencode buffers.
;OUT_FIL, OUT_BUF, and UUBUFF aren't used until AFTER Init completes.
;They can overwrite Init code, startup data, INP_FIL filename buffer, etc.
;However, UUBUFF and LINE_IN ARE used to get the output filename OUT_FIL),
; so UUBUFF and LINE_IN can't overwrite OUT_FIL.
;LINE_IN needs 80 bytes:
OUT_FIL equ LINE_IN + 80 ;15 bytes long,
; output filename buffer.
; Sits below LINE_IN,
; overwrites Init code/data.
OUT_BUF equ LINE_IN + 80 ;80 bytes long,
; uudecoded data buffer.
; Sits below LINE_IN,
; overwrites OUT_FIL filename
; and Init code/data.
;OUT_BUF needs 80 bytes
UUBUFF equ OUT_BUF + 80 ; uuencoded file read buffer.
; Sits below OUT_BUF,
; uses remaining code space.
;UU200 equ NOT(UUBUFF + 200H) ;200H bytes oughtta be enough v1.9
UU200 equ (offset cseg_ofs - offset UUBUFF) - 200H
;for the stack.
logo db 'UUDECODE v2.0',0
db 'Originally by Theodore A. Kaldis',0
db 'Thoroughly rehacked by David P Kirschbaum, Toad Hall',0
usage$ db 'UUDECODE [-?][-o] [d:][\path\]binary.UUE <RETURN>',CR,LF
db 'Using the filename.typ in the "begin" line,',CR,LF
db 'produces uudecoded filename.typ on current drive\path',CR,LF
db '(providing filename.typ doesn''t already exist).',CR,LF
db '-o switch forces overwrite of existing filename.typ',CR,LF
db '-? produces this help message.'
USAGE_LEN equ $ - offset usage$
db CR,LF,'$' ;v2.0
CSEG ENDS
END Uudecode