@q
@program cmd-finger
1 9999 d
i
( cmd-finger    v1.3   Jessy @ FurryMUCK   9/96
  
  cmd-finger is a player information utility, in the vein to 'pinfo'
  or '+finger'. 
  
  Four fields are automatically supported: Home, Email, IC Profile, and 
  OOC Profile. Players may define additional fields. Wizards and the owner 
  of the program may set parameters controlling the maximum number of lines 
  for each field, the maximum number of player-defined fields, and MPI 
  parsing. Notifications can be turned on and off.
  
  
  Installation:
  
  Create a global action and link it two this program. cmd-finger
  requires the list editor libraries lib-lmgr, lib-editor, and lib-strings.
  Each should already be installed on any established MUCK. The program
  requires Mucker level 3 if MPI parsing is enabled; otherwise it requires
  Mucker level 2.
  
  The help screen and some program output incorporates the command name 
  or the *first* alias in the command name, so the action should be
  named with this in mind. If the primary name of the command is
  'finfo', but you want to support '+finger' as well, name the command
  'finfo;+finger' rather than '+finger;finfo'.
  
  Use:
  
    <action> <player>      Show information for <player>
    <action> #edit         Edit an information field.
    <action> #delete       Delete an information field.
    <action> #tell         Receive notifications of <action> use.
    <action> #!tell        Turn off notifications.
    <action> #format       Set format for notifications.
    <action> #parameters   Display current program parameters.
    <action> #configure    Set program parameters. <wiz & owner only>
    <action> #defaults     Reset to default parameters. <wiz & owner only>
  
  cmd-finger.muf may be freely ported. Please comment any changes.
)
  
$include $lib/lmgr
$include $lib/editor
$include $lib/strings
  
$def LMGRgetcount lmgr-getcount
$def LMGRgetrange lmgr-getrange
$def LMGRputrange lmgr-putrange
$def LMGRdeleterange lmgr-deleterange
  
$define Tell me @ swap notify $enddef
 
lvar ourString                                      (* input from keyboard *)
lvar ourPlayer                                     (* player being checked *)
lvar counter                                  (* loop-controlling variable *)
lvar scratch                                  (* loop-controlling variable *)
  
: pad  ( s i -- s )                        (* pad a string to i characters *)
    
    swap "                                                                 "
    strcat
    swap strcut pop
;
  
