;
; chmod.asm
; Written: Jan/14/87 by SDB
; Copyright (c) 1987 by Scott Ballantyne.
;
;       This program may be freely distributed.  Improvements are welcome,
;       but any code based on this program can never become proprietary.
;       You are welcome to use, improve, and distribute this program.
;       You are forbidden to prevent anyone else from using, improving,
;       or distributing this program.
;
;       Another Contribution to the holy cause of replacing ADOS commands.
;       This comes in at 588 bytes, using the manx assembler.  As usual,
;       using metacomposts assembler should shave about 72 bytes away.
;       The BCPL version is 1092 bytes.
;       This is a replacement for Protect, which is based on the Un*x
;       chmod command.
;       Syntax is:
;
;       Chmod [operand] [permissions] file(s)
;
;       Operand must be given, and is one of '+' '-' or '='.
;       Permissions are optional.  If given must follow immediately with
;       no intervening whitespace.
;       i.e. +rwed or -red or =rwed.
;       files are any file names - multiple filenames may be given.  Aside
;       from convenience, this allows shell programs to expand wild carded
;       names. You can use spaces as delimiters, or quotes - your choice.
;
;       Here is how it works:
;       + will add the permissions following to the file(s).  No other
;       permissions are affected.
;       - will add remove the permissions following from the file(s). No other
;       permissions are affected
;       = sets permissions absolutely - only the permissions given in the
;       command line will be allowed.
;
;       Chmod +r file1 file2 file3
;       Will allow reading for file1 file2 file3 without changing any other
;       permission.
;       Chmod -rwe file1 file2 file3
;       Will disallow reading writing and executing for file1 file2 and file3
;       without affecting anyother permissions.
;       Chmod =r file1 file2 file3
;       File1 file2 and file3 permissions are set absolutely to r(ead) only.
;       All other permissions are disallowed.
;
;       Differences from the UN*X:
;
;       Only the symbolic rwed characters are accepted - no octal digits.
;       Won't accept multiple permissions separated by comma's. I.E.
;       under un*x you can
;       Chmod a-x,g-r file(s)
;       but not in this version.  Didn't seem needed here, since there is
;       only one owner of a file.  Easily added if turns out to be a need
;       for it.
;       Files are pushed on the stack, and so are processed in reverse order
;       of entry.
;
LIBRARY_VERSION         equ     31
_AbsExecBase            equ     4

_LVOOpenLibrary         equ     -552
_LVOCloseLibrary        equ     -414
_LVOFreeMem             equ     -210
_LVOAllocMem            equ     -198

_LVOSetProtection       equ     -186
_LVOExamine             equ     -102
_LVOUnLock              equ     -90
_LVOLock                equ     -84
_LVOOutput              equ     -60
_LVOWrite               equ     -48

FIB_SIZE                equ     260     ; size of struct fib
fib_Protection          equ     116     ; offset to protection var

EXCLUSIVE_LOCK          equ     -1

        entry   Start
        rts
Start:
        move.l  sp,a5           ; save stack pointer.
        move.l  a0,a2           ; move ptr to args
        move.l  #20,d4          ; set error
        move.l  _AbsExecBase,a6
        move.l  #FIB_SIZE,d0    ; get a fib
        moveq.l #0,d1           ; anywhere
        jsr     _LVOAllocMem(a6)
        tst.l   d0
        beq.s   gone
        move.l  d0,a4
        move.l  #LIBRARY_VERSION,d0
        lea     DOSName(pc),a1
        jsr     _LVOOpenLibrary(a6)
        tst.l   d0
        beq.s   nodos
        move.l  d0,a6
        moveq.l #0,d6           ; clear permissions
sbl:    move.b  (a2)+,d7        ; skip blanks
        cmp.b   #' ',d7
        beq.s   sbl
        move.l  #'-+=',d5
oploop:
        cmp.b   d5,d7
        beq.s   getp
        lsr.l   #8,d5
        bne.s   oploop
        move.b  #'=',d7         ; set default operator
        subq.l  #1,a2           ; unget a character
getp:
        move.b  (a2)+,d0
        cmp.b   #$0a,d0
        beq.s   usage
        cmp.b   #' ',d0
        bne.s   lab
        cmp.b   #'=',d7         ; missing permissions only make sense with =
        bne.s   done            ; subtract nothing, add nothing
        move.b  #$00,d6         ; After this is inverted, it will disallow
        bra.s   files           ; all permissions.
lab:
        and.b   #$5f,d0
        moveq.l #0,d3           ; permission bit number
        moveq.l #1,d4           ; permission bit mask
        move.l  #'RWED',d5      ; permission symbols
lab1:
        cmp.b   d5,d0
        beq.s   gotit
        addq.b  #1,d3           ; up the stakes
        lsr.l   #8,d5           ; shift next character in
        bne.s   lab1            ; check next
        bra.s   usage           ; ah oh.
