@q
@program lib-mucktools
1 9999 d
i
( Lib-MuckTools   v1.0    Jessy @ FurryMUCK    3/97
  
  This is the library of shared code for the MuckTools programs.
  Most functions are very general; the library may be useful for
  anyone needing to handle lists and directories, read input, or 
  format output. NOTE: This library works, but really isn't mature
  at present... things currently handled by variables need to be
  handled on the stack, and the directory-handling functions need
  to be made recursive.
  
  INSTALLATION:
  
  Port the program, and set it Link_OK. Do not set the program 
  Wizard: players would easily be able to write to restricted
  lists and properties.
  
  Link a temporary action to the program and type the action name.
  This installs the program, setting _defs, _doc, and the global
  registered name. Recycle the install action.
  
  PUBLIC FUNCTIONS:
  
  3-col      [ {rng} i --  ]
    Outputs the top i things on the stack in 3 columns, formatted
    to 72 char screen width. The top item on the stack is shown
    last. Strings are shown as-is; integers are converted to strings;
    #dbrefs are converted to names.
    
  3-coln     [ {rng} i --  ]
    Numbers and outputs the top i things on the stack in 3 columns,
    formatted to 72 char screen width. The top item on the stack is
    shown last. Strings are shown as-is; integers are converted to
    strings; #dbrefs are converted to names.
    
  A-An       [ s -- s' ]
    Returns s prepended with 'an' if s begins with a vowel; otherwise,
    prepends with 'a'.
    
  Pad        [ s i -- s' ]
    Returns s padded to i chars, spaces right.
    
  LPad       [ s i -- s' ]
    Returns s padded to i chars, spaces left.
    
  Dots       [ s i -- s' ]
    Returns s padded with dot leader to i chars, dots right.
    
  LDots      [ s i -- s' ]
    Returns s padded with dot leader to i chars, dots left.
    
  SayPose    [   -- s ]
    Performs READ, scanning for says and poses. Says and poses
    are output; returns first s that is not a say or pose. Output
    to user is appended with >. The .muf extension
    is trimmed from the program name.
    
  QCheck     [ s -- s ]
    Kills process if s is '.quit', '.end', or a prefix of these. 
    Notifies user with string ">>  Done."
    
  ReadYesNo  [   -- s ]
    Reads via SayPose, returning true for 'yes' or a prefix of 'yes',
    and false for 'no' or 'n'. Kills process for '.quit', '.end', or
    or a prefix of these.
    
  NoPlayer   [ s --  ]
    Notifies user that no player with name s was found, then kills
    process.
    
  MainName   [ s -- s ]
    Return s, stripped of alias forms. 'Out <0>;out;ou;o' would return
    'Out '.
    
  ParseThis  [ d s --  ]
    Returns d's prop s, parsed for MPI.
    
  RemoveDir  [ d s --  ]
    Removes directory s from d.
    
  RemoveList [ d s --  ]
    Removes list s from d.
    
  MoveDir    [ d s d' s' --  ]
    Moves directory s on d to directory s' on d'. NOTE: MoveDir
    is currently not recursive: subdirectories and dirs with
    subdirs are efficiently moved into oblivion.
    
  Lib-MuckTools may be freely ported. Please comment any changes.
)
  
lvar libBoolean
lvar libString
lvar libCounter
lvar libCounter2
lvar libScratch
  
$define Tell me @ swap notify $enddef
$define counter++ libCounter @ 1 + libCounter ! $enddef
$define scounter++ libCounter @ atoi 1 + intostr libCounter ! $enddef
      
: Pad  ( s i -- s )                         (* pad string s to i chars *)
                             (* Pads are often done in loops; use the
                                cheaper but limited-length method of
                                cat/cutting a string, rather than loop *)
    swap
    "                                                                   "
    strcat
    swap strcut pop
;
Public Pad
  
: LPad  ( s i -- s )           (* pad string s to i chars, spaces left *)
    swap
    "                                                                   "
    swap strcat dup strlen rot - strcut swap pop
;
Public LPad
   
: Dots  ( s i -- s )          (* pad string s with dot leader, i chars *)
   
    swap
    "..................................................................."
    strcat
    swap strcut pop
;
Public Dots
  
: LDots  ( s i -- s ) (* pad string s with dot leader to left, i chars *)
    swap
    "..................................................................."
    swap strcat dup strlen rot - strcut swap pop
;
Public LDots
  
: A-An  ( s -- s' )               (* return s prepended w/ 'a' or 'an' *)
    
    dup 1 strcut pop "{a|e|i|o|u}" smatch if
        "an " swap strcat
        else
            "a " swap strcat
    then
;   
Public A-An
  
: ParseThis  ( d s -- s )        (* returns d's prop s, parsed for MPI *)
 
   dup 3 pick swap getpropstr 0 parseprop
;
Public ParseThis
  
: SayPose  (  --  )         (* scan keyboard input for poses and says. *)
                                  (* emit poses and says, and continue *)
 
    begin                                 (* BEGIN INPUT-SCANNING LOOP *)
           (* does input begin with " or say ? -- say if so & continue *)
        read 
                                  (* emit poses and says, and continue *)
        dup "\"" stringpfx
        over "say " stringpfx or if
            dup "say " stringpfx if
                4 strcut
                else
                    1 strcut
            then swap pop
            me @ name " says, \"" strcat swap strcat "\"" strcat dup
            loc @ me @ rot notify_except
            
                                             (* tack on an 'in program' 
                                                note for the player    *)
            " (in " strcat
            caller name dup "*.muf" smatch if
                dup strlen 4 - strcut pop
            then
            strcat ")" strcat Tell
            continue        
    
        then
 
         (* does input begin with : or pose ? -- pose if so & continue *)
        dup ":" stringpfx
        over "pose " stringpfx or if
            dup "pose " stringpfx if
                5 strcut
                else
                    1 strcut
            then swap pop
            me @ name
            over "'*" smatch not if
                " " strcat
            then
            swap strcat dup
            loc @ me @ rot notify_except
            " (in " strcat
            caller name dup "*.muf" smatch if
                dup strlen 4 - strcut pop
            then
            strcat ")" strcat Tell
            continue
        then
        exit                           (* it's not a pose or say; exit *)
   repeat                                   (* END INPUT-SCANNING LOOP *)
;
Public SayPose
  
: QCheck  (  -- i )(* wrap smatch for .q in an if, to avoid null string
                      match error if user enters a string of all spaces,
                      which SayPose would strip to a null string       *)
    dup if
        dup ".quit" swap stringpfx 
        over ".end" swap stringpfx or if
            pop ">>  Done." Tell pid kill
        then
    then
;
Public QCheck
  
: ReadYesNo  (  -- i )            (* read user input; return 1 for 'yes', 
                                     2 for 'no'; kill process for .quiut *)
    begin
        SayPose strip QCheck
    
        dup "yes" swap stringpfx if
            pop 1 break
            else
                "no" swap stringpfx if
                    0 break
                then
        then
        ">>  Entry not understood." Tell
    repeat
;
Public ReadYesNo
  
: NoPlayer  ( s --  )          (* notify: player s not found. kill job *)
    
    ">>  Player " swap 1 strcut swap toupper swap 
    strcat strcat " not found." strcat
    pid kill
;
Public NoPlayer
  
: MainName  ( s -- s )              (* strips aliases from a trigger name; 
                                      used here to format help screen    *)
                                       
                (* exit if no aliases; else separate and record how many *)
    ";" explode dup 1 = not if  
        libCounter !
        else
           pop exit
    then
                                     (* loop, popping an alias each time *)
    begin                                     (* BEGIN NAME-POPPING LOOP *)
        libCounter @ 1 = if
            break                          (* break, returning main name *)
        then
        swap pop
        libCounter @ 1 - libCounter !
    repeat                                      (* END NAME-POPPING LOOP *)
;
Public MainName
   
: RemoveList  ( d s --  )                      (* remove list s from d *)
    
    "#" strcat libString ! libScratch !
    libScratch @ libString @ remove_prop
    libString @ "/" strcat libString !
    
    "1" libCounter !
    begin
        libScratch @ libString @ libCounter @ strcat over over
        getpropstr while
        remove_prop
        scounter++
    repeat
    pop pop
;
Public RemoveList
  
: RemoveDir  ( d s --   )                      (* remove list s from d *)
    
    libString ! libScratch !
    libScratch @ libString @ remove_prop
    libString @ "/" strcat libString !
    
    "1" libCounter !
    begin
        libScratch @ libString @ libCounter @ strcat over over
        getpropstr while
        remove_prop
        scounter++
    repeat
    pop pop
;
Public RemoveDir
  
: ShowList  ( d s --  )                    (* show list s on d to user *)
    
    "#/" strcat libString ! libScratch !
    "1" libCounter !
    begin
        libScratch @ libString @ libCounter @ strcat 
        getpropstr dup while
        Tell
        scounter++
    repeat
    pop
; 
Public ShowList
    
: MoveDir  ( d s d' s' --  )                  (* move directory s on d
                                                 to directory s' on d' *)
    
    libCounter2 ! libString ! libCounter ! libScratch !
    libScratch @ libCounter @ nextprop libCounter !
    begin
        libCounter @ while
        libScratch @ libCounter @ getprop
        libCounter @ dup "/" rinstr dup if
            strcut libCounter2 @ swap strcat swap pop
            else
                pop pop
        then
        swap libString @ rot rot setprop
        libScratch @ libCounter @ over over
        nextprop libCounter ! 
        remove_prop
    repeat
;
Public MoveDir
  
: MakeString  ( x -- s )                     (* convert i's to strings; 
                                                convert d's to names   *)
    dup int? if intostr exit then
    dup dbref? if name then
;
  
: 3-col  ( {rng} i --  )            (* output the i top things on the 
                                       stack in 3 columns; top item on 
                                       stack will be shown last        *)
    
    dup 3 %                                   (* fill to multiple of 3 *)
    dup 1 = if
        pop 2 + " " " " rot
        else
        dup 2 = if
            pop 1 + " " swap
            else
                pop
        then
    then
    begin                                  (* get next 3; format; show *)        
        dup 3 > while
        dup 1 + rotate swap
        dup 1 + rotate swap
        dup 1 + rotate swap
        4 rotate MakeString 24 Pad 
        4 rotate MakeString 24 Pad strcat 
        3 rotate MakeString 24 Pad strcat me @ swap notify
        3 -
    repeat
    pop
                                             (* format and show last 3 *)
    rot 24 Pad rot 24 Pad strcat swap strcat me @ swap notify
;
public 3-col
  
: 3-coln  ( {rng} i --  )    (* output the top i things on the stack in
                                3 columns of numbered items; top thing
                                on the stack will be shown last        *)
    
    dup 3 %                                   (* fill to multiple of 3 *)
    dup 1 = if
        pop 2 + " " " " rot
        else
        dup 2 = if
            pop 1 + " " swap
            else
                pop
        then
    then
    1 swap
    begin                                (* get next 3; format; output *)
        dup 3 > while
        dup 2 + rotate rot rot
        dup 2 + rotate rot rot
        dup 2 + rotate rot rot
        5 rotate 3 pick intostr ")" strcat 4 Pad swap 
        MakeString strcat 24 Pad
        3 pick 1 + 3 put
        5 rotate 4 pick intostr ")" strcat 4 Pad swap 
        MakeString strcat 24 Pad strcat 
        3 pick 1 + 3 put
        4 Rotate 4 pick intostr ")" strcat 4 Pad swap 
        MakeString strcat 24 Pad strcat 
        3 pick 1 + 3 put
        me @ swap notify
        3 -
    repeat
                                            (* format and outpu last 3 *)
    pop 4 rotate 4 rotate 4 rotate swap rot
    dup " " smatch not if
        4 pick intostr ")" strcat 4 Pad swap 
        MakeString strcat 24 Pad 
        4 pick 1 + 4 put    
        else
            pop pop pop pop exit
    then
    
    over " " smatch not if
        4 pick intostr ")" strcat 4 Pad rot
        MakeString strcat 24 pad strcat
        3 pick 1 + 3 put
        else
            me @ swap notify pop pop pop exit
    then
    
    over " " smatch not if
        rot intostr ")" strcat 4 Pad strcat swap
        MakeString strcat me @ swap notify
        else
            me @ swap notify pop pop exit
    then
;
public 3-coln
  
: DoInstall
    
    #0 "_reg/lib/mucktools" prog setprop 
    
    prog "_defs/Pad"      
    "\"$lib/mucktools\" match \"Pad\" call"            setprop
    prog "_defs/LPad"     
    "\"$lib/mucktools\" match \"LPad\" call"           setprop
    prog "_defs/Dots"     
    "\"$lib/mucktools\" match \"Dots\" call"           setprop
    prog "_defs/LDots"    
    "\"$lib/mucktools\" match \"LDots\" call"          setprop
    prog "_defs/A-An"     
    "\"$lib/mucktools\" match \"A-An\" call"           setprop
    prog "_defs/ParseThis"     
    "\"$lib/mucktools\" match \"ParseThis\" call"      setprop
    prog "_defs/SayPose"  
    "\"$lib/mucktools\" match \"SayPose\" call"        setprop
    prog "_defs/QCheck"   
    "\"$lib/mucktools\" match \"QCheck\" call"         setprop
    prog "_defs/ReadYesNo" 
    "\"$lib/mucktools\" match \"ReadYesNo\" call"      setprop
    prog "_defs/NoPlayer" 
    "\"$lib/mucktools\" match \"NoPlayer\" call"       setprop
    prog "_defs/MainName"
    "\"$lib/mucktools\" match \"MainName\" call"       setprop
    prog "_defs/ShowList"
    "\"$lib/mucktools\" match \"ShowList\" call"       setprop
    prog "_defs/ReadLine"
    "\"$lib/mucktools\" match \"SayPose\" call"        setprop
    prog "_defs/RemoveList"     
    "\"$lib/mucktools\" match \"RemoveDir\" call"      setprop
     prog "_defs/RemoveDir"     
    "\"$lib/mucktools\" match \"RemoveDir\" call"      setprop
    prog "_defs/MoveDir"    
    "\"$lib/mucktools\" match \"MoveDir\" call"        setprop
    prog "_defs/3-col" 
    "\"$lib/mucktools\" match \"3-col\" call"          setprop
    prog "_defs/3-coln"
    "\"$lib/mucktools\" match \"3-coln\" call"         setprop
    
    prog "_docs" "@list $lib/mucktools=1-85"           setprop
    
    trig getlink prog dbcmp if
        ">>  Installed. Please recycle #" trig intostr strcat .tell
    then
;
.
c
q
@set lib-mucktools=L
@reg lib-mucktools=lib/mucktools