: ParseThis  ( d s -- s )             (* return d's prop s, parsed for MPI *)
    
    dup 3 pick swap getpropstr 0 parseprop
;
   
: MainName  ( s --  )               (* return name of trig, or first alias
                                        if the name includes multiple alia *)
    
    trig name dup ";" instr dup if
        1 - strcut pop
    else
        pop
    then
;
  
: SayPose  (  --  )             (* scan keyboard input for poses and says. *)
                                      (* emit poses and says, and continue *)
  
    begin                                     (* BEGIN INPUT-SCANNING LOOP *)
 
        read    (* does input begin with " or say ? ; say if so & 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 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 Tell
            continue
        then
        exit                              (* it's not a pose or say; exit *)
        
   repeat                                      (* END INPUT-SCANNING LOOP *)
;
  
(**************************************************************************
 *             Next three functions are borrowed from cmd-lsedit          *
 **************************************************************************)
  
: LMGRdeletelist
    over over LMGRgetcount
    1 4 rotate 4 rotate LMGRdeleterange
;
  
: LMGRgetlist
    over over LMGRgetcount
    rot rot 1 rot rot
    LMGRgetrange
; 
  
: lsedit-loop             (* listname dbref {rng} mask currline cmdstr -- *)
    EDITORloop
    dup "save" stringcmp not if
        pop pop pop pop
        3 pick 3 + -1 * rotate
        over 3 + -1 * rotate
        dup 5 + pick over 5 + pick
        over over LMGRdeletelist
        1 rot rot LMGRputrange
        4 pick 4 pick LMGRgetlist
        dup 3 + rotate over 3 + rotate
        "< List saved. >" .tell
        "" lsedit-loop exit
    then
    dup "abort" stringcmp not if
        "< list not saved. >" .tell
        pop pop pop pop pop pop pop pop pop exit
    then
    dup "end" stringcmp not if
        pop pop pop pop pop pop
        dup 3 + rotate over 3 + rotate
        over over LMGRdeletelist
        1 rot rot LMGRputrange
        "< list saved. >" .tell exit
    then
;
  
(**************************************************************************)
 
: GetMax  ( s -- i )              (* return maximum # of lines for field s *)
    
                                            (* match field; return # lines *)
    dup "home" smatch if
        trig "_max_home" getpropstr dup if
            atoi
        else
            2
        then swap pop exit
    then
    
    dup "email" smatch if
        trig "_max_email" getpropstr dup if
            atoi
        else
            pop 1
        then swap pop exit
    then
    
    dup "ic" smatch if
        trig "_max_ic" getpropstr dup if
            atoi
        else
            pop 10
        then swap pop exit
    then
    
    dup "ooc" smatch if
        trig "_max_ooc" getpropstr dup if
            atoi
        else
            pop 10
        then swap pop exit
    then
    
    dup "other" smatch if
        trig "_max_other" getpropstr dup if
            atoi
        else
            pop 4
        then swap pop exit
    then
;
  
: MakeField  ( s -- i )          (* set props to handle user-defined field *)
                (* return false if user .quits or has reached _max_ofields *)
    pop
    
    0 scratch !                         (* check: alread has _max_ofields? *)
    me @ "_prefs/finfo/ofields/" nextprop counter !
    begin
         counter @ while
         scratch @ 1 + scratch !
         me @ counter @ nextprop counter !
    repeat
    trig "_max_ofields" getpropstr dup if
        atoi
    else
        pop 3
    then
                                      (* tell and abort if too many fields *)
    scratch @ <= if
        ">>  Creating a new field would put you over the maximum number "
        "of allowed user-defined fields." strcat Tell
        ">>  Entry aborted." Tell 0 exit
    then
    
    ">> What is the title of this field?" Tell
    ">> [Enter name of field, or .q to quit.]" Tell 
    SayPose strip
                                                  (* check: wants to quit? *)
    dup ".q" smatch if
        ">>  Entry aborted." Tell 0 exit
    then
                                      (* include name of field in /ofields *)
    dup "_prefs/finfo/ofields/" over strcat me @ swap 3 pick setprop
    
                                                         (* make list prop *)
    "_prefs/finfo/" swap strcat
    swap pop 1
;
  
: EditList  ( s --  )                        (* edit new or existing field *)
    
    pop                                               (* get name of field *)
    begin
        ">>  What field do you want to edit?" Tell
        ">> [Enter 'home', 'email', 'ic', 'ooc', 'other', or .q to quit.]" 
        Tell
    
        SayPose strip
    
        dup ".q" smatch if
            ">>  Entry aborted." Tell exit
        then
    
        dup "home"   swap stringpfx if "home"  break else
        dup "email"  swap stringpfx if "email" break else
        dup "ic"     swap stringpfx if "ic"    break else
        dup "ooc"    swap stringpfx if "ooc"   break else
        dup "other"  swap stringpfx if "other" break else
            ">>  Entry not understood." Tell continue    
        then then then then then
    repeat
                                                  (* get max allowed lines *)
    dup GetMax swap
    
                                                       (* set up list name *)
    dup "other" smatch if
        MakeField not if
            exit
        then
    else
        "_prefs/finfo/" swap strcat
    then
    
    dup ourString !
    
                                                     (* warn of max limits *)
    "*********************** Maximum lines this field: " 
    3 pick intostr strcat
    " ******************************" strcat 78 strcut pop Tell
    "********************** Maximum line length: 78 chars"  
    " ******************************" strcat 78 strcut pop Tell
    
                                                           (* enter editor *)
"<    Welcome to the list editor.  You can get help by entering '.h'     >"
    Tell
"< '.end' will exit and save the list.  '.abort' will abort any changes. >"
    Tell
"<    To save changes to the list, and continue editing, use '.save'     >"
    Tell
    
    me @
    over over LMGRgetlist
    "save" 1 ".i $" lsedit-loop
    
                                 (* check: more than max lines? Warn if so *)
    "1" counter !
    ourString @ "#/" strcat ourString !
    begin
        me @ ourString @ counter @ strcat getprop while
        counter @ atoi 1 + intostr counter !
    repeat
    counter @ atoi 1 - < if
        ">>  Warning: Entry is longer than maximum, and will not be "
        "displayed completely." strcat Tell
    then
    
                            (* check: more than max characters? Warn if so *)
    0 scratch !
    "1" counter !
    begin
        me @ ourString @ counter @ strcat getprop dup while
        strlen 78 > if
            1 scratch !
        then
        counter @ atoi 1 + intostr counter !
    repeat
    
    scratch @ if
        ">>  Warning: Some lines are longer than 78 characters, "
        "and will not be displayed completely." strcat Tell
        ">>  Suggestion: Use '" MainName strcat " #edit' to edit " strcat
        "the field, and enter '.format 1 "
        counter @ "1" smatch not if 
            counter @ atoi 1 - intostr strcat
        then
        " = 78' to re-format the list." strcat strcat Tell
    then
;
  
: ShowList  ( i s --  )                   (* show list s from lines 1 to i *)
    
    "1" counter !
    "#/" strcat
    begin
        dup counter @ strcat ourPlayer @ swap over over
        getpropstr while
        trig "_no_mpi" getpropstr if
            getpropstr 
        else
            ParseThis
        then 78 strcut pop Tell
        counter @ atoi 1 + dup 4 pick > if
            "pop me!" break
        then
        intostr counter !
    repeat
    pop pop pop pop
;
  
: DoHelp  ( s --  )                                    (* show help screen *)
    
    pop 
    " " Tell
    "cmd-finger  (#" prog intostr strcat ")" strcat Tell
    " " Tell
    "  " MainName strcat ourString !
    ourString @
    " <player>        Show information for <player>." strcat Tell
    ourString @
    " #edit           Edit an information field." strcat Tell
    ourString @
    " #delete         Delete an information field." strcat Tell
    ourString @
    " #tell           Receive notifications of " MainName strcat " use."
    strcat strcat Tell
    ourString @
    " #!tell          Turn off notifications." strcat Tell
    ourString @
    " #format         Set format for notifications." strcat Tell
    ourString @
    " #parameters     Display current program parameters." strcat Tell
    me @ "W" flag?
    me @ prog owner dbcmp or if
        ourString @
        " #configure      Set program parameters." strcat Tell
        ourString @
        " #defaults       Reset to default parameters." strcat Tell
    then
    " " Tell
    "Settings are stored on your character in directory _prefs/finfo/." Tell
;
  
: DoParams  ( s --  )                   (* show current program parameters *)
    
    pop " " Tell
    "Current Program Parameters: " Tell
    " " Tell
    "Fields: " Tell
    
    "  Home (max: "
    trig "_max_home" getpropstr dup not if
        pop "2"
    then
    dup "1" smatch if
        " line)" else
            " lines)"
    then strcat strcat Tell
    
    "  Email (max: "
    trig "_max_email" getpropstr dup not if
        pop "1"
    then
    dup "1" smatch if
        " line)" else
            " lines)"
    then strcat strcat Tell
    
    "  IC Profile (max: "
    trig "_max_ic" getpropstr dup not if
        pop "10"
    then
    dup "1" smatch if
        " line)" else
            " lines)"
    then strcat strcat Tell
    
    "  OOC Profile (max: "
    trig "_max_ooc" getpropstr dup not if
        pop "10"
    then
    dup "1" smatch if
        " line)" else
            " lines)"
    then strcat strcat Tell
    
    " " Tell
    "User-Defined Fields:" Tell
    "  Maximum number of fields: "
    trig "_max_ofields" getpropstr dup not if
        pop "3"
    then
    strcat Tell
    "  Maximum lines per field: "
    trig "_max_other" getpropstr dup not if
        pop "4"
    then strcat Tell
    
    " " Tell
    "MPI Parsing: "
    trig "_no_mpi" getpropstr if
        "no"
    else
        "yes"
    then strcat Tell
;
    
: DoDefaults  ( s --  )   (* remove parameter props, restoring to defaults *)
    
    pop
    trig "_max_home" remove_prop
    trig "_max_email" remove_prop
    trig "_max_ic" remove_prop
    trig "_max_ooc" remove_prop
    trig "_max_other" remove_prop
    trig "_max_ofields" remove_prop
    trig "_no_mpi" remove_prop
    
    ">>  Default settings restored." Tell
;
  
: DoConfigure  ( s --  )   (* read from keyboard to set program parameters *)
                    (* loops continue when user enters an invalid response *)
    
    pop                                                (* check permission *)
    me @ "W" flag?
    me @ prog owner dbcmp or not if
        ">>  permission denied." Tell exit
    then
    
    begin
        ">>  Maximum number of lines for Home field? (currently "
        trig "_max_home" getpropstr dup not if
            pop "2"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_home" rot setprop break
    repeat
    
    begin
        ">>  Maximum number of lines for Email field? (currently "
        trig "_max_email" getpropstr dup not if
            pop "1"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_email" rot setprop break
    repeat
    
    begin
        ">>  Maximum number of lines for IC Profile field? (currently "
        trig "_max_ic" getpropstr dup not if
            pop "10"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_ic" rot setprop break
    repeat
    
    begin
        ">>  Maximum number of lines for OOC Profile field? (currently "
        trig "_max_ooc" getpropstr dup not if
            pop "10"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_ooc" rot setprop break
    repeat
    
    begin
        ">>  Maximum number of other fields? (currently "
        trig "_max_ofields" getpropstr dup not if
            pop "3"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_ofields" rot setprop break
    repeat
    
    begin
        ">>  Maximum number of lines for other fields? (currently "
        trig "_max_other" getpropstr dup not if
            pop "4"
        then
        strcat ")" strcat Tell
        ">> [Enter number, or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup number? not if
           pop ">> That's not a number." Tell continue
        then
        trig "_max_other" rot setprop break
    repeat
    
    begin
        ">>  Should the program parse MPI? (current setting: "
        trig "_no_mpi" getpropstr if
            "no"
        else
            "yes"
        then
        strcat ")" strcat Tell
        ">>  Enter 'yes', 'no', or .q to quit]" Tell
        SayPose strip
        dup ".q" smatch if
            ">>  Done." Tell pop exit
        then
        dup "yes" swap stringpfx
        over "no" swap stringpfx or not if
           pop ">> Entry not understood." Tell continue
        then
        "no" swap stringpfx if
            trig "_no_mpi" "yes" setprop
        else
            trig "_no_mpi" remove_prop
        then break
    repeat
    
    ">>  Done." Tell
;
  
: Tell-On  ( s --  )  (* remove notify: player will receive notifications *)
    
    pop
    me @ "_prefs/finfo/notify" "yes" setprop
    ">>  You will now be notified when "
    MainName strcat
    " is used on you." strcat Tell
;
  
: Tell-Off  ( s --  )(* set notify: player will not receive notifications *)
    
    pop
    me @ "_prefs/finfo/notify" remove_prop
    ">>  You will not be notified when "
    MainName strcat
    " is used on you." strcat Tell
;
   
: DoDelete ( s --  )                              (* delete indicated list *)
    
    pop 
    ">>  What field do you want to delete?" Tell
                                        (* make list of valid fields; tell *)
    ">> [Fields: home email ic ooc"
    me @ "_prefs/finfo/ofields/" nextprop dup if 
        scratch !
        begin                                  (* BEGIN FIELD-LISTING LOOP *)
            scratch @ while
            " " strcat
            me @ scratch @ getpropstr strcat
            me @ scratch @ nextprop scratch !
        repeat                                   (* END FIELD-LISTING LOOP *)
    then
    "]" strcat Tell    
    ">> [Enter name of field, or .q to quit.]" Tell
    Saypose strip
                                                  (* check: wants to quit? *)
    dup ".q" smatch if
        ">>  Done." Tell pop exit
    then
                                                    (* store name of field *)
    ourString !
                                             (* remove field from ofields/ *)
    me @ "_prefs/finfo/ofields/" ourString @ strcat remove_prop
    
                                                            (* remove list *)
    "_prefs/finfo/" ourString @ strcat 
    dup "#" strcat me @ swap remove_prop
    "/" strcat ourString !
    "1" counter !
    begin
        me @ ourString @ counter @ strcat over over
        getpropstr while
        remove_prop
        counter @ atoi 1 + intostr counter !
    repeat
    pop pop
    
    ">>  Done." Tell
;
  
: DoFormat  ( s --  )                        (* define notification format *)
    
    pop                                               (* give instructions *)
    ">>  The string '<name>' will be replaced by the triggering "
    "player's name." strcat Tell
    trig "_no_mpi" getpropstr if
       ">> O"
       else
            ">>  MPI and o"
    then
    "ther substitution strings may be included as well." strcat Tell
    ">> [Enter format, 'clear' to remove previous format, or .q to quit.]" 
    Tell
    
    SayPose strip
                                                  (* check: wants to quit? *)
    dup ".q" smatch if
        ">>  Entry aborted." Tell exit
    then
                                                 (* check: wants to clear? *)
    dup "clear" smatch if
        me @ "_prefs/finfo/tformat" remove_prop
        ">>  Format cleared." Tell exit
    then
                                                             (* set format *)
    me @ "_prefs/finfo/tformat" rot setprop
    ">>  Set." Tell
;
 
: Finfo  ( s --  )                        (* show info for selected player *)
    
                                     (* match player; store in 'ourPlayer' *)
    .pmatch dup not if
        pop ourString @
        1 strcut swap toupper swap strcat 
        " is not a player here." strcat Tell exit
    else
        ourPlayer !
    then
                                       (* notify checked player if desired *)
    ourPlayer @ "_prefs/finfo/notify" getpropstr if
        ourPlayer @ "_prefs/finfo/tformat" getpropstr if
            ourPlayer @ "_prefs/finfo/tformat" 
            trig "_no_mpi" getpropstr if
                getpropstr
            else
                ParseThis
            then
            me @ name "<name>" subst
            me @ swap pronoun_sub
            ourPlayer @ swap notify
        else
            "+++ " me @ name strcat
            " " strcat MainName strcat "'d you. +++" strcat
            ourPlayer @ swap notify
        then
    then
   
                                                        (* List basic data *)
    ourPlayer @ name Tell
   
"----------------------------------------------------------------------------"
    Tell
    
    ourPlayer @ "species" ParseThis dup not if
        pop "Species unknown"
    then
    23 Pad
    
    ourPlayer @ "sex" ParseThis dup not if
        pop ourPlayer @ "gender" getpropstr dup not if
           pop "Gender unknown" 
        then
    then
    strcat 38 Pad
    
    ourPlayer @ "_/do" getpropstr strcat 78 pad Tell
    
                                                            (* list fields *)
    ourPlayer @ "_prefs/finfo/home#/1" getpropstr if
        "~" Tell "Home: " Tell
        "home" GetMax "_prefs/finfo/home" ShowList
    then
    
    ourPlayer @ "_prefs/finfo/email#/1" getpropstr if
        "~" Tell "Email: " Tell
        "email" GetMax "_prefs/finfo/email" ShowList
    then
    
    ourPlayer @ "_prefs/finfo/ic#/1" getpropstr if
        "~" Tell "IC Profile: " Tell
        "ic" GetMax "_prefs/finfo/ic" ShowList
    then
    
    ourPlayer @ "_prefs/finfo/ooc#/1" getpropstr if
        "~" Tell "OOC Profile: " Tell
        "ooc" GetMax "_prefs/finfo/ooc" ShowList
    then
                                           (* displacy user-defined fields *)
                            (* var ourString is enlisted as a loop-counter *)
    ourPlayer @ "_prefs/finfo/ofields/" nextprop dup if
        trig "_max_ofields" getpropstr dup not if
            pop "3"
        then 
        atoi ourString !
        scratch !
        begin
            scratch @ while
            ourString @ while
            ourPlayer @ scratch @ getpropstr
            "~" Tell dup ": " strcat Tell 
            "_prefs/finfo/" swap strcat
            "other" GetMax swap ShowList
            ourPlayer @ scratch @ nextprop scratch !
            ourString @ 1 - ourString !
        repeat
    then
;
  
: main
    
   "me" match me !
   strip dup ourString !
   dup if
       dup "#*" smatch if
           dup "#help"       swap stringpfx if DoHelp exit         else
           dup "#edit"       swap stringpfx if EditList exit       else
           dup "#delete"     swap stringpfx if DoDelete exit       else
           dup "#tell"       swap stringpfx if Tell-On exit        else
           dup "#!tell"      swap stringpfx if Tell-Off exit       else
           dup "#format"     swap stringpfx if DoFormat exit       else
           dup "#parameters" swap stringpfx if DoParams exit       else
           dup "#configure"  swap stringpfx if DoConfigure exit    else
           dup "#defaults"   swap stringpfx if DoDefaults exit     else
           dup "#" stringpfx if 
               "Sorry, this program doesn't know how to " swap 1 strcut
               swap pop strcat "." strcat Tell exit else
           then then then then then then then then then then
       then
   then
   
   dup not if
       DoHelp exit
   then
   
   Finfo
;
.
c
q