dewi@druca.UUCP (WilliamsD) (04/14/86)
Well, it's just been pointed out to me that the ansiecho source that I
issued to the net a few weeks back was scrogged (I missed the comment to
that effect on this newsgroup).
It turned out that the public domain xmodem I *was* using dropped blocks on
a NAK (!) so it never even made it unscathed to the local UNIX box.
Long live kermit...
So here it is, reposted. Also thrown in are two other CLI routines. A "touch"
command, useful for taming a rogue make and getting rid of those "Future"
dates. WARNING: due to a bug in the RAM: handler, it won't update the datestamp
for ramdisk-resident files. It'll handle zero length files and checks for its
argument being a directory.
I've also added an assembler version of "list" called "alist", just in case
anybody wants to build on it. It's the bare-bones list, it just prints out
the file names. I originally wrote it as a response to a BBS discussion
that claimed that list and dir were slow because they were written in a
high-level language. In case you're curious, alist only turned out to be
around 2-3 seconds faster than list on my C: directory.
About the only advantage of this list command is its size, around 660
bytes (list is 8664 bytes long). Still, it shouldn't be that much work
to add "ls -l" functionality. If somebody does it, please send me a copy!
You'll need V1.1 of the Macro Assembler for these.
Finally, for amusement only, here's a small CLI mis-feature:
makedir junk
echo >junk blow away the directory!
Yes. If you do a list on junk, you'll find that it's changed to a text
file! Before you panic, this only works with *empty* directories. But
still...
Dewi.
------------------------- snip it, snip it good -----------------------------
echo "creating source files:"
echo "\ttouch.asm"
cat <<"FUNKYSTUFF" >touch.asm
* TOUCH.ASM: A recreation in assembler of the UN*X command of the same name.
* Works by read & write rather than attempting to muck directly
* with filesystem datestamps.
* Perpetrator: Dewi Williams ..!ihnp4!druca!dewi
* Unconditionally placed in the public domain.
* Included equate files.
* -----------------------------------------------------------------------
NOLIST
INCLUDE "exec/types.i"
INCLUDE "exec/libraries.i"
INCLUDE "libraries/dos.i"
INCLUDE "libraries/dosextens.i"
INCLUDE "exec/memory.i"
LIST
* External references
* -----------------------------------------------------------------------
EXTERN_LIB OpenLibrary
EXTERN_LIB Read
EXTERN_LIB Write
EXTERN_LIB Output
EXTERN_LIB Open
EXTERN_LIB Close
EXTERN_LIB Lock
EXTERN_LIB UnLock
EXTERN_LIB IoErr
EXTERN_LIB Seek
EXTERN_LIB AllocMem
EXTERN_LIB FreeMem
EXTERN_LIB Examine
EXTERN_LIB DeleteFile
* Local Equates
* ------------------------------------------------------------------------
SysBase EQU 4 ; The one known address in the system.
TRUE EQU -1
FALSE EQU 0
SEEKFAIL EQU -1
* Macros
* ------------------------------------------------------------------------
* Calls to dos.library and exec library. Differing calls scrog different
* registers. I chose to take no chances...
callsys MACRO
MOVEM.L D1-D7/A0-A6,-(SP) ; save registers
CALLLIB _LVO\1
MOVEM.L (SP)+,D1-D7/A0-A6 ; and restore (AmigaDOS scrogs!).
ENDM
callexec MACRO
MOVEM.L D1-D7/A0-A6,-(SP) ; save registers
LINKLIB _LVO\1,SysBase
MOVEM.L (SP)+,D1-D7/A0-A6 ; and restore (AmigaDOS scrogs!).
ENDM
* The print routine and argument setup
PRINT MACRO
lea.l \1(PC),A0
move.l D1,-(SP)
move.l d5,d1
jsr print
move.l (SP)+,D1
ENDM
* General purpose close, print, and quit macro
QUIT MACRO
callsys IoErr ; return from IoErr will be exit code
move.l D0,D6 ; save error return. Could use stack...
PRINT \1
callsys Close ; assumes handle in D1
move.l d6,d0 ; restore error return
bra FINISHED
ENDM
* The code segment
* -------------------------------------------------------------------------
RORG 0 ; Relocatable code.
jsr argtrim ; clean up command line
;------ get Exec's library base pointer:
LEA.L DOSName(PC),A1 ; name of dos library
move.l #LIBRARY_VERSION,d0
callexec OpenLibrary,SysBase
MOVE.L D0,A6 ; Save library pointer (always in A6).
BNE.S gotdos
; Should really issue an alert here...
moveq #RETURN_FAIL,D0 ; give up
bra FINISHED
gotdos:
* Obtain the output handle (needed for error prints).
callsys Output
MOVE.L D0,D5 ; Save for the write
* Now check if we have an argument.
cmpi.b #0,(A0) ; a null string for an argument ?
beq noarg ; yes
; Actions differ depending on whether or not the file exists. If it
; does, we read and write the first byte (if it isn't empty!). If it
; doesn't, we create it as a zero length file. So we start off by
; attempting to lock the given name.
move.l a0,a5 ; safe copy
move.l a0,d1 ; supply the name
move.l #ACCESS_READ,d2 ; only checking for existence
callsys Lock
move.l d0,d1 ; remember for later
beq nolock ; did we get a lock?
; It exists: let's Examine it and see what it is. Need to deny a touch
; of a directory and do special actions for a zero length file.
jsr CheckFile
cmpi.l #0,D0
beq proceed
cmpi.l #1,D0
beq unlinkit
cmpi.l #2,D0
beq isadir ; touch a directory?!
; Get here if we encountered an error in CheckFile
bra examerror
; It's a zero length file, so unlink it and re-create.
unlinkit:
callsys UnLock ; lock's still in D1
move.l a5,d1 ; the filename
callsys DeleteFile ; blow it away
bra nolock ; create a new zero length file
; It exists and is not empty, so unlock it and proceed with open read and
; write.
proceed:
callsys UnLock
move.l a5,d1 ; the filename
move.l #MODE_OLDFILE,d2 ; allows reading & writing, no creation.
callsys Open
move.l d0,d1 ; for read, write, seek and close
beq noopen ; couldn't open, but it exists!
lea.l char(pc),a4 ; where the single char read goes
move.l a4,d2
moveq #1,d3 ; single character read
callsys Read
cmpi.l #1,d0 ; did it work?
bne noread
; Seek back to the beginning.
moveq #0,d2
move.l #OFFSET_BEGINNING,d3 ; seek mode -- 0 bytes from beginning
callsys Seek
cmpi.l #SEEKFAIL,D0
beq noseek ; seek failed for some reason
move.l a4,d2 ; the character that was read
moveq #1,d3 ; the count of characters
callsys Write
cmpi.l #1,d0 ; did it work?
bne nowrite ; no
callsys Close ; clean up
move.l #RETURN_OK,D0
bra FINISHED ; finally
nolock:
; It doesn't exist, so just create a zero length file.
move.l a5,d1 ; the filename
move.l #MODE_NEWFILE,d2 ; create
callsys Open
move.l d0,d1 ; for close
beq nocreate
callsys Close ; this should leave us with a zero length file
move.l #RETURN_OK,D0
bra FINISHED
* Error traps.
isadir:
callsys UnLock ; lock's still in D1
PRINT S_ISADIR
move.l #RETURN_FAIL,D0
bra FINISHED
examerror:
PRINT S_EXAMERR
move.l #RETURN_FAIL,D0
bra FINISHED
noseek:
QUIT S_NOSEEK
nowrite:
QUIT S_NOWRITE
noread:
QUIT S_NOREAD
noopen:
PRINT S_NOOPEN
callsys IoErr ; return from IoErr will be exit code
bra FINISHED
nocreate:
PRINT S_NOCREATE
callsys IoErr ; return from IoErr will be exit code
bra FINISHED
noarg:
PRINT S_NOARG
move.l #RETURN_ERROR,D0
; Fall through
FINISHED:
rts
* Subroutines
* ------------------------------------------------------------------------
* argtrim: a routine to trim the end of a command line and null terminate
* it. This is achieved in the following manner:
* Start at the end and run back until a non-whitespace (nl/blank)
* character is encountered. If this is a quote, toss it.
* If the *first* character is a quote, bump the start address by
* one. No pretense of quote matching here.
* Inputs: address in A0, length in D0
* Outputs: address in A0.
argtrim:
movem.l d1-d7/a1-a6,-(sp) ; save registers
cmpi.b #'"',(a0) ; starts with a quote?
bne.s checkline ; no
subq #1,d0 ; yes - decrement count
addq #1,a0 ; and bump start address
checkline:
cmpi.b #1,D0 ; length includes the newline
bne.s isline ; yes - there is something there
move.b #0,(a0) ; create null string
bra.s argfini ; done
isline:
; strip off any run of blanks on the end...
move.l a0,a1 ; computing end address of line
add.l d0,a1 ;
subq #2,a1 ;
toploop:
cmp.l a0,a1
beq.s empty ; single char or run of blanks
cmpi.b #' ',(a1)
bne.s endloop ; finished the scan
subq #1,a1 ; else back one more
bra.s toploop ; and try again
endloop:
cmpi.b #'"',(a1)
beq.s nullit
nullnext:
addq #1,a1
nullit:
move.b #0,(a1)
bra.s argfini
empty: ; could be blanks or a single char
cmpi.b #' ',(a1)
bne.s endloop ; or maybe a single quote!
move.b #0,(a0)
argfini:
movem.l (sp)+,d1-d7/a1-a6 ; restore registers
rts
* PRINT: Passed an EA in A0, and a handle in D1, print the string.
print:
MOVEM.L D0-D7/A0-A6,-(SP)
; Let's find out how long this string is...
JSR strlen ; Addr in A0 returns len in D0
MOVE.L D0,D3 ; length
MOVE.L A0,D2 ; Address
callsys Write ; Handle already in D1
; We don't check error returns from write, because there isn't a whole
; lot we can do if console writes fail.
MOVEM.L (SP)+,D0-D7/A0-A6 ; Restore registers
rts
* STRLEN: How long is a piece of string? Pass a string in A0, the
* result comes back in D0.
strlen:
MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers A0, D1 on stack.
MOVEQ #0,D0 ; Initialize count
STRLOOP:
; Investigate current character.
cmpi.b #0,(A0)+ ; Is it a NUL?
beq.s STRFINI
ADDQ #1,D0 ; Up the count
bra STRLOOP
STRFINI:
MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers
rts
; Passed a lock in register D1 this routine checks out the file attributes.
; Returns 0 to proceed, 1 if empty, 2 if a directory, and 3 for error.
CheckFile:
MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers on stack.
move.l d1,d4 ; save it for future use
; A lock in D1 and a FileInfoBlock address in D2 are all that are needed.
; We allocmem the FileInfoBlock to assure alignment.
move.l #fib_SIZEOF,D0 ; size of the block
move.l #MEMF_PUBLIC,D1 ; memory requirement
callexec AllocMem ; args D0, D1 returns into D0
move.l d0,a5 ; save it for later
beq examnomem ; but did it work?
move.l d0,d2 ; fileinfoblock address
move.l d4,d1 ; the lock
callsys Examine ; see what the lock corresponds to.
cmpi.l #FALSE,d0 ; it should work...
beq noexam ; but it didn't!
; We have an Examine structure. Now check out if it's a directory or not.
; fib_DirEntryType is >0 for a directory and < 0 for a file
tst.l fib_DirEntryType(A5)
bgt directory ; it's a directory
; It's just a filename. Let's see how large it is.
tst.l fib_Size(A5)
beq isempty ; nothing in it -- so we can't read
moveq #0,D6 ; the flag character
bra examfree
* Error & cleanup for this routine
examnomem: ; couldn't get memory
moveq #3,D6
bra examfini
noexam: ; couldn't Examine
moveq #3,D6
bra examfree
directory: ; it's a directory
moveq #2,D6 ; set return code
bra examfree ; release memory
isempty: ; it's empty
moveq #1,D6 ; fall through
examfree: ; free the memory here
move.l #fib_SIZEOF,D0 ; Address in A1, size in D0
move.l a5,a1
callexec FreeMem ; restore memory
examfini:
move.l d6,d0 ; set return code
MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers
rts
* Data declarations
* -------------------------------------------------------------------------
DOSName DOSNAME
char DS.B 1 ; scratch
S_NOARG DC.B 'Usage: touch file',10,0
S_NOCREATE DC.B 'Failed to create file',10,0
S_NOOPEN DC.B 'Cannot open file',10,0
S_NOREAD DC.B 'Cannot read from file',10,0
S_NOWRITE DC.B 'Cannot write to file',10,0
S_NOSEEK DC.B 'Cannot rewind file',10,0
S_EXAMERR DC.B 'Error in examining file',10,0
S_ISADIR DC.B 'Cannot touch a directory!',10,0
END
FUNKYSTUFF
echo "\talist.asm"
cat <<"FUNKYSTUFF" >alist.asm
* ALIST.ASM: An assembler list command. Does the bare minimum directory
* listing. Of no real utility, it was mainly written to compare
* assembler and C implementations of "trivial" CLI programs.
* It's merely the framework with which an assembler variant of
* full-blown list could be written (but not by me!).
* In case you're interested, it really isn't much faster than
* list. I suspect the difference (2-3s on a large c: directory)
* is due to list's formatting of the FileInfoBlock and
* associated greater console driver use.
* However, alist is ~660 bytes long, instead of 8664 !!
* Perpetrator: Dewi Williams ..!ihnp4!druca!dewi
* Unconditionally placed in the public domain.
* (I could make money off of this?!)
* Included equate files.
* -----------------------------------------------------------------------
NOLIST
INCLUDE "exec/types.i"
INCLUDE "exec/libraries.i"
INCLUDE "libraries/dos.i"
INCLUDE "libraries/dosextens.i"
INCLUDE "exec/memory.i"
LIST
* External references
* -----------------------------------------------------------------------
EXTERN_LIB OpenLibrary
EXTERN_LIB Write
EXTERN_LIB Output
EXTERN_LIB Examine
EXTERN_LIB ExNext
EXTERN_LIB Lock
EXTERN_LIB UnLock
EXTERN_LIB CurrentDir
EXTERN_LIB AllocMem
EXTERN_LIB FreeMem
* Local Equates
* ------------------------------------------------------------------------
SysBase EQU 4 ; The one known address in the system.
TRUE EQU -1
FALSE EQU 0
* Macros
* ------------------------------------------------------------------------
* Calls to dos.library and exec library
callsys MACRO
CALLLIB _LVO\1
ENDM
callexec MACRO
LINKLIB _LVO\1,SysBase
ENDM
* The print routine and argument setup
PRINT MACRO
lea.l \1(PC),A0
move.l d5,d1
jsr print
ENDM
* The newline print macro -- called after PRINT so D1 always set up.
NL MACRO
lea.l NLCHAR(PC),A0
jsr print
ENDM
* The code segment
* -------------------------------------------------------------------------
RORG 0 ; Relocatable code.
jsr argtrim ; clean up command line
MOVEM.L D0/A0-A2,-(SP) ; Save command line (OpenLibrary trashes).
;------ get Exec's library base pointer:
LEA.L DOSName(PC),A1 ; name of dos library
move.l #LIBRARY_VERSION,d0
callexec OpenLibrary,SysBase
MOVE.L D0,A6 ; Save library pointer (always in A6).
BNE.S gotdos
; Should really issue an alert here...
moveq #RETURN_FAIL,D0 ; give up
bra FINISHED
gotdos:
; REGISTER USAGE:
; A0 = scratch after command line used.
; A5 = FileInfoBlock address
; A6 = dos library base pointer
; D1-3 = AmigaDOS scratch argument registers
; D4 = current directory lock
; D5 = output handle
; D6 = root lock flag
* Obtain the output handle (needed for write)
callsys Output
MOVE.L D0,D5 ; Save for the write
MOVEM.L (SP)+,D0/A0-A2 ; restore command line etc.
* Setup D6 as the restore directory flag
moveq #FALSE,D6
* Now check if we have an argument. If we have, attempt to lock it.
* Otherwise, go for current directory.
cmpi.b #0,(A0) ; a null string for an argument ?
beq CURDIR ; yes
; Lock to specified directory. Place name in D1, access mode in D2
move.l a0,d1
move.l #ACCESS_READ,d2
callsys Lock
move.l d0,d4 ; remember for later
beq nolock ; did we get a lock?
bra uselock ; for the Examine
CURDIR:
; Get lock to current directory. The most bulletproof way of doing this
; (due to bugs in the V1.1 RAM: handler) is to make the root directory the
; current one, because CurrentDir will return the old lock value.
moveq #0,D1
callsys CurrentDir ; got the lock
move.l d0,d4 ; for later
moveq #TRUE,d6 ; remember to cd
uselock:
; A lock in D1 and a FileInfoBlock address in D2 are all that are needed.
; We allocmem the FileInfoBlock to assure alignment.
move.l #fib_SIZEOF,D0 ; size of the block
move.l #MEMF_PUBLIC,D1 ; memory requirement
MOVEM.L D2-D7/A0-A6,-(SP) ; in case AllocMem trashes (it does...)
callexec AllocMem ; args D0, D1 returns into D0
MOVEM.L (SP)+,D2-D7/A0-A6 ; restore registers
move.l d0,a5 ; save it for later
beq nomem ; but did it work?
move.l d0,d2 ; fileinfoblock address
move.l d4,d1 ; the lock
callsys Examine ; see what the lock corresponds to.
cmpi.l #FALSE,d0 ; don't need to cd in cleanup code
beq noexam
; We have an Examine structure. Now check out if it's a directory or not.
; Incredibly silly things happen if you call ExNext for a file.
; fib_DirEntryType is >0 for a directory and < 0 for a file
lea.l fib_DirEntryType(A5),A0
tst.l (a0)
bgt again
; It's just a filename. Emit it's name and finish.
move.l d5,d1
lea.l fib_FileName(A5),A0
jsr print
NL
bra cleanup ; get next file
again:
move.l a5,d2 ; restore fileinfoblock address
move.l d4,d1 ; and the lock
callsys ExNext
cmpi.l #FALSE,d0
beq cleanup ; Should really check with IoErr
move.l d5,d1 ; restore output handle
lea.l fib_FileName(A5),A0
jsr print
NL
bra again ; get next file
nomem:
PRINT S_NOMEM
bra afterfree
noexam:
PRINT S_NOEXAM
bra cleanup
nolock:
PRINT S_NLOCK
bra FINISHED
cleanup:
; start off by freeing allocated memory.
move.l a5,a1
MOVEM.L D1-D7/A0-A6,-(SP) ; in case FreeMem trashes
move.l #fib_SIZEOF,D0
callexec FreeMem
MOVEM.L (SP)+,D1-D7/A0-A6 ; restore registers
afterfree:
; May have to throw away two locks and do a cd here
cmpi.l #FALSE,d6
beq nocd
move.l d4,d1
callsys CurrentDir ; get back to where we were
move.l d0,d1
callsys UnLock
move.l #RETURN_OK,D0
bra FINISHED
nocd:
move.l d4,d1
callsys UnLock
move.l #RETURN_OK,D0
FINISHED:
rts
* Subroutines
* ------------------------------------------------------------------------
* argtrim: a routine to trim the end of a command line and null terminate
* it. This is achieved in the following manner:
* Start at the end and run back until a non-whitespace (nl/blank)
* character is encountered. If this is a quote, toss it.
* If the *first* character is a quote, bump the start address by
* one. No pretense of quote matching here.
* Inputs: address in A0, length in D0
* Outputs: address in A0.
* This routine is probably overkill for flist.
argtrim:
movem.l d1-d7/a1-a6,-(sp) ; save registers
cmpi.b #'"',(a0) ; starts with a quote?
bne.s checkline ; no
subq #1,d0 ; yes - decrement count
addq #1,a0 ; and bump start address
checkline:
cmpi.b #1,D0 ; length includes the newline
bne.s isline ; yes - there is something there
move.b #0,(a0) ; create null string
bra.s argfini ; done
isline:
; strip off any run of blanks on the end...
move.l a0,a1 ; computing end address of line
add.l d0,a1 ;
subq #2,a1 ;
toploop:
cmp.l a0,a1
beq.s empty ; single char or run of blanks
cmpi.b #' ',(a1)
bne.s endloop ; finished the scan
subq #1,a1 ; else back one more
bra.s toploop ; and try again
endloop:
cmpi.b #'"',(a1)
beq.s nullit
nullnext:
addq #1,a1
nullit:
move.b #0,(a1)
bra.s argfini
empty: ; could be blanks or a single char
cmpi.b #' ',(a1)
bne.s endloop ; or maybe a single quote!
move.b #0,(a0)
argfini:
movem.l (sp)+,d1-d7/a1-a6 ; restore registers
rts
* PRINT: Passed an EA in A0, and a handle in D1, print the string.
print:
MOVEM.L D0-D7/A0-A6,-(SP)
; Let's find out how long this string is...
JSR strlen ; Addr in A0 returns len in D0
MOVE.L D0,D3 ; length
MOVE.L A0,D2 ; Address
callsys Write ; Handle already in D1
; We don't check error returns from write, because there isn't a whole
; lot we can do if console writes fail.
MOVEM.L (SP)+,D0-D7/A0-A6 ; Restore registers
rts
* STRLEN: How long is a piece of string? Pass a string in A0, the
* result comes back in D0.
strlen:
MOVEM.L D1-D7/A0-A6,-(SP) ; Save registers A0, D1 on stack.
MOVEQ #0,D0 ; Initialize count
STRLOOP:
; Investigate current character.
cmpi.b #0,(A0)+ ; Is it a NUL?
beq.s STRFINI
ADDQ #1,D0 ; Up the count
bra STRLOOP
STRFINI:
MOVEM.L (SP)+,D1-D7/A0-A6 ; Restore registers
rts
* Data declarations
* -------------------------------------------------------------------------
DOSName DOSNAME
NLCHAR DC.B 10,0
S_NLOCK DC.B 'directory or file name not found',10,0
S_NOEXAM DC.B 'Could not examine directory',10,0
S_NOMEM DC.B 'No memory',10,0
END
FUNKYSTUFF
echo "\techo.asm"
cat <<"FUNKYSTUFF" >echo.asm
* ANSIECHO.ASM: Extended echo that handles backslash constructs such as \t
* etc. Unlike UNIX echo, it takes \3A hex constructs instead
* of octal. I'd rather grow 6 new fingers than lose 2!
*
* The following backslash constructs are supported:
* \t tab
* \n newline
* \f formfeed (clear screen)
* \e escape character
* \c on end of string, suppresses newline
* \b backspace
* \\ single backslash
* \" explicit string quote, otherwise eaten.
* \NN hexadecimal characters (*must* be upper case).
*
* As an example, try echo \e[37mhello world\e[0m
*
* Perpetrator: Dewi Williams ..!ihnp4!druca!dewi
* Unconditionally placed in the public domain.
* Included equate files.
* -----------------------------------------------------------------------
NOLIST
INCLUDE "exec/types.i"
INCLUDE "exec/libraries.i"
INCLUDE "libraries/dos.i"
LIST
* External references
* -----------------------------------------------------------------------
EXTERN_LIB OpenLibrary
EXTERN_LIB Write
EXTERN_LIB Output
* Local Equates
* ------------------------------------------------------------------------
SysBase EQU 4 ; The one known address in the system.
TRUE EQU -1
FALSE EQU 0
CMD_SIZE EQU 300 ; size of expansion area on stack
* Macros
* ------------------------------------------------------------------------
* Calls to dos.library and exec library
callsys MACRO
CALLLIB _LVO\1
ENDM
* The code segment
* -------------------------------------------------------------------------
RORG 0 ; Relocatable code.
LINK A2,#(-CMD_SIZE) ; expand onto stack
LEA.L -CMD_SIZE(A2),A1 ; start of expansion buffer
jsr argtrim ; clean up command line
MOVEM.L D0/A0-A2,-(SP) ; Save command line (OpenLibrary trashes).
;------ get Exec's library base pointer:
move.l SysBase,a6
LEA.L DOSName(PC),A1 ; name of dos library
move.l #LIBRARY_VERSION,d0
callsys OpenLibrary
MOVE.L D0,A6 ; Save library pointer (always in A6).
BNE.S gotdos
; Should really issue an alert here...
moveq #RETURN_FAIL,D0 ; give up
bra FINISHED
* Set up loop addressing: A0 goes through command line, A1 references
* stack build area, A2 is the frame pointer, D4 is the nonl flag and
* D3 is a counter. The command line is terminated by a newline character.
gotdos:
* Obtain the output handle (needed for write)
callsys Output
MOVE.L D0,D5 ; Save for the write
MOVEM.L (SP)+,D0/A0-A2 ; restore command line etc.
MOVEQ #FALSE,D4 ; nonl assumed false as default
CLR.L D3 ; zero characters so far
mainloop:
CMPI.B #0,(A0) ; Reached end of string yet?
BEQ eos ; yes
CMPI.B #'\',(A0) ; is it escaped?
BNE notesc ; no
CMPI.B #0,1(A0) ; at the end of the string?
BEQ notesc ; not escaping anything, is itself
; Check out the \t \f etc. cases first
ADDQ #1,A0 ; move to next character
CMPI.B #'t',(A0) ; a tab?
BNE next1 ; no
MOVE.B #9,(A1)+ ; yes
bra gotit
next1:
cmpi.b #'b',(A0) ; a backspace?
bne next2 ; no
move.b #8,(A1)+ ; yes
bra gotit
next2:
cmpi.b #'n',(A0) ; a newline?
bne next3 ; no
move.b #10,(A1)+ ; yes
bra gotit
next3:
cmpi.b #'e',(a0) ; an escape char (not backslash!)
bne next4 ; no
move.b #27,(A1)+ ; yes
bra gotit
next4:
cmpi.b #'\',(a0) ; an escaped backslash?
bne next5 ; no
bra notesc ; yes
next5:
cmpi.b #'f',(a0) ; formfeed
bne next6 ; no
move.b #12,(A1)+ ; yes
bra gotit
next6:
cmpi.b #'c',(a0) ; no newline construct
bne next7 ; no
cmpi.b #0,1(A0) ; A \c at the end of the string?
bne notesc ; no
moveq #TRUE,D4 ; set up the nonl flag
bra eos ; know we've finished
next7:
cmpi.b #'"',(A0) ; a string quote?
bne maybehex ; no
move.b #'"',(A1)+ ; yes
bra gotit
; get here -- maybe it's 2 hex digits. Call ishexdig to find out.
maybehex:
move.b (a0),D1
jsr ishexdig ; Check the first character
beq notesc ; failed the test
move.b 1(a0),D1 ; Now check the second
jsr ishexdig
beq notesc ; second one failed
jsr hexstr ; translate it into a real character
addq #1,a0 ; lose both characters
move.b d0,(A1)+ ; and fall through to gotit.
gotit:
addq #1,a0 ; next input character
addq #1,d3 ; bump output count
bra mainloop ; round again
notesc:
; A normal character
move.b (a0)+,(a1)+ ; copy it over
addq #1,d3 ; bump output count
bra mainloop ; round again
; Left the loop.
eos:
TST.L D4 ; Check out the nonl flag
BNE skipnl ; set
move.b #10,(a1)+ ; add the newline.
addq #1,d3
skipnl:
; All set up for printing out the gathered string. Start by recalculating
; the start address of the output buffer.
lea.l -CMD_SIZE(A2),A0
move.l a0,d2 ; start address
move.l d5,d1 ; restore handle
callsys Write
MOVEQ #0,D0 ; success
FINISHED:
UNLK A2
RTS
* Subroutines
* ------------------------------------------------------------------------
* ishexdig: passed a character in D1, will report as a boolean in D0.
* Passes 0..9 and A..F (upper case).
ishexdig:
cmpi.b #'0',D1
blt hexlab
cmpi.b #'9',D1
ble yes
hexlab:
cmpi.b #'A',D1
blt no
cmpi.b #'F',D1
bgt no
yes:
moveq #TRUE,D0
bra endhex
no:
moveq #FALSE,D0
endhex:
rts
* hexstr: passed a hex string in A0, return its value in D0.
* Stop either at length 2 or invalid hex character.
* In this case, the string has already been validated.
* CREDITS: Slightly hacked version of p430 of "Programming the 68000"
* by Steve Williams. Sybex books. Recommended.
hexstr:
movem.l d1-d3/a0,-(sp) ; save starting registers
clr.l d0 ; clear accumulator
clr.l d3 ; count of digits processed
loop:
clr.l d1 ; zero out D1
cmpi.b #'9',(a0) ; upper bound
bhi notdec ; not a decimal digit
cmpi.b #'0',(a0) ; lower bound
blt nothex ; not a hex digit
move.b #'0',d1 ; correction factor
bra gotdig ; accumulate
notdec:
cmpi.b #'A',(a0) ; check letters
blt nothex ; not a hex digit
cmpi.b #'F',(a0) ; upper case hex?
bhi nothex ; no, and lower case is not allowed
move.b #'A'-10,d1 ; correction factor / fall through to gotdig
gotdig:
clr.l d2 ; zero high byte
move.b (a0)+,d2 ; get next digit
sub.l d1,d2 ; convert to binary
lsl.l #4,d0 ; multiply by 16
add.l d2,d0 ; add in digit
cmpi.l #1,d3 ; reached the end yet?
beq hexdone ; yes
addq.l #1,d3 ; bump the count
bra loop ; try another digit
hexdone:
nothex:
movem.l (sp)+,d1-d3/a0 ; unsave registers
rts ; return to caller
* argtrim: a routine to trim the end of a command line and null terminate
* it. This is achieved in the following manner:
* Start at the end and run back until a non-whitespace (nl/blank)
* character is encountered. If this is a quote, toss it.
* If the *first* character is a quote, bump the start address by
* one. No pretense of quote matching here.
* Inputs: address in A0, length in D0
* Outputs: address in A0.
argtrim:
movem.l d1-d7/a1-a6,-(sp) ; save registers
cmpi.b #'"',(a0) ; starts with a quote?
bne.s checkline ; no
subq #1,d0 ; yes - decrement count
addq #1,a0 ; and bump start address
checkline:
cmpi.b #1,D0 ; length includes the newline
bne.s isline ; yes - there is something there
move.b #0,(a0) ; create null string
bra.s argfini ; done
isline:
; strip off any run of blanks on the end...
move.l a0,a1 ; computing end address of line
add.l d0,a1 ;
subq #2,a1 ;
toploop:
cmp.l a0,a1
beq.s empty ; single char or run of blanks
cmpi.b #' ',(a1)
bne.s endloop ; finished the scan
subq #1,a1 ; else back one more
bra.s toploop ; and try again
endloop:
cmpi.b #'"',(a1)
beq.s nullit
nullnext:
addq #1,a1
nullit:
move.b #0,(a1)
bra.s argfini
empty: ; could be blanks or a single char
cmpi.b #' ',(a1)
bne.s endloop ; or maybe a single quote!
move.b #0,(a0)
argfini:
movem.l (sp)+,d1-d7/a1-a6 ; restore registers
rts
* Data declarations
* -------------------------------------------------------------------------
DOSName DOSNAME
END
FUNKYSTUFF
echo "source file creation complete"
exit 0
--
----------------------------------------------------------------------
Dewi Williams.
uucp: ..!ihnp4!druca!dewi
phone: (303) 538 4884