home *** CD-ROM | disk | FTP | other *** search
- 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.
-
- ;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@braggvax.ARPA
- ;-------------------------------------------
- CR EQU 0DH
- LF EQU 0AH
- SPC EQU 20H
- CMD_TAIL EQU 80H ;PSP cmd line offset
- ;-------------------------------------------
- Cseg SEGMENT PARA PUBLIC 'CODE'
- ASSUME CS:Cseg,DS:Cseg, ES:Cseg
- org 100H
-
- ;-------------------------------------------
- Uudecode PROC near
- jmp Start ;jump over data
-
- ;This data will be used during the uudecode run. v1.7
-
- err_inp DB 'Input file error.',CR,LF
- ERR_INP_LEN EQU $-err_inp
- err_out DB 'Output file error.',CR,LF
- ERR_OUT_LEN EQU $-err_out
- err_begin db 'start not found.',CR,LF
- ERR_START_LEN EQU $-err_begin
-
- err_end DB 'End not found.',CR,LF
- ERR_END_LEN EQU $-err_end
-
- 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 v1.7
- ; (e.g., buffer end) v1.7
- outptr DW out_buf ;pointer to binary output buffer v1.7
- ; (where to stuff NEXT uudecoded byte)
- ;-------------------------------------------
-
- Start:
- call Init ;cmd line parsing, file opening v1.7
- ;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 v1.7
- 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 v1.7
-
- ;AH is already a space for fast comparisons
- Strip_Spc:
- ;Gobble spaces until we hit the number after 'start'
- LODSB
- CMP AL,ah ;space?
- jbe Strip_Spc ;gobble spaces, tabs, ctrl chars v1.7
- 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? v1.7
- jbe Strip_Spc2 ;gobble until real char v1.7
-
- ;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 v1.7
- je Out_Fin ;yep, name end v1.7
- STOSB ;input byte > out_fil filename buff
- lodsb ;next filename char v1.7
- 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 v1.7
- 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 v1.7
- 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? v1.7
- jz Prog_End ;yep v1.7
-
- mov bx,2020H ;a handy constant v1.7
- sub al,bl ;20H ;Deasciify v1.7
- 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 v1.7
- mov bp,ax ;BP=binary byte count v1.7
-
- ;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 v1.7
- ;CL=4, CH=6 v1.7
-
- 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 v1.7
- shl ah,1 ;(faster this way) v1.7
- 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 v1.7
- jz New_Line ;uuencoded line is done v1.7
-
- 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 v1.7
- shr al,1 ;(faster this way) v1.7
- OR AL,AH ;shifted quad[3] OR shifted quad[2]
- STOSB ;=hunk[2], stuff in out_buf
- dec bp ;decr binary byte ctr v1.7
- jz New_Line ;uuencoded line is done v1.7
-
- 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 v1.7
- 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 v1.7
- ;for Write_File test v1.7
- ;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 v1.7
-
- ;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 v1.7
- mov cx,39 ;clear remaining 78 bytes v1.7
- mov ax,2020H ;fill with spaces v1.7
- 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 v1.7
- CALL Write_File ;write our binary output (if any)
- CALL Read_File ;read more uuencoded input
- Not_Mt:
- lodsb ;snarf uuencoded line char v1.7
- 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
-
- ;Don't like this looking for a CR .. what about Unix systems that don't
- ;HAVE LF's in their text/uuencoded files?
- Not_Hi: CMP AL,CR ;CR means line end
- JE Eol_CR ;end of line, stop for now v1.7
- cmp al,LF ;How about Unix EOL? v1.7
- je Eol_LF ;Yep, was Unix EOL v1.7
- Was_Space:
- STOSB ;stuff uuencoded line char
- dec bp ;count down allowable length v1.7
- 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? v1.7
- jb Strip_NoRead ;nope v1.7
- call Read_File ;refill the uubuf v1.7
- 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 v1.7
- JMP Flush_Lin ;EOL, keep working thru raw
- ;input buffer
- ;-------------------------------------------
- ;Hit CR, got a uuencoded line
- Eol_CR:
- INC SI ;bump raw input buffer ptr
- 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 v1.7
- xchg cx,outptr ;outptr=0,CX=outptr v1.7
- sub cx,dx ;-buffer start=buffer byte count v1.7
-
- ;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 v1.7
- 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
-
- 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.7
- not cx ;= remaining segment memory v1.7
- 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 v1.7
-
- MOV SI,dx ;point to input uubuff start v1.7
- add ax,si ;chars read+uubuff start v1.7
- MOV uuEndptr,AX ;points beyond last uubuff char v1.7
- RET ;read done
-
- Read_EOF:
- ; CALL End_Err ;Unexpected end of file error
- ; JMP File_End_X ;and terminate
-
- ;-------------------------------------------
- ;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
- 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
-
- ;v1.7 line_in is not used until after Init,
- ;so it can overwrite this code and data.
- line_in equ $ ;80 bytes long v1.7
-
- ;Some start-up data moved down here to minimize memory requirements. v1.7
- msg_v1 DB 'This Program Requires DOS Version 2.0 '
- DB 'or higher.',CR,LF,'$'
- pr_inp DB CR,LF,'Input path/file: '
- PR_INP_LEN EQU $-pr_inp
- no_action db 'No action',CR,LF,'$'
-
- ;v1.7 Some start-up code.
- Init proc near ; v1.7
-
- ;First make sure we have DOS 2.0 or higher, or handles won't work. v1.7
- MOV AH,30h ;get DOS version
- INT 21h
- CMP AL,2 ;2.0 or above?
- JAE Chk_CmdLine ;yep, ok v1.7
- 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. v1.7
- Chk_CmdLine:
- MOV SI,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 Prompt_User ;yep, nothing there v1.7
-
- mov ah,20H ;get a handy space
- Strip_Ct:
- LODSB ;next cmd line char
- CMP AL,ah ;gobble spaces,tabs, etc.
- JBE Strip_Ct
- Ct_Char:
- CMP AL,ah ;ctrl char? (e.g., CR)
- JBE Prog_Go ;yep, done v1.7
- STOSB ;stuff filename byte
- LODSB ;snarf next cmdline char
- JMP SHORT Ct_Char ;and loop
- ;-------------------------------------------
- ;DI points to
- ; (1) the filename buffer byte just beyond our file name
- ; (so we can AsciiZ), or
- ; (2) to filename buffer start (indicating no input!).
-
- ;-------------------------------------------
- Prog_Go:
- cmp di,offset inp_fil ;did we get a cmd line filename?
- ja Open_Inp_Fil ;yep
-
- ;Let's be nice and prompt the user for an input filename:
- Prompt_User: ;v1.7
- 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 dx,di ;DI points to filename buff start
- MOV CX,80 ;up to 80 chars v1.7
- xor bx,bx ;standard input handle (always kbd)
- MOV AH,3Fh ;read from file or device
- INT 21h
- add di,AX ;nr bytes read
- dec di ;adjust for add and CR v1.7
- dec di ; v1.7
-
- Open_Inp_Fil:
- MOV DX,offset inp_fil ;input file name buffer
- cmp di,dx ;no name at all?
- ja Open1 ;ok, continue
- mov dx,offset no_action ;'No action'
- jmp short Msg_Die ;display, terminate
-
- Open1:
- MOV AX,3D00h ;open file
- mov [di],al ;make input filename AsciiZ
- INT 21h
- jb Open_Die ;failed, terminate w/error v1.7
- MOV inp_handle,AX ;save input file handle v1.7
- ret ;go uudecode v1.7
-
- Open_Die:
- JMP Inp_Err ;input file open error, die
- Init endp
-
- EVEN ;v1.7
-
- ;inp_file is used by Init startup, so it must sit below the code.
- inp_fil equ $ ;80 bytes long, v1.7
- ; input filename buffer. v1.7
- ; Overwrites out_file buffer v1.7
- ; and uuencode buffers. v1.7
-
- ;out_fil, out_buf, and uubuff aren't used until AFTER Init completes.
- ;They can overwrite Init code, startup data, inp_file 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, v1.7
- ; output filename buffer. v1.7
- ; Sits below line_in, v1.7
- ; overwrites Init code/data. v1.7
- out_buf equ line_in + 80 ;80 bytes long, v1.7
- ; uudecoded data buffer. v1.7
- ; Sits below line_in, v1.7
- ; overwrites out_fil filename v1.7
- ; and Init code/data. v1.7
- ;out_buf needs 80 bytes
- uubuff equ out_buf + 80 ;READSIZE bytes long, v1.7
- ; uuencoded file read buffer. v1.7
- ; Sits below out_buf. v1.7
- uu200 equ uubuff + 200H ;200H bytes oughtta be enough v1.7
- ;for the stack. v1.7
-
- Cseg ENDS
- END Uudecode