gotit:
        asl.l   d3,d4           ; calc permission
        or.l    d4,d6
        move.b  (a2)+,d0        ; get another character
        cmp.b   #$0a,d0
        beq.s   usage
        cmp.b   #' ',d0
        bne.s   lab
files:
        clr.l   -(sp)           ; push a NULL
        subq.l  #1,a2           ; unget a character
getfiles:
        move.b  #' ',d1
skipbl: move.b  (a2)+,d0
        cmp.b   d1,d0
        beq.s   skipbl
        cmp.b   #$0a,d0
        beq.s   gotfiles
        move.l  a2,-(sp)        ; push pointer on the stack
        cmp.b   #'"',d0
        bne.s   notq
        move.b  d0,d1           ; stuff quote
        bra.s   null
notq:   subq.l  #1,(sp)         ; move pointer back to start of filename
null:   move.b  (a2)+,d0
        cmp.b   #$0a,d0
        beq.s   zap
        cmp.b   d1,d0
        bne.s   null
zap:    clr.b   -1(a2)          ; zap
        cmp.b   #$0a,d0
        bne.s   getfiles        
        ;
        ; Got the files, now get on with the show
        ;
gotfiles:
        tst.l   (sp)            ; Did we get a filename?
        beq.s   usage           ; no files....
        cmp.b   #'-',d7         ; need to flip bits?
        beq.s   main
        eor.b   #$0f,d6         ; + = need to be inverted.
main:
        move.l  (sp)+,a3        ; get pointer to filename
        cmp.l   #0,a3
        beq.s   done
        cmp.b   #'=',d7         ; is it absolute?
        bne.s   longway         ; go longway around
        move.l  d6,d2
        bra.s   dothedeed
        ;
        ; get current permission
        ;
longway:
        move.l  a3,d1           ; filename
        move.l  #EXCLUSIVE_LOCK,d2
        jsr     _LVOLock(a6)
        tst.l   d0
        beq.s   warn            ; warning, then back to main
ok:     move.l  d0,d5           ; save lock so we can free later
        move.l  d0,d1
        move.l  a4,d2           ; fib pointer
        jsr     _LVOExamine(a6) ; file in fib
        move.l  d0,d2           ; save return code
        move.l  d5,d1
        jsr     _LVOUnLock(a6)
        tst.l   d0
        beq.s   warn            ; warning, then back to main
okok:   move.l  fib_Protection(a4),d2
        cmp.b   #'+',d7         ; set or clear dem bits
        bne.s   orem
        and.b   d6,d2
        bra.s   dothedeed
orem:   or.b    d6,d2
dothedeed:
        move.l  a3,d1           ; filename
        jsr     _LVOSetProtection(a6)
        tst.l   d0
        bne.s   main            ; if ok, loop, otherwise, fall through to
        ;
        ; print warning message, and loop back to main.
        ;
warn:   lea     warning(pc),a0
        bsr.s   writeout
        move.l  a3,a0           ; current filename to a0
        bsr.s   writeout        ; write filename
        lea     lf(pc),a0
        bsr.s   writeout        ; linefeed and return to caller
        bra.s   main            ; and loop back to main
        ;
        ; Some body did something wrong, so give them a usage message.
        ;
usage:  lea     usemsg(pc),a0
        bsr.s   writeout
        moveq.l #0,d4           ; set return code and fall through to done.
        ;
        ; done - enter with d4 = return code.
        ; Deallocate resources, and exit.
        ;
done:   ; Enter here if got FIB and Doslib ok.
        ;
        move.l  a6,a1
        move.l  _AbsExecBase,a6
        jsr     _LVOCloseLibrary(a6)
nodos:  ;
        ; Enter here if FIB but no dos
        move.l  a4,a1
        move.l  #FIB_SIZE,d0
        jsr     _LVOFreeMem(a6)
        ;
        ; Enter here if everything went wrong
        ;
gone:   move.l  a5,sp           ; restore stack pointer
        move.l  d4,d0           ; set return code
        rts
        ;
        ;
        ; enter with pointer to null terminated string in A0
        ; Trashes d0-d3/a0
writeout:
        move.l  a0,d2           ; set string pointer for Write()
        move.l  #-1,d3
cnt:    addq.l  #1,d3
        tst.b   (a0)+
        bne.s   cnt     
        jsr     _LVOOutput(a6)
        move.l  d0,d1
        jmp     _LVOWrite(a6)

usemsg:         dc.b    "Usage: chmod [=] [+] [-] [r][w][e][d] file(s)",10,0
warning:        dc.b    "Can't change permission(s) for '",0
lf:             dc.b    "'.",10,0
DOSName:        dc.b    "dos.library",0
