[ nwe help ]
Search Help   »  General   »  MOO  »   JHC  »   Core Utilities Help

Core Utilities Help



  $biglist_utils         $building_utils        $code_utils
  $command_utils         $container             $english
  $english_utils         $exit                  $gender_utils
  $generic_db            $generic_editor        $generic_help
  $generic_options       $help                  $housekeeper
  $integration_utils     $list_utils            $lock_utils
  $login                 $mail_agent            $mail_recipient
  $math_utils            $name_utils            $no_one
  $object_utils          $parse_utils           $perm_utils
  $player_db             $quota_utils           $room
  $seq_utils             $set_utils             $string_utils
  $time_utils            $trig_utils            $who_utils
  $wiz_utils             core-index             mail-format
  mail-recipient         mail-resolve           mail-system
  MR-access              MR-naming              MR-reading
  MR-searching           MR-sequences           MR-subscribing
  MR-writing             object-matching        receiving-mail
  sending-mail       


General Information on $biglist_utils

Generic BigList Utilities
----------------------------

This is a package for maintaining huge persistent (sorted) lists in a
format that is less likely to spam the server (which runs into a
certain amount of trouble dealing with long ordinary lists --- btw we
use `biglist' to refer to the huge data structure we're about to
describe and `list' to refer to ordinary MOO lists {...}).  The
biglist in question lives on a particular object, to which we will
refer in the discussion below as the `home' object, and its various
elements appear as leaves of a tree whose nodes are kept in properties
of the home object.  It should be noted that the home object does not
need to be (and in fact should *not* be) a descendant of this one;
this object merely provides utilities for manipulating the properties
on the home object that are used in a particular biglist manipulation.

All of the utilities below refer to `caller' to locate the home
object.  Thus verbs to manipulate a given biglist must be located on
or inherited by its home object itself.  The home object needs to
define the following verbs

  :_make(@args)     => new property on home object with value args
  :_kill(prop)      delete a given property that was created by :_make
  :_get(prop)       => home.prop
  :_put(prop,@args) set home.prop = args
  :_ord(element)    given something that is of the form of a biglist element
                    return the corresponding ordinal (for sorting purposes).
                    If you never intend to use :find_ord, then this can be a 
                    routine that always returns 0 or some other random value.

See #5546 (Generic Biglist Resident) or #6425 (Huge Mail Recipient)
for examples.

Those of the following routines that take a biglist argument are expecting
either {} (empty biglist) or some biglist returned by one of the other routines

  :length(biglist)          => length(biglist) (i.e., number of elements)
  :find_nth(biglist,n)      => biglist[n]
  :find_ord(biglist,k,comp) => n where n is
     the largest such that home:(comp)(k,home:_ord(biglist[n])) is false, or
     the smallest such that home:(comp)(k,home:_ord(biglist[n+1])) is true.
     Always returns a value between 0 and length(biglist) inclusive.
     This assumes biglist to be sorted in order of increasing :_ord values 
     with respect to home:(comp)().
     Standard situation is :_ord returns a number and comp is a < verb.

  :start(biglist,s,e)  => {biglist[s..?],@handle} or {}
  :next(@handle)       => {biglist[?+1..??],@newhandle} or {}
     These two are used for iterating over a range of elements of a biglist
     The canonical incantation for doing
        for elt in (biglist[first..last])
          ...
        endfor
     is
        handle = :start(biglist,first,last);
        while(handle)
          for elt in (handle[1])
            ...
          endfor
          handle = :next(@listdelete(handle,1));
        endwhile

The following all destructively modify their biglist argument(s) L (and M).

  :set_nth(L,n,value)  =>  L[n] = value
     replaces the indicated element

  :insert_before(L,M,n) => {@L[1..n-1],@M,@L[n..length(L)]}
  :insert_after (L,M,n) => {@L[1..n],  @M,@L[n+1..length(L)]}
     takes two distinct biglists, inserts one into the other at the given point
     returns the resulting consolidated biglist

  :extract_range(L,m,n) => {{@L[1..m-1],@L[n+1..]}, L[m..n]} 
     breaks the given biglist into two distinct biglists.

  :delete_range(L,m,n[,leafkiller]) => {@L[1..m-1],@L[n+1..]}
  :keep_range  (L,m,n[,leafkiller]) => L[m..n]
     like extract_range only we destroy what we don't want.

  :insertlast(L,value)  => {@L,value}
     inserts a new element at the end of biglist.  
     If find_ord is to continue to work properly, it is assumed that the 
     home:_ord(elt) is greater (comp-wise) than all of the :_ord values
     of elements currently in the biglist.

  :kill(L[,leafkiller]) 
     destroys all nodes used by biglist.  
     Calls home:leafkiller on each element.


General Information on $building_utils

Verbs useful for building.  For a complete description of a given
verb, do `help $building_utils:verbname'[2].

make_exit(spec,source,dest[,don't-really-create]) => a new exit
          spec is an exit-spec as described in `help @dig'

set_names(object, spec) - sets name and aliases for an object
parse_names(spec) => list of {name, aliases}
          in both of these, spec is of the form
            <name>[[,:]<alias>,<alias>,...]
          (as described in `help @rename')

recreate(object, newparent) - effectively recycle and recreate object
          as a child of newparent
transfer_ownership(object, old-owner, new-owner) - just what it sounds like


General Information on $code_utils

parse_propref("foo.bar")  => {"foo","bar"} (or 0 if arg. isn't a property ref.)
parse_verbref("foo:bar")  => {"foo","bar"} (or 0 if arg. isn't a verb ref.)
parse_argspec("any","in","front","of","this","baz"...)
                          => {{"any", "in front of", "this"},{"baz"...}} 
                                           (or string if args don't parse)

tonum(string)           => number (or E_TYPE if string is not a number)
toobj(string)           => object (or E_TYPE if string is not an object)
toerr(number or string) => error value (or 1 if out of range or unrecognized)
error_name(error value) => name of error (e.g., error_name(E_PERM) => "E_PERM")

verb_perms()    => the current task_perms (as set by set_task_perms()).
verb_location() => the object where the current verb is defined.
verb_documentation([object,verbname]) => documentation at beginning of
           verb code, if any -- default is the calling verb

   Preposition routines

prepositions()     => full list of prepostions
full_prep ("in")   => "in/inside/into"
short_prep("into") => "in"
short_prep("in/inside/into") => "in"
get_prep  ("off", "of", "the", "table") => {"off of", "the", "table"}

   Verb routines

verbname_match (fullname,name) => can `name' be used to call `fullname'
find_verb_named          (object,name[,n]) => verb number or -1 if not found
find_callable_verb_named (object,name[,n]) => verb number or -1 if not found
find_verbs_containing (pattern[,object|objlist])

verbs(object) => (partial) list of verbs on the object

   Verbs that do the actual dirty work for @show:

show_object  (object)
show_property(object,propname)
show_verbdef (object,verbname)

   Dirty work for explain_syntax

explain_verb_syntax(thisname,verbname,@verbargs)

   A random but useful verb

verb_or_property(object,name[,@args]) => result of verb or property call,
                                         or E_PROPNF

   Player manipulation:
connected_players()   =>   returns all actual players connected
players()             =>   returns all actual players


General Information on $command_utils


$command_utils is the repository for verbs that are of general
usefulness to authors of all sorts of commands.

Detecting and Handling Failures in Matching
-------------------------------------------
:object_match_failed(match_result, name)
    Test whether or not a :match_object() call failed and print messages if so.
:player_match_failed(match_result, name)
    Test whether or not a :match_player() call failed and print messages if so.
:player_match_result(match_results, names)
    ...similar to :player_match_failed, but does a whole list at once.
:room_match_failed(match_result, name)
    Test whether or not a :match_room() call failed and print messages if so.

Reading Input from the Player
-----------------------------
:read()         -- Read one line of input from the player and return it.
:yes_or_no([prompt])
                -- Prompt for and read a `yes' or `no' answer.
:read_lines()   -- Read zero or more lines of input from the player.
:dump_lines(lines) 
                -- Return list of lines quoted so that feeding them to 
                   :read_lines() will reproduce the original lines.

Utilities for Suspending
------------------------
:running_out_of_time()
                -- Return true if we're low on ticks or seconds.
:suspend_if_needed(time)
                -- Suspend (and return true) if we're running out of time.

Client Support for Lengthy Commands
-----------------------------------
:suspend(args)  -- Handle PREFIX and SUFFIX for clients in long commands.


$container

The Generic Container (for programmers)

In addition to the command verbs described under `help containers'
and the _msg properties described in `help container-messages',
the following verbs and properties are available for use within programs

.opened == TRUE iff the container is open
.dark   == TRUE iff the contents of the container may be seen
.opaque -- describes the correlation between .open and .dark
   == 0  container is always !dark
   == 1  container is dark iff it is closed
   == 2  container is always dark              

:set_opaque(newvalue)   
  changes the .opaque value for the container
  => newvalue or E_PERM or E_INVARG

:set_opened(newvalue)   
  opens/closes the container (updates .open and .dark) according to newvalue
  => newvalue or E_PERM

:is_openable_by(player) 
 what the :open command uses to test whether the player should be able to open
 the container.  By default this refers to .open_key  (set by
 @(un)lock_for_open), but the object owner is free to customize this.

N.B.:  There is no way to directly set .dark; .dark can be changed only by 
changing one of .opaque or .opened.  Use :set_opaque(0) and :set_opaque(2)
to have .dark change independently of the value of .opened.


$english

An object for storing verbs and properties dealing with the english language.

Properties:

vowels                     {"a", "e", "i", "o", "u"}

[non]vowel_exceptions      A list of prefixes which start with vowels [consonants]
                           but don't take 'an' ['a'] as indefinite articles.

Verbs:

@add-[non]vowel-exc*eption <exception> to #116
    Add an exception to the 'an' before vowel ['a' before consonant] rule.
    Sends mail to *English if the player is not a wizard.


General Information on $english_utils

An object for storing verbs and properties dealing with the english language.

Properties:

vowels                     {"a", "e", "i", "o", "u"}

[non]vowel_exceptions      A list of prefixes which start with vowels [consonants] but
                           don't take 'an' ['a'] as indefinite articles.

Verbs:

@add-[non]vowel-exc*eption <exception> to $english
    Add an exception to the 'an' before vowel ['a' before consonant] rule.
    Sends mail to *English if the player is not a wizard.


$exit

Exits
-----
:move(object)  - moves the object via this exit
:invoke()      - equivalent to :move(player)

When an exit is invoked on a particular object (via exit:move(object)), the following occurs.

(1) The exit may be locked against the object, in which case we print the 
   nogo messages and quit.

(2) (room=exit.dest):bless_for_entry(object) is called.  Assuming that
exit is recognized by room as being a legitimate entrance (i.e., is in
room.entrances), this will enable room:accept(object) to return true.

(3) object:moveto(room) is called and the various messages (see `help
exit-messages'[1]) are :announced/:told.  Note that this, in
accordance with the way the builtin move() (and hence the default
:moveto()) works, we get a call to room:accept(object) which checks
for the room itself being locked against the object, and otherwise
returns true if the blessing in the previous step worked.  The move is
performed, here:exitfunc(object) and room:enterfunc(object) are
called.  In particular, room:enterfunc clears the blessing bestowed in
(2) now that it is no longer needed.

In general, the move may fail, in which case we :announce the
(o)nogo_msgs.

Transparent exits (the default on this MOO) can have extra
substitutions in their description and messages; the new substitutions
let you put information from the exit's destination into the exit's
messages. %c is replaced with a list of the contents of the
destination. %w is a list of the players in the destination. %l is the
description (looks) of the destination. These substitutions don't
conflict with the standard pronoun substitutions; you can still use
them in messages. Example: @describe my_exit as "You see %w standing
in the other room.".


General Information on $gender_utils


Defines the list of standard genders, the default pronouns for each,
and routines for adding or setting pronoun properties on any gendered
object.

Properties:
  .genders  -- list of standard genders
  .pronouns -- list of pronoun properties
  .ps .po .pp .pq .pr .psc .poc .ppc .pqc .prc 
            -- lists of pronouns for each of the standard genders

  If foo is of gender this.gender[n], 
  then the default pronoun foo.p is this.p[n] 
  (where p is one of ps/po/pp/pq...)

Verbs:
  :set(object,newgender) -- changes pronoun properties to match new gender.
  :add(object[,perms[,owner]]) -- adds pronoun properties to object.

  :get_pronoun     (which,object) -- return pronoun for a given object
  :get_conj*ugation(verbspec,object) -- return appropriately conjugated verb


$generic_db

Generic Database
----------------
This holds a collection of {string key, datum} pairs, where datum can
be anything.  At most one datum may be associated with any given
string.  Data may be anything (lists, strings, numbers, objectids).
If you like, you can think of this as an array indexed by strings.
Verbs supplied include

  :find(string)          => datum, $ambiguous_match or $failed_match
  :find_key(string)      => full string key,  $ambiguous_match or $failed_match
  :find_exact(string)    => datum or $failed_match (no partial matches)
  :find_all(string)      => list of all data corresponding to matching strings
  :find_all_keys(string) => list of all matching strings

  :insert(string,datum)  
       if the string is already present in the db, 
       changes the associated datum and returns {old_datum};
       otherwise enters a new {string,datum} pair and return 0.
  :delete(string)
       if there is a datum associated with string, 
       remove this association and return {datum}; otherwise return 0.
  :delete2(string,datum)
       if the given datum is associated with string, 
       removes that association and return {datum}, 
       if some other datum is associated with string, just return {other datum}
       otherwise return 0.
  :clearall([4|3])
       removes all associations from the database.
       optional argument changes the type of the database 
       (4 is normal, 3 is a kludge for when the data are simply boolean flags
        i.e., this is a set of strings rather than a string-indexed array;
        more on this below)

  count [entries|chars] in this
        provide some vague statistics about how big this thing is.

N.B.  As entries get made, properties belonging to $generic_db.owner
will be created on the db object itself.  These properties will be
created having flags as specified by .node_perms, which by default is
"r", but can be changed to "" should you want to ensure that randoms
don't have access to the raw information.

Implementation notes
 - - - - - - - - - -
The representation is as a `trie', a tree in which each internal node
corresponds to a prefix shared by two or more strings in the db.  Each
internal node is kept in a property named " "+<prefix>, where
<prefix> is a prefix shared by all strings in the subtree under
this node.  The property value is a 4 element list

this.(" "+<prefix>)[1] = <common>
   maximal continuation shared by all strings beginning with prefix
   i.e., all these names actually begin with <prefix>+<common>

this.(" "+<prefix>)[2] = <continuations>
   string of all characters <c> that can follow <prefix>+<common> for which
   there is more than one string in the db beginning with <prefix>+<common>+<c>

this.(" "+<prefix>)[3] = <exact_matches>
   list of all strings in this subtree for which 
   the character (or lack thereof) following the <prefix>+<common> substring 
   suffices to determine the string.

this.(" "+<prefix>)[4] = <data>
   list of data corresponding to the strings in [3].

Child nodes are       this.(" "+<prefix>+<common>+<c>) 
       for all <c> in this.(" "+<prefix>)[2].
The root node is this.(" ").
If, e.g., there are 2 or more strings in the db beginning with a, 
there will be a node this.(" a").  
If all of these strings actually begin with "ani", then this.(" a")[1]=="ni".
The db consisting of the 5 correspondences

  {"animal", #1}
  {"anime",  #2}
  {"anil",   #3}
  {"anile",  #4}
  {"banal",  #5}

would be represented

this.(" ")    =={"",  "a",  {"banal"},         {#5}}
this.(" a")   =={"ni","lm", {},                {}}
this.(" anim")=={"",  "",   {"animal","anime"},{#1,#2}}
this.(" anil")=={"",  "",   {"anil","anile"},  {#3,#4}}

In some cases one may merely wish to hold a collection of strings
without trying to associate a particular datum with each string.  One
may then instead set up a db without the fourth field on each of the
properties.  In this case the datum is taken to be the found string
itself and that is what gets returned by :find*() in the event of a
successful search.  :find and :find_key are then equivalent as are
:find_all and :find_all_keys.  To setup the db this way, do a
:clearall(3).  :clearall(4) reverts to the above described type of db
with a separately kept datum.  Note that you can't change the type
without emptying the db.  3 and 4 are currently the only db types
allowed.


$generic_editor


The Generic Editor enables a player to edit a list of strings.  While
one might contrive to use it directly, it is rather intended as a
parent for some actual editor.  It supplies the following commands:

say         <text>                      w*hat       
emote       <text>                      abort       
lis*t       [<range>] [nonum]           q*uit,done,pause 
ins*ert     [<ins>] ["<text>]           
n*ext,p*rev [n] ["<text>]               
del*ete     [<range>]                   
f*ind       /<str>[/[c][<range>]]       
s*ubst      /<str1>/<str2>[/[g][c][<range>]]
m*ove,c*opy [<range>] to <ins>          
join*l      [<range>]                   
fill        [<range>] [@<col>]          

$editor_help.(cmdname) descrbes cmdname
$editor_help.insert    descrbes insertion points (<ins>)
$editor_help.ranges    descrbes range specifications (<range>)

You'll notice that nowhere does it say how to load in a given list of
strings or how and where one may save said list away when one is done
editing.  These commands are supplied by the child editor object.  The
generic editor contains only the code for editing lines, though it
defines additional functions for use by its children:

  :loaded(player)
     returns the index (player in this.active) iff text has been loaded
     from somewhere, otherwise returns 0.

Note that, by default, there is a difference between 

        having nothing loaded                (:text(who)==0) and 
        having loaded something with no text (:text(who)=={}).

If you don't care about this distinction in a particular case, just do
(player in this.active) instead of this:loaded(player).  If you don't
want your editor to make this distinction at all, do

        @stateprop texts={} for <youreditor>
     which changes the initial value of :text() to {} 

In all functions below, 'who' is the index returned by :loaded(player) 

BTW, be careful about using 'player' in non-user (i.e., +x
this-none-this) verbs --- much better to have the user verb get the
index with :loaded() and then pass that around.  For your non-user
verbs, we have

  :ok(who)
     returns E_PERM if the caller is not an editor verb and E_RANGE
     if 'who' does not point to a valid session.

which should take care of the more egregious security holes (but maybe
not the less egregious ones).

For getting and loading text, we have

  :text(who)    
     the current text string list or 0 if nothing loaded yet.
  :load(who,text)
     loads the given list of strings as the text to be edited.
     this also resets the 'changed' flag and pushes the insertion 
     point to the end.

and various flags and properties (all of the set_* routines return
E_PERM when not called from an editor verb, E_RANGE if who is out of
bounds, E_INVARG if something is wrong with the 2nd arg, or the new
value, which may not necessarily be the same as the 2nd arg (e.g.,
set_insertion(..,37) on a 5 line text buffer returns 6).

  :changed(who)
     has the text been altered since the last save/load?
     (the child editor gets to define what "save" means).
  :set_changed(who,value)
     Any child editor command that is considered to save the text should do a 
     :set_changed(who,0).  
     Note that if the changed flag is 0, the session will be flushed when 
     the player leaves the editor, so you may also want certain commands to
     do set_changed(who,1)...

  :origin(who)
     room where the player came from.  
  :set_origin(who,room)
     can be used to change the room the player will return to when finished
     editing.  Since origin gets set even in cases where the player teleports
     into the editor you probably won't usually need to do this.

  :insertion(who)
     current insertion point.
  :set_insertion(who,linenumber)
     linenumber needs to be a positive integer and will get 

  :readable(who)
     whether the current editing session has been made globally readable.
  :set_readable(who,boolean)
     change the readability of the current editing session.
     This is used by the publish/perish verbs.

We also provide

  :invoke(...)
      If the player has a previous unsaved (i.e., :changed()!=0)
      session, we return to it, moving the player to the editor.  
      If the player is already in the editor, this has no effect other
      than to print a few nasty messages.  In any case a :changed()
      session must be aborted or set_changed(,0) before anything else 
      can be started

      Otherwise, we pass the arguments (which are assumed to be the
      result of some munging of the command line) to :parse_invoke(),
      move the player to the editor and load whatever parse_invoke()
      specified.  The only interpretation the generic editor makes on
      the arguments is that if the boolean value of the first is true,
      this indicates that the player wanted to load something as
      opposed to resume a previous session.  Usually a command calling
      :invoke will have a true (i.e., nonzero number, nonempty list or
      string) first arg iff the command line is nonempty, in which case 
      'args' works fine for this purpose.

      If the command parses sucessfully (:parse_invoke() returns a list),
      we move the player to the editor if necessary and then call 
      :init_session() to set things up.

The child editor is assumed to provide

  :parse_invoke(...)
     given :invoke()'s arguments, determines what the player wants to edit.
     It either returns 0 and reports syntax errors to player,
     or it returns some list that :init_session() will understand.

  :init_session(who,@spec)
     where spec is something that was returned by :parse_invoke().
     Loads the text and sets the stateprops (below) to indicate that 
     we are working on whatever it is we're suppose to be working on.

  :working_on(who)   
     returns a string X as in "You are working on X."
     This is called by the 'w*hat' command, among other things.

Child editors may have their own properties giving state information
for the various editing sessions.  The value of each such property
will be a list giving a value for each player in the editor.  For each
such property, you should, once the editor object has been created, do
one of

    @stateprop <propname>                 for <editor>
    @stateprop <propname>=<default-value> for <editor>
               (0 is the default <default-value>)

Henceforth, adding and deleting new editing sessions will amend the
list held by the given property.

The value of the property for a given session can be obtained via
this.<propname>[player in this.active] and can be changed with a
corresponding indexed property assignment.  The usual idiom for an
editor command is

   if(!(who=this:loaded(player)))
     player:tell(nothing_loaded_msg());
   else
      ... various references to  this.<propname>[who] ...
   endif

To remove such a property from the list of such state properties:

    @rmstateprop <propname> from <editor>

Note that you can only do this with properties defined on the child
editor itself.  Using @stateprop or @rmstateprop also has the effect
of flushing any editing sessions in progress and booting anyone
currently in the editor.

Incidentally, the @flush command may be used at any time to clean out the
editor or to remove all sessions older than a given date.

There are also numerous _msg properties that may be customized

    @depart          announced at the origin when :invoke() is called. 
    @return          announced at the origin the player is returned there.
    @nothing_loaded  printed when user attempts editing 
                     before anything has been loaded.
    @no_text         response to 'list' when :text()=={}
    @no_change       printed by 'what' when :changed()==0
    @change          printed by 'what' when :changed()==1
    @no_littering    printed upon leaving the editor with :changed()==1.
    @previous_session  printed by :invoke() when player tries to start a 
                     new session without aborting or saving the old one

The general procedure for creating a child editor:

. @create #5400 named <editor>

. define additional <editor> verbs/properties
    At the very least you need 'edit' and 'save' commands.
    Usually you can get away with just having 'edit' call :invoke();
    Presumably, you'll need at least a command to load text from somewhere
    as well as a command to save it back out.

. define a verb (somewhere) to invoke the editor 
    This could be just a one-liner that calls <editor>:invoke(args,verb).
    Either that or
      .  you have to set up an exit somewhere whose destination is <editor>
      .  you have to advertise the object number so that people can 
         teleport to it.
  
. @stateprop x for <editor>

. if you want the 'abort' command to boot the player from the editor do
    <editor>.exit_on_abort = 1;

. set <editor>.commands to be the list of additional commands defined
    by <editor>.  
    Each element of the list is itself a list of the form {name,args}.
  set <editor>.commands2 to be the list of commands that should appear
    in the `look' listing, and should be a list of strings appearing 
    as names in .commands on either <editor> or some editor ancestor.
  look at $verb_editor or $note_editor for an example.

. If you want to have help text for new verbs you define, create a child of 
    $generic_help and add properties to this object for each of the topics 
    that you want to provide help text.
    Finally, set <editor>.help = {this object} so that the help system
    knows to consult this object.


The Help System


When a player types help, the following list of objects is consulted
for .help properties: the player itself, all ancestors of player up to
and including $player, and, if the current location is a room, the
current location together with all ancestors of the current location
back to and including $room.  Each help property should have as value
either an object or a list of objects (otherwise we just ignore it).
These objects are then strung together as a list of "help databases"
to be searched in order for the requested topic.

A help database (in the sense of anything that is usable by
$player:help()) is any object having the following verbs:

  :find_topics([string])
     where string is a supposed help topic, returns a list of strings,
     i.e., actual help topics that this db knows about, or some boolean 
     false value in the event that this db is clueless...
     If no arguments are given, this should return a list of all topics
     in the db

  :get_topic(string,dblist)
     given one of the strings returned by :find_topics this either
     returns a list of strings (text to be spewed to the player) or
     returns 1 to indicate that it has already taken care of printing
     information to the player.
     The second argument is the list of all help dbs that are being 
     consulted.

  :topic_text(string)
     like get_topic, but instead returns the raw contents of a help topic

  :set_topic_text(string, text)
     sets the contents of a help topic

  :dump_topic(string)
     like topic_text, but instead returns a script that can be used to
     upload the text.  this is somewhat obsolete now, as it doesn't need
     to be customized on any help databases but instead always uses
     :set_topic-text.

  :has_topic(string)
     returns true if and only if this db has a topic matching that name
     exactly

  :add_topic(string)
     returns true if the topic was added

  :delete_topic(string)
     likewise with deletion

If :find_topic reports that a particular db knows about a given topic
it returns the full topic name, so that :get_topic may be called on it
later.

The remaining verbs are used by maintainers (see $prog:@gethelp) to
edit help topics.

$GENERIC_HELP AND $HELP
-----------------------

The Generic Help Database, $generic_help, is the parent class of a
particular kind of help database of which $help is an instance.  On
help databases of this type, every help topic has a corresponding
property, interpreted as follows:

  this.(topic) = string             
      one-line help text.

  this.(topic) = {"*<verb>*",@args}
      call this:<verb>(args,dblist) to get text; dblist is the full list of 
      help objects that are being consulted (as passed in by :get_topic).

  this.(topic) = other list of strings 
      multi-line help text--see `help help-format' for more information

For the {"*<verb>*",...} form, the current verbs available are

  {"*forward*", topic, @rest}   
     - get help text for topic and then append the lines of `rest'.  
       rest may, in turn, begin with a "*<verb>*"...

  {"*pass*", topic, @rest}   
     - get help text for topic from the first help database after this one
       that actually has help text for topic, and then append lines of `rest'.
       As with "*forward*" rest may, in turn, begin with a "*<verb>*"...

  {"*unpass*", topic, @rest}
     - get help text for topic from the first help database
       that actually has help text for topic, and then append lines of `rest'.
       This is identical to *pass* except that we begin the search of help
       dbs from the beginning of the list.  Care should be exercised since
       this can cause loops...

  {"*subst*", @lines} 
     - All occurences of %[exp] in lines are replaced with the value of exp
         which is assumed to evaluate to a string.  
       All lines beginning with %;exp are replaced with the value of exp 
         which is assumed to evaluate to a list of strings.
       Evaluation is done using $no_one's permissions so exp in either case
       can only refer to public information.

  {"*index*", title}
     - returns a list of all topics in this database, arranged in columns.
       title is used as a heading for this index.

  {"*objectdoc*", object}
     - gets the documentation for the given object (i.e., object:help_msg())
       N.B. as with all other *verb* arguments, object must be a string.

  {"*verbdoc*", objec
       N.B. as with all other *verb* arguments, object must be a string.

  {"*verbdoc*", object, verbname}
     - gets the documentation for the named verb on the given object
       (i.e., any strings at the beginnine documentation for the named verb on the given object
       (i.e., any strings at the beginning of said verbcode)

Individual help dbs are free to define additional verbs that may be
used in this context.  $help itself defines the following additional
such verbs:

  {"*index_list*"}
     - returns a list of all index topics in all databases in the search list.
       An index topic is one whose actual text is {"*index*", something}.
       When creating a help db, you should be sure to make an index topic.

  {"*full_index*"}
     - prints indices for all help databases in the search list.

It should be noted (once again) that help databases need not be
children of $generic_help, so long as they have
:find_topics/:get_topic/:dump_topic working as specified above.


$generic_options


Generic Option Package
----------------------
If one has a command or set of commands for which one wishes to
provide several options/flags that a player can set to customize how
the command behaves for him.  Making each option a separate property
is a bit expensive, especially when the option in question is merely
be a boolean flag (possibly being set to false in most cases).  This
package provides an alternative, as well as providing a uniform set of
commands for setting these flags/options and checking that the values
given are of appropriate types.

Instead of needing several properties, only one is required to store a
list containing values for all of the options.  An "option package"
(pkg, below) is then an object of this class, which provides routines
for manipulating such lists.

The set of option names is divided into a set of "real" options, those
whose names will actually appear in a given list, and "extras" which
are either synonyms for or represent combinations of real options.

 pkg:add_name(name)      adds name to .names  (remove it from .extras if there)
 pkg:add_name(name,1)    adds name to .extras (remove it from .names if there)
    => 1 - ok, 0 - already added, E_INVARG - illegal name, E_PERM

 pkg:remove_name(name)   remove name from either .names or .extras
    => 1 - ok, 0 - not present, E_PERM

For setting or retrieving values we have

 pkg:get(options,name) 
    => value (or 0 if name isn't a real option)
 pkg:set(options,name,value)
    => revised options (or string error message if something goes wrong)

By default, a given option can only be a boolean flag, having one of
the values 0 (absent from the list), or 1 (present in the list).  :set
translates 0/""/{} to 0 and any other non-object value to 1.

One may however designate a wider range of possible values for an
option "foo" by either installing one of

  pkg.type_foo
    -- list of allowed types, 
       e.g., {NUM,STR}   => must be a number or a string
       e.g., {OBJ,{OBJ}} => must be an object or a list of objects
    for anything fancier use:

  pkg:check_foo(value)
    => string error message or {value munged as desired}

In general, the only restriction on option values is that 0 is the
only false value; setting an option to "" or {} sets it to 0.  Every
option defaults to 0, and no matter what you install as .type_foo or
:check_foo(), 0 will always be a legal value for option "foo".

When presented with an option that is in .extras, :set will typecheck
the value as described, however, then :actual(name, value) will be
called to obtain a list of {name-of-real-option, value} pairs
indicating which combination of real options should be set.

Other verbs
  pkg:parse(args,...)
    parses the command line arguments of a @whatever_option command
    => {optionname, value}  if the player wants to set an option
    => {optionname}         if the player wants to view an option
    => string error message  otherwise

  one may install pkg:parse_foo to parse arguments for option "foo" 
    !foo     => {"foo",0}  (:parse_foo not called)
    foo=     => {"foo",0}  (:parse_foo not called)
    -foo     => {"foo",0}  (:parse_foo not called)
    +foo     => pkg:parse_foo("foo",1)
    foo=word => pkg:parse_foo("foo","word")
    foo word1 word2    => pkg:parse_foo("foo",{"word1","word2"})
    foo is word1 word2 => pkg:parse_foo("foo",{"word1","word2"})

  If you install a .type_foo or a :check_foo verb,
  you need to also provide :parse_foo.

 pkg:show(options,name|list of names)
    => list of strings describing the current value of the named option(s).
       calls     pkg:show_foo(options,list of names) or
       refers to pkg.show_foo
       to describe option "foo"

(see sources for details...  at some point I'll finish writing this...)


The Help System


When a player types help, the following list of objects is consulted
for .help properties: the player itself, all ancestors of player up to
and including $player, and, if the current location is a room, the
current location together with all ancestors of the current location
back to and including $room.  Each help property should have as value
either an object or a list of objects (otherwise we just ignore it).
These objects are then strung together as a list of "help databases"
to be searched in order for the requested topic.

A help database (in the sense of anything that is usable by
$player:help()) is any object having the following verbs:

  :find_topics([string])
     where string is a supposed help topic, returns a list of strings,
     i.e., actual help topics that this db knows about, or some boolean 
     false value in the event that this db is clueless...
     If no arguments are given, this should return a list of all topics
     in the db

  :get_topic(string,dblist)
     given one of the strings returned by :find_topics this either
     returns a list of strings (text to be spewed to the player) or
     returns 1 to indicate that it has already taken care of printing
     information to the player.
     The second argument is the list of all help dbs that are being 
     consulted.

  :topic_text(string)
     like get_topic, but instead returns the raw contents of a help topic

  :set_topic_text(string, text)
     sets the contents of a help topic

  :dump_topic(string)
     like topic_text, but instead returns a script that can be used to
     upload the text.  this is somewhat obsolete now, as it doesn't need
     to be customized on any help databases but instead always uses
     :set_topic-text.

  :has_topic(string)
     returns true if and only if this db has a topic matching that name
     exactly

  :add_topic(string)
     returns true if the topic was added

  :delete_topic(string)
     likewise with deletion

If :find_topic reports that a particular db knows about a given topic
it returns the full topic name, so that :get_topic may be called on it
later.

The remaining verbs are used by maintainers (see $prog:@gethelp) to
edit help topics.

$GENERIC_HELP AND $HELP
-----------------------

The Generic Help Database, $generic_help, is the parent class of a
particular kind of help database of which $help is an instance.  On
help databases of this type, every help topic has a corresponding
property, interpreted as follows:

  this.(topic) = string             
      one-line help text.

  this.(topic) = {"*<verb>*",@args}
      call this:<verb>(args,dblist) to get text; dblist is the full list of 
      help objects that are being consulted (as passed in by :get_topic).

  this.(topic) = other list of strings 
      multi-line help text--see `help help-format' for more information

For the {"*<verb>*",...} form, the current verbs available are

  {"*forward*", topic, @rest}   
     - get help text for topic and then append the lines of `rest'.  
       rest may, in turn, begin with a "*<verb>*"...

  {"*pass*", topic, @rest}   
     - get help text for topic from the first help database after this one
       that actually has help text for topic, and then append lines of `rest'.
       As with "*forward*" rest may, in turn, begin with a "*<verb>*"...

  {"*unpass*", topic, @rest}
     - get help text for topic from the first help database
       that actually has help text for topic, and then append lines of `rest'.
       This is identical to *pass* except that we begin the search of help
       dbs from the beginning of the list.  Care should be exercised since
       this can cause loops...

  {"*subst*", @lines} 
     - All occurences of %[exp] in lines are replaced with the value of exp
         which is assumed to evaluate to a string.  
       All lines beginning with %;exp are replaced with the value of exp 
         which is assumed to evaluate to a list of strings.
       Evaluation is done using $no_one's permissions so exp in either case
       can only refer to public information.

  {"*index*", title}
     - returns a list of all topics in this database, arranged in columns.
       title is used as a heading for this index.

  {"*objectdoc*", object}
     - gets the documentation for the given object (i.e., object:help_msg())
       N.B. as with all other *verb* arguments, object must be a string.

  {"*verbdoc*", objec
       N.B. as with all other *verb* arguments, object must be a string.

  {"*verbdoc*", object, verbname}
     - gets the documentation for the named verb on the given object
       (i.e., any strings at the beginnine documentation for the named verb on the given object
       (i.e., any strings at the beginning of said verbcode)

Individual help dbs are free to define additional verbs that may be
used in this context.  $help itself defines the following additional
such verbs:

  {"*index_list*"}
     - returns a list of all index topics in all databases in the search list.
       An index topic is one whose actual text is {"*index*", something}.
       When creating a help db, you should be sure to make an index topic.

  {"*full_index*"}
     - prints indices for all help databases in the search list.

It should be noted (once again) that help databases need not be
children of $generic_help, so long as they have
:find_topics/:get_topic/:dump_topic working as specified above.


$housekeeper


The housekeeper is an object that can help keep other objects where
they belong.  New MOOs may want to add their own user interface for
the housekeeper; here is some information that may be helpful.

To indicate what objects should be cleaned:

  :add_cleanup(object[, requestor[, where]])
    Ask the housekeeper to clean 'object' for 'requestor' to 'where'.
    Requestor defaults to 'player'.
    Where defaults to object.location.

  :remove_cleanup(what[, requestor])
    Remove 'what' from the cleanup list at 'requestor's request.
    Will remove it only if 'requestor' made the original request and owns
    the object or the destination.

To actually get the housekeeper to clean stuff up:

  :cleanup([insist])
    Clean up player's objects.  Argument is 'up' or 'up!' for manually
    requested cleanups.  'up!' means to clean things even if it's against
    the housekeeper's better judgement.

  :replace(object[, insist])
    Clean up the indicated object.  'insist' is as in :cleanup.

  :continuous()
    Starts the housekeeper cleaning continuously, killing any previous
    continuous task.  This should be called only when starting up a new MOO,
    or if something has gone wrong, as normally it will just keep going
    without any help.

  :litterbug()
    Clean up all the places in housekeeper.public_places by getting rid of
    all contents not in their .residents lists.  This is called by
    :continuous, so it doesn't need to be called directly.

To find out what's being cleaned to where for whom:

  :cleanup_list([whom])
    Show 'player' the personal cleanup list for 'whom', or the housekeeper's
    complete list if no argument is given.

  :clean_status()
    Show 'player' a brief summary of eir personal cleanup list.


General Information on $integration_utils


Utilities for implementing integrating descriptions.  (See `help
integration'[2] for a basic explanation.)

USEFUL VERBS
------------

  :integrate  -- Make text out of .integrate_paragraphs lists
  :visible_of -- Which of a given set of objects are visible?

RELATED VERBS ON $ROOM
----------------------

  :look_self       -- Decides what to integrate, calls :description.
  :description     -- Integrates objects using $integration_utils.
  :ok_to_integrate -- Can a given object be integrated?
  :visible         -- Should a given object be seen in `look'?


General Information on $list_utils


append            (list,list,..) => result of concatenating the given lists
flatten           (list of lists)=> list of all non-list elements
reverse           (list)         => reversed list
remove_duplicates (list)         => list with all duplicates removed
compress          (list)         => list with consecutive duplicates removed
setremove_all     (list,elt)     => list with all occurrences of elt removed
find_insert       (sortedlist,e) => index of first element > e in sortedlist
sort              (list[,keys])  => sorted list

make              (n[,e])        => list of n copies of e
range             (m,n)          => {m,m+1,...,n}

arrayset   (list,val,i[,j,k...]) => array modified so that list[i][j][k]==val

-- Mapping functions (take a list and do something to each element):

map_prop ({o...},prop)              => list of o.(prop)            for all o
map_verb ({o...},verb[,args)        => list of o:(verb)(@args)     for all o
map_arg  ([n,]obj,verb,{a...},args) => list of obj:(verb)(a,@args) for all a

-- Association list functions --

An association list (alist) is a list of pairs (2-element lists),
though the following functions have been generalized for lists of
n-tuples (n-element lists).  In each case i defaults to 1.

make_alist      (lists[,pad]) => alist from list of parallel lists
assoc        (targ,alist[,i]) => 1st tuple in alist whose i-th element is targ
iassoc       (targ,alist[,i]) => index of same.
assoc_prefix (targ,alist[,i]) => ... whose i-th element has targ as a prefix
iassoc_prefix(targ,alist[,i]) => index of same.
slice             (alist[,i]) => list of i-th elements
sort_alist        (alist[,i]) => alist sorted on i-th elements.


General Information on $lock_utils


A set of related utilities.


$login


This object manages command parsing for unconnected players and
governs the initiation of an actual connection.  There are verbs
pertaining to registration, controlling player creation, and doing
site-locks (see `help blacklist'[1] on $wiz_help).

COMMANDS FOR UNCONNECTED PLAYERS
--------------------------------

Recall that for each line that an unconnected player types, the server
parses that line into words (the same way normal commands are parsed
into a list of words that is then assigned to `args') and then
#0:do_login_command is called.

  :parse_command (@args) => {verb, @args}
    given the sequence of arguments that were fed to #0:do_login_command
    this returns the name of a verb on $login to be called together with a 
    list of arguments to be passed to it.

By default this just returns args iff args[1] names an actual verb on
$login that is +x and has args {"any","none","any"}.  Otherwise, it
returns one of

  .blank_command   -- verb to call if command line is empty
  .bogus_command   -- verb to call if command line otherwise unintelligible

In both cases :parse_command returns a verbname followed by the entire
args list passed to it (including the would-be verb at the beginning
if any).

Currently the following verbs are availabe to non-connected players

  h*elp @h*elp       -- print .welcome_message
  ?                  -- print a short list of available commands
  w*ho @w*ho         -- print a list of logged in players (excluding wizards)
  co*nnect @co*nnect -- connect to an existing player
  cr*eate @cr*eate   -- create a new player
  up*time @up*time   -- tell how long the server has been running
  version @version   -- tell which version of the server is running
  q*uit @q*uit       -- logoff

Adding a new command is fairly straightforward; just create a verb on
$login, making sure a previous verb doesn't already match the name you
want to give it.  Then give it args of "any" "none "any" and make sure
it is +x.  Such a verb should begin with `if (caller != #0) return
E_PERM; ...' so as to prevent anyone other from a not-logged-in player
from making use of it.

CUSTOMIZATIONS
--------------

  .welcome_message 
    -- the message for "help" to print.
  .create_enabled 
    == 0 => @create prints .registration_string if one tries to use it
    == 1 => anyone from a non-blacklisted site (see `help blacklist')
            may use @create to make a new player

  .registration_address
    -- an email address for character creation requests
  .registration_string  
    -- string to print to players to give them information about how to get 
       a character created for them, .registration_address is substituted 
       for %e, % for %%
  .newt_registration_string
    -- string to print to @newted players (see `help @newt').
       same substitutions as for .registration_string.

  .sitematch_guests
    -- use $country_db to connect guests from ".fr" as "French guest", etc.

OTHER VERBS
-----------

   :registration_string()      => .registration_string with substitutions
   :newt_registration_string() => .newt_registration_string with substitutions
   :player_creation_enabled(connection) 
       decides whether someone on connection should be allowed to create 
       a player.  If you decide this shouldn't depend strictly on the blacklist
       and on the value of .create_enabled, here's where the extra code can go.
   :check_for_shutdown()
       prints a warning message to append to the login banner in the event 
       that the server will be going down soon.
   :check_player_db()
       prints a warning message to append to the login banner in the event 
       that $player_db is being reloaded to warn players that their character
       names might not be recognized.

SITE LOCKS
----------

See `help blacklist'[2].


$mail_agent



$mail_agent

This object contains a two distinct sets of routines:

  1.  utilities for performing basic mailsystem functions, e.g.,
      matching on recipient names, resolving mail forwarding, 
      formatting messages, sending messages

Recipient Matching

match           - match on a $mail_recipient
match_recipient - match on either a $mail_recipient or a player
match_failed    - print angry messages to the user for $failed/ambiguous_match

look_self  - provides a list of available $mail_recipients
check_names
touch
accept

Message Format

make_message        - produces a message in the canonical transmission format
name                - single recipient     => string for address field
name_list           - list of recipients   => string for address field
parse_address_field - address field string => object list

Sending Messages

send_message  - advertised message sending routine.
raw_send      - raw message sending routine 
                (only called by $mail_editor:send and this:send_message)
resolve_addr  - converts a given list recipients into a list of actual 
                recipients and objects to be notified.
sends_to      - Does X forward (transitively) to Y

Mail Options

option         
option_verbose

  2.  canonical versions of mail_recipient verbs

Ideally, the verbs to perform operations on a given mail recipient
would be located on the recipient itself, except for the fact that
these verbs also need to be located on players, which for various
reasons, shouldn't be children of $mail_recipient.  Multiple
inheritance would solve our problems, but we don't have it yet.  Ergo,
both $mail_recipient and $player refer to the following verbs here:

display_seq_full     print entire text of messages  (@read)
display_seq_headers  print headers of messages      (@mail)
rm_message_seq       remove messages                (@rmm)
undo_rmm             undo last rm_message_seq       (@unrmm)
expunge_rmm          flush removed messages         (@unrmm expunge)
list_rmm             list removed messages          (@unrmm list)
renumber             renumber messages              (@renumber)
msg_summary_line     msg header => display_seq_headers/list_rmm summary line

parse_message_seq    command line msg sequence spec => message sequence
new_message_num      => message number of next new message
length_all_msgs      => number of messages (total)
length_num_le        => number of messages numbered <= some number
length_date_le       => number of messages dated <= some date
exists_num_eq        => true iff there exists a messsage with the given number
from_msg_seq         => message sequence of msgs from given sender(s)
to_msg_seq           => message sequence of msgs to given recipient(s)
subject_msg_seq      => message sequence of msgs with subjects containing text
body_msg_seq         => message sequence of msgs with bodies containing text
messages_in_seq      => list of {message number, message} pairs

messages             == :messages_in_seq(1,:length_all_msgs()+1)   (obsolete)

The $mail_agent versions of these verbs are
set_task_perms(caller_perms()) and perform their operations on caller,
which in turn is assumed to have done any necessary security checks.


Generic Mail Recipient


A "mail recipient" is, by definition, an object that can be sent mail.
Mail recipients must either be players or descendants of
$mail_recipient.

One source of confusion is that the terms "mail recipient", "mail
folder", "mailing list", and "mail collection" really all refer to the
same object.  It so happens that $mail_recipient serve several
distinct functions and we tend to use whatever term happens to best
match the application under discussion, e.g., it's a "mailing list" if
we're playing with its .mail_forward property but it's also a "mail
folder" if we're examining the messages that have been saved in it.

TOPICS
------

MR-access[1]
      controlling read, write and send access to a recipient
MR-naming[2]
      naming conventions and how to match on recipient names
MR-sequences[3]
      message sequence arguments to $mail_recipient verbs
MR-reading[4]
      reading messages/headers on recipients
MR-searching[5]
      searching message lists for patterns in certain fields
MR-writing[6]
      removing and renumbering messages
MR-subscribing[7]
      updating .mail_forward, .mail_notify and the story of .current_message


General Information on $math_utils


Trigonometric/Exponential functions:
  sin(a),cos(a),tan(a) -- returns 10000*(the value of the corresponding
       trigonometric function) angle a is in degrees.
  arctan([x,]y) -- returns arctan(y/x) in degrees in the range -179..180.
       x defaults to 10000.  Quadrant is that of (x,y).
  exp(x[,n]) -- calculates e^x with an nth order taylor polynomial

Statistical functions:
  combinations(n,r) -- returns the number of combinations given n objects
       taken r at a time.
  permutations(n,r) -- returns the number of permutations possible given
       n objects taken r at a time.

Number decomposition:
  div(n,d) -- correct version of / (handles negative numbers correctly)
  mod(n,d) -- correct version of % (handles negative numbers correctly)
  divmod(n,d) -- {div(n,d),mod(n,d)}
  parts(n,q[,i]) -- returns a list of two elements {integer,decimal fraction}

Other math functions:
  sqrt(x)      -- returns the largest integer n <= the square root of x
  pow(x,n)     -- returns x^n
  factorial(x) -- returns x!

Series:
  fibonacci(n) -- returns the 1st n fibonacci numbers in a list
  geometric(x,n) -- returns the value of the nth order geometric series at x

Integer Properties:
  gcd(a,b) -- find the greatest common divisor of the two numbers
  lcm(a,b) -- find the least common multiple of the two numbers
  are_relatively_prime(a,b) -- return 1 if a and b are relatively prime
  is_prime(n) -- returns 1 if the number is a prime and 0 otherwise
  
Miscellaneous:
  random(n) -- returns a random number from 0..n if n > 0 or n..0 if n < 0
  random_range(n[,mean]) -- returns a random number from mean - n..mean + n
       with mean defaulting to 0
  simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- returns the numerical
      approximation of an integral using simpson's rule

Bitwise Arithmetic:
  AND(x,y) -- returns x AND y
  OR(x,y) -- returns x OR y
  XOR(x,y) -- returns x XOR y (XOR is the exclusive-or function)
  NOT(x) -- returns the complement of x
      All bitwise manipulation is of 32-bit values.


General Information on $name_utils


A set of utilities dealing with name flags.


$no_one


..is a powerless player.  He owns no objects, not even himself; nor
does he own any verbs.  He is, however, a programmer and thus may use
eval().  In fact his sole purpose is to evaluate questionable code.

`Questionable' could be in either or both of the following senses

(1) Its origin is sufficiently uncertain so that there is no obvious
way of deciding whose permissions it should run under.

(2) The code itself is potentially malicious, i.e., to the extent that
one does not want to be evaluating it using one's own permissions.

    set_task_perms($no_one);

is thus the canonical idiom in wizard code for rendering anything that
follows mostly harmless.  For use by ordinary programmers, we have:

    $no_one:eval(string)

which attempts to evaluate an arbitrary string using $no_one's
permissions.  string is either an expression or ";" followed by one or
more statements, of which the final semicolon may be omitted.  return
values are what eval() would return (either {1,value} or
{0,@error_messages}).

Similarly, we have

    $no_one:eval_d(string)

which attempts to evaluate the specified string, but does it without
the debug flag turned on (so that, for example, you'll get an error as
opposed to terminating by traceback).

And, as a helpful utility for calling verbs whose behavior may be
unpredictable, there is

    $no_one:call_verb(object, verb name, args)

which calls the specified verb with $no_one's permissions.


General Information on $object_utils


These routines are useful for finding out information about individual objects.

Examining everything an object has defined on it:
  all_verbs          (object) => like it says
  all_properties     (object) => likewise
  findable_properties(object) => tests to see if caller can "find" them
  owned_properties   (object[, owner]) => tests for ownership

Investigating inheritance:
  ancestors(object[,object...]) => all ancestors
  descendants      (object)     => all descendants
  ordered_descendants(object)   => descendants, in a different order
  leaves           (object)     => descendants with no children
  branches         (object)     => descendants with children 
  isa        (object,class) => true iff object is a descendant of class (or ==)

Considering containment:
  contains      (obj1, obj2) => Does obj1 contain obj2 (nested)?
  all_contents      (object) => return all the (nested) contents of object

Verifying verbs and properties:
  has_property(object,pname) => false/true   according as object.(pname) exists
  has_verb    (object,vname) => false/{#obj} according as object:(vname) exists
  has_callable_verb          => same, but verb must be callable from a program
  match_verb  (object,vname) => false/{location, newvname}
                               (identify location and usable name of verb)
Suspending:
  Many of the above verbs have ..._suspended versions to assist with very large object
  hierarchies.  The following exist:

   descendants_suspended              
   branches_suspended                 
   leaves_suspended                   
   all_properties_suspended           
   descendants_with_property_suspended


General Information on $parse_utils


A set of related utilities.


General Information on $perm_utils


A set of related utilities.


Database Of Players


This is an instance of the Generic Database ($generic_db) that holds
the {name/alias,#objectid} pairs for every name and alias of every
player in the MOO.

Verbs supplied include

  :find(string)        => player or $ambiguous_match or $failed_match
  :find_exact(string)  => player or $failed_match (does not do partial matches)
  :find_all(string)    => list of all matching players

  :insert(string,player) 
       records that string is now a name or alias of player
  :delete(string) 
       removes string from the db
  :available(string)
       returns 1 if string is available as a player name or alias,
       an object if string is in use, or 0 if string is otherwise unavailable.
  :load()
       resets the db, inserting all current player names and aliases.

The internal representation and all of the above verbs (except :load()
and :available()) are as described for $generic_db.

It should be noted that for any application that involves resolving a
player name from a command line, you should be using
$string_utils:match_player() rather than $player_db:find(), since the
former will deal correctly with other ways of referring to players
apart from their names and aliases (e.g., literal object numbers,
"me", "$no_one"...).

:load() needs to be done periodically as it is possible for the player
db to get out of synch with reality.  In particular, there is
currently no way to block someone writing his own player :recycle()
verb that neglects to remove his names from the player db.

While a :load() is in progress the .frozen property is set to 1 to
indicate that any results of :find*() are not to be trusted.


General Information on $quota_utils


Verbs a user might want to call from a program:
 :bi_create -- built-in create() call, takes same args.

 :get_quota(who) -- just get the raw size_quota property
 :display_quota(who) -- prints to player the quota of who.  If caller_perms() controls
                        who, include any secondary characters.  Called by @quota.
 :get_size_quota(who [allchars]) -- return the quota of who, if allchars flag set, add info
                                    from all secondary chars, if caller_perms() permits.

 :value_bytes(value) -- computes the size of the value.
 :object_bytes(object) -- computes the size of the object and caches it.
 :recent_object_bytes(object, days) -- computes and caches the size of object only if cached value
                                       more than days old.  Returns cached value.
 :do_summary(user) -- prints out the results of summarize-one-user.
 :summarize_one_user(user) -- summarizes and caches space usage for user.
                              See verb help for details.

Verbs the system calls:
 :"creation_permitted verb_addition_permitted property_addition_permitted"(who) -- returns true if who
                                                                                   is permitted to build.
 :initialize_quota(who) -- sets quota for newly created players
 :adjust_quota_for_programmer(who) -- empty; might add more quota to newly @progged player.
 :enable_create(who) -- sets .ownership_quota to 1
 :disable_create(who) -- sets .ownership_quota back to -1000 to prohibit create()
 :charge_quota(who, object) -- subtract the size of object from who's quota.  Manipulates
                               the #-unmeasured if what is not currently measured.
                               Called by $wiz_utils:set_owner.
 :reimburse_quota(who, object) -- add the size of object to who's quota.  Ditto.
 :preliminary_reimburse_quota(who, object) -- Because the set_owner is done *after* an object has been
                                              turned into $garbage, ordinary reimbursement fails.  So 
                                              we use this verb in the $recycler.
 :set_quota(who, howmuch)
 :quota_remaining(who) 
 :display_quota_summary -- internal, called by display quota

See help @measure and help @quota for the command line verbs.


The Generic Room ($room)


ANNOUNCEMENTS
-------------

:announce         (@text)         => broadcasts to all except player
:announce_all     (@text)         => broadcasts to all
:announce_all_but (objects,@text) => broadcasts to all except those in objects

say, emote


COMMAND RECOVERY
----------------

:huh            (verb,args) - server hook: last chance to make sense of verb
:here_huh       (verb,args) - room's last attempt to parse something
:here_explain_syntax (this,verb,args) - attempts to explain usage of verb


RESIDENCY
---------

free_home  - true => @sethome allows anyone to set his .home to be here
residents  - objects on this list may teleport in and/or set their homes here.

:accept_for_abode(player) 
            => true iff player should be allowed to set .home to this room.

@resident*s

LOOKING
-------

dark  - true => contents are not visible
ctype - 1..4 for four different styles of .contents lists

:match         (string)        => exit or object in room's .contents
:tell_contents (objects,ctype) - format objects according to ctype, tell player

l*ook


TOPOLOGY AND MOVEMENT VIA EXITS
-------------------------------

See `help $exit' for an explanation of how the generic $exit works.

free_entry     - true  => `teleporting' in is allowed
                  false => only residents may teleport in
exits          - list of invokable exits leading from this room
entrances      - list of recognized exits leading to this room
blessed_object - object currently entering via an exit
blessed_task   - task_id for entering object

:match_exit      (string) => exit whose name matches string
:bless_for_entry (object) - set up room to accept object arriving from entrance
:add_exit        (exit)
:add_entrance    (exit)
:remove_exit     (exit)
:remove_entrance (exit)

e/east/w/west/s/south/n/north/ne/northeast/nw/northwest/se/southeast/sw/southwest/u/up/d/down,
go, @add-exit, @add-entrance, @remove-exit, @remove-entrance, @exits, @entrances 


EJECTION
--------

victim_ejection_msg/oejection_msg/ejection_msg
:*_msg()  messages

@eject


General Information on $seq_utils


A sequence is a set of integers (*)
This package supplies the following verbs:

  :add      (seq,f,t)  => seq with [f..t] interval added
  :remove   (seq,f,t)  => seq with [f..t] interval removed
  :range    (f,t)      => sequence corresponding to [f..t]
  {}                   => empty sequence
  :contains (seq,n)    => n in seq
  :size     (seq)      => number of elements in seq
  :first    (seq)      => first integer in seq or E_NONE
  :firstn   (seq,n)    => first n integers in seq (as a sequence)
  :last     (seq)      => last integer in seq  or E_NONE
  :lastn    (seq,n)    => last n integers in seq (as a sequence)

  :complement(seq)       => [-2147483648..2147483647] - seq
  :union    (seq,seq,...) 
  :intersect(seq,seq,...) 

  :extract(seq,array)           => array[@seq]
  :for([n,]seq,obj,verb,@args)  => for s in (seq) obj:verb(s,@args); endfor

  :tolist(seq)            => list corresponding to seq
  :tostr(seq)             => contents of seq as a string
  :from_list(list)        => sequence corresponding to list
  :from_sorted_list(list) => sequence corresponding to list (assumed sorted)
  :from_string(string)    => sequence corresponding to string

For boolean expressions, note that
  the representation of the empty sequence is {} (boolean FALSE) and
  all non-empty sequences are represented as nonempty lists (boolean TRUE).

The representation used works better than the usual list
implementation for sets consisting of long uninterrupted ranges of
integers.  For sparse sets of integers the representation is decidedly
non-optimal (though it never takes more than double the space of the
usual list representation).

(*) Actually what this package implements is sets of
integers-mod-2^32, but this assumes the underlying machine on which
the server runs has 32-bit integers.  If not, you need to change
this.maxneg to be the largest negative ("smallest"?) integer
available.


General Information on $set_utils


This object is useful for operations that treat lists as sets (i.e.,
without concern about order and assuming no duplication).

 union(set, set, ...)        => union
 intersection(set, set, ...) => intersection

 diff*erence(set 1, set 2, ..., set n)
        => result of removing all elements of sets 2..n from set 1.
 exclusive_or(set, set, set, ...)
        => all elements that are contained in exactly one of the sets

 contains(set 1, set 2, ..., set n)
        => true if and only if all of sets 2..n are subsets of set 1


General Information on $string_utils


For a complete description of a given verb, do `help $string_utils:verbname'[2]

    Conversion routines:

:from_list    (list [,sep])                          => "foo1foo2foo3"
:english_list (str-list[,none-str[,and-str[, sep]]]) => "foo1, foo2, and foo3"
:title_list*c (obj-list[,none-str[,and-str[, sep]]]) => "foo1, foo2, and foo3"
                                                  or => "Foo1, foo2, and foo3"

:from_value   (value [,quoteflag [,maxlistdepth]])   => "{foo1, foo2, foo3}"
:to_value     (string)                               => {1, value}
                                                  or => {0, error message}

:print            (value)                            => "{foo1, foo2, foo3}"
:print_suspended  (value)                            => same, but may suspend
:print_truncated  (value, length, suffix)            => "{foo1, fo.."

:english_number(42 [,zero])  => "forty-two"
:english_ordinal(42)         => "forty-second"
:ordinal(42)                 => "42nd"
:group_number(42135 [,sep])  => "42,135"

    Type checking:

:is_numeric   (string) => return true if string is composed entirely of digits

    Parsing:

:explode (string,char) -- string => list of words delimited by char
:words   (string)      -- string => list of words (as with command line parser)
:word_start (string)   -- string => list of start-end pairs.

    Matching:

:match_string (string, pattern, options)       => * wildcard matching
:find_prefix  (prefix, string-list)=>list index of element starting with prefix
:index_delimited(string,target[,case]) =>index of delimited string occurrence
:match        (string, [obj-list, prop-name]+) => matching object
:match_player (string-list[,me-object])        => list of matching players
:match_object (string, location)               => default object match...

    Pretty printing:

:space         (n/string[,filler])     => n spaces
:left          (string,width[,filler]) => left justified string in field 
:right         (string,width[,filler]) => right justified string in field
:center/re     (string,width[,filler]) => centered string in field
:columnize/se  (list,n[,width])        => list of strings in n columns

    Substitutions

:substitute (string,subst_list [,case])   -- general substitutions.
:pronoun_sub (string/list[,who[,thing[,location]]])
                                          -- pronoun substitutions.
:pronoun_sub_secure (string[,who[,thing[,location]]],default)
                                          -- substitute and check for names.
:pronoun_quote (string/list/subst_list)   -- quoting for pronoun substitutions.

    Miscellaneous string munging:

:trim         (string)       => string with outside whitespace removed.
:triml        (string)       => string with leading whitespace removed.
:trimr        (string)       => string with trailing whitespace removed.
:strip_chars  (string,chars) => string with all chars in `chars' removed.
:strip_all_but(string,chars) => string with all chars not in `chars' removed.
:capitalize/se(string)       => string with first letter capitalized.
:uppercase/lowercase(string) => string with all letters upper or lowercase.
:is_uppercase(string)        => true iff no lowercase letters in string
:names_of     (list of OBJ)  => string with names and object numbers of items.

    Conversions to and from common string formats:

:character_to_ascii(char)    => integer ASCII rep of char
:character_to_hex_ascii(char) => two-digit hexadecimal string ASCII rep of char
:ascii_to_character(num)     => character represented in ASCII by num
:hex_ascii_to_character(hex) => ASCII character represented by two-character hexadecimal string hex

    A few useful properties:

alphabet                    => "abcdefghijklmnopqrstuvwxyz"
tab                         => a single tab character


General Information on $time_utils


    Converting from seconds-since-1970    
dhms          (time)                 => string ...DD:HH:MM:SS
english_time  (time[, reference time)=> string of y, m, d, m, s

    Converting to seconds
to_seconds    ("hh:mm:ss")           => seconds since 00:00:00
from_ctime    (ctime)                => corresponding time-since-1970
from_day      (day_of_week, which)   => time-since-1970 for the given day*
from_month    (month, which)         => time-since-1970 for the given month*
    (* the first midnight of that day/month)

    Converting to some standard English formats
day           ([c]time)              => what day it is
month         ([c]time)              => what month it is
ampm          ([c]time[, precision]) => what time it is, with am or pm

    Substitution
time_sub      (string, time)         => substitute time information

    Miscellaneous
sun           ([time])               => angle between sun and zenith
dst_midnight  (time)                 
lunar_phase   (time)                 => string of phase of the moon


General Information on $trig_utils


Trigonometric/Exponential functions:
  sin(a),cos(a),tan(a) -- returns 10000*(the value of the corresponding
       trigonometric function) angle a is in degrees.
  arctan([x,]y) -- returns arctan(y/x) in degrees in the range -179..180.
       x defaults to 10000.  Quadrant is that of (x,y).
  exp(x[,n]) -- calculates e^x with an nth order taylor polynomial

Statistical functions:
  combinations(n,r) -- returns the number of combinations given n objects
       taken r at a time.
  permutations(n,r) -- returns the number of permutations possible given
       n objects taken r at a time.

Number decomposition:
  div(n,d) -- correct version of / (handles negative numbers correctly)
  mod(n,d) -- correct version of % (handles negative numbers correctly)
  divmod(n,d) -- {div(n,d),mod(n,d)}
  parts(n,q[,i]) -- returns a list of two elements {integer,decimal fraction}

Other math functions:
  sqrt(x)      -- returns the largest integer n <= the square root of x
  pow(x,n)     -- returns x^n
  factorial(x) -- returns x!

Series:
  fibonacci(n) -- returns the 1st n fibonacci numbers in a list
  geometric(x,n) -- returns the value of the nth order geometric series at x

Integer Properties:
  gcd(a,b) -- find the greatest common divisor of the two numbers
  lcm(a,b) -- find the least common multiple of the two numbers
  are_relatively_prime(a,b) -- return 1 if a and b are relatively prime
  is_prime(n) -- returns 1 if the number is a prime and 0 otherwise
  
Miscellaneous:
  random(n) -- returns a random number from 0..n if n > 0 or n..0 if n < 0
  random_range(n[,mean]) -- returns a random number from mean - n..mean + n
       with mean defaulting to 0
  simpson({a,b},{f(a),f((a+b)/2),f(b)}) -- returns the numerical
      approximation of an integral using simpson's rule

Bitwise Arithmetic:
  AND(x,y) -- returns x AND y
  OR(x,y) -- returns x OR y
  XOR(x,y) -- returns x XOR y (XOR is the exclusive-or function)
  NOT(x) -- returns the complement of x
      All bitwise manipulation is of 32-bit values.


General Information on $who_utils


an option package in need of a description.  See `help $generic_option'[2]...


General Information on $wiz_utils


Wizard Utilities
----------------
The following functions are substitutes for various server builtins.
Anytime one feel tempted to use one of the expressions on the right,
use the corresponding one on the left instead.  This will take care
of various things that the server (for whatever reason) does not handle.

:set_programmer(object)             object.programmer = 1;
    chparent object to $prog
    send mail to $prog_log

:set_player(object[,nochown])       set_player_flag(object,1);
    set player flag, 
    add name/aliases to $player_db,
    and maybe do a self chown.

:unset_player(object[,newowner])    set_player_flag(object,0);
    unset player flag,
    remove name/aliases from $player_db
    chown to newowner if given

:set_owner(object, newowner)        object.owner = newowner;
    change ownership on object
    change ownership on all +c properties
    juggle .ownership_quotas

:set_property_owner(object, property, newowner)
    change owner on a given property
    if this is a -c property, we change the owner on all descendants
    for which this is also a -c property.
    Polite protest if property is +c and newowner != object.owner.

:set_property_flags(object, property, flags)
    change the permissions on a given property and propagate these to 
    *all descendants*.  property ownership is changed on descendants 
    where necessary.


Mail Transmission Format


There is a standard message format used for transmitting messages.
This is the format that $mail_editor:make_message produces, and that
:receive_message verbs on players and $mail_recipients expect to see.
The (currently experimental) @refile and @copy commands also use this
format to transfer messages.

This *transmission* format is distinct from the *storage* format,
though, for convenience this same format is often used as well for
storing messages in player collections and ordinary $mail_recipient
children though, in general, there is no requirement that this be the
case.

A transmitted message is a list in the following form

   date (number),
     the time() value at the time the message was sent.
   from (string),
     the sending object (address list form)
     if this is not a player, an additional header will indicate the 
     current ownership of the object.
   to  (string),
     recipients (address list form) which can either be players 
     or $mail_recipient descendents.
   subject (string),
     subject of the message, or " " if there is no subject,
  @additional optional headers (list of strings),
     each header has the form "<header-name>: text" where <header-name>: 
     is padded out to a width of 10 columns for the convenience of 
     :display_message.  Currently "Reply-to: <address list>" is the only 
     additional header in use,
   "",
  @body of message (list of strings)

Note that the from, to and subject lines do *not* include a header
name like "From:", "To:", or "Subject:".  The @'s indicate that the
lists in question get spliced in (as usual), thus the entire message
is a list whose first element is a number and the rest are strings.

The address lists that appear in the from and to lines is a string in
the form a sequence of object ids, each enclosed in parentheses and
preceded by optional text, e.g.,

  "*Core-DB-Issues (#8175), Rog (#4292), and Haakon (#2)"

The text is intended to give the current name of each object for the
benefit of human readers, but is actually ignored by all header
parsing routines.  The convention is that the text is either a player
name or a * followed by a mailing list name.


Generic Mail Recipient


A "mail recipient" is, by definition, an object that can be sent mail.
Mail recipients must either be players or descendants of
$mail_recipient.

One source of confusion is that the terms "mail recipient", "mail
folder", "mailing list", and "mail collection" really all refer to the
same object.  It so happens that $mail_recipient serve several
distinct functions and we tend to use whatever term happens to best
match the application under discussion, e.g., it's a "mailing list" if
we're playing with its .mail_forward property but it's also a "mail
folder" if we're examining the messages that have been saved in it.

TOPICS
------

MR-access[1]
      controlling read, write and send access to a recipient
MR-naming[2]
      naming conventions and how to match on recipient names
MR-sequences[3]
      message sequence arguments to $mail_recipient verbs
MR-reading[4]
      reading messages/headers on recipients
MR-searching[5]
      searching message lists for patterns in certain fields
MR-writing[6]
      removing and renumbering messages
MR-subscribing[7]
      updating .mail_forward, .mail_notify and the story of .current_message


Resolving Mail Forwarding And Notification


For each recipient of a given mail message, the following two verbs
are called to determine where the message should actually go and who
should be notified about it:

:MAIL_FORWARD
-------------

:mail_forward([from]) should return either

  o a list of objects (either players or $mail_recipients) to which
    mail for this recipient will be redirected.

  o a string error message to be printed to the player sending the message.
    If this recipient is one of the original destinations (i.e., not the
    result of a previous forwarding), no mail is actually sent.

If :mail_forward returns a nonempty list, the recipient itself will
*not* actually receive the mail message unless it is included in the
list.  #-1 is allowed to be on the list; it is ignored but does make
the list nonempty.  Thus, having :mail_forward() return {#-1} is the
canonical way to have arriving mail disappear without being kept or
forwarded.

:MAIL_NOTIFY
------------

:mail_notify([from]) should return a list of objects that are to be
told about any mail sent to this recipient (whether or not the
recipient actually receives it). Said objects must have a :notify_mail
verb, but other from that, there is no restriction on what these can
be.

:NOTIFY_MAIL
------------

object:notify_mail is called with the arguments (sender,recipients,msgnumbers) where 

  o  recipients  == list of recipients including object in .mail_notify
  o  msgsnumbers == corresponding list of :receive_message return values
     (or 0 if :receive_message is not actually called, which will be the
     case if the recipient forwards without keeping)

When called as part of a mail send, the `from' argument is the
immediate predecessor on the forwarding chain.  The default versions
of these verbs return the values of .mail_forward and .mail_notify
respectively (pronoun_subbing if the value is a string), unless this
is a moderated mailing list and `from' is an unapproved sender (see
`help MR-access') in which case the following verbs are called
instead:

:MODERATOR_FORWARD
------------------

:moderator_forward(from) is what :mail_forward should return for mail
coming from unapproved senders. This returns .moderator_forward
(pronoun_subbed if a string) by default.

:MODERATOR_NOTIFY
-----------------

:moderator_notify(from) is what :mail_notify should return for mail
coming from unapproved senders.  This returns .moderator_notify
(pronoun_subbed if a string) by default.

Since the :mail_forward verbs only see the previous sender in the
forwarding chain, if, e.g, B is moderated but A can send to B (i.e.,
B:mail_forward(A) returns an actual list), then any mail sent to A
goes to B even if the original sender isn't normally allowed to send
to B directly.

These verbs should all allow `from' to be omitted in which case they
should return as if `from' were a generic approved sender (e.g.,
wizard).

It should rarely be necessary to actually modify any of
:*_forward/*_notify verbs, since one has a fair amount of control over
their behavior via the following properties:

  o  .mail_forward
  o  .mail_notify
  o  .moderated          (see `help MR-access'[1])
  o  .moderator_forward
  o  .moderator_notify


Mail System


The following topics describe the guts of the LambdaCore mail system:

sending-mail[1]
      how to send mail from a program; what happens.
mail-forwarding[2]
      how to do mail forwarding/notification (the simple version)
mail-resolution[3]
      how mail forwarding/notification works, in gory detail
receiving-mail[4]
      what :receive_message should do
mail-format[5]
      format of transmitted messages

mail-command-parsing   (TODO) -- routines for parsing mail commands

$mail_recipient[6]
      generic non-player mail recipient
$mail_agent[7]
      mail utility object


Controlling Access To Mail Recipients


:is_writable_by(one) - one may alter/add/remove saved messages

:is_readable_by(one) - one may read messages.

:is_usable_by(one)   - one may send to this list

By default, these verbs refer to the following properties:

writers   - list of players other from the owner who can do anything
readers   - if == 1, indicates a public mailing list.
            list of additional readers (by default anyone who receives mail 
            sent to the list can read the saved messages).
moderated - if false, indicates a normal mail recipient everyone can send to.
            otherwise this should be a list of approved senders.

Terminology:

A mailing list is "public" if everyone can read it.

A mailing list is "moderated" if not everyone can send to it.

Note that while being able to write to a recipient implies being able
to read from it or send to it, neither of read-ability or send-ability
implies the other.

It is highly recommended that if you are creating custom mail
recipients with variable reader/sender lists, i.e., you find you need
to write your own :is_readable/usable/writabe_by verbs, you are best
off if such verbs are of the form

  return pass(@args) || << your_test(args[1]) >>

and have .writers == .readers == {} and .moderated == 1.  This will ensure

  o  Wizards having write access.  This is necessary in order for :receive_message to work.
  o  Writers being able to read and send (the converse being a ludicrous situation.
  o  Persons on the mail_forward list of someone with reader access will also have read access (convenient).


MR-naming


One may always refer to a list by its object number.  In order to
refer to it by name, it must be contained in $mail_agent, which holds
all mailing lists, i.e., those that you want others to be able to
refer to by name.

The .aliases field holds the names by which one may refer to the list,
but only those names not containing spaces actually count for
anything.  As with certain other types of objects (e.g., players),
set_aliases() needs to be called in order to change the .aliases
field.

$mail_agent:match(name) 
    is the canonical way to obtain the objectid of a mailing list 
    given the name ("*" is assumed; an initial "*" will be dropped).

$mail_agent:match_recipient(name) 
    is the canonical way to obtain the objectid of a list or player
    matching the given name.  An initial "*" indicates that this is 
    supposed to be a list.

$mail_agent:match_failed(objid,name) 
    is the mail_recipient counterpart to $command_utils:object_match_failed


Read Verbs


The following verbs may be used to extract headers/messages from
readable mail recipients/players;

:display_seq_headers (message sequence, current message number, last_read_date)

Does a @mail listing of the given message sequence.  If current
message number is given and the sequence includes it, we mark it with
a `>'.  Likewise if the sequence includes any new messages (i.e.,
dated after last_read_date), these are also indicated as such.

display_seq_full (message sequence, preamble)

Does a @read listing of the given message sequence.  Each message is
preceded by preamble.

=> {new current message number, new last_read_date}

:messages_in_seq (index)
  => {n, msg}

:messages_in_seq (message sequence)
  => {{n_1,msg_1},{n_2,msg_2},...}

where the n_i are message numbers and the msg_i are messages in
transmission format (see `help mail-format'[1])

:list_rmm ()

Does an `@unrmm list' listing of messages in .messages_going


Search Verbs


The following verbs can be used on a readable mail-recipient/player to
search for messages with fields matching a given pattern.

from_msg_seq (objectid or list [,mask])
  => message sequence: messages from (one of) the given objectid(s)

%from_msg_seq (string or list [,mask])
  => message sequence: messages with (one of) the given string(s)
     in the From: line

to_msg_seq (objectid or list [,mask])
  => message sequence: messages to (one of) the given objectid(s)

%to_msg_seq (string or list [,mask])
  => message sequence: messages with (one of) the given string(s)
     in the To: line

subject_msg_seq (string [,mask])
  => message sequence: messages with given string occurring in Subject:

body_msg_seq (string [,mask])
  => message sequence: messages with given string occurring in body of message

In all cases `mask' is a message sequence which one may supply to
limit the range of the search.  One way of looking at it is that the
message sequence to be returned is first intersected with mask.


Message Sequences


A "message sequence" is a handle by which one may refer to a
particular subset of a mail recipient's (player or
$mail_recipient-descendant) saved messages.  Routines like
rcpt:display_seq_headers or rcpt:display_seq_full need to be supplied
with message-sequence arguments to deterimine which headers or
full-messages to display.

Message sequences can in turn be obtained from routines like
rcpt:parse_message_seq, which takes a command-line description of a
message sequence on that particular recipient and returns the
corresponding message sequence handle.

The actual form of a message sequence (though you shouldn't actually
need to make use of this) is that of a set of integers in the format
used by $seq_utils (see `help $seq_utils').  It should however be
noted that these integers are *not* themselves message numbers, but
rather indices into the list of saved messages.  For example, if a
particular recipient holds 5 messages numbered 1,3,5,7,9.  Then the
message sequence handle representing messages 3,5,7 collectively,
would be {2,5} which is $seq_utils-ese for the range 2..4, namely the
second, third and fourth messages saved on that recipient.

The following verbs are available for obtaining indices to use in message sequences

  :length_all_msgs()    => total number of messages, or equivalently,
                        => index of last message
  :length_num_le(n)     => number of messages numbered <= n, or equivalently,
                        => index of highest numbered message <= n
  :exists_num_eq(n)     => 0 unless there exists a message numbered n in which
                           case we return the index of that message.
  :length_date_le(date) => number of messages dated <= date, or equivalently,
                        => index of most recent message dated <= date
 
  :length_date_gt(date) => number of messages dated > date

Note that r:length_date_gt(date) == r:length_all_msgs()-r:length_date_le(date).
The only reason :length_date_gt is provided as a separate routine is in order 
to do quick checks for the existence of new mail (as @rn needs to do).


Subscribing To Mail Recipients


There are two notions of being "subscribed" to a mailing
list/recipient.

  o  Hard subscribed == being on the recipient's .mail_forward list so
     that mail sent to this list is forwarded to one's own .messages
     as well (see `help mail-forwarding').
  o  Soft subscribed == keeping track of a current message for this
     recipient and (optionally) being on the recipient's .mail_notify list.

Each player has a .current_message property that contains, for each
recipient the player cares to keep track of, a current message number
and a last read date.

player:current_message(rcpt)                 (somewhat obsolete)
 => player's current message number for rcpt 

player:get_current_message(rcpt) 
 => player's {current message number for rcpt, last-read-date for rcpt}

player:make_current_message(rcpt)
 => adds a current_message entry for rcpt  (NOOP if rcpt == player)

player:set_current_message(rcpt,n|E_NONE,[,date])
 => sets player's current message number for rcpt to n iff n!=E_NONE
    updates the last-read-date for rcpt to date iff date > last-read-date

player:kill_current_message(rcpt)
 => removes current-message info for rcpt  (NOOP if rcpt == player)

On $mail_recipient, .mail_forward and .mail_notify are -c so one needs
to use the following verbs to actually modify them.

    :add_forward(@new_recipients)
    :delete_forward(@recpients)
    :add_notify(@new_notifiees)
    :delete_notify(@notifiees)

A recipient's owner is, of course, allowed to make arbitrary changes
to .mail_forward and .mail_notify.  However, the default versions of
these verbs also allow any player to add him/herself to a recipient's
.mail_forward or .mail_notify if the recipient is readable (see `help
MR-access') by him/her.

Likewise any player may use the :delete* verbs to delete him/herself
from any .mail_forward/.mail_notify list, regardless of his actual
access to the list.


Write Verbs


The following verbs can be used to manipulate writable mail
recipients/players:

:rm_message_seq (message sequence)
  Does an @rmmail.  Messages in message sequence are removed from this 
  recipient's saved .messages and written to .messages_going.

:undo_rmm ()
  Does an @unrmm.  Messages in .messages_going are copied back to .messages.

:expunge_rmm ()
  Does an @unrmm expunge.  Blows away .messages_going.

:renumber ()
  Does a @renumber.


Which :match...() Verb Do I Call?


There are many situations where one wishes to obtain an object from a
room or a player's .contents whose name/aliases matches a particular
string.  There are four main verbs available for this and it is
important to understand the distinctions between them and how they are
supposed to be used.

LOC:MATCH("X")
--------------

What you get looking for something that is inside LOC and named "X".
By default, this looks through LOC.contents to find a unique object
having a name or alias that has "X" as a prefix.

Essentially, you can think of :match as a contents-matching verb,
though, e.g., for rooms you also get matches on exits as well.

:MATCH_OBJECT AND :MY_MATCH_OBJECT
----------------------------------

   LOC:match_object("X", YOU)           [YOU defaults to player]
   YOU:my_match_object("X", LOC)        [LOC defaults to player.location]

What YOU get being located at LOC and looking for something named
"X". By default these both return $string_utils:match_object("X",LOC,YOU).

$STRING_UTILS:MATCH_OBJECT
--------------------------

$string_utils:match_object("X", LOC, YOU)

What you *would* get *if* YOU were a typical player, YOU were inside
LOC, YOU were looking for something named "X", *and* LOC were a
typical place.

In other words, $string_utils:match_object describes the
:match_object() algorithm for "typical places" and the
:my_match_object for "typical players":

  o  check for "X" being one of "", "me", "here", "$something", or "#n"
  o  try YOU:match("X") i.e., something in your inventory (maybe)
  o  try LOC:match("X") i.e., some object in the room (maybe)

The distinction between these location:match_object and
player:my_match_object has to do with whether the player or the
location should determine what the matching algorithm is.  Which one
you should use depends on the command that you are writing.  If you
are writing a command with a virtual-reality flavor, then you should
be respecting the room owner's idea of which objects you can "see" and
thus the command should be calling the location's :match_object verb.
If you are writing a building/programming command where it is
appropriate for the player to determine the matching algorithm ---
whether because the current location is irrelevant, not to be trusted,
or both --- then the player's :my_match_object verb should be called.

Examples
========

`look diamond in box'

calls box:match("diamond").  This is a match on the contents of box.

`take ball'

calls player.location:match_object("ball") to determine which "ball"
to take.  Note that if the room is dark, we might not be able to find
any "ball".

`@program widget:foo'

calls player:my_match_object("widget") to get the player's own idea of
what "widget" should be.  Note that if I were carrying something named
"widget" and expecting to be programming a :foo() verb on it, it would
be potentially disastrous should the room where I am decide for me to
be programming something else (not even necessarily called "widget").

OBJECT MATCHING FAILURES
------------------------

As with other matching routines, one gets back 

  o  $failed_match in the case of no matching object
  o  $ambiguous_match in the case of more than one matching object
  o  $nothing in the case of a blank string argument

or an object-id.  In these first 3 cases, one usually wants to
translate these nonresults to the player; this is what
$command_utils:object_match_failed.  The standard idiom to mimic what
the builtin parser does, say, with the direct object is

  dobj = foo:match_???(dobjstr);
  if($command_utils:object_match_failed(dobj, dobjstr))
    "...give up.  nothing to do.   error message has already printed...";
  else
    "...dobj is something useful.  Continue...";
    ...
  endif


Receiving-Mail


By definition a recipient "receives" a mail message when its
:receive_message verb is called with that message as an argument.

:new_message_num()
=> number that will be assigned to the next incoming message.

By default this returns the maximum of the message numbers appearing
in messages or .messages_going, incremented by 1.  If the recipient is
a player then the value returned will be 1 higher if it conflicts with
the player's current message number for him/herself.

:receive_message(msg,sender)

By default this first calls this:new_message_num to obtain a message
number to assign to the incoming message and then appends {num,msg} to
this.messages.  `sender', the original sender, is supplied in case one
wants different action depending on who is sending the message (e.g.,
mail-gagging).  The return value should be an error or string if
:receive_message is considered to have failed in some way.  Otherwise,
a number should be returned --- this number is given to any
:notify_mail routines that are called and is expected to either be 0
or the number assigned to the incoming message.

Note that :receive_message can do arbitrary things, including
resending the same message to a new destination.  Hacking
:receive_message to resend messages is different from using
.mail_forward in the following respects

  o  the resent message is considered to be a distinct message having
     this object as its "author" --- i.e., the From: line will necessarily
     be different.

  o  since this "forwarding" is invisible to the mailsystem, there is no
     protection against loops and multiple copies.



Sending-Mail


$mail_agent:send_message(from,recipients,headers,body)

  from:        sender of the message 
               (this must be you or something you own; otherwise => E_PERM)
  recipients:  object or list of objects (must all be players or 
               $mail_recipient descendants)
  headers:     either a string (contents of the Subject: line) 
               or a list {subject,replytos} replytos is a list 
               of objects designated to receive replies.
               Use {"",replytos} to have a Reply-to: without a Subject:

This is the canonical way to send a mail message from a program.

This calls $mail_agent:make_message to format the arguments into an
actual message (see `help mail-format'[1]) and then
$mail_agent:raw_send to do the actual sending which goes as follows:

  (1) Call :mail_forward on all recipients add any new recipients thus
      obtained to final recipient list, keep calling mail:forward on the new
      recipients until we obtain no additional recipients.  If one of the
      initial recipients is invalid, is not a player or $mail_recipient, or
      has its mail_forward return a string error, then we print the error
      message and abort at this point with no mail being sent.  If one of
      the later recipients bombs similarly, error messages are printed, but
      in this case mail still goes out to the other recipients.

  (2) Call :mail_notify on all recipients encountered in stage (1) to get a
      list of objects to notify.

  (3) All final recipients receive the message (see `help receive-mail'[2])

  (4) All notifications are delivered (using :notify_mail())

We return {0, @failed_recipients} if we bombed out at step
1. Otherwise return {1, @actual_rcpts} indicating what mail was sent.

 


help@nwe.ufl.edu
http://web.nwe.ufl.edu/writing/help/moo/jhc/core_utilities_help.html
Last modified: Tue May 15 14:55:22 2001