This is 1.8.2p1-1.8.3p0 patch. After applying this patch to PennMUSH 1.8.2p1,
you will have version 1.8.3p0. But if you don't have any good reason not to,
it's better to start with a fresh 1.8.3p0 tarball.

To apply this patch, save it to a file in your top-level MUSH directory,
and do the following:
        patch -p1 < 1.8.2p1-1.8.3p0.patch
        ./Configure
        make clean
        make install

If you use GNU patch 2.2, you probably want the above to be 'patch -b -p1',
not just 'patch -p1'.

Unix (or cygwin) users need not worry about failed hunks in src/switchinc.c,
hdrs/switches.h, hdrs/cmds.h, or hdrs/funs.h. These files are automatically
rebuilt on compile. On the off chance they appear not to be, simply
rm them and re-run make.

Then @shutdown and restart your MUSH.
    - Ervin/Noltar

In this patch:
Major changes:
  *  Rewrite of color handling. [GM]

Minor changes:
  * Cleaned up the internals of @wipe. [SW]
  * strmatch() now takes a third argument, to store wildcard captures. [GM]
  * Termination of OS/2 support. [SW]

Fixes:
  * Fixes included from versions up to 1.8.2p2.
*** 1_8_2p1/CHANGES.182	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/CHANGES.182	Sat Jan 27 02:21:10 2007
***************
*** 14,19 ****
--- 14,39 ----
  
  ==========================================================================
  
+ Version 1.8.2 patchlevel 2                      January 27, 2007
+ 
+ Fixes:
+   * Vector functions with an empty vector didn't return anything.
+     Reported by Talvo. [SW]
+   * Several typos in server messages. Fixed by Sketch, Stoko.
+   * Help fixes by Malix, [GM], Talvo, Sketch, and others.
+   * Crash bug in lmath() fixed. [GM]
+   * Crash bug in list functions fixed. [GM]
+   * list2arr in C now removes markup. list2arr_ansi() was added. [GM]
+   * Compilation problems on IRIX and similar OSes. [SW]
+   * Matcher bug with multiple wildcards fixed. Reported by Ian. [SW]
+   * Garbled output of locks from examine. Reported by Intrevis. [SW].
+   * regraballi() couldn't use its output seperator argument. Reported
+     by Jules. [SW]
+   * Looking at an object used the looker, not the lookee, as the origin
+     of the name for @ahear/@aahear/@amhear. [SW]
+   * Fixed the distribution of random numbers with a huge range. Reported
+     by Luke. 
+ 
  Version 1.8.2 patchlevel 1                      November 26, 2006
  
  Fixes:
*** 1_8_2p1/CHANGES.183	Wed Dec 31 16:00:00 1969
--- 1_8_3p0/CHANGES.183	Sat Jan 27 02:21:10 2007
***************
*** 0 ****
--- 1,28 ----
+ 
+ This is the most current changes file for PennMUSH. Please look it
+ over; each version contains new things which might significantly affect
+ the function of your server.  Changes are reported in reverse
+ chronological order (most recent first)
+ 
+ [TAP] is T. Alexander Popiel, a PennMUSH developer (aka Talek)
+ [SW] is Shawn Wagner, a PennMUSH developer (aka Raevnos)
+ [EEH] is Ervin Hearn III, a PennMUSH developer (aka Noltar)
+ [GM] is Greg Millam, a PennMUSH developer (aka Walker)
+ [3] refers to code by (or inspired by) TinyMUSH 3.0
+ [MUX] refers to code by (or inspired by) TinyMUX 2.x
+ [Rhost] refers to code by (or inspired by) RhostMUSH
+ 
+ ==========================================================================
+ 
+ Version 1.8.3 patchlevel 0                      January 27, 2007
+ 
+ Major changes:
+   *  Rewrite of color handling. [GM]
+ 
+ Minor changes:
+   * Cleaned up the internals of @wipe. [SW]
+   * strmatch() now takes a third argument, to store wildcard captures. [GM]
+   * Termination of OS/2 support. [SW]
+ 
+ Fixes:
+   * Fixes included from versions up to 1.8.2p2.
*** 1_8_2p1/INSTALL	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/INSTALL	Sat Jan 27 02:21:10 2007
***************
*** 32,39 ****
  (current databases), "txt" (text files and directories for building them),
  "log" (log files), and "save" (backup databases).  Finally, the "hints"
  directory is used during the installation process, the "po" directory
! holds translation message files, and the "os2" directory contains files
! of using in building for OS/2.
  
   pennmush--+-> src
             +-> hdrs 
--- 32,38 ----
  (current databases), "txt" (text files and directories for building them),
  "log" (log files), and "save" (backup databases).  Finally, the "hints"
  directory is used during the installation process, the "po" directory
! holds translation message files.
  
   pennmush--+-> src
             +-> hdrs 
***************
*** 46,52 ****
             |              +-> log 
             |              \-> save 
             +-> hints 
-            +-> os2
             +-> po
             +-> utils 
             \-> win32 
--- 45,50 ----
***************
*** 55,84 ****
  PennMUSH has been tested on a fairly wide variety of machines and
  operating systems including at least:
  
! 	NeXT Mach 2.1
! 	Sun Sparc SunOS 4.1.x
! 	Sun Sparc and i386 Solaris 2.x 
! 	DEC Decstation Ultrix 4.x and OSF/1
! 	DEC Alpha OSF/1 and Linux
! 	SGI Indy Irix 5.x and 6.x
! 	HP 9000 series HP-UX 8.x
! 	IBM RS/6000 AIX 3.2
! 	IBM S/390 Linux
! 	Novell Unixware SVR4
! 	Linux
! 	FreeBSD
! 	AT&T SVR4
! 	Windows 95/NT cygwin, mingw32, and MSVC++
! 	OS/2
  
  There's no real reason why PennMUSH shouldn't compile on any 32-bit
! or better BSD, System V, or POSIX operating system.  Javelin does his
! development on a Linux PC these days.
! 
! If you have serious problems, contact Javelin and he will try to help
! you. Email is the best way to get a fast response; in an emergency, you
! can bother him on a MUD, but for code problems, email will probably get
! you a better response.
  
  ============================================================================
  
--- 53,66 ----
  PennMUSH has been tested on a fairly wide variety of machines and
  operating systems including at least:
  
! 	GNU/Linux, NetBSD, FreeBSD on many architectures
!         Mac OS X on PowerPC
!         Microsoft Windows on x86
!          
  
  There's no real reason why PennMUSH shouldn't compile on any 32-bit
! or better BSD, System V, or POSIX operating system.  Development is
! primarily done on GNU/Linux and Mac OS X systems.
  
  ============================================================================
  
***************
*** 146,152 ****
  A pre-built binary is frequently available for win32 users who don't
  want to customize their MUSH server, and don't feel like compiling it
  themselves.  This binary distribution may not contain the src, hdrs,
! hints, or os2 directories and may be missing several key files (like
  Configure) from the pennmush directory.  It does include the options.h
  that it was built with, as an aid to those who decide later that they
  want to customize the server; they are useful as a baseline to work from.
--- 128,134 ----
  A pre-built binary is frequently available for win32 users who don't
  want to customize their MUSH server, and don't feel like compiling it
  themselves.  This binary distribution may not contain the src, hdrs,
! or hints directories and may be missing several key files (like
  Configure) from the pennmush directory.  It does include the options.h
  that it was built with, as an aid to those who decide later that they
  want to customize the server; they are useful as a baseline to work from.
*** 1_8_2p1/MANIFEST	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/MANIFEST	Sat Jan 27 02:21:10 2007
***************
*** 6,11 ****
--- 6,12 ----
  CHANGES.180
  CHANGES.181
  CHANGES.182
+ CHANGES.183
  CHANGES.OLD
  COPYRITE
  Configure
***************
*** 59,64 ****
--- 60,66 ----
  game/txt/hlp/pennv180.hlp
  game/txt/hlp/pennv181.hlp
  game/txt/hlp/pennv182.hlp
+ game/txt/hlp/pennv183.hlp
  game/txt/hlp/pennvOLD.hlp
  game/txt/index-files.pl
  game/txt/motd.txt
***************
*** 127,143 ****
  hints/mingw32.sh
  hints/next.sh
  hints/openbsd.sh
- hints/os2.sh
  hints/solaris_2.sh
  hints/sunos_4.sh
  hints/ultrix.sh
  hints/win32-gcc.sh
  hints/win32.sh
  options.h.dist
- os2/GCCOPT.CMD
- os2/Makefile
- os2/Penn-OS2.htm
- os2/config.h
  po/Makefile
  src/Makefile.SH
  src/SWITCHES
--- 129,140 ----
*** 1_8_2p1/Makefile.SH	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/Makefile.SH	Sat Jan 27 02:21:10 2007
***************
*** 93,99 ****
  src/switchinc.c: src/SWITCHES Patchlevel
  	(cd utils; sh mkcmds.sh switches)
  
! hdrs/funs.h: src/fun*.c src/bsd.c src/conf.c src/extmail.c src/help.c src/wiz.c src/sql.c Patchlevel
  	(cd utils; sh mkcmds.sh functions)
  
  hdrs/patches.h: patches/*
--- 93,99 ----
  src/switchinc.c: src/SWITCHES Patchlevel
  	(cd utils; sh mkcmds.sh switches)
  
! hdrs/funs.h: src/fun*.c src/bsd.c src/conf.c src/extmail.c src/help.c src/markup.c src/wiz.c src/sql.c Patchlevel
  	(cd utils; sh mkcmds.sh functions)
  
  hdrs/patches.h: patches/*
*** 1_8_2p1/Patchlevel	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/Patchlevel	Sat Jan 27 02:21:10 2007
***************
*** 1,2 ****
  Do not edit this file. It is maintained by the official PennMUSH patches.
! This is PennMUSH 1.8.2p1
--- 1,2 ----
  Do not edit this file. It is maintained by the official PennMUSH patches.
! This is PennMUSH 1.8.3p0
*** 1_8_2p1/game/restart	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/restart	Sat Jan 27 02:21:10 2007
***************
*** 138,143 ****
  fi
  
  DATEMSK="${GAMEDIR}/getdate.template"
- export DATEMSK
  
  LC_ALL=$LANG LANG=$LANG ./netmush $GAMEDIR/$CONF_FILE &
--- 138,142 ----
*** 1_8_2p1/game/txt/hlp/pennattr.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennattr.hlp	Sat Jan 27 02:21:10 2007
***************
*** 1,3 ****
--- 1,44 ----
+ & ATTRIBUTE FLAGS
+   Attribute flags are set on an object's attributes using @set, or applied
+   to attributes globally using @attrib. Their names (and, when applicable,
+   the character used in examine as shorthand for the flag) include:
+ 
+   no_command ($)    Attribute will not be checked for '$' commands and
+                     '^' listen patterns.
+   visual (v)        Attribute can be seen by anyone via get(), eval(),
+                     ufun(), zfun(), and similar functions.
+   no_inherit (i)    Attribute will not be inherited by the children of
+                     this object.
+   no_clone (c)      Attribute will not be copied if the object is @clone'd.
+   regexp (R)        Match $-commands and ^-listens using regular expressions.
+                     See 'help regexps'.
+   case (C)          Match $-commands and ^-listens case sensitively.
+   safe (S)          Attribute may not be modified, without unsetting this flag.
+   mortal_dark (m)   Attribute cannot be seen by mortals. This flag can only
+                     be set by royalty and wizards.  "hidden" is a synonym.
+ 
+   Continued in 'help attribute flags2'
+ & ATTRIBUTE FLAGS2
+   noname (N)        Attribute won't show name in @o-* messages.
+   nospace (s)       Attribute won't append a space in @o-* messages.
+   wizard (w)        Attribute can only be set by wizards.
+                     This flag can only be set by royalty and wizards.
+   veiled (V)        Attribute value will not be shown on default examine,
+                     but is still otherwise accessible (for spammy attribs).
+   debug (b)         Show debug output when this attribute is evaluated.
+   nearby (n)        Even if this attribute is visual, it can only be
+                     retrieved if you're co-located with the object.
+   public (p)        This attribute can be evaluated by any object, even
+                     if safer_ufun is in use. DANGEROUS! AVOID!
+   aahear (A)        ^-listens on this attribute match like @aahear
+   amhear (M)        ^-listens on this attribute match like @amhear
+ 
+   Continued in 'help attribute flags3'
+ & ATTRIBUTE FLAGS3
+   prefixmatch       When a user attempts to set an attribute using @<attrib>,
+                     this attribute will be matched down to its unique
+                     prefixes. This flag is primarily used internally.
+   `                 This attribute is a branch. See: help ATTRIBUTE TREES
  & ATTRIBUTE TREES
  & ATTR TREES
  & ATTRIB TREES
***************
*** 23,29 ****
  act as a branch. If you try to create an unsupported leaf, branch
  attributes will be created as needed to support it.
  
! See help attribute trees2 for more information and examples.
  
  & ATTRIBUTE TREES2
  & ATTR TREES2
--- 64,70 ----
  act as a branch. If you try to create an unsupported leaf, branch
  attributes will be created as needed to support it.
  
! See 'help attribute trees2' for more information and examples.
  
  & ATTRIBUTE TREES2
  & ATTR TREES2
***************
*** 46,52 ****
  Branch attributes will be displayed with a ` in the attribute flags
  on examine. 
  
! See help attribute trees3 for more information and examples.
  
  & ATTRIBUTE TREES3
  & ATTR TREES3
--- 87,93 ----
  Branch attributes will be displayed with a ` in the attribute flags
  on examine. 
  
! See 'help attribute trees3' for more information and examples.
  
  & ATTRIBUTE TREES3
  & ATTR TREES3
***************
*** 73,79 ****
    &CHAR`SKILLS #30 = coding:3 documentation:1 obfuscation:5
    ...
  
! See help attribute trees4 for information about @parent and attribute trees.
  & ATTRIBUTE TREES4
  & ATTR TREES4
  & ATTRIB TREES4
--- 114,121 ----
    &CHAR`SKILLS #30 = coding:3 documentation:1 obfuscation:5
    ...
  
! See 'help attribute trees4' for information about @parent and attribute trees.
! 
  & ATTRIBUTE TREES4
  & ATTR TREES4
  & ATTRIB TREES4
*** 1_8_2p1/game/txt/hlp/pennchat.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennchat.hlp	Sat Jan 27 02:21:10 2007
***************
*** 33,39 ****
    as normal, preventing user-defined commands like "+last" from
    being clobbered by the chat system.
  
!   See also: chat, CHAN_USEFIRSTMATCH
  & CHAN_USEFIRSTMATCH
    CHAN_USEFIRSTMATCH (any type)
  
--- 33,39 ----
    as normal, preventing user-defined commands like "+last" from
    being clobbered by the chat system.
  
! See also: chat, CHAN_USEFIRSTMATCH
  & CHAN_USEFIRSTMATCH
    CHAN_USEFIRSTMATCH (any type)
  
***************
*** 56,62 ****
    @nscemit is a wizard-only version that does not include nospoof
    information in broadcasts.
  
!   See also: chat
  & @channel
    @channel/list [<channel-prefix>]
    @channel/what [<channel-prefix>]
--- 56,62 ----
    @nscemit is a wizard-only version that does not include nospoof
    information in broadcasts.
  
! See also: chat
  & @channel
    @channel/list [<channel-prefix>]
    @channel/what [<channel-prefix>]
***************
*** 80,86 ****
    @channel/what will show you the channel's name, access information,
    and a description of the channel's purpose.
  
!   More commands are provided in "help @channel2".  See also: chat
  & @channel2
    @channel/who <channel>
    @channel/hide <channel> = <yes|no>
--- 80,88 ----
    @channel/what will show you the channel's name, access information,
    and a description of the channel's purpose.
  
!   More commands are provided in "help @channel2".
!   
! See also: chat
  & @channel2
    @channel/who <channel>
    @channel/hide <channel> = <yes|no>
***************
*** 149,155 ****
  
    @channel/delete removes a channel. You must own it or be Wizard.
    @channel/desc sets the channel's description, shown on @channel/what.
!     Descriptions are limited to 256 characters.
    @channel/rename is used to rename a channel.
  
    See "help @channel5" for more. See also "help @clock".
--- 151,158 ----
  
    @channel/delete removes a channel. You must own it or be Wizard.
    @channel/desc sets the channel's description, shown on @channel/what.
!     Descriptions are limited to 256 characters. If there are any commas
!     in the description, the whole string should be enclosed in {}'s.
    @channel/rename is used to rename a channel.
  
    See "help @channel5" for more. See also "help @clock".
***************
*** 334,340 ****
    With two arguments, sets the lock if you would be able to do so via
    @clock.
  
!   See also: @clock
  & CRECALL()
    crecall(<channel>[, <lines> [, <start line> [, <osep> [, <timestamps?> ]]]])
  
--- 337,343 ----
    With two arguments, sets the lock if you would be able to do so via
    @clock.
  
! See also: @clock
  & CRECALL()
    crecall(<channel>[, <lines> [, <start line> [, <osep> [, <timestamps?> ]]]])
  
***************
*** 343,349 ****
    <osep>. If <timestamps?> is a true value, the recalled lines will
    include their timestamps; otherwise, they will not.
  
!   See also: @channel3
  & Channel functions
    Channel functions work with the channel system.
  
--- 346,352 ----
    <osep>. If <timestamps?> is a true value, the recalled lines will
    include their timestamps; otherwise, they will not.
  
! See also: @channel3
  & Channel functions
    Channel functions work with the channel system.
  
*** 1_8_2p1/game/txt/hlp/penncmd.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/penncmd.hlp	Sat Jan 27 02:21:10 2007
***************
*** 16,21 ****
--- 16,23 ----
   
    @-ATTRIBUTES   @-BUILDING     @-GENERAL      @-WIZARD
   
+   Commands that can only be used by connected players are listed in
+   HELP SOCKET COMMANDS.
  & @-ATTRIBUTES
  These '@' commands set standard message/action sets on objects. Each comes
  in 3 versions: @<whatever>, @o<whatever>, and @a<whatever>. Only the
***************
*** 300,306 ****
  
    @alias has no effect on non-players.
  
!   See also: @name, alias(), fullalias()
  & @allhalt
    @allhalt
  
--- 302,308 ----
  
    @alias has no effect on non-players.
  
! See also: @name, alias(), fullalias()
  & @allhalt
    @allhalt
  
***************
*** 446,452 ****
    don't like @switch. If <command> is given, it is executed instead of
    the rest of the commands in the current queue.
  
!   @assert does the inverse: stopping execution if <boolean> evaluse to false.
  
    Examples:
    > @va obj=$testme *:@pemit %#=Before break;@break %0;@pemit %#=After break
--- 448,454 ----
    don't like @switch. If <command> is given, it is executed instead of
    the rest of the commands in the current queue.
  
!   @assert does the inverse: it stops execution if <boolean> evaluates to false.
  
    Examples:
    > @va obj=$testme *:@pemit %#=Before break;@break %0;@pemit %#=After break
***************
*** 461,471 ****
    Third
    (The @switch is run, which queues 'think Third', think First is
     run, displaying 'First', command execution is broken (so we never
!    think Second, and then the queued 'think Third' is run, displaying
     Third. If you figured that out, you have a very good understanding
     of the PennMUSH queue. :)
  
!   See also: ACTION LISTS, QUEUE, BOOLEAN VALUES
  & @charges
    @charges <object> = <integer>
  
--- 463,473 ----
    Third
    (The @switch is run, which queues 'think Third', think First is
     run, displaying 'First', command execution is broken (so we never
!    think Second), and then the queued 'think Third' is run, displaying
     Third. If you figured that out, you have a very good understanding
     of the PennMUSH queue. :)
  
! See also: ACTION LISTS, QUEUE, BOOLEAN VALUES
  & @charges
    @charges <object> = <integer>
  
***************
*** 553,562 ****
    
  See also: @chzone, ZONES
  & @clone
!   @clone <object, room, or exit>[=<new name>]
!   @clone/preserve <object, room, or exit>[=<new name>]
  
!   For objects, creates an exact duplicate of it and puts it in the
    current room. For exits, it creates an exact duplicate of that
    exit, except the clone's source is the current room rather than
    whatever the original exit's source was. For rooms, creates an
--- 555,564 ----
    
  See also: @chzone, ZONES
  & @clone
!   @clone <thing, room, or exit>[=<new name>]
!   @clone/preserve <thing, room, or exit>[=<new name>]
  
!   For things, creates an exact duplicate of it and puts it in the
    current room. For exits, it creates an exact duplicate of that
    exit, except the clone's source is the current room rather than
    whatever the original exit's source was. For rooms, creates an
***************
*** 687,693 ****
    through '@conformat here = Contents: [iter(%0,name(##))]',
    for example. More complex things are, obviously, possible.
   
!   See also: @exitformat, @nameformat, @descformat
  & @invformat
    @invformat <object> [=<format>]
  
--- 689,695 ----
    through '@conformat here = Contents: [iter(%0,name(##))]',
    for example. More complex things are, obviously, possible.
   
! See also: @exitformat, @nameformat, @descformat
  & @invformat
    @invformat <object> [=<format>]
  
***************
*** 702,708 ****
    through '@invformat me = You've got: [iter(%0,name(##))]',
    for example. More complex things are, obviously, possible.
   
!   See also: inventory
  & @descformat
    @descformat <object> [=<text>]
  
--- 704,710 ----
    through '@invformat me = You've got: [iter(%0,name(##))]',
    for example. More complex things are, obviously, possible.
   
! See also: inventory
  & @descformat
    @descformat <object> [=<text>]
  
***************
*** 714,720 ****
    This is useful for things like room parents that enforce a consistent
    "look" for each room's @desc.
  
!   See also: @exitformat, @nameformat, @conformat, @idescformat
  & @idescformat
    @idescformat <object> [=<text>]
  
--- 716,722 ----
    This is useful for things like room parents that enforce a consistent
    "look" for each room's @desc.
  
! See also: @exitformat, @nameformat, @conformat, @idescformat
  & @idescformat
    @idescformat <object> [=<text>]
  
***************
*** 726,732 ****
    This is useful for things like object parents that enforce a consistent
    "look" for each object's @idesc.
  
!   See also: @exitformat, @nameformat, @conformat, @descformat
  & @nameaccent
    @nameaccent <object> [=<accent template>]
  
--- 728,734 ----
    This is useful for things like object parents that enforce a consistent
    "look" for each object's @idesc.
  
! See also: @exitformat, @nameformat, @conformat, @descformat
  & @nameaccent
    @nameaccent <object> [=<accent template>]
  
***************
*** 740,746 ****
    If a container has both a @nameaccent and a @nameformat, the
    @nameformat is used.
  
!   See also: accent(), @nameformat
  & @nameformat
    @nameformat <object> [=<format>]
  
--- 742,748 ----
    If a container has both a @nameaccent and a @nameformat, the
    @nameformat is used.
  
! See also: accent(), @nameformat
  & @nameformat
    @nameformat <object> [=<format>]
  
***************
*** 754,760 ****
    Example: Show the room's zone after its name.
    @nameformat here = %1 [if(isdbref(zone(%0)),<[name(zone(%0))]>)]
  
!   See also: @exitformat, @conformat, @descformat
  & @cost
    @cost <object> = <amount> 
  
--- 756,762 ----
    Example: Show the room's zone after its name.
    @nameformat here = %1 [if(isdbref(zone(%0)),<[name(zone(%0))]>)]
  
! See also: @exitformat, @conformat, @descformat
  & @cost
    @cost <object> = <amount> 
  
***************
*** 803,818 ****
  & @create
    @create <name> [=<cost>]
  
!   Creates a thing with the specified name. Creating an object costs
    a certain amount of MUSH money, which usually defaults to 10 pennies.
    You can specify a higher cost if you wish, but not a lower one.
!   This cost is refunded if you @destroy/@recycle the object.
  
!   Once you have created an object, you can use it as a PUPPET, to store
    USER-DEFINED COMMANDS, or just as a prop. Some MUSHes choose to limit 
    the number of objects that players can create by setting a QUOTA.
   
!   See also: give, @quota, MONEY
  & @dbck
    @dbck
    This is a wizard only command.  It forces the database to perform a
--- 805,820 ----
  & @create
    @create <name> [=<cost>]
  
!   Creates a thing with the specified name. Creating a thing costs
    a certain amount of MUSH money, which usually defaults to 10 pennies.
    You can specify a higher cost if you wish, but not a lower one.
!   This cost is refunded if you @destroy/@recycle the thing.
  
!   Once you have created a thing, you can use it as a PUPPET, to store
    USER-DEFINED COMMANDS, or just as a prop. Some MUSHes choose to limit 
    the number of objects that players can create by setting a QUOTA.
   
! See also: give, @quota, MONEY
  & @dbck
    @dbck
    This is a wizard only command.  It forces the database to perform a
***************
*** 885,894 ****
      Don't output commands to set attribute flags if those flags are the
      defaults for that attribute on that MUSH.
    
!   If an attribute contains special characters, such as %r or %t, or
!   begins or ends with spaces, then the command sent will be an @set command
!   that has its special characters escaped, and with such %rs, %ts, and %bs
!   as to exactly duplicate the attribute as it is currently set.
  
  (continued in help @decompile3)
  & @decompile3
--- 887,896 ----
      Don't output commands to set attribute flags if those flags are the
      defaults for that attribute on that MUSH.
    
!   If an attribute contains special characters, such as %r or %t, ansi, pueblo,
!   or begins or ends with spaces, then the command sent will be an @set command
!   that has its special characters escaped, and with such %rs, %ts, and %bs as
!   to exactly duplicate the attribute as it is currently set.
  
  (continued in help @decompile3)
  & @decompile3
***************
*** 999,1006 ****
      This will do just the same as the above, except it will also create
      an exit named "Out <S>;s;south;out;o" coming back from the kitchen
      to whatever room you are currently in.
!     
! See also: @open, @link, EXITS, @create
  & @doing
    @doing <message>
    @doing/header <message>
--- 1001,1008 ----
      This will do just the same as the above, except it will also create
      an exit named "Out <S>;s;south;out;o" coming back from the kitchen
      to whatever room you are currently in.
! 
! See also: @open, @link, EXITS, @create, DBREF
  & @doing
    @doing <message>
    @doing/header <message>
***************
*** 1053,1065 ****
    @drop <object> [=<message>]. <object> can be a thing, player, exit,
    or room, specified as <name> or #<number> or 'me' or 'here'. Sets 
    the drop message for <object>. The message is displayed when a 
!   player drops <object>. Without  a message argument, it clears the 
    message. 
  
    On an exit, this message is sent to a player after they pass through
    the exit.
  
!   See also: drop, @odrop, @adrop.
  & @dump
    @dump [/paranoid] [check interval]
   
--- 1055,1067 ----
    @drop <object> [=<message>]. <object> can be a thing, player, exit,
    or room, specified as <name> or #<number> or 'me' or 'here'. Sets 
    the drop message for <object>. The message is displayed when a 
!   player drops <object>. Without a message argument, it clears the 
    message. 
  
    On an exit, this message is sent to a player after they pass through
    the exit.
  
! See also: drop, @odrop, @adrop.
  & @dump
    @dump [/paranoid] [check interval]
   
***************
*** 1083,1089 ****
    If you have a chair, you could "@ealias chair = sit down" and then just
    type "sit down" instead of "enter chair" - using the object name is
    not necessary. Note that the enter alias is checked after normal exits.
!   Like an exit, it may have a semi-colon separated list of words,
    i.e. sit down;sit;sit on chair
  & @edit
  & @gedit
--- 1085,1091 ----
    If you have a chair, you could "@ealias chair = sit down" and then just
    type "sit down" instead of "enter chair" - using the object name is
    not necessary. Note that the enter alias is checked after normal exits.
!   Like an exit, it may have a semicolon separated list of words,
    i.e. sit down;sit;sit on chair
  & @edit
  & @gedit
***************
*** 1160,1166 ****
    @enable <option> is the same thing as @config/set <option>=yes
    @disable <option> is the same thing as @config/set <option>=no
  
!   See also: @config
  & @zenter
  & @ozenter
  & @azenter
--- 1162,1168 ----
    @enable <option> is the same thing as @config/set <option>=yes
    @disable <option> is the same thing as @config/set <option>=no
  
! See also: @config
  & @zenter
  & @ozenter
  & @azenter
***************
*** 1251,1257 ****
    One could change the format to 'Exits: Exit1 Exit2 Exit3' through
    '@exitformat here = Exits: [iter(%0,name(##))]', for example.
  
!   See also: TRANSPARENT, @conformat, @nameformat, @descformat
  & @failure
    @failure <object> [=<message>]
  
--- 1253,1259 ----
    One could change the format to 'Exits: Exit1 Exit2 Exit3' through
    '@exitformat here = Exits: [iter(%0,name(##))]', for example.
  
! See also: TRANSPARENT, @conformat, @nameformat, @descformat
  & @failure
    @failure <object> [=<message>]
  
***************
*** 1333,1338 ****
--- 1335,1347 ----
    the object (using the 'follow' command). 
  
  See also: follow, unfollow, followers(), @ofollow, @afollow
+ & @unfollow
+   @unfollow <object> = <message>
+ 
+   Sets the message shown to someone after they stop following 
+   the object (using the 'unfollow' command). 
+ 
+ See also: follow, unfollow, followers(), @ounfollow, @aunfollow
  & @FORWARDLIST
    @forwardlist <object> [=<list of dbrefs>]
  
***************
*** 1364,1376 ****
    @lock/forward me=$me)
  
  See also: DEBUG
- & @unfollow
-   @unfollow <object> = <message>
- 
-   Sets the message shown to someone after they stop following 
-   the object (using the 'unfollow' command). 
- 
- See also: follow, unfollow, followers(), @ounfollow, @aunfollow
  & @force
    @force[/noeval] <object>=<command>
  
--- 1373,1378 ----
***************
*** 1391,1397 ****
    the forcee runs the command. @force/noeval will only
    evaluate the command when the forcee runs it.
  
! Example:
    @create Lackey
    > Created: Object #103 
    @fo Lackey = go east
--- 1393,1399 ----
    the forcee runs the command. @force/noeval will only
    evaluate the command when the forcee runs it.
  
! Examples:
    @create Lackey
    > Created: Object #103 
    @fo Lackey = go east
***************
*** 1433,1439 ****
        flag table. It requires the exact flag name or alias to be used.
        Be very very careful with this. 
  
!   See also: help flags. See help @flag2 for information on @flag/add
  & @flag2
    @flag/add is used to add a new flag with the given name. Arguments
    other than the flag name are optional:
--- 1435,1441 ----
        flag table. It requires the exact flag name or alias to be used.
        Be very very careful with this. 
  
! See also: help flags. See help @flag2 for information on @flag/add
  & @flag2
    @flag/add is used to add a new flag with the given name. Arguments
    other than the flag name are optional:
***************
*** 1899,1954 ****
    > Recorder records: Whee!
  
  See also: LISTENING, @ahear, @amhear, @aahear
- & NEW LOCKS
-   In PennMUSH 1.7.5, several new features have been added to locks.
- 
-   Locks can now be inherited off of parents, just like attributes.
-   By default, locks are set no_inherit, but this flag can be cleared.
- 
-   There are now lock flags including ones to control inheritance,
-   copying in a @clone, who can set them, and so on. Details are
-   in HELP @LSET.
-   
-   Indirect lock keys (@#1234) can now refer to other lock names on
-   objects, not just a lock of the same name. See HELP @LOCK4.
- 
-   There is a new lock key for testing flags and object types. 
-   See HELP @LOCK9 for more information.
  & LOCKING
  & LOCKS
  & @lock
    @lock[/<switch>] <object>=<key> 
    
!   This command "locks" the object, specifying (by the key) who or what can
!   do certain things with the object. There are many different types of locks,
!   all of which are described in "help locktypes" and which are designated by
!   the switch. The "basic" lock determines, for players and objects, who can
!   pick them up. For exits, it determines who can go through the exit.
!   All other locks can be set the same way as the basic lock.
  
    Whenever you "pass" the basic lock, you succeed in doing something with
    the object. This triggers the @success/@osuccess/@asuccess
    messages and actions. If you fail to pass the basic lock, you trigger
    the @failure/@ofailure/@afailure messages and actions. Other locktypes
!   may also have such success/failure messages.
  
    You can specify <object> and <key> as either the name of an object in
    the immediate area, a DBREF number, "me", or "here". 
  
-   Many new features have recently been added to locks. See HELP NEW LOCKS
-   for details.
- 
  (continued in help @lock2)  
  & @lock2
    You can lock an object in several different ways. The simplest lock is to
!   lock it to one other thing:
     @lock My Toy = = me
       This locks the object "My Toy" to you and you alone. It is recommended
       that you @lock me == me in order to prevent anyone else from picking
!      you up. The two = signs are NOT a typo!
! 
!   You can lock an object -against- one other object as well, using the '!' 
!   symbol:
      @lock Shared Toy = !Vector Sigma
        This locks the object "Shared Toy" to everyone -except- Vector Sigma.
        Everyone except Vector will be able to pick up the object.
--- 1901,1940 ----
    > Recorder records: Whee!
  
  See also: LISTENING, @ahear, @amhear, @aahear
  & LOCKING
  & LOCKS
  & @lock
    @lock[/<switch>] <object>=<key> 
    
!   This command "locks" the object, specifying a key which determines who or
!   what can do certain things with the object. There are many different types
!   of locks, all of which are described in "help locktypes" and which are
!   designated by the switch. The "basic" lock determines, for players and
!   things, who can pick them up. For exits, it determines who can go through
!   the exit. All other locks can be set the same way as the basic lock.
  
    Whenever you "pass" the basic lock, you succeed in doing something with
    the object. This triggers the @success/@osuccess/@asuccess
    messages and actions. If you fail to pass the basic lock, you trigger
    the @failure/@ofailure/@afailure messages and actions. Other locktypes
!   may also have such success/failure messages: see "help failure" for info.
  
    You can specify <object> and <key> as either the name of an object in
    the immediate area, a DBREF number, "me", or "here". 
  
  (continued in help @lock2)  
  & @lock2
    You can lock an object in several different ways. The simplest lock is to
!   lock it to one other object with the '=', signifying a DBRef # match.:
     @lock My Toy = = me
       This locks the object "My Toy" to you and you alone. It is recommended
       that you @lock me == me in order to prevent anyone else from picking
!      you up. The two = signs are NOT a typo! The first is part of the @lock
!      syntax (as shown at the top of "help @lock") the second is a lock key
!      that means "exactly this object".
!   
!   You can lock an object -against- another object as well, using the '!' 
!   symbol before any other key:
      @lock Shared Toy = !Vector Sigma
        This locks the object "Shared Toy" to everyone -except- Vector Sigma.
        Everyone except Vector will be able to pick up the object.
***************
*** 2101,2107 ****
      &deny commands = #200 #1020
      @lock/use commands = !dbreflist^deny & dbreflist^allow 
  
! See also: locktypes, @clock, objid()
  & locktypes
  & locklist
  & lock types
--- 2087,2093 ----
      &deny commands = #200 #1020
      @lock/use commands = !dbreflist^deny & dbreflist^allow 
  
! See also: locktypes, @clock, elock(), lock(), objid()
  & locktypes
  & locklist
  & lock types
***************
*** 2137,2149 ****
    @lock/examine         Who can examine this object if it's VISUAL
    @lock/chzone          Who can @chzone to this object if it's a ZMO
    @lock/forward         Who can @forwardlist a message to this object 
!   @lock/control		Who can control this object (only if set)
!   @lock/dropto		Who can trigger this container's drop-to.
!   @lock/destroy		Who can destroy this object if it's DESTROY_OK
    @lock/interact        Who can send sound (say/pose/emit/etc) to this object
    @lock/take            Who can get things contained in this object
  
!   See also: @lock, @lset, @clock, FAILURE
  & @lset
    @lset <object>/<lock type>=[!]<flag>
  
--- 2123,2135 ----
    @lock/examine         Who can examine this object if it's VISUAL
    @lock/chzone          Who can @chzone to this object if it's a ZMO
    @lock/forward         Who can @forwardlist a message to this object 
!   @lock/control	        Who can control this object (only if set)
!   @lock/dropto	        Who can trigger this container's drop-to.
!   @lock/destroy	        Who can destroy this object if it's DESTROY_OK
    @lock/interact        Who can send sound (say/pose/emit/etc) to this object
    @lock/take            Who can get things contained in this object
  
! See also: @lock, @lset, @clock, FAILURE
  & @lset
    @lset <object>/<lock type>=[!]<flag>
  
***************
*** 2166,2172 ****
    are /check, /cmd, /conn, /err, /trace, and /wiz, specifying which
    file to log to.  /cmd is default.
  
!   See also: @logwipe
  & @logwipe
    @logwipe/<switch> <password>
  
--- 2152,2158 ----
    are /check, /cmd, /conn, /err, /trace, and /wiz, specifying which
    file to log to.  /cmd is default.
  
! See also: @logwipe
  & @logwipe
    @logwipe/<switch> <password>
  
***************
*** 2175,2181 ****
    log wipe password from the MUSH's configuration file to use this
    command.
  
!   See also: @log
  & @map
    @map[/delim] [<delim>] <list> = <function or pattern>
   
--- 2161,2167 ----
    log wipe password from the MUSH's configuration file to use this
    command.
  
! See also: @log
  & @map
    @map[/delim] [<delim>] <list> = <function or pattern>
   
***************
*** 2257,2263 ****
    ONAME and ANAME. The old name will be passed at %0 to these;
    the new name will be passed as %1.
  
!   See also: @alias
  & @newpassword
    @newpassword <player> = <password>
  
--- 2243,2249 ----
    ONAME and ANAME. The old name will be passed at %0 to these;
    the new name will be passed as %1.
  
! See also: @alias
  & @newpassword
    @newpassword <player> = <password>
  
***************
*** 2310,2318 ****
    nospoof information is just useless noise. They take all switches
    of their respective commands.
  
! See also: @emit, @lemit, @pemit, @prompt, @remit, @oemit, @zemit, 
!           nsemit(), nslemit(), nspemit(), nsprompt(), nsremit(), nsoemit(), 
!           nszemit(), PROMPT_NEWLINES
  & @odeath
    @odeath <player> [=<message>]
  
--- 2296,2304 ----
    nospoof information is just useless noise. They take all switches
    of their respective commands.
  
! See also: @emit, @lemit, @pemit, @prompt, @remit, @oemit, @zemit, nsemit(),
!   nslemit(), nspemit(), nsprompt(), nsremit(), nsoemit(), nszemit(),
!   PROMPT_NEWLINES
  & @odeath
    @odeath <player> [=<message>]
  
***************
*** 2493,2499 ****
    
    If the =<message> part is omitted, the message will be reset.
  
! Ex: @opay Vending Machine=sticks a quarter in the vending machine.
  
  See also: give, @cost, @payment, @apayment, MONEY
  & @open
--- 2479,2485 ----
    
    If the =<message> part is omitted, the message will be reset.
  
!   Ex: @opay Vending Machine=sticks a quarter in the vending machine.
  
  See also: give, @cost, @payment, @apayment, MONEY
  & @open
***************
*** 2504,2511 ****
    This command opens an exit in the room you are standing in with the 
    specified name. You can then use the @link command to set the exit's
    destination, or you can set it automatically by using the DBREF of a
!   destination, which can be a room or object. (Note that you CANNOT open
!   exits from objects.) If you also include the second exit name, an exit
    from the destination room will be opened back to the room you are in.
  
    NOTE: you can have as many exit aliases as you like by adding more,
--- 2490,2497 ----
    This command opens an exit in the room you are standing in with the 
    specified name. You can then use the @link command to set the exit's
    destination, or you can set it automatically by using the DBREF of a
!   destination, which can be a room or thing. (Note that you CANNOT open
!   exits from things.) If you also include the second exit name, an exit
    from the destination room will be opened back to the room you are in.
  
    NOTE: you can have as many exit aliases as you like by adding more,
***************
*** 2513,2519 ****
    the full exit name to go through the exit. Only the exit name appears in
    the list of Obvious Exits in a room.
  
! Ex: @open Up;u;climb = #255, Down;dow;do;d;fall
  
  See also: EXITS, @link, @dig
  & @osuccess
--- 2499,2505 ----
    the full exit name to go through the exit. Only the exit name appears in
    the list of Obvious Exits in a room.
  
!   Ex: @open Up;u;climb = #255, Down;dow;do;d;fall
  
  See also: EXITS, @link, @dig
  & @osuccess
***************
*** 2529,2535 ****
    if the name of the exit is included in the message. It is recommended 
    that you put @osuccs on all exits and all takeable objects.
  
! Ex: @osucc North=heads north into the catacombs.
    
    If the =<message> part is omitted, the message will be reset.
  
--- 2515,2521 ----
    if the name of the exit is included in the message. It is recommended 
    that you put @osuccs on all exits and all takeable objects.
  
!   Ex: @osucc North=heads north into the catacombs.
    
    If the =<message> part is omitted, the message will be reset.
  
***************
*** 2609,2615 ****
    it a certain amount of MUSH money. If =<message> is omitted, the message
    is reset to nothing. May be abbreviated @pay. 
  
! See also: give, @apay, @opay, and @cost.
  & @receive
  & @oreceive
  & @areceive
--- 2595,2601 ----
    it a certain amount of MUSH money. If =<message> is omitted, the message
    is reset to nothing. May be abbreviated @pay. 
  
! See also: give, @apay, @opay, @cost.
  & @receive
  & @oreceive
  & @areceive
***************
*** 2659,2665 ****
    @prompt supports the following @pemit switches: /silent, /noisy,
    /spoof, /noeval
  
!   See also: @pemit, @nsprompt, prompt(), nsprompt(), PROMPT_NEWLINES
  & PROMPT_NEWLINES
    PROMPT_NEWLINES <1|0>
  
--- 2645,2651 ----
    @prompt supports the following @pemit switches: /silent, /noisy,
    /spoof, /noeval
  
! See also: @pemit, @nsprompt, prompt(), nsprompt(), PROMPT_NEWLINES
  & PROMPT_NEWLINES
    PROMPT_NEWLINES <1|0>
  
***************
*** 2675,2681 ****
    can be used to disable the newline and is recommended for users with
    these clients.
  
!   See also: @prompt, prompt()
  & @pemit
    @pemit[/<switch>] <object> = <message>
    
--- 2661,2667 ----
    can be used to disable the newline and is recommended for users with
    these clients.
  
! See also: @prompt, prompt()
  & @pemit
    @pemit[/<switch>] <object> = <message>
    
***************
*** 2704,2710 ****
    Note that page-lock and the HAVEN flag will block @pemits as well, 
    except from Wizards or those with the pemit_all power.
  
! See also @emit, @oemit, @remit, NOSPOOF, and SPOOFING.
  & @poll
    @poll <poll question>
    This wizard-only command sets the "poll" - the Doing question. If
--- 2690,2696 ----
    Note that page-lock and the HAVEN flag will block @pemits as well, 
    except from Wizards or those with the pemit_all power.
  
!   See also @emit, @oemit, @remit, NOSPOOF, and SPOOFING.
  & @poll
    @poll <poll question>
    This wizard-only command sets the "poll" - the Doing question. If
***************
*** 2867,2873 ****
     dbref instead of the executor's dbref, and requires control over
     the enactor or the Can_nspemit power.
  
! See also: @emit, @pemit, @oemit, SPOOFING, NOSPOOF and CONTROL.
  & @restart
    @restart <object> or @restart/all
    
--- 2853,2859 ----
     dbref instead of the executor's dbref, and requires control over
     the enactor or the Can_nspemit power.
  
! See also: @emit, @pemit, @oemit, SPOOFING, NOSPOOF, CONTROL.
  & @restart
    @restart <object> or @restart/all
    
***************
*** 2979,2985 ****
      @search eval=gt(money(##),10)     <-- list all objects owned by me  
                                            worth more than 10 coins.
   
!   See also: lsearch, lsearchr 
  & @select
    @select <string>=<expr1>,<action1>[,<exprN>,<actionN>]...[,<default>]
    This is similar to @switch, except it only executes the action
--- 2965,2971 ----
      @search eval=gt(money(##),10)     <-- list all objects owned by me  
                                            worth more than 10 coins.
   
! See also: lsearch, lsearchr 
  & @select
    @select <string>=<expr1>,<action1>[,<exprN>,<actionN>]...[,<default>]
    This is similar to @switch, except it only executes the action
***************
*** 3028,3074 ****
    
    The fourth form sets (or unsets) an attribute flag on the specified
    attribute. See 'help attribute flags'.
- & attribute flags
-   Attribute flags are set on an object's attributes using @set, or applied
-   to attributes globally using @attrib. Their names (and, when applicable,
-   the character used in examine as shorthand for the flag) include:
-  
-   no_command ($)    Attribute will not be checked for '$' commands and
-                     '^' listen patterns. 
-   visual (v)        Attribute can be seen by anyone via get(), eval(),
-                     ufun(), zfun(), and similar functions. 
-   no_inherit (i)    Attribute will not be inherited by the children of
-                     this object. 
-   no_clone (c)      Attribute will not be copied if the object is @clone'd.
-   regexp (R)        Match $-commands and ^-listens using regular expressions.
-                     See 'help regexps'.
-   case (C)          Match $-commands and ^-listens case sensitively.
-   safe (S)          Attribute may not be modified, without unsetting this flag.
-   mortal_dark (m)   Attribute cannot be seen by mortals. This flag can only 
-                     be set by royalty and wizards.  "hidden" is a synonym.
- 
-   Continued in 'help attribute flags2'
- & attribute flags2
-   noname (N)        Attribute won't show name in @o-* messages.
-   nospace (s)       Attribute won't append a space in @o-* messages.
-   wizard (w)        Attribute can only be set by wizards. 
-                     This flag can only be set by royalty and wizards.
-   veiled (V)        Attribute value will not be shown on default examine,
-                     but is still otherwise accessible (for spammy attribs).
-   debug (b)         Show debug output when this attribute is evaluated.
-   nearby (n)        Even if this attribute is visual, it can only be
-                     retrieved if you're co-located with the object.
-   public (p)        This attribute can be evaluated by any object, even
-                     if safer_ufun is in use. DANGEROUS! AVOID!
-   aahear (A)        ^-listens on this attribute match like @aahear
-   amhear (M)        ^-listens on this attribute match like @amhear
- 
-   Continued in 'help attribute flags3'
- & attribute flags3
-   prefixmatch       When a user attempts to set an attribute using @<attrib>,
-                     this attribute will be matched down to its unique
-                     prefixes. This flag is primarily used internally.
-   `                 This attribute is a branch. See: help ATTRIBUTE TREES
  & @sex
    @sex <player> = <gender>  
  
--- 3014,3019 ----
***************
*** 3185,3191 ****
  
    Example: @sql SHOW TABLES
  
!   See also: sql(), sqlescape(), mapsql()
  & @squota
    @squota <victim> [= [+|-] <amount>]
  
--- 3130,3136 ----
  
    Example: @sql SHOW TABLES
  
! See also: sql(), sqlescape(), mapsql()
  & @squota
    @squota <victim> [= [+|-] <amount>]
  
***************
*** 3231,3242 ****
  & @sweep
    @sweep [connected | here | inventory | exits ]
   
!   @sweep gives you a list of all objects/players that are listening in
!   the room you are currently in, as well as the objects you are
!   carrying. Most objects only listen for a particular string or
!   phrase, so they normally do not pose a problem if you need privacy.
!   You will have to be careful of players and puppets since they will
!   hear everything you say and do. (And might post the same to r.g.m!)
    AUDIBLE exits are also shown on an ordinary sweep, if the room is
    also AUDIBLE. (Audible exits aren't active unless the room is audible).
   
--- 3176,3187 ----
  & @sweep
    @sweep [connected | here | inventory | exits ]
   
!   @sweep gives you a list of all nearby objects that are listening,
!   including the room you are in and the objects you are carrying.
!   Most objects only listen for a particular string or phrase, so they
!   normally do not pose a problem if you need privacy. You will have to be
!   careful of players and puppets since they will hear everything you say
!   and do. (And might post the same to r.g.m!)
    AUDIBLE exits are also shown on an ordinary sweep, if the room is
    also AUDIBLE. (Audible exits aren't active unless the room is audible).
   
***************
*** 3312,3318 ****
    As a special case, using "home" as the <room> teleports the object
    to its home.
  
!   See also: JUMP_OK, NO_TEL, @oxtport, @tport, @otport, @atport, @lock
  & @tport
    @tport <object> [=<message>]
  
--- 3257,3263 ----
    As a special case, using "home" as the <room> teleports the object
    to its home.
  
! See also: JUMP_OK, NO_TEL, @oxtport, @tport, @otport, @atport, @lock
  & @tport
    @tport <object> [=<message>]
  
***************
*** 3324,3330 ****
    an object. It can also pass values to that attribute on the stack 
    as %0 - %9.
  
!   Example:
      &GREET me=POSE waves hi.
      @tr me/GREET
      > Cyclonus waves hi.
--- 3269,3275 ----
    an object. It can also pass values to that attribute on the stack 
    as %0 - %9.
  
!   Examples:
      &GREET me=POSE waves hi.
      @tr me/GREET
      > Cyclonus waves hi.
***************
*** 3447,3453 ****
    See "help @verb3" for examples.
    
  & @verb3
!   Example:
    
    &VERB_EXAMPLE Test Object=$test:@verb me=%#,TEST,You just tested.,OTEST,
         just tested the example.,ATEST,%N
--- 3392,3398 ----
    See "help @verb3" for examples.
    
  & @verb3
!   Examples:
    
    &VERB_EXAMPLE Test Object=$test:@verb me=%#,TEST,You just tested.,OTEST,
         just tested the example.,ATEST,%N
***************
*** 3501,3514 ****
    whichever happens first.
   
    More forms that support semaphores on arbitrary attributes are described in
!   help @wait2
!   See also the help for: SEMAPHORES, @drain, @notify
  & @wait2
    Normally, a semaphore wait depends on the SEMAPHORE attribute of the object
    in question. However, it is useful to be able to use other attributes as
    semaphores, so one object can be used as the blocker for multiple different
    things at once. Possible attribute names aren't completely arbitrary. See
!   HELP SEMAPHORES5 for details.
  
    The syntax for these are:
   
--- 3446,3460 ----
    whichever happens first.
   
    More forms that support semaphores on arbitrary attributes are described in
!   "help @wait2".
! 
! See also: SEMAPHORES, @drain, @notify
  & @wait2
    Normally, a semaphore wait depends on the SEMAPHORE attribute of the object
    in question. However, it is useful to be able to use other attributes as
    semaphores, so one object can be used as the blocker for multiple different
    things at once. Possible attribute names aren't completely arbitrary. See
!   "HELP SEMAPHORES5" for details.
  
    The syntax for these are:
   
***************
*** 3668,3674 ****
    ch <name> <password>
    cv <name> <password>
  
!   Not really a MUSH command, but a command available at the connect
    screen for Wizards. If enabled, Wizards who use 'cd' instead of
    'connect' to connect will be set DARK as they connect, and will not
    show arrival messages. Their arrival will, however, be broadcast
--- 3614,3620 ----
    ch <name> <password>
    cv <name> <password>
  
!   Not really MUSH commands, but commands available at the connect
    screen for Wizards. If enabled, Wizards who use 'cd' instead of
    'connect' to connect will be set DARK as they connect, and will not
    show arrival messages. Their arrival will, however, be broadcast
***************
*** 3798,3804 ****
      on the parent.
    The /all switch shows the values of VEILED attributes.
  
!   See also: ATTRIBUTE TREES
  & follow
    follow <object>
  
--- 3744,3750 ----
      on the parent.
    The /all switch shows the values of VEILED attributes.
  
! See also: ATTRIBUTE TREES
  & follow
    follow <object>
  
***************
*** 3809,3816 ****
    through. This doesn't prevent you from going somewhere else on your
    own.
  
! See also: unfollow, dismiss, desert, followers(), following(), 
!           @follow, @ofollow, @afollow
  & dismiss
    dismiss <object>
    dismiss
--- 3755,3762 ----
    through. This doesn't prevent you from going somewhere else on your
    own.
  
! See also: unfollow, dismiss, desert, followers(), following(), @follow,
!   @ofollow, @afollow
  & dismiss
    dismiss <object>
    dismiss
***************
*** 3866,3872 ****
    from PRICELIST. The item purchased is passed in as %0,
    and the amount paid as %1
  
!   See also: buy, @buy, @obuy, @pricelist, MONEY, ACTION LISTS
  & @buy
    @buy <object> [=<message>]
  
--- 3812,3818 ----
    from PRICELIST. The item purchased is passed in as %0,
    and the amount paid as %1
  
! See also: buy, @buy, @obuy, @pricelist, MONEY, ACTION LISTS
  & @buy
    @buy <object> [=<message>]
  
***************
*** 3874,3880 ****
    the object, using the 'buy' command. The item purchased is passed
    in as %0, and the amount paid as %1.
  
!   See also: buy, @abuy, @obuy, @pricelist, MONEY
  & @obuy
    @obuy <object> [=<message>]
  
--- 3820,3826 ----
    the object, using the 'buy' command. The item purchased is passed
    in as %0, and the amount paid as %1.
  
! See also: buy, @abuy, @obuy, @pricelist, MONEY
  & @obuy
    @obuy <object> [=<message>]
  
***************
*** 3884,3890 ****
    the beginning of the message. The item purchased is passed in as %0,
    and the amount paid as %1.
  
!   See also: buy, @abuy, @obuy, @pricelist, MONEY
  & @pricelist
    @pricelist <object>=<item1>:<price1>[,<price2>][ <item2>:...]
  
--- 3830,3836 ----
    the beginning of the message. The item purchased is passed in as %0,
    and the amount paid as %1.
  
! See also: buy, @abuy, @obuy, @pricelist, MONEY
  & @pricelist
    @pricelist <object>=<item1>:<price1>[,<price2>][ <item2>:...]
  
***************
*** 3900,3906 ****
    ex:
      @PRICELIST vendor=mansion:1000+ large_house:100-200 house:20,30,50
  
!   See also: buy, @abuy, @buy, @obuy, MONEY
  & buy
    buy <item>[ from <vendor>][ for <cost>]
  
--- 3846,3852 ----
    ex:
      @PRICELIST vendor=mansion:1000+ large_house:100-200 house:20,30,50
  
! See also: buy, @abuy, @buy, @obuy, MONEY
  & buy
    buy <item>[ from <vendor>][ for <cost>]
  
***************
*** 3920,3926 ****
    > buy coke
    You enjoy a delicious coke.
  
!   See also: @ABUY, @BUY, @PRICELIST
  & give
    give[/silent] <player>=<number>
    give[/silent] <number> to <player>
--- 3866,3872 ----
    > buy coke
    You enjoy a delicious coke.
  
! See also: @ABUY, @BUY, @PRICELIST
  & give
    give[/silent] <player>=<number>
    give[/silent] <number> to <player>
***************
*** 3985,3993 ****
    
  See also: score, take, drop, OPAQUE, @lock, @invformat
  & kill
!   kill <player> [=<cost>]
  
!   Attempts to kill the specified player. Killing costs <cost> pennies, which 
    gives you a <cost>% chance of killing the player. Thus, spending 100
    pennies always works (except against wizards,  who can never be killed).
    If you don't specify a cost, the default is 10 (i.e. 10%).  The player,
--- 3931,3939 ----
    
  See also: score, take, drop, OPAQUE, @lock, @invformat
  & kill
!   kill <player/thing> [=<cost>]
  
!   Attempts to kill the specified object. Killing costs <cost> pennies, which 
    gives you a <cost>% chance of killing the player. Thus, spending 100
    pennies always works (except against wizards,  who can never be killed).
    If you don't specify a cost, the default is 10 (i.e. 10%).  The player,
***************
*** 4151,4157 ****
    of the message, and is handy when you want to say things that use special 
    characters like % or []'s.
  
!   See also: whisper, pose
  & score
    score
    
--- 4097,4103 ----
    of the message, and is handy when you want to say things that use special 
    characters like % or []'s.
  
! See also: pose, whisper
  & score
    score
    
***************
*** 4162,4170 ****
    
  See also: LOOPING, @ps, QUEUE, MONEY
  & slay
!   slay <player/object> 
  
!   This is a Wizard-only command that kills players without paying any 
    insurance to the victims. It is used in places where 'suicide' should 
    not pay.
    
--- 4108,4116 ----
    
  See also: LOOPING, @ps, QUEUE, MONEY
  & slay
!   slay <player/thing>
  
!   This is a Wizard-only command that kills objects without paying any 
    insurance to the victims. It is used in places where 'suicide' should 
    not pay.
    
***************
*** 4246,4248 ****
--- 4192,4206 ----
    are checked for commands as if it was a master room.
  
    See 'help USER-DEFINED COMMANDS'.
+ & socket commands
+   These commands can only be entered by a connected player through their
+   client. They generally do things that only affect a specific connection
+   and would be meaningless if run by an object or disconnected player.
+ 
+   DOING	    IDLE      LOGOUT     OUTPUTPREFIX OUTPUTSUFFIX QUIT
+   SESSION   WHO
+ 
+   In addition, the following commands can only be used at the login
+   screen:
+ 
+   cd ch connect create 
\ No newline at end of file
*** 1_8_2p1/game/txt/hlp/pennconf.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennconf.hlp	Sat Jan 27 02:21:10 2007
***************
*** 35,41 ****
  
    chan_cost=<number>: How many pennies a channel costs to create.
    max_channels=<number>: How many channels can exist total.
!   max_player_channels=<number>: How many channels can each non-admin
     player create? If 0, mortals cannot create channels.
  & @config cmds
   These options affect command behavior.
--- 35,41 ----
  
    chan_cost=<number>: How many pennies a channel costs to create.
    max_channels=<number>: How many channels can exist total.
!   max_player_chans=<number>: How many channels can each non-admin
     player create? If 0, mortals cannot create channels.
  & @config cmds
   These options affect command behavior.
***************
*** 238,244 ****
     considered a 0?
    tiny_booleans=<boolean>: Use Tiny-style boolean values where only
     non-zero numbers are true.
!   tiny_trim_func=<boolean>: Are the second and third arguments to trim()
     reversed?
    tiny_math=<boolean>: Is a string where a number is expected considered
     a 0?
--- 238,244 ----
     considered a 0?
    tiny_booleans=<boolean>: Use Tiny-style boolean values where only
     non-zero numbers are true.
!   tiny_trim_fun=<boolean>: Are the second and third arguments to trim()
     reversed?
    tiny_math=<boolean>: Is a string where a number is expected considered
     a 0?
*** 1_8_2p1/game/txt/hlp/pennflag.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennflag.hlp	Sat Jan 27 02:21:10 2007
***************
*** 32,39 ****
    indicate the TYPE of an object. The absence of these four special flags
    indicates the object is of type THING.
  
!   See also: examine, flags(), hasflag(), orflags(), andflags(),
!   orlflags(), andlflags(), types of objects, type(), hastype(), @flag
  & FLAG LIST
  & FLAGS LIST
  Flag  Title           Flag  Title              Flag  Title
--- 32,39 ----
    indicate the TYPE of an object. The absence of these four special flags
    indicates the object is of type THING.
  
! See also: examine, flags(), hasflag(), orflags(), andflags(), orlflags(),
!   andlflags(), types of objects, type(), hastype(), @flag
  & FLAG LIST
  & FLAGS LIST
  Flag  Title           Flag  Title              Flag  Title
***************
*** 72,78 ****
    To make a room your home, type '@link me=here' while standing in the
    room.
  
!   See also: @link
  & ANSI
    Flag:  ANSI  (players)
    
--- 72,78 ----
    To make a room your home, type '@link me=here' while standing in the
    room.
  
! See also: @link
  & ANSI
    Flag:  ANSI  (players)
    
***************
*** 86,92 ****
    See also the COLOR flag. If COLOR is not set, and ANSI is, you will
    see vt100 ANSI codes, but not color ANSI codes.
  
!   See also: COLOR, ansi(), @config
  & AUDIBLE
    Flag:  AUDIBLE  (all types)
  
--- 86,92 ----
    See also the COLOR flag. If COLOR is not set, and ANSI is, you will
    see vt100 ANSI codes, but not color ANSI codes.
  
! See also: COLOR, ansi(), @config
  & AUDIBLE
    Flag:  AUDIBLE  (all types)
  
***************
*** 143,149 ****
    > <a whole buncha commands>
    GAME: Object Walker(#123) lost a Penny to queue loss.
  
!   See also: no_pay
  & BUILDER
    BUILDER 
  
--- 143,149 ----
    > <a whole buncha commands>
    GAME: Object Walker(#123) lost a Penny to queue loss.
  
! See also: no_pay
  & BUILDER
    BUILDER 
  
***************
*** 154,160 ****
    power. Only wizards can grant @powers. To see if building is limited, 
    check @command for the various building commands.
  
!   See also: POWERS, @power, @dig, @open, @create
  & CHOWN_OK
    Flag:  CHOWN_OK  (things, rooms, exits)
  
--- 154,160 ----
    power. Only wizards can grant @powers. To see if building is limited, 
    check @command for the various building commands.
  
! See also: POWERS, @power, @dig, @open, @create
  & CHOWN_OK
    Flag:  CHOWN_OK  (things, rooms, exits)
  
***************
*** 163,169 ****
    You must be carrying the thing, or in the room in order to set the
    flag, unless you use the DBREF number.
  
!   See also: @chown
  & CLOUDY
    Flag:  CLOUDY (exits)
  
--- 163,169 ----
    You must be carrying the thing, or in the room in order to set the
    flag, unless you use the DBREF number.
  
! See also: @chown
  & CLOUDY
    Flag:  CLOUDY (exits)
  
***************
*** 180,186 ****
    When set on a player, this flag allows the player to see ANSI color.
    The ANSI flag must also be set.
  
!   See also: ANSI, ansi()
  & CONNECTED
    Flag:  CONNECTED  (players)
  
--- 180,186 ----
    When set on a player, this flag allows the player to see ANSI color.
    The ANSI flag must also be set.
  
! See also: ANSI, ansi()
  & CONNECTED
    Flag:  CONNECTED  (players)
  
***************
*** 191,197 ****
    Mortal code can't use hasflag(<x>,connected) to test if a player is 
    connected. Consider using conn(), lwho(), or mwho() instead.
  
!   See also: conn(), lwho(), mwho()
  & DARK
    Flag:  DARK  (all types)
  
--- 191,197 ----
    Mortal code can't use hasflag(<x>,connected) to test if a player is 
    connected. Consider using conn(), lwho(), or mwho() instead.
  
! See also: conn(), lwho(), mwho()
  & DARK
    Flag:  DARK  (all types)
  
***************
*** 267,273 ****
  
    DESTROY_OK takes precedence over SAFE.
  
!   See also: @destroy
  & ENTER_OK
    Flag:  ENTER_OK  (all types)
   
--- 267,273 ----
  
    DESTROY_OK takes precedence over SAFE.
  
! See also: @destroy
  & ENTER_OK
    Flag:  ENTER_OK  (all types)
   
***************
*** 280,286 ****
  
    This flag has no effect on rooms.
  
!   See also: enter, leave, give, @lock
  & FIXED
    Flag: FIXED (players)
    
--- 280,286 ----
  
    This flag has no effect on rooms.
  
! See also: enter, leave, give, @lock
  & FIXED
    Flag: FIXED (players)
    
***************
*** 320,326 ****
    actions, listen, be triggered, evaluate functions or substitutions,
    etc.
  
!   See also: @halt, @restart
  & HAVEN
    Flag:  HAVEN (players, rooms)
  
--- 320,326 ----
    actions, listen, be triggered, evaluate functions or substitutions,
    etc.
  
! See also: @halt, @restart
  & HAVEN
    Flag:  HAVEN (players, rooms)
  
***************
*** 331,337 ****
  
    If a room is set HAVEN, the 'kill' command cannot be used in that room.
  
!   See also: @haven, kill
  & HEAVY
    Flag:  HEAVY (all types)
  
--- 331,337 ----
  
    If a room is set HAVEN, the 'kill' command cannot be used in that room.
  
! See also: @haven, kill
  & HEAVY
    Flag:  HEAVY (all types)
  
***************
*** 339,345 ****
    they own, unless the thing to be teleported is set HEAVY.
    This flag can only be set by royalty or wizards.
  
!   See also: @tel
  & LOUD
    Flag:  LOUD (all types)
  
--- 339,345 ----
    they own, unless the thing to be teleported is set HEAVY.
    This flag can only be set by royalty or wizards.
  
! See also: @tel
  & LOUD
    Flag:  LOUD (all types)
  
***************
*** 407,413 ****
    Objects, players, and exits which have the LIGHT flag set on them
    (and are not also set DARK) appear in the contents of DARK rooms.
  
!   See also: DARK
  & LINK_OK
    Flag: LINK_OK  (rooms, things)
  
--- 407,413 ----
    Objects, players, and exits which have the LIGHT flag set on them
    (and are not also set DARK) appear in the contents of DARK rooms.
  
! See also: DARK
  & LINK_OK
    Flag: LINK_OK  (rooms, things)
  
***************
*** 442,448 ****
    This flag is only valid for players; objects belonging to MYOPIC
    players are automatically considered to be MYOPIC.
  
!   See also: DBREF
  & MISTRUST
    Flag:  MISTRUST  (things, rooms, exits)
  
--- 442,448 ----
    This flag is only valid for players; objects belonging to MYOPIC
    players are automatically considered to be MYOPIC.
  
! See also: DBREF
  & MISTRUST
    Flag:  MISTRUST  (things, rooms, exits)
  
***************
*** 461,474 ****
    still have unlimited money, etc. This flag works best when no other
    privileges are granted to the object.)
  
!   See also: control
  & NOACCENTS
    Flag: NOACCENTS  (players)
  
    This flag causes all accented characters to be converted to non-accented
    before being sent to a connection. See HELP STRIPACCENTS() for caveats.
  
!   See also: i18n, accent(), stripaccents()
  & NO_COMMAND
    Flag:  NO_COMMAND  (all types)
    
--- 461,474 ----
    still have unlimited money, etc. This flag works best when no other
    privileges are granted to the object.)
  
! See also: control
  & NOACCENTS
    Flag: NOACCENTS  (players)
  
    This flag causes all accented characters to be converted to non-accented
    before being sent to a connection. See HELP STRIPACCENTS() for caveats.
  
! See also: i18n, accent(), stripaccents()
  & NO_COMMAND
    Flag:  NO_COMMAND  (all types)
    
***************
*** 479,485 ****
    be set NO_COMMAND. Many MUSHes choose to have all objects initially set
    NO_COMMAND at creation. The flag has no effect on exits.
  
!   See also: USER-DEFINED COMMANDS
  & NO_LEAVE
  & NOLEAVE
    Flag: NO_LEAVE (objects)
--- 479,485 ----
    be set NO_COMMAND. Many MUSHes choose to have all objects initially set
    NO_COMMAND at creation. The flag has no effect on exits.
  
! See also: USER-DEFINED COMMANDS
  & NO_LEAVE
  & NOLEAVE
    Flag: NO_LEAVE (objects)
***************
*** 488,494 ****
    Attempts to leave the object will trigger its @LFAIL, @OLFAIL,
    and @ALFAIL, if set.
  
!   See also: leave
  & NO_TEL
    Flag:  NO_TEL  (rooms)
  
--- 488,494 ----
    Attempts to leave the object will trigger its @LFAIL, @OLFAIL,
    and @ALFAIL, if set.
  
! See also: leave
  & NO_TEL
    Flag:  NO_TEL  (rooms)
  
***************
*** 562,568 ****
  
    Meaningless for rooms.
  
!   See also: TRANSPARENT, look
  & ORPHAN
    Flag:  ORPHAN   (all types)
  
--- 562,568 ----
  
    Meaningless for rooms.
  
! See also: TRANSPARENT, look
  & ORPHAN
    Flag:  ORPHAN   (all types)
  
***************
*** 571,577 ****
    retrieved from the ancestor. It has no effect on the object's 
    true @parents, only on its use of the ancestor.
  
!   See also: @parent, ancestors
  & PLAYER
    Flag:  PLAYER  (player)
  
--- 571,577 ----
    retrieved from the ancestor. It has no effect on the object's 
    true @parents, only on its use of the ancestor.
  
! See also: @parent, ancestors
  & PLAYER
    Flag:  PLAYER  (player)
  
***************
*** 671,677 ****
    the exit's description.  The exit list and succ/fail messages of the
    room will NOT be displayed. See also CLOUDY.
  
!   See also: CLOUDY, OPAQUE, EXITS, @exitformat
  & UNFINDABLE
    Flag:  UNFINDABLE  (all types)
    
--- 671,677 ----
    the exit's description.  The exit list and succ/fail messages of the
    room will NOT be displayed. See also CLOUDY.
  
! See also: CLOUDY, OPAQUE, EXITS, @exitformat
  & UNFINDABLE
    Flag:  UNFINDABLE  (all types)
    
***************
*** 707,713 ****
    "#<object>] <command>". Something like "#1300] @trigger me/test" is a
    good example of typical VERBOSE output.
  
!   See also: PUPPET, DEBUG
  & VISUAL 
    Flag:  VISUAL  (all types)
  
--- 707,713 ----
    "#<object>] <command>". Something like "#1300] @trigger me/test" is a
    good example of typical VERBOSE output.
  
! See also: PUPPET, DEBUG
  & VISUAL 
    Flag:  VISUAL  (all types)
  
***************
*** 716,722 ****
    It does not enable them to make changes to the object. Very useful
    for getting help with code.
  
!   See also: examine, brief
  & WIZARD
    Flag:  WIZARD    (all types)
  
--- 716,722 ----
    It does not enable them to make changes to the object. Very useful
    for getting help with code.
  
! See also: examine, brief
  & WIZARD
    Flag:  WIZARD    (all types)
  
***************
*** 730,736 ****
    powers of a Wizard except that they cannot @nuke players.
    Only Wizards may set the WIZARD flag on objects.
  
!   See also: ROYALTY, @power
  & Z_TEL
    Flag:  Z_TEL  (things, rooms)
  
--- 730,736 ----
    powers of a Wizard except that they cannot @nuke players.
    Only Wizards may set the WIZARD flag on objects.
  
! See also: ROYALTY, @power
  & Z_TEL
    Flag:  Z_TEL  (things, rooms)
  
***************
*** 741,747 ****
    rooms zoned to it. Like NO_TEL, the "home" command will still work.
    This flag is intended for use in puzzle rooms and IC areas.
  
!   See also: ZONES, ZONE MASTERS, @chzone, ZONE MASTER ROOMS
  & SHARED
  & ZONE
    Flag:  SHARED   (players) 
--- 741,747 ----
    rooms zoned to it. Like NO_TEL, the "home" command will still work.
    This flag is intended for use in puzzle rooms and IC areas.
  
! See also: ZONES, ZONE MASTERS, @chzone, ZONE MASTER ROOMS
  & SHARED
  & ZONE
    Flag:  SHARED   (players) 
***************
*** 750,756 ****
    Objects owned by a Zone Master are controlled by anyone who passes the 
    player's zone lock.  
  
!   See also: ZONE MASTERS
  & LISTEN_PARENT
    Flag:  LISTEN_PARENT (things, rooms)
    
--- 750,756 ----
    Objects owned by a Zone Master are controlled by anyone who passes the 
    player's zone lock.  
  
! See also: ZONE MASTERS
  & LISTEN_PARENT
    Flag:  LISTEN_PARENT (things, rooms)
    
***************
*** 758,762 ****
    flag causes ^ listen patterns to be checked on the object's parents as
    well as on the object.
  
!   See also: MONITOR, LISTENING
  
--- 758,762 ----
    flag causes ^ listen patterns to be checked on the object's parents as
    well as on the object.
  
! See also: MONITOR, LISTENING
  
*** 1_8_2p1/game/txt/hlp/pennfunc.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennfunc.hlp	Sat Jan 27 02:21:10 2007
***************
*** 1,7 ****
  & FUNCTIONS
    Functions are specialized commands used to manipulate strings and
    other input. Function take the general form:  [FUNCTION(<input>)]
!     
    The brackets are used to delimit and force evaluation of the function 
    (or nested functions). The brackets can also be used to group functions 
    for the purposes of string concatenation. In general, more than one pair 
--- 1,7 ----
  & FUNCTIONS
    Functions are specialized commands used to manipulate strings and
    other input. Function take the general form:  [FUNCTION(<input>)]
! 
    The brackets are used to delimit and force evaluation of the function 
    (or nested functions). The brackets can also be used to group functions 
    for the purposes of string concatenation. In general, more than one pair 
***************
*** 17,43 ****
        Wizard - Set.
        > say [first([rest([v(va)] [v(vb)])])]
        You say, "is"
!   
    See "help FUNCTIONS2" for more.
!  
  & FUNCTIONS2
!   
    A list of available built-in functions can be obtained via the command
    "@config/functions". In the help text, the list is under the topic
    "FUNCTION LIST".
!   
    In addition to these built-in functions are MUSH-defined "global user
    functions."  These are defined by wizards or those with the "Function"
    power, via the "@function" command. To the user, they act just like
    the built-in game functions. For details on global user functions,
    see "help @function".
!   
!   See also: MUSHCODE
  & FUNCTION LIST
    Several major variants of functions are available. The help topics
    are listed below, together with a quick summary of the function type
    and some examples of that type of function.
!  
    Attribute functions: attribute-related manipulations (GET, UFUN)
    Bitwise functions: Manipulation of individual bits of numbers (SHL, BOR)
    Boolean functions:  produce 0 or 1 (false or true) answers  (OR, AND)
--- 17,43 ----
        Wizard - Set.
        > say [first([rest([v(va)] [v(vb)])])]
        You say, "is"
! 
    See "help FUNCTIONS2" for more.
! 
  & FUNCTIONS2
! 
    A list of available built-in functions can be obtained via the command
    "@config/functions". In the help text, the list is under the topic
    "FUNCTION LIST".
! 
    In addition to these built-in functions are MUSH-defined "global user
    functions."  These are defined by wizards or those with the "Function"
    power, via the "@function" command. To the user, they act just like
    the built-in game functions. For details on global user functions,
    see "help @function".
! 
! See also: MUSHCODE
  & FUNCTION LIST
    Several major variants of functions are available. The help topics
    are listed below, together with a quick summary of the function type
    and some examples of that type of function.
! 
    Attribute functions: attribute-related manipulations (GET, UFUN)
    Bitwise functions: Manipulation of individual bits of numbers (SHL, BOR)
    Boolean functions:  produce 0 or 1 (false or true) answers  (OR, AND)
***************
*** 55,76 ****
    String functions:  string manipulation (ESCAPE, FLIP)
    Time functions: Formatting and display of time (TIME, CONVSECS)
    Utility functions: general utilities (ISINT, COMP)
!   
    The command "@config/functions" lists all of the game's built-in functions.
    The command "@function" lists all of the game's custom global functions
      defined via the @function command.
!   
  & Attribute functions
    The primary purpose of these functions is to access information
    stored in attributes on objects.
!   
    aposs()       attrib_set()  default()     edefault()    eval()
    get()         grep()        grepi()       lattr()       nattr()
    obj()         poss()        regrep()      regrepi()     subj()
    udefault()    ufun()        uldefault()   ulocal()      v-function
    wildgrep()    wildgrepi()   xget()        zfun()
  
!   See also: ATTRIBUTES, NON-STANDARD ATTRIBUTES
  & Bitwise functions
    These functions treat integers as a sequence of binary bits (Either 0
    or 1) and manipulate them.
--- 55,76 ----
    String functions:  string manipulation (ESCAPE, FLIP)
    Time functions: Formatting and display of time (TIME, CONVSECS)
    Utility functions: general utilities (ISINT, COMP)
! 
    The command "@config/functions" lists all of the game's built-in functions.
    The command "@function" lists all of the game's custom global functions
      defined via the @function command.
! 
  & Attribute functions
    The primary purpose of these functions is to access information
    stored in attributes on objects.
! 
    aposs()       attrib_set()  default()     edefault()    eval()
    get()         grep()        grepi()       lattr()       nattr()
    obj()         poss()        regrep()      regrepi()     subj()
    udefault()    ufun()        uldefault()   ulocal()      v-function
    wildgrep()    wildgrepi()   xget()        zfun()
  
! See also: ATTRIBUTES, NON-STANDARD ATTRIBUTES
  & Bitwise functions
    These functions treat integers as a sequence of binary bits (Either 0
    or 1) and manipulate them.
***************
*** 90,101 ****
    are all considered "true" when passed to these functions.
    Alternatively, your MUSH may be using TinyMUSH 2.2 booleans,
    in which case only non-zero numbers are "true".
!   
    and()         cand()        cor()         eq()          gt()
    gte()         lt()          lte()         nand()        neq()
    nor()         not()         or()          t()           xor()
!     
!   See also: BOOLEAN VALUES, @config
  & Communication functions
    Communication functions are side-effect functions that send a message
    to an object or objects.
--- 90,101 ----
    are all considered "true" when passed to these functions.
    Alternatively, your MUSH may be using TinyMUSH 2.2 booleans,
    in which case only non-zero numbers are "true".
! 
    and()         cand()        cor()         eq()          gt()
    gte()         lt()          lte()         nand()        neq()
    nor()         not()         or()          t()           xor()
! 
! See also: BOOLEAN VALUES, @config
  & Communication functions
    Communication functions are side-effect functions that send a message
    to an object or objects.
***************
*** 116,133 ****
  & Dbref functions
    Dbref functions return a dbref or list of dbrefs related to some value
    on an object.
!   
    children()    con()         entrances()   exit()        followers()
    following()   home()        lcon()        lexits()      loc()
    locate()      lparent()     lplayers()    lsearch()     lvcon()
    lvexits()     lvplayers()   namelist()    next()        nextdbref()
    num()         owner()       parent()      pmatch()      rloc()
    rnum()        room()        where()       zone()
!   
!   See also: DBREF
  & Information functions
    Information functions return values related to objects or the game.
!  
    alias()       andflags()    andlflags()   config()      controls()
    ctime()       elock()       findable()    flags()       fullalias()
    fullname()    hasattr()     hasattrp()    hasflag()     haspower()
--- 116,133 ----
  & Dbref functions
    Dbref functions return a dbref or list of dbrefs related to some value
    on an object.
! 
    children()    con()         entrances()   exit()        followers()
    following()   home()        lcon()        lexits()      loc()
    locate()      lparent()     lplayers()    lsearch()     lvcon()
    lvexits()     lvplayers()   namelist()    next()        nextdbref()
    num()         owner()       parent()      pmatch()      rloc()
    rnum()        room()        where()       zone()
! 
! See also: DBREF
  & Information functions
    Information functions return values related to objects or the game.
! 
    alias()       andflags()    andlflags()   config()      controls()
    ctime()       elock()       findable()    flags()       fullalias()
    fullname()    hasattr()     hasattrp()    hasflag()     haspower()
***************
*** 152,159 ****
    setunion()    shuffle()     sort()        sortby()      sortkey()
    splice()      step()        table()       unique()      wordpos()
    words()
!     
!   See also: LISTS
  & Math functions
    Math functions take one or more floating point numbers and return 
    a numeric value.
--- 152,159 ----
    setunion()    shuffle()     sort()        sortby()      sortkey()
    splice()      step()        table()       unique()      wordpos()
    words()
! 
! See also: LISTS
  & Math functions
    Math functions take one or more floating point numbers and return 
    a numeric value.
***************
*** 165,171 ****
    mul()         pi()          power()       root()        round()
    sign()        sin()         sqrt()        stddev()      sub()
    tan()         trunc()       val()
!  
    These functions operate only on integers (if passed floating point
    numbers, they will return an error or misbehave):
    dec()         div()         floordiv()    inc()         mod()
--- 165,171 ----
    mul()         pi()          power()       root()        round()
    sign()        sin()         sqrt()        stddev()      sub()
    tan()         trunc()       val()
! 
    These functions operate only on integers (if passed floating point
    numbers, they will return an error or misbehave):
    dec()         div()         floordiv()    inc()         mod()
***************
*** 178,190 ****
  & Regular expression functions
    These functions take a regular expression (regexp, or re) and match
    it against assorted things.
!   
    regedit()     regeditall() regeditalli()  regediti()    regmatch()
    regmatchi()   regrab()     regraball()    regraballi()  regrabi()
    regrep()      regrepi()    reswitch()     reswitchall() reswitchalli()
    reswitchi()
!  
!   See also: string functions, regexp
  & SQL functions
    These functions perform queries or other operations on an SQL
    database to which the MUSH is connected, if SQL support is
--- 178,190 ----
  & Regular expression functions
    These functions take a regular expression (regexp, or re) and match
    it against assorted things.
! 
    regedit()     regeditall() regeditalli()  regediti()    regmatch()
    regmatchi()   regrab()     regraball()    regraballi()  regrabi()
    regrep()      regrepi()    reswitch()     reswitchall() reswitchalli()
    reswitchi()
! 
! See also: string functions, regexp
  & SQL functions
    These functions perform queries or other operations on an SQL
    database to which the MUSH is connected, if SQL support is
***************
*** 195,225 ****
  & String functions
    String functions take at least one string and return a transformed
    string, parts of a string, or a value related to the string(s).
!   
!   accent()      after()       alphamin()    alphamax()    art()
!   before()      brackets()    capstr()      case()        caseall()
!   cat()         center()      comp()        chr()         decrypt()
!   delete()      digest()      edit()        encrypt()     escape()
!   if()          ifelse()      foreach()     lcstr()       left()        
!   lit()         ljust()       merge()       mid()         ord()         
!   ordinal()     pos()         regedit()     lpos()        regmatch()    
!   repeat()      reverse()     right()       rjust()       scramble()    
!   secure()      sha0()        space()       spellnum()    squish()      
!   strcat()      strinsert()   stripaccents()stripansi()   strlen()      
!   strmatch()    strreplace()  switch()      trim()        ucstr()       
!   wrap()
!  
!   See also: STRINGS
  & Time functions
    These functions return times or format times.
!   
    convsecs()    convutcsecs() convtime()    ctime()       etimefmt()
    isdaylight()  mtime()       restarttime() secs()        starttime()
    stringsecs()  time()        timefmt()     timestring()  utctime()
  
  & Utility functions
    These functions don't quite fit into any other category.
!   
    allof()       ansi()        atrlock()     beep()        checkpass()
    clone()       create()      die()         dig()         firstof()
    functions()   isdbref()     isint()       isnum()       isword()
--- 195,225 ----
  & String functions
    String functions take at least one string and return a transformed
    string, parts of a string, or a value related to the string(s).
! 
!   accent()      after()       align()       alphamin()    alphamax()
!   art()         before()      brackets()    capstr()      case()
!   caseall()     cat()         center()      comp()        chr()
!   decompose()   decrypt()     delete()      digest()      edit()
!   encrypt()     escape()      if()          ifelse()      foreach()
!   lcstr()       left()        lit()         ljust()       merge()
!   mid()         ord()         ordinal()     pos()         regedit()
!   lpos()        regmatch()    repeat()      reverse()     right()
!   rjust()       scramble()    secure()      sha0()        space()
!   spellnum()    squish()      strcat()      strinsert()   stripaccents()
!   stripansi()   strlen()      strmatch()    strreplace()  switch()
!   trim()        ucstr()       wrap()
! 
! See also: STRINGS
  & Time functions
    These functions return times or format times.
! 
    convsecs()    convutcsecs() convtime()    ctime()       etimefmt()
    isdaylight()  mtime()       restarttime() secs()        starttime()
    stringsecs()  time()        timefmt()     timestring()  utctime()
  
  & Utility functions
    These functions don't quite fit into any other category.
! 
    allof()       ansi()        atrlock()     beep()        checkpass()
    clone()       create()      die()         dig()         firstof()
    functions()   isdbref()     isint()       isnum()       isword()
***************
*** 242,248 ****
    output of functions when you don't use that output.
  & ABS()
    abs(<number>)
!  
    Returns the absolute value of a number. i.e. ABS(-4) returns 4;
    ABS(2) returns 2, etc.
  & ACCENT()
--- 242,248 ----
    output of functions when you don't use that output.
  & ABS()
    abs(<number>)
! 
    Returns the absolute value of a number. i.e. ABS(-4) returns 4;
    ABS(2) returns 2, etc.
  & ACCENT()
***************
*** 257,265 ****
    the one assumes (ISO 8859-1), and some clients strip these 8-bit
    characters.
  
!   See HELP ACCENT2 for a description of the template argument.
  
!   See also: stripaccents(), NOACCENTS
  & ACCENT2
    For each character in <string>, the corresponding character of
    <template> is checked according to the table below, and a replacement
--- 257,265 ----
    the one assumes (ISO 8859-1), and some clients strip these 8-bit
    characters.
  
!   See 'HELP ACCENT2' for a description of the template argument.
  
! See also: stripaccents(), NOACCENTS
  & ACCENT2
    For each character in <string>, the corresponding character of
    <template> is checked according to the table below, and a replacement
***************
*** 284,290 ****
    cedilla    Small tail below    ,          C,c
               letter
  
!   See HELP ACCENT3 for more
  & ACCENT3
    These are non-accent special characters, mostly punctuation and
    non-roman letters.
--- 284,290 ----
    cedilla    Small tail below    ,          C,c
               letter
  
!   See 'HELP ACCENT3' for more
  & ACCENT3
    These are non-accent special characters, mostly punctuation and
    non-roman letters.
***************
*** 302,308 ****
    Capital eth         -          D
    Lower-case eth      &          o
    
!   See HELP ACCENT4 for examples
  & ACCENT4
    Some examples of accent() and their expected outputs:
  
--- 302,308 ----
    Capital eth         -          D
    Lower-case eth      &          o
    
!   See 'HELP ACCENT4' for examples
  & ACCENT4
    Some examples of accent() and their expected outputs:
  
***************
*** 327,333 ****
    Returns the angle that has the given <cosine> (arc-cosine), with the
    angle expressed in the given angle type, or radians by default.
  
!   See HELP CTU() for more on the angle type.
  & ADD()
    add(<number>, <number>[, ... , <numberN>])
  
--- 327,333 ----
    Returns the angle that has the given <cosine> (arc-cosine), with the
    angle expressed in the given angle type, or radians by default.
  
!   See 'HELP CTU()' for more on the angle type.
  & ADD()
    add(<number>, <number>[, ... , <numberN>])
  
***************
*** 345,350 ****
--- 345,351 ----
     > think after(foo bar baz,ba)
     r baz
  
+ See also: before(), rest()
  & ALIGN()
    align(<widths>,<col1>,...,<coln>[,<filler>[,<colsep>[,<rowsep>]]])
  
***************
*** 359,365 ****
    left-aligned. A '-' causes it to be centered, and '>' makes it
    right-aligned. No prefix defaults to left-aligned. A '.' after the
    number implies the column is to be repeated for as long as there is
!   a non-repeating column.
  
    <filler> is a single character that, if given, is the character used
    to fill empty columns and remaining spaces. <colsep>, if given, is
--- 360,366 ----
    left-aligned. A '-' causes it to be centered, and '>' makes it
    right-aligned. No prefix defaults to left-aligned. A '.' after the
    number implies the column is to be repeated for as long as there is
!   text remaining in a non-repeating column.
  
    <filler> is a single character that, if given, is the character used
    to fill empty columns and remaining spaces. <colsep>, if given, is
***************
*** 388,393 ****
--- 389,395 ----
           +                  Luke                  +
           +----------------------------------------+
  
+ See also: center(), ljust(), rjust()
  & ALLOF()
    allof(<expr1>[, ... , <exprN>],<osep>)
  
***************
*** 407,413 ****
    > say allof(#-1,#101,#2970,,#-3,0,#319,null(This Doesn't Count),|)
    You say, "#101|#2970|#319"
  
!   See also: firstof(), BOOLEAN VALUES
  & ALPHAMAX()
    alphamax(<word1>[, ... , <wordN>])
  
--- 409,415 ----
    > say allof(#-1,#101,#2970,,#-3,0,#319,null(This Doesn't Count),|)
    You say, "#101|#2970|#319"
  
! See also: firstof(), BOOLEAN VALUES
  & ALPHAMAX()
    alphamax(<word1>[, ... , <wordN>])
  
***************
*** 432,438 ****
    effects), while cand() stops evaluation after the first argument
    that evaluates to false.
  
!   See also: BOOLEAN VALUES, or(), xor(), not()
  & ANDFLAGS()
    andflags(<object>,<string of flag letters>)
  
--- 434,440 ----
    effects), while cand() stops evaluation after the first argument
    that evaluates to false.
  
! See also: BOOLEAN VALUES, or(), xor(), not()
  & ANDFLAGS()
    andflags(<object>,<string of flag letters>)
  
***************
*** 472,478 ****
          u - underscore                  U - not underscore
          i - inverse                     I - not inverse
          n - normal                      
!  
          x - black foreground            X - black background
          r - red foreground              R - red background
          g - green foreground            G - green background
--- 474,481 ----
          u - underscore                  U - not underscore
          i - inverse                     I - not inverse
          n - normal                      
! 
!         d - default foreground          D - default background
          x - black foreground            X - black background
          r - red foreground              R - red background
          g - green foreground            G - green background
***************
*** 483,490 ****
          w - white foreground            W - white background
   
    For example, "ansi(fc, Test)" would hilight "Test" in flashing cyan.
   
!   See also: ANSI, COLOR 
  & APOSS()
    aposs(<object>)
  
--- 486,495 ----
          w - white foreground            W - white background
   
    For example, "ansi(fc, Test)" would hilight "Test" in flashing cyan.
+   Default foreground and background use the client's default color for
+   fore and back.
   
! See also: ANSI, COLOR 
  & APOSS()
    aposs(<object>)
  
***************
*** 501,507 ****
    Returns the angle with the given <sine> (arc-sine), with the angle
    expressed in the given angle type, or radians by default.
  
!   See HELP CTU() for more on the angle type.
  & ATAN()
  & ATAN2()
    atan(<tangent>[,<angle type>])
--- 506,512 ----
    Returns the angle with the given <sine> (arc-sine), with the angle
    expressed in the given angle type, or radians by default.
  
!   See 'HELP CTU()' for more on the angle type.
  & ATAN()
  & ATAN2()
    atan(<tangent>[,<angle type>])
***************
*** 515,521 ****
    result. It is useful in converting between cartesian and polar
    coordinates.
  
!   See HELP CTU() for more on the angle type.
  & ATRLOCK()
    atrlock(<object>/<attrib>[, <on|off>])
  
--- 520,526 ----
    result. It is useful in converting between cartesian and polar
    coordinates.
  
!   See 'HELP CTU()' for more on the angle type.
  & ATRLOCK()
    atrlock(<object>/<attrib>[, <on|off>])
  
***************
*** 540,546 ****
    attrib_set(me/foo,%0) will _always_ create an attribute.
  
    Of course, if the empty_attrs configuration option is turned off,
!   then the above paragraph doesn't apply.  See @config attribs.
    
  & BAND()
    band(<integer1>, <integer2>[, ... , <integerN>])
--- 545,551 ----
    attrib_set(me/foo,%0) will _always_ create an attribute.
  
    Of course, if the empty_attrs configuration option is turned off,
!   then the above paragraph doesn't apply.  See '@config attribs'.
    
  & BAND()
    band(<integer1>, <integer2>[, ... , <integerN>])
***************
*** 572,577 ****
--- 577,583 ----
     > think before(foo bar baz,r)
     foo ba
  
+ See also: after(), first()
  & BRACKETS()
    brackets([<string>])
  
***************
*** 607,613 ****
    bound is returned. If it's higher than the higher bound,
    the higher bound is returned.
    
!   See also: ceil(), floor(), round(), trunc()
  & BXOR()
    bxor(<integer>, <integer>[, ... , <integerN>])
  
--- 613,619 ----
    bound is returned. If it's higher than the higher bound,
    the higher bound is returned.
    
! See also: ceil(), floor(), round(), trunc()
  & BXOR()
    bxor(<integer>, <integer>[, ... , <integerN>])
  
***************
*** 619,636 ****
    Returns <string> with the first character capitalized.
    Example: capstr(foo bar baz) returns "Foo bar baz"
  
!   See also: lcstr(), ucstr()
  & CAT()
    cat(<string1>[, ... , <stringN>])
  
    cat() concatenates strings, separating each string by a space.
    So "[cat(one, two)]" will return 'one two'.
  & CEIL()
    ceil(<number>)
   
    Returns the least integral value greater than or equal to <number>.
  
!   See also: floor(), bound(), round(), trunc()
  & CENTER()
    center(<string>, <width>[, <fill>[, <rightfill>]])
   
--- 625,644 ----
    Returns <string> with the first character capitalized.
    Example: capstr(foo bar baz) returns "Foo bar baz"
  
! See also: lcstr(), ucstr()
  & CAT()
    cat(<string1>[, ... , <stringN>])
  
    cat() concatenates strings, separating each string by a space.
    So "[cat(one, two)]" will return 'one two'.
+ 
+ See also: strcat()
  & CEIL()
    ceil(<number>)
   
    Returns the least integral value greater than or equal to <number>.
  
! See also: floor(), bound(), round(), trunc()
  & CENTER()
    center(<string>, <width>[, <fill>[, <rightfill>]])
   
***************
*** 643,649 ****
    If <string> divides <width> into uneven portions, the left side
    will be one character shorter than the right side.
   
!   Example:
      > say center(X,5,-)
      You say, "--X--"
      > say center(X,5,-=)
--- 651,657 ----
    If <string> divides <width> into uneven portions, the left side
    will be one character shorter than the right side.
   
!   Examples:
      > say center(X,5,-)
      You say, "--X--"
      > say center(X,5,-=)
***************
*** 653,658 ****
--- 661,667 ----
      > say center(hello,16,12345)
      You say, "12345hello543215"
  
+ See also: align(), ljust(), rjust()
  & CHECKPASS()
    checkpass(<player>, <string>)
  
***************
*** 690,696 ****
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
!   See also: Connection Functions
  & SENT()
    sent(<player|descriptor>)
  
--- 699,705 ----
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
! See also: Connection Functions
  & SENT()
    sent(<player|descriptor>)
  
***************
*** 700,706 ****
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
!   See also: Connection Functions
  & RECV()
    recv(<player|descriptor>)
  
--- 709,715 ----
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
! See also: Connection Functions
  & RECV()
    recv(<player|descriptor>)
  
***************
*** 710,716 ****
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
!   See also: Connection Functions
  & COMP()
    comp(<value1>, <value2>[,<type>])
  
--- 719,725 ----
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
! See also: Connection Functions
  & COMP()
    comp(<value1>, <value2>[,<type>])
  
***************
*** 741,747 ****
    or the enactor (%#).  You CANNOT get the contents of anything else,
    regardless of whether or not you have objects in it.
  
!   See also: lcon(), next()
  & CONFIG()
    config()
    config(<option>)
--- 750,756 ----
    or the enactor (%#).  You CANNOT get the contents of anything else,
    regardless of whether or not you have objects in it.
  
! See also: lcon(), next()
  & CONFIG()
    config()
    config(<option>)
***************
*** 760,766 ****
    Players who are not connected have a conn value of "-1", as do dark
    wizards, when conn() is used on them by a non-priv'ed player.
   
!   See also: CONNECTED
  & CONTROLS()
    controls(<object>, <victim>)
    
--- 769,775 ----
    Players who are not connected have a conn value of "-1", as do dark
    wizards, when conn() is used on them by a non-priv'ed player.
   
! See also: CONNECTED
  & CONTROLS()
    controls(<object>, <victim>)
    
***************
*** 770,776 ****
    object). You must control <object> or <victim>, or have the See_All
    power, to use this function.
  
!   See also: CONTROL
    
  & CONVSECS()
  & CONVUTCSECS()
--- 779,785 ----
    object). You must control <object> or <victim>, or have the See_All
    power, to use this function.
  
! See also: CONTROL
    
  & CONVSECS()
  & CONVUTCSECS()
***************
*** 785,798 ****
    convutcsecs() and convsecs() with a second argument of 'utc' return
    the time based on UTC time instead of the server's local time.
    
!   Example:
    > say [secs()]
    You say, "709395750"
    > say [convsecs(709395750)]
    You say, "Wed Jun 24 10:22:54 1992"
    > say [convutcsecs(709395750)]
    You say, "Wed Jun 24 14:22:30 1992"
!   See also: convtime(), time()
  & CONVTIME()
    convtime(<time string>)
  
--- 794,808 ----
    convutcsecs() and convsecs() with a second argument of 'utc' return
    the time based on UTC time instead of the server's local time.
    
!   Examples:
    > say [secs()]
    You say, "709395750"
    > say [convsecs(709395750)]
    You say, "Wed Jun 24 10:22:54 1992"
    > say [convutcsecs(709395750)]
    You say, "Wed Jun 24 14:22:30 1992"
! 
! See also: convtime(), time()
  & CONVTIME()
    convtime(<time string>)
  
***************
*** 814,839 ****
    > say [convtime(Wed Jun 24 10:22:54 1992)]
    You say, "709395774"
  
!   See also: convsecs(), time()
  & COS()
    cos(<angle>[,<angle type>])
   
    Returns the cosine of <angle>. Angle must be in the given angle
    type, or radians by default. 
  
!   Example:
    > say cos(90, d)
    You say, "0" 
    > say cos(1.570796)
    You say, "0"
  
!   See HELP CTU() for more on the angle type.
  & PCREATE()
    pcreate(<name>, <password>)
  
    Creates a player with a given name and password. Wizard-only.
  
!   See also: @pcreate
  & CREATE()
    create(<object>, <cost>)
   
--- 824,849 ----
    > say [convtime(Wed Jun 24 10:22:54 1992)]
    You say, "709395774"
  
! See also: convsecs(), time()
  & COS()
    cos(<angle>[,<angle type>])
   
    Returns the cosine of <angle>. Angle must be in the given angle
    type, or radians by default. 
  
!   Examples:
    > say cos(90, d)
    You say, "0" 
    > say cos(1.570796)
    You say, "0"
  
!   See 'HELP CTU()' for more on the angle type.
  & PCREATE()
    pcreate(<name>, <password>)
  
    Creates a player with a given name and password. Wizard-only.
  
! See also: @pcreate
  & CREATE()
    create(<object>, <cost>)
   
***************
*** 879,885 ****
  
    Note especially the last example, which will trip you up if you use
    floating point numbers with dec() and expect it to work like sub().
!   See also: inc()
  & DECOMPOSE()
    decompose(<string>)
  
--- 889,896 ----
  
    Note especially the last example, which will trip you up if you use
    floating point numbers with dec() and expect it to work like sub().
! 
! See also: inc()
  & DECOMPOSE()
    decompose(<string>)
  
***************
*** 887,893 ****
    parse-able characters to recreate <string> exactly after one parsing. It
    takes care of multiple spaces, '%r's, and '%t's.
  
!   See also: @decompile2, escape(), secure()
  & DECRYPT()
    decrypt(<string>, <password>)
  
--- 898,904 ----
    parse-able characters to recreate <string> exactly after one parsing. It
    takes care of multiple spaces, '%r's, and '%t's.
  
! See also: @decompile2, escape(), secure()
  & DECRYPT()
    decrypt(<string>, <password>)
  
***************
*** 915,921 ****
      > say default(me/Test, No fruits!)
      You say "No fruits!"
   
!   See also: get(), eval(), ufun(), edefault(), udefault(), uldefault()
   
  & DELETE()
    delete(<string>, <first>, <len>)
--- 926,932 ----
      > say default(me/Test, No fruits!)
      You say "No fruits!"
   
! See also: get(), eval(), ufun(), edefault(), udefault(), uldefault()
   
  & DELETE()
    delete(<string>, <first>, <len>)
***************
*** 931,937 ****
      You say, "abcfgh"
      > say delete(abcdefgh, 3, -2)
      You say, "abefgh"
!  
  & DIE()
    die(<number of times to roll die>, <number of sides on die>[, <show>])
   
--- 942,949 ----
      You say, "abcfgh"
      > say delete(abcdefgh, 3, -2)
      You say, "abefgh"
! 
! See also: strreplace()
  & DIE()
    die(<number of times to roll die>, <number of sides on die>[, <show>])
   
***************
*** 944,950 ****
    be a space-seperated list of the individual rolls rather than their
    sum.
  
!   Example:
    > think die(3, 6)
    6
    > think die(3, 6, 1)
--- 956,962 ----
    be a space-seperated list of the individual rolls rather than their
    sum.
  
!   Examples:
    > think die(3, 6)
    6
    > think die(3, 6, 1)
***************
*** 969,975 ****
    Without SSL, only the sha algorithm is enabled. In both cases, sha returns
    the same thing as the sha0() function.
  
!   See also: sha0()
  & DIST2D()
    dist2d(<x1>, <y1>, <x2>, <y2>)
  
--- 981,987 ----
    Without SSL, only the sha algorithm is enabled. In both cases, sha returns
    the same thing as the sha0() function.
  
! See also: sha0()
  & DIST2D()
    dist2d(<x1>, <y1>, <x2>, <y2>)
  
***************
*** 999,1012 ****
    Note that add(mul(div(%0,%1),%1),remainder(%0,%1)) always yields %0,
    and add(mul(floordiv(%0,%1),%1),modulo(%0,%1)) also always yields %0.
  
!   See also: modulo(), fdiv()
  & DOING()
    doing(<player|descriptor>)
  
    Given the name of a connected player, returns that player's @doing
    string if they can be seen on the WHO list.
  
!   See also: @poll, @doing, poll()
  & E()
    e()
   
--- 1011,1024 ----
    Note that add(mul(div(%0,%1),%1),remainder(%0,%1)) always yields %0,
    and add(mul(floordiv(%0,%1),%1),modulo(%0,%1)) also always yields %0.
  
! See also: modulo(), fdiv()
  & DOING()
    doing(<player|descriptor>)
  
    Given the name of a connected player, returns that player's @doing
    string if they can be seen on the WHO list.
  
! See also: @poll, @doing, poll()
  & E()
    e()
   
***************
*** 1029,1035 ****
      > say edefault(me/Test,You have no marbles.)
      You say "You have no marbles."
    
!   See also: get(), eval(), ufun(), default(), udefault()
   
  & EDIT()
    edit(<string>, <search>, <replace>[, ... , <searchN>, <replaceN>])
--- 1041,1047 ----
      > say edefault(me/Test,You have no marbles.)
      You say "You have no marbles."
    
! See also: get(), eval(), ufun(), default(), udefault()
   
  & EDIT()
    edit(<string>, <search>, <replace>[, ... , <searchN>, <replaceN>])
***************
*** 1052,1058 ****
  
    edit() can not replace a literal single ^ or $. Use regedit() for that.
    
!   See also: @edit, regedit()
  & ELEMENT()
    element(<list>, <item>,<single-character separator>)
    
--- 1064,1070 ----
  
    edit() can not replace a literal single ^ or $. Use regedit() for that.
    
! See also: @edit, regedit()
  & ELEMENT()
    element(<list>, <item>,<single-character separator>)
    
***************
*** 1065,1071 ****
      > say [element(this|is|a|test|string,is,|)]
      You say, "2"
  
!   See also: match(), grab()
  & ELEMENTS()
    elements(<list of words>, <list of numbers>[,<delim>[, <osep>]])
   
--- 1077,1083 ----
      > say [element(this|is|a|test|string,is,|)]
      You say, "2"
  
! See also: match(), grab()
  & ELEMENTS()
    elements(<list of words>, <list of numbers>[,<delim>[, <osep>]])
   
***************
*** 1079,1085 ****
      > say elements(Foof|Ack|Beep|Moo,3 1,|)
      You say "Beep|Foof"
  
!   See also: extract(), index(), grab()
  & ELOCK()
    elock(<object>[/<locktype>], <victim>)
  
--- 1091,1097 ----
      > say elements(Foof|Ack|Beep|Moo,3 1,|)
      You say "Beep|Foof"
  
! See also: extract(), index(), grab()
  & ELOCK()
    elock(<object>[/<locktype>], <victim>)
  
***************
*** 1095,1101 ****
        think elock(Dancing Slippers/drop, Princess)
        > 0
    
!   See also: @lock, locktypes
  & EMIT()
  & NSEMIT()
    emit(<message>)
--- 1107,1113 ----
        think elock(Dancing Slippers/drop, Princess)
        > 0
    
! See also: @lock, locktypes
  & EMIT()
  & NSEMIT()
    emit(<message>)
***************
*** 1110,1116 ****
  
    Returns an encrypted string produced by a simple password-based
    encrypted algorithm. Good passwords are long passwords.
!   This is not high-security encryption.  See also decrypt().
  & ENTRANCES()
    entrances([<object> [,<type>[, <begin>[, <end>]]]])
  
--- 1122,1130 ----
  
    Returns an encrypted string produced by a simple password-based
    encrypted algorithm. Good passwords are long passwords.
!   This is not high-security encryption.
! 
! See also: decrypt()
  & ENTRANCES()
    entrances([<object> [,<type>[, <begin>[, <end>]]]])
  
***************
*** 1135,1141 ****
  
    Takes two numbers, and returns 1 if they are equal, 0 otherwise.
    
!   See also: neq()
  & ESCAPE()
    escape(<string>)
   
--- 1149,1155 ----
  
    Takes two numbers, and returns 1 if they are equal, 0 otherwise.
    
! See also: neq()
  & ESCAPE()
    escape(<string>)
   
***************
*** 1152,1158 ****
    message object).  Since the function preserves the original string, 
    it is, in most cases, a better choice than SECURE().
  
!   See also: decompose(), secure()
  & EVAL()
  & GET_EVAL()
    eval(<object>, <attribute>)
--- 1166,1172 ----
    message object).  Since the function preserves the original string, 
    it is, in most cases, a better choice than SECURE().
  
! See also: decompose(), secure()
  & EVAL()
  & GET_EVAL()
    eval(<object>, <attribute>)
***************
*** 1175,1181 ****
    of xget() -- using a slash rather than a comma to separate the object from
    the attribute. It is included for TinyMUSH 2.x compatibility.
    
!   See also: get(), u(), xget()
  & EXIT()
    exit(<object>)
  
--- 1189,1195 ----
    of xget() -- using a slash rather than a comma to separate the object from
    the attribute. It is included for TinyMUSH 2.x compatibility.
    
! See also: get(), u(), xget()
  & EXIT()
    exit(<object>)
  
***************
*** 1187,1193 ****
    enactor (%#).  You CANNOT get the exit list of anything else,
    regardless of whether or not you have objects in it.
  
!   See also: lexits(), next()
  & EXP()
    exp(<number>)
   
--- 1201,1207 ----
    enactor (%#).  You CANNOT get the exit list of anything else,
    regardless of whether or not you have objects in it.
  
! See also: lexits(), next()
  & EXP()
    exp(<number>)
   
***************
*** 1205,1218 ****
      think extract(This is a test string,3,2)
      > a test
  
!   See also: index(), elements(), grab()
  & FDIV()
    fdiv(<numerator>, <denominator>)
   
    Returns the quotient of the two numbers. Note that the DIV() and MOD()
    functions cannot be used on floating point numbers.
  
!   See also: div()
  & FILTER()
  & FILTERBOOL()
    filter([<obj>/]<attr>, <list>[,<delimiter>[, <osep>]])
--- 1219,1232 ----
      think extract(This is a test string,3,2)
      > a test
  
! See also: index(), elements(), grab()
  & FDIV()
    fdiv(<numerator>, <denominator>)
   
    Returns the quotient of the two numbers. Note that the DIV() and MOD()
    functions cannot be used on floating point numbers.
  
! See also: div()
  & FILTER()
  & FILTERBOOL()
    filter([<obj>/]<attr>, <list>[,<delimiter>[, <osep>]])
***************
*** 1234,1240 ****
      > say [filter(test/is_odd, 1 2 3 4 5 6)]
      You say, "1 3 5"
  
!   See also: anonymous attributes
  & FINDABLE()
    findable(<object>, <victim>)
   
--- 1248,1254 ----
      > say [filter(test/is_odd, 1 2 3 4 5 6)]
      You say, "1 3 5"
  
! See also: anonymous attributes
  & FINDABLE()
    findable(<object>, <victim>)
   
***************
*** 1248,1254 ****
  
    Returns the first element of a list.
  
!   See also: rest(), last()
  & FIRSTOF()
    firstof(<expr1>, <expr2>[, ... , <exprN>])
  
--- 1262,1268 ----
  
    Returns the first element of a list.
  
! See also: before(), rest(), last()
  & FIRSTOF()
    firstof(<expr1>, <expr2>[, ... , <exprN>])
  
***************
*** 1272,1278 ****
    > say firstof(get(%#/royal cheese),#-1 This Has No Meaning,0,)
    You say, ""
  
!   See also: allof(), BOOLEAN VALUES
  & FLAGS()
    flags(<object>)
    flags(<object>/<attribute>)
--- 1286,1292 ----
    > say firstof(get(%#/royal cheese),#-1 This Has No Meaning,0,)
    You say, ""
  
! See also: allof(), BOOLEAN VALUES
  & FLAGS()
    flags(<object>)
    flags(<object>/<attribute>)
***************
*** 1287,1293 ****
    some flags may not have flag letters, and multiple flags may have
    the same letter (and will appear twice).
  
!   See also: lflags()
  & LFLAGS()
    lflags(<object>)
    lflags(<object>/<attribute>)
--- 1301,1307 ----
    some flags may not have flag letters, and multiple flags may have
    the same letter (and will appear twice).
  
! See also: lflags()
  & LFLAGS()
    lflags(<object>)
    lflags(<object>/<attribute>)
***************
*** 1299,1305 ****
    Given no arguments, this function returns a space-separated list
    of all flag names known to the server.
  
!   See also: flags()
  & FLIP()
  & REVERSE()
    flip(<string>)
--- 1313,1319 ----
    Given no arguments, this function returns a space-separated list
    of all flag names known to the server.
  
! See also: flags()
  & FLIP()
  & REVERSE()
    flip(<string>)
***************
*** 1307,1318 ****
  
    This function reverses a string. For example, "flip(foo bar baz)"
    returns "zab rab oof".
  & FLOOR()
    floor(<number>)
   
    Returns the greatest integral value less than or equal to <number>.
  
!   See also: ceil(), bound(), round(), trunc()
  & FMOD()
    fmod(<number>, <divisor>)
  
--- 1321,1334 ----
  
    This function reverses a string. For example, "flip(foo bar baz)"
    returns "zab rab oof".
+ 
+ See also: revwords()
  & FLOOR()
    floor(<number>)
   
    Returns the greatest integral value less than or equal to <number>.
  
! See also: ceil(), bound(), round(), trunc()
  & FMOD()
    fmod(<number>, <divisor>)
  
***************
*** 1358,1364 ****
      > say [fold(test/add_nums,1 2 3 4 5)]
      You say, "15"
  
!   See also: anonymous attributes
  & FOLLOWERS()
    followers(<object>)
  
--- 1374,1380 ----
      > say [fold(test/add_nums,1 2 3 4 5)]
      You say, "15"
  
! See also: anonymous attributes
  & FOLLOWERS()
    followers(<object>)
  
***************
*** 1399,1405 ****
      > say [foreach(is_alphanum,jt1o+)]
      You say, "1 1 1 1 0 "
  
!   See also: anonymous attributes
   
  & FRACTION()
    fraction(<number>)
--- 1415,1421 ----
      > say [foreach(is_alphanum,jt1o+)]
      You say, "1 1 1 1 0 "
  
! See also: anonymous attributes
   
  & FRACTION()
    fraction(<number>)
***************
*** 1454,1461 ****
    extract(list, match(list, pattern, delimiter), 1, delimiter) or
    the regular expression variation thereof.
  
!   See also: match(), extract(), element(), elements(), index(), regmatch(),
!             graball()
  & GRABALL()
  & REGRABALL()
  & REGRABALLI()
--- 1470,1477 ----
    extract(list, match(list, pattern, delimiter), 1, delimiter) or
    the regular expression variation thereof.
  
! See also: match(), extract(), element(), elements(), index(), regmatch(),
!   graball()
  & GRABALL()
  & REGRABALL()
  & REGRABALLI()
***************
*** 1477,1483 ****
    > say regraball(This is testing a test,s$)
    You say "This is"
    
!   See also: match(), matchall(), grab(), regmatch()
  & GREP()
  & REGREP()
  & WILDGREP()
--- 1493,1499 ----
    > say regraball(This is testing a test,s$)
    You say "This is"
    
! See also: match(), matchall(), grab(), regmatch()
  & GREP()
  & REGREP()
  & WILDGREP()
***************
*** 1551,1559 ****
    HASTYPE() function.
  
    If an attribute is given, checks to see if the attribute has the 
!   given attribute flag. See help attribute flags for attribute flag names.
  
!   See also: orlflags(), andlflags(), orflags(), andflags()
  & HASPOWER()
    haspower(<object>, <power name>)
   
--- 1567,1575 ----
    HASTYPE() function.
  
    If an attribute is given, checks to see if the attribute has the 
!   given attribute flag. See 'help attribute flags' for attribute flag names.
  
! See also: orlflags(), andlflags(), orflags(), andflags()
  & HASPOWER()
    haspower(<object>, <power name>)
   
***************
*** 1593,1599 ****
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
!   See also: Connection Functions, ipaddr(), ports(), lports()
  & IDLE()
  & IDLESECS()
    idle(<player|descriptor>)
--- 1609,1615 ----
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
! See also: Connection Functions, ipaddr(), ports(), lports()
  & IDLE()
  & IDLESECS()
    idle(<player|descriptor>)
***************
*** 1613,1619 ****
    if the <condition> is true, or <false expression> (if provided) if the
    <condition> is false.
  
!   See also:  BOOLEAN VALUES, switch()
  & INAME()
    iname(<object>)
  
--- 1629,1635 ----
    if the <condition> is true, or <false expression> (if provided) if the
    <condition> is false.
  
! See also: BOOLEAN VALUES, switch()
  & INAME()
    iname(<object>)
  
***************
*** 1624,1630 ****
    You must be see_all, control <object>, or be inside it to use this
    function.
  
!   See also: @nameformat, name(), fullname()
  & INC()
    inc(<integer>)
    inc(<string-ending-in-integer>)
--- 1640,1646 ----
    You must be see_all, control <object>, or be inside it to use this
    function.
  
! See also: @nameformat, name(), fullname()
  & INC()
    inc(<integer>)
    inc(<string-ending-in-integer>)
***************
*** 1643,1649 ****
  
    Note especially the last example, which will trip you up if you use
    floating point numbers with inc() and expect it to work like add().
!   See also: dec()
  & INDEX()
    index(<list>,<character>, <first>, <length>)
    
--- 1659,1666 ----
  
    Note especially the last example, which will trip you up if you use
    floating point numbers with inc() and expect it to work like add().
! 
! See also: dec()
  & INDEX()
    index(<list>,<character>, <first>, <length>)
    
***************
*** 1663,1669 ****
      blue tribble^
      cute doll"
  
!   See also: extract(), elements(), grab()
  & INSERT()
    insert(<list>, <position>, <new item>[,<single-character separator>])
  
--- 1680,1686 ----
      blue tribble^
      cute doll"
  
! See also: extract(), elements(), grab()
  & INSERT()
    insert(<list>, <position>, <new item>[,<single-character separator>])
  
***************
*** 1702,1715 ****
    This function returns 1 if the string is a valid object dbref, and
    0 if the string is not a valid object dbref.
   
!   See also: DBREFS
  & ISINT()
    isint(<string>)
  
    Returns 1 if its argument is an integer, and 0 otherwise. Integers can
    begin with a '+' or '-' sign, but the rest of the string must be digits.
  
!   See also: isnum()
  & ISNUM()
    isnum(<string>)
  
--- 1719,1732 ----
    This function returns 1 if the string is a valid object dbref, and
    0 if the string is not a valid object dbref.
   
! See also: DBREFS
  & ISINT()
    isint(<string>)
  
    Returns 1 if its argument is an integer, and 0 otherwise. Integers can
    begin with a '+' or '-' sign, but the rest of the string must be digits.
  
! See also: isnum()
  & ISNUM()
    isnum(<string>)
  
***************
*** 1718,1724 ****
    the characters in the string must be digits, and an optional decimal
    point.
  
!   See also: isint()
  & ISWORD()
    isword(<string>)
    
--- 1735,1741 ----
    the characters in the string must be digits, and an optional decimal
    point.
  
! See also: isint()
  & ISWORD()
    isword(<string>)
    
***************
*** 1769,1776 ****
    <pattern> is what will be "mapped" onto each element of the list,
    with the token "##" being replaced successively by the next word in
    the list, and the token "#@" being replaced by the word's position
!   in the list (see also help itext() and help inum()).  The result is
!   concatenated and returned as a space separated list.  This is similar
    to @dolist, but the results are made into a list rather than executed.
  
    The list may be <delimiter>-separated.
--- 1786,1793 ----
    <pattern> is what will be "mapped" onto each element of the list,
    with the token "##" being replaced successively by the next word in
    the list, and the token "#@" being replaced by the word's position
!   in the list (also see 'help itext()' and see 'help inum()').  The result
!   is concatenated and returned as a space separated list.  This is similar
    to @dolist, but the results are made into a list rather than executed.
  
    The list may be <delimiter>-separated.
***************
*** 1850,1862 ****
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
!   See also: Connection Functions, hostname(), ports(), lports()
  & LAST()
    last(<list>[,<delimiter>])
    
    Returns the last element of a list.
  
!   See also: first(), rest()
  & LATTR()
  & LATTRP()
    lattr(<object>[/<attribute pattern>])
--- 1867,1879 ----
    The caller can use the function on himself, but using on any other
    player requires privileged power such as Wizard, Royalty or SEE_ALL.
  
! See also: Connection Functions, hostname(), ports(), lports()
  & LAST()
    last(<list>[,<delimiter>])
    
    Returns the last element of a list.
  
! See also: first(), rest()
  & LATTR()
  & LATTRP()
    lattr(<object>[/<attribute pattern>])
***************
*** 1874,1880 ****
  
    lattrp() also includes attributes inherited from parents.
  
!   See also: nattr(), examine
  & NATTR()
  & NATTRP()
  & ATTRCNT()
--- 1891,1897 ----
  
    lattrp() also includes attributes inherited from parents.
  
! See also: nattr(), examine
  & NATTR()
  & NATTRP()
  & ATTRCNT()
***************
*** 1891,1897 ****
  
    nattrp() and attrpcnt() also count matching attributes on the parent.
  
!   See also: lattr()
  & LCON()
    lcon(<object>)
  
--- 1908,1914 ----
  
    nattrp() and attrpcnt() also count matching attributes on the parent.
  
! See also: lattr()
  & LCON()
    lcon(<object>)
  
***************
*** 1903,1916 ****
    or the enactor (%#).  You CANNOT get the contents of anything else,
    regardless of whether or not you have objects in it.
  
!   See also: lexits(), lplayers(), con(), next(), lvcon()
  & LCSTR()
    lcstr(<string>)
  
    Returns <string> with all letters converted to lowercase.
    Example: lcstr(Foo BAR bAz) returns "foo bar baz"
  
!   See also: capstr(), ucstr()
  & LDELETE()
    Ldelete(<list>, <position>[,<single-character separator>])
    
--- 1920,1933 ----
    or the enactor (%#).  You CANNOT get the contents of anything else,
    regardless of whether or not you have objects in it.
  
! See also: lexits(), lplayers(), con(), next(), lvcon()
  & LCSTR()
    lcstr(<string>)
  
    Returns <string> with all letters converted to lowercase.
    Example: lcstr(Foo BAR bAz) returns "foo bar baz"
  
! See also: capstr(), ucstr()
  & LDELETE()
    Ldelete(<list>, <position>[,<single-character separator>])
    
***************
*** 1928,1933 ****
--- 1945,1951 ----
  
    Returns the first <length> characters from string.
  
+ See also: left(), mid()
  & NSLEMIT()
  & LEMIT()
    lemit(<message>)
***************
*** 1948,1954 ****
    enactor (%#).  You CANNOT get the exit list of anything else,
    regardless of whether or not you have objects in it.
  
!   See also: lcon(), exit(), next(), lvexits()
  & LJUST()
    ljust(<string>, <length>[,<fill>])
    
--- 1966,1972 ----
    enactor (%#).  You CANNOT get the exit list of anything else,
    regardless of whether or not you have objects in it.
  
! See also: lcon(), exit(), next(), lvexits()
  & LJUST()
    ljust(<string>, <length>[,<fill>])
    
***************
*** 1966,1972 ****
      You say, "
      0foo---7
      01234567"
!  
  & LINK()
    link(<name>, <destination>[, <preserve>])
   
--- 1984,1991 ----
      You say, "
      0foo---7
      01234567"
! 
! See also: align(), center(), rjust()
  & LINK()
    link(<name>, <destination>[, <preserve>])
   
***************
*** 2011,2017 ****
    Supported <op>'s are: add and band bor bxor div fdiv max mean 
    median min modulo mul nand nor or remainder stddev sub xor
  
!   Example:
    >think lmath(add, 1|2|3, |)
    6
    >think lmath(max, 1 2 3)
--- 2030,2036 ----
    Supported <op>'s are: add and band bor bxor div fdiv max mean 
    median min modulo mul nand nor or remainder stddev sub xor
  
!   Examples:
    >think lmath(add, 1|2|3, |)
    6
    >think lmath(max, 1 2 3)
***************
*** 2058,2064 ****
    > say [setr(0, Outside)]-[localize(setr(0, Inside))]-%q0
    You say, "Outside-Inside-Outside"
  
!   See also: setq(), setr(), r(), ulocal(), uldefault(), s()
  & LOCATE()
    locate(<looker>, <name>, <parameters>)
   
--- 2077,2083 ----
    > say [setr(0, Outside)]-[localize(setr(0, Inside))]-%q0
    You say, "Outside-Inside-Outside"
  
! See also: setq(), setr(), r(), ulocal(), uldefault(), s()
  & LOCATE()
    locate(<looker>, <name>, <parameters>)
   
***************
*** 2115,2121 ****
  
    This is a side-effect function and may not be enabled on some MUSHes.
   
!   See also: @lock, locktypes, lockflags(), llockflags(), lset(), llocks()
  & LLOCKS()
  & LOCKS()
    llocks(<object>)
--- 2134,2141 ----
  
    This is a side-effect function and may not be enabled on some MUSHes.
   
! See also: @lock, locktypes, elock(), lockflags(), llockflags(), lset(),
!   llocks()
  & LLOCKS()
  & LOCKS()
    llocks(<object>)
***************
*** 2130,2136 ****
    > th llocks(me)
    Basic USER:ITSME Use
  
!   See also: lock(), lset(), lockflags(), llockflags()
  & LOCKFLAGS()
    lockflags(<object>[/<locktype>])
    lockflags()
--- 2150,2156 ----
    > th llocks(me)
    Basic USER:ITSME Use
  
! See also: lock(), lset(), lockflags(), llockflags()
  & LOCKFLAGS()
    lockflags(<object>[/<locktype>])
    lockflags()
***************
*** 2142,2148 ****
    Given no arguments, this function returns a string consisting of
    all the flag letters the server knows.
  
!   See also: llockflags(), lset(), lock(), llocks()
  & LLOCKFLAGS()
    llockflags(<object>[/<locktype>])
    llockflags()
--- 2162,2168 ----
    Given no arguments, this function returns a string consisting of
    all the flag letters the server knows.
  
! See also: llockflags(), lset(), lock(), llocks()
  & LLOCKFLAGS()
    llockflags(<object>[/<locktype>])
    llockflags()
***************
*** 2153,2159 ****
    Given no arguments, this function returns a space-separated list
    of all flag names known to the server.
  
!   See also: lockflags(), lset(), lock(), llocks()
  & LSET()
    lset(<object>/<lock type>,[!]<flag>)
    
--- 2173,2179 ----
    Given no arguments, this function returns a space-separated list
    of all flag names known to the server.
  
! See also: lockflags(), lset(), lock(), llocks()
  & LSET()
    lset(<object>/<lock type>,[!]<flag>)
    
***************
*** 2179,2192 ****
    see_all power. You must be in <object> or control it to use this
    function.
  
!   See also: lvplayers(), lcon(), lthings()
  & LTHINGS()
    lthings(<object>)
  
    This function returns the dbrefs of all things, dark or not, in 
    <object>. You must be in <object> or control it to use this function.
  
!   See also: lvthings(), lcon()
  & LPOS()
    lpos(<string>, <character>)
  
--- 2199,2212 ----
    see_all power. You must be in <object> or control it to use this
    function.
  
! See also: lvplayers(), lcon(), lthings()
  & LTHINGS()
    lthings(<object>)
  
    This function returns the dbrefs of all things, dark or not, in 
    <object>. You must be in <object> or control it to use this function.
  
! See also: lvthings(), lcon()
  & LPOS()
    lpos(<string>, <character>)
  
***************
*** 2203,2209 ****
    > say lpos(a-bc-def-g, -)
    You say, "1 4 8"
  
!   See also: pos()
  & LSEARCH()
  & NLSEARCH()
  & SEARCH()
--- 2223,2229 ----
    > say lpos(a-bc-def-g, -)
    You say, "1 4 8"
  
! See also: pos()
  & LSEARCH()
  & NLSEARCH()
  & SEARCH()
***************
*** 2289,2302 ****
    and visible (non-dark). You must be in <object> or control it to use this
    function.
  
!   See also: lcon()
  & LVEXITS()
    lvexits(<room>)
  
    This function returns the dbrefs of all visible (non-dark) exits from
    <room>. You must be in the room or control it to use this function.
  
!   See also: lexits()
  & LVPLAYERS()
    lvplayers(<object>)
  
--- 2309,2322 ----
    and visible (non-dark). You must be in <object> or control it to use this
    function.
  
! See also: lcon()
  & LVEXITS()
    lvexits(<room>)
  
    This function returns the dbrefs of all visible (non-dark) exits from
    <room>. You must be in the room or control it to use this function.
  
! See also: lexits()
  & LVPLAYERS()
    lvplayers(<object>)
  
***************
*** 2324,2330 ****
  
    lwohid() returns a list of objid's instead.
  
!   See also: mwho(), nwho(), xwho()
  & MAP()
    map([<object>/]<attribute>, <list>[,<delim>[, <osep>]])
    
--- 2344,2350 ----
  
    lwohid() returns a list of objid's instead.
  
! See also: mwho(), nwho(), xwho()
  & MAP()
    map([<object>/]<attribute>, <list>[,<delim>[, <osep>]])
    
***************
*** 2347,2353 ****
      > say [map(times_two,1;2;3;4;5,;)]
      You say, "2;4;6;8;10"
  
!   See also: anonymous attributes
  & MATCH()
    match(<list>, <pattern>[,<delimiter>])
  
--- 2367,2373 ----
      > say [map(times_two,1;2;3;4;5,;)]
      You say, "2;4;6;8;10"
  
! See also: anonymous attributes
  & MATCH()
    match(<list>, <pattern>[,<delimiter>])
  
***************
*** 2363,2369 ****
    To match an entire string  (for example, to match "red blue green"
    to "*bl*"), use the strmatch() function.
  
!   See also: element(), grab()
  & MATCHALL()
    matchall(<list>, <pattern>[,<delim>[, <osep>]])
   
--- 2383,2389 ----
    To match an entire string  (for example, to match "red blue green"
    to "*bl*"), use the strmatch() function.
  
! See also: element(), grab()
  & MATCHALL()
    matchall(<list>, <pattern>[,<delim>[, <osep>]])
   
***************
*** 2380,2386 ****
    > say matchall(This is testing a test,tes*)
    You say "3 5"
   
!   See also: match(), strmatch(), graball()
  & MAX()
    max(<number>[, ... , <numberN>])
  
--- 2400,2406 ----
    > say matchall(This is testing a test,tes*)
    You say "3 5"
   
! See also: match(), strmatch(), graball()
  & MAX()
    max(<number>[, ... , <numberN>])
  
***************
*** 2391,2410 ****
  
    Returns the mean (arithmetic average) of its arguments.
  
!   See also: median(), stddev()
  & MEDIAN()
    median(<number>[, ... , <numberN>)
  
    Returns the median (the middlemost numerically) of its arguments.
  
!   See also: mean(), stddev()
  & MEMBER()
    member(<list>, <word>[,<delimiter>])
  
    Takes a list and a word, and returns the position of <word>
    if <word> is a word in <list>.  A word is defined as a string which
    has no interior spaces.  So '  hello  ' would be one word, while
!   'hello there' would be two.  See LISTS
  
    member() is case-sensitive and requires an exact match. For wild
    card patterns, use match().
--- 2411,2430 ----
  
    Returns the mean (arithmetic average) of its arguments.
  
! See also: median(), stddev()
  & MEDIAN()
    median(<number>[, ... , <numberN>)
  
    Returns the median (the middlemost numerically) of its arguments.
  
! See also: mean(), stddev()
  & MEMBER()
    member(<list>, <word>[,<delimiter>])
  
    Takes a list and a word, and returns the position of <word>
    if <word> is a word in <list>.  A word is defined as a string which
    has no interior spaces.  So '  hello  ' would be one word, while
!   'hello there' would be two.  See 'LISTS'
  
    member() is case-sensitive and requires an exact match. For wild
    card patterns, use match().
***************
*** 2427,2433 ****
      > say [merge(AB[space(2)]EF,abcdef,)]
      You say, "ABcdEF"
  
!   See also: TR()  
  & MID()
    mid(<string>, <first>, <length>)
  
--- 2447,2453 ----
      > say [merge(AB[space(2)]EF,abcdef,)]
      You say, "ABcdEF"
  
! See also: splice(), tr()  
  & MID()
    mid(<string>, <first>, <length>)
  
***************
*** 2443,2449 ****
      > say mid(foobar, 2, -2)
      You say, "oo"
  
!   See also: LEFT(), RIGHT()  
  & MIN()
    min(<number>[, ... , <numberN>])
  
--- 2463,2469 ----
      > say mid(foobar, 2, -2)
      You say, "oo"
  
! See also: left(), right()  
  & MIN()
    min(<number>[, ... , <numberN>])
  
***************
*** 2461,2467 ****
    specified, it defaults to a space. If using more than 2 lists, the last
    argument must be a delimiter.
  
!   See HELP MIX2 for examples
  & MIX2  
    Examples of mix():
   
--- 2481,2487 ----
    specified, it defaults to a space. If using more than 2 lists, the last
    argument must be a delimiter.
  
!   See 'MIX2' for examples
  & MIX2  
    Examples of mix():
   
***************
*** 2475,2481 ****
    > say [mix(add_nums, 1:1:1, 2:2:2, 3:3:3, :)]
    You say, "6:6:6"
  
!   See also: anonymous attributes
  & MOD()
  & MODULO()
  & MODULUS()
--- 2495,2501 ----
    > say [mix(add_nums, 1:1:1, 2:2:2, 3:3:3, :)]
    You say, "6:6:6"
  
! See also: anonymous attributes
  & MOD()
  & MODULO()
  & MODULUS()
***************
*** 2500,2506 ****
  
    Mod and modulus are just aliases for modulo.
  
!   See also: DIV
  & MONEY()
    money(<object>)
    money(<integer>)
--- 2520,2526 ----
  
    Mod and modulus are just aliases for modulo.
  
! See also: div()
  & MONEY()
    money(<object>)
    money(<integer>)
***************
*** 2557,2563 ****
    doesn't match an element in the original <list 1>, a corresponding
    element from <list 2> does not appear in the final result.
  
!   See HELP MUNGE2 for examples.
  & MUNGE2
    For example: Consider attribute PLACES, which contains "Fort Benden Ista",
    and another attribute DBREFS contains the dbrefs of the main JUMP_OK
--- 2577,2583 ----
    doesn't match an element in the original <list 1>, a corresponding
    element from <list 2> does not appear in the final result.
  
!   See 'MUNGE2' for examples.
  & MUNGE2
    For example: Consider attribute PLACES, which contains "Fort Benden Ista",
    and another attribute DBREFS contains the dbrefs of the main JUMP_OK
***************
*** 2570,2576 ****
    > say [munge(sort,v(places),v(dbrefs))]
    You say, "#9000 #20 #5000"
   
!   See HELP MUNGE3 for another example
  & MUNGE3
    Another common task that munge() is well suited for is sorting a list
    of dbrefs of players by order of connection. This example reuses the 
--- 2590,2596 ----
    > say [munge(sort,v(places),v(dbrefs))]
    You say, "#9000 #20 #5000"
   
!   See 'MUNGE3' for another example.
  & MUNGE3
    Another common task that munge() is well suited for is sorting a list
    of dbrefs of players by order of connection. This example reuses the 
***************
*** 2581,2587 ****
    > say [munge(sort,iter(v(faction_members),conn(##)),v(faction_members))]
    You say, "#12 #234 #3"
  
!   See also: anonymous attributes
  & MWHO()
  & MWHOID()
    mwho()
--- 2601,2607 ----
    > say [munge(sort,iter(v(faction_members),conn(##)),v(faction_members))]
    You say, "#12 #234 #3"
  
! See also: anonymous attributes
  & MWHO()
  & MWHOID()
    mwho()
***************
*** 2675,2681 ****
  
    Basically the same as [not(eq(<num1>,<num2>))].
  
!   See also: eq(), not()
  & NEXT()
    next(<object>)
  
--- 2695,2701 ----
  
    Basically the same as [not(eq(<num1>,<num2>))].
  
! See also: eq(), not()
  & NEXT()
    next(<object>)
  
***************
*** 2693,2699 ****
    regardless of whether or not you have objects in it.  These rules
    apply to exits, as well.
  
!   See also: lcon(), lexits(), con(), exit()
  & NEXTDBREF()
    nextdbref()
  
--- 2713,2719 ----
    regardless of whether or not you have objects in it.  These rules
    apply to exits, as well.
  
! See also: lcon(), lexits(), con(), exit()
  & NEXTDBREF()
    nextdbref()
  
***************
*** 2709,2722 ****
    for use on privileged global objects who need an unprivileged count
    of who's online.
  
!   See also: nwho(), mwho(), xmwho()
  & NOR()
    nor(<boolean>[, ... , <booleanN>])
  
    Returns 1 if all its arguments are false, 0 if one is true.
    Equivalent to not(or()), but more efficient.
  
!   See also: and(), or(), xor(), not()
  & NOT()
    not(<boolean value>)
  
--- 2729,2742 ----
    for use on privileged global objects who need an unprivileged count
    of who's online.
  
! See also: nwho(), mwho(), xmwho()
  & NOR()
    nor(<boolean>[, ... , <booleanN>])
  
    Returns 1 if all its arguments are false, 0 if one is true.
    Equivalent to not(or()), but more efficient.
  
! See also: and(), or(), xor(), not()
  & NOT()
    not(<boolean value>)
  
***************
*** 2725,2733 ****
    the input is equivalent to false(0), it returns a 1.
  
    The definition of truth and falsehood depends on configuration settings;
!   see help BOOLEAN VALUES for details.
   
!   See also: and(), or(), nor(), xor()
  
  & NUM()
    num(<object>)
--- 2745,2753 ----
    the input is equivalent to false(0), it returns a 1.
  
    The definition of truth and falsehood depends on configuration settings;
!   see 'BOOLEAN VALUES' for details.
   
! See also: and(), or(), nor(), xor()
  
  & NUM()
    num(<object>)
***************
*** 2744,2750 ****
    ncon(<object>)  is identical to words(lcon(<object>))
    nvcon(<object>) is identical to words(lvcon(<object>))
  
!   See also: nexits(), nplayers(), xcon(), lcon(), lvcon()
  & NVEXITS()
  & NEXITS()
    nexits(<room>)
--- 2764,2770 ----
    ncon(<object>)  is identical to words(lcon(<object>))
    nvcon(<object>) is identical to words(lvcon(<object>))
  
! See also: nexits(), nplayers(), xcon(), lcon(), lvcon()
  & NVEXITS()
  & NEXITS()
    nexits(<room>)
***************
*** 2755,2761 ****
    nexits(<room>)  is identical to words(lexits(<room>))
    nvexits(<room>) is identical to words(lvexits(<room>))
  
!   See also: ncon(), nplayers(), xexits(), lexits(), lvexits()
  & NVPLAYERS()
  & NPLAYERS()
    nplayers(<object>)
--- 2775,2781 ----
    nexits(<room>)  is identical to words(lexits(<room>))
    nvexits(<room>) is identical to words(lvexits(<room>))
  
! See also: ncon(), nplayers(), xexits(), lexits(), lvexits()
  & NVPLAYERS()
  & NPLAYERS()
    nplayers(<object>)
***************
*** 2766,2772 ****
    nplayers(<object>)  is identical to words(lplayers(<object>))
    nvplayers(<object>) is identical to words(lvplayers(<object>))
  
!   See also: ncon(), nexits(), xplayers(), lplayers(), lvplayers()
  & NVTHINGS()
  & NTHINGS()
    nthings(<object>)
--- 2786,2792 ----
    nplayers(<object>)  is identical to words(lplayers(<object>))
    nvplayers(<object>) is identical to words(lvplayers(<object>))
  
! See also: ncon(), nexits(), xplayers(), lplayers(), lvplayers()
  & NVTHINGS()
  & NTHINGS()
    nthings(<object>)
***************
*** 2777,2790 ****
    nthings(<object>)  is identical to words(lthings(<object>))
    nvthings(<object>) is identical to words(lvthings(<object>))
  
!   See also: ncon(), nexits(), xthings(), lthings(), lvthings()
  & NWHO()
    nwho()
  
    This returns a count of all currently-connected players. When
    mortals use this function, DARK wizards or royalty are NOT counted.
  
!   See also: lwho(), nmwho(), xwho()
  & OBJ()
    obj(<object>)
  
--- 2797,2810 ----
    nthings(<object>)  is identical to words(lthings(<object>))
    nvthings(<object>) is identical to words(lvthings(<object>))
  
! See also: ncon(), nexits(), xthings(), lthings(), lvthings()
  & NWHO()
    nwho()
  
    This returns a count of all currently-connected players. When
    mortals use this function, DARK wizards or royalty are NOT counted.
  
! See also: lwho(), nmwho(), xwho()
  & OBJ()
    obj(<object>)
  
***************
*** 2821,2827 ****
    This function returns the amount of memory, in bytes, being used
    by the object. It can only be used by players with Search powers.
  
!   See also: playermem()
  
  & OEMIT()
  & NSOEMIT()
--- 2841,2847 ----
    This function returns the amount of memory, in bytes, being used
    by the object. It can only be used by players with Search powers.
  
! See also: playermem()
  
  & OEMIT()
  & NSOEMIT()
***************
*** 2850,2856 ****
    (including side effects), while cor() stops evaluation after the
    first argument that evaluates to true.
  
!   See also: BOOLEAN VALUES, and()
  & ORFLAGS()
    orflags(<object>,<string of flag characters>)
    
--- 2870,2876 ----
    (including side effects), while cor() stops evaluation after the
    first argument that evaluates to true.
  
! See also: BOOLEAN VALUES, and()
  & ORFLAGS()
    orflags(<object>,<string of flag characters>)
    
***************
*** 2909,2915 ****
    the @prompt command. nsprompt() is a wizard-only variation that
    works like @nsprompt.
   
!   See also: @prompt, @nsprompt, PROMPT_NEWLINES 
  & PI()
    pi()
    
--- 2929,2935 ----
    the @prompt command. nsprompt() is a wizard-only variation that
    works like @nsprompt.
   
! See also: @prompt, @nsprompt, PROMPT_NEWLINES 
  & PI()
    pi()
    
***************
*** 2922,2928 ****
    by everything owned by the player. It can only be used by players
    with Search powers.
  
!   See also: objmem()
  
  & PMATCH()
    pmatch(<string>)
--- 2942,2948 ----
    by everything owned by the player. It can only be used by players
    with Search powers.
  
! See also: objmem()
  
  & PMATCH()
    pmatch(<string>)
***************
*** 2942,2948 ****
  
    This function returns the current @poll. 
  
!   See also: @poll, doing(), @doing 
  & LPORTS()
  & PORTS()
    lports()
--- 2962,2968 ----
  
    This function returns the current @poll. 
  
! See also: @poll, doing(), @doing 
  & LPORTS()
  & PORTS()
    lports()
***************
*** 2978,2984 ****
   
    Returns <number> to the power of <exponent>.
  
!   See also: root()
  & POWERS()
    powers(<object>)
    powers(<object>, <power>)
--- 2998,3004 ----
   
    Returns <number> to the power of <exponent>.
  
! See also: root()
  & POWERS()
    powers(<object>)
    powers(<object>, <power>)
***************
*** 3005,3011 ****
    The '%qN' percent-substitution can also be used to access these local
    registers, where N is register <register> needed.
    
!   See "help SETQ()" for details about registers.
  & RAND()
    rand(<num>)
    rand(<min>, <max>)
--- 3025,3031 ----
    The '%qN' percent-substitution can also be used to access these local
    registers, where N is register <register> needed.
    
!   See 'HELP SETQ()' for details about registers.
  & RAND()
    rand(<num>)
    rand(<min>, <max>)
***************
*** 3046,3058 ****
    argument is evaluated once for each match, allowing for more complex
    transformations than is possible with straight replacement.
  
!   Example:
    > say regedit(this test is the best string, (?P<char>.)est, $<char>rash)
    You say "this trash is the best string"
    > say regeditall(this test is the best string, (.)est, [capstr($1)]rash)
    You say "this Trash is the Brash string"
  
!   See also: edit(), regmatch()
  & REGMATCH()
  & REGMATCHI()
    (Help text from TinyMUSH 2.2.4, with permission)
--- 3066,3078 ----
    argument is evaluated once for each match, allowing for more complex
    transformations than is possible with straight replacement.
  
!   Examples:
    > say regedit(this test is the best string, (?P<char>.)est, $<char>rash)
    You say "this trash is the best string"
    > say regeditall(this test is the best string, (.)est, [capstr($1)]rash)
    You say "this Trash is the Brash string"
  
! See also: edit(), regmatch()
  & REGMATCH()
  & REGMATCHI()
    (Help text from TinyMUSH 2.2.4, with permission)
***************
*** 3083,3089 ****
   
    See 'help regexp syntax' for an explanation of regular expressions.
  
!   See also: regrab()
  & REMIT()
  & NSREMIT()
    remit(<object>, <message>)
--- 3103,3109 ----
   
    See 'help regexp syntax' for an explanation of regular expressions.
  
! See also: regrab()
  & REMIT()
  & NSREMIT()
    remit(<object>, <message>)
***************
*** 3114,3120 ****
    Example:
      > say [repeat(Test, 5)]
      You say, "TestTestTestTestTest"
!   
  & REPLACE()
    replace(<list>, <position>, <new item>[,<single-character separator>])
    
--- 3134,3141 ----
    Example:
      > say [repeat(Test, 5)]
      You say, "TestTestTestTestTest"
! 
! See also: space()
  & REPLACE()
    replace(<list>, <position>, <new item>[,<single-character separator>])
    
***************
*** 3133,3139 ****
  
    Returns a list minus its first element.
  
!   See also: first(), last()
  & REVWORDS()
    revwords(<list of words>[,<delimiter>[, <output separator>]])
   
--- 3154,3160 ----
  
    Returns a list minus its first element.
  
! See also: after(), first(), last()
  & REVWORDS()
    revwords(<list of words>[,<delimiter>[, <output separator>]])
   
***************
*** 3142,3153 ****
    Example:
      > say revwords(foo bar baz eep)
      You say, "eep baz bar foo"
!   
  & RIGHT()
    right(<string>, <length>)
  
    Returns the <length> rightmost characters from string.
  
  & RJUST()
    rjust(<string>, <length>[,<fill>])
    
--- 3163,3176 ----
    Example:
      > say revwords(foo bar baz eep)
      You say, "eep baz bar foo"
! 
! See also: flip()  
  & RIGHT()
    right(<string>, <length>)
  
    Returns the <length> rightmost characters from string.
  
+ See also: left(), mid()
  & RJUST()
    rjust(<string>, <length>[,<fill>])
    
***************
*** 3165,3171 ****
      You say, "
      0---foo7
      01234567"
!  
  & RLOC()
    rloc(<object>, <levels>) 
    
--- 3188,3195 ----
      You say, "
      0---foo7
      01234567"
! 
! See also: align(), center(), ljust()
  & RLOC()
    rloc(<object>, <levels>) 
    
***************
*** 3210,3229 ****
    Returns the n-th root of <number>. The 2nd root is the square root,
    the 3rd the cube root, and so on.
  
!   Example:
    > think root(27, 3)
    3
    > think power(3, 3)
    27
  
!   See also: sqrt(), power()
  & ROUND()
    round(<number>, <places>)
   
    Rounds <number> to <places> decimal places. <places> must be between
    0 and 6.
   
!   See also: ceil(), floor(), bound(), trunc()
  & S()
  & S-FUNCTION
    s(string)
--- 3234,3253 ----
    Returns the n-th root of <number>. The 2nd root is the square root,
    the 3rd the cube root, and so on.
  
!   Examples:
    > think root(27, 3)
    3
    > think power(3, 3)
    27
  
! See also: sqrt(), power()
  & ROUND()
    round(<number>, <places>)
   
    Rounds <number> to <places> decimal places. <places> must be between
    0 and 6.
   
! See also: ceil(), floor(), bound(), trunc()
  & S()
  & S-FUNCTION
    s(string)
***************
*** 3261,3266 ****
--- 3285,3291 ----
    special characters; it will scramble these characters just like normal
    characters.
  
+ See also: shuffle()
  & SECS()
    secs()
  
***************
*** 3276,3282 ****
    can make output slightly ugly, but it's a good way of preventing other
    people from doing nasty things with your objects.
  
!   See also: decompose(), escape()
  & SET()
    set(<object>, <flag>)
    set(<object>/<attribute>, <attribute flag>)
--- 3301,3307 ----
    can make output slightly ugly, but it's a good way of preventing other
    people from doing nasty things with your objects.
  
! See also: decompose(), escape()
  & SET()
    set(<object>, <flag>)
    set(<object>/<attribute>, <attribute flag>)
***************
*** 3298,3310 ****
    elements in <list1> that aren't in <list2>. The list that is
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in help sorting. If used with exactly four arguments where the fourth
!   is not a sort type, it's treated instead as the output separator.
  
    Example:
      > say setdiff(foo baz gleep bar, bar moof gleep)
      You say, "baz foo"
!  
  & SETINTER()
    setinter(<list1>, <list2>[,<delimiter>[, <sort type>[,<osep>]]])
   
--- 3323,3337 ----
    elements in <list1> that aren't in <list2>. The list that is
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in "help sorting". If used with exactly four arguments where the
!   fourth is not a sort type, it's treated instead as the output
!   separator.
  
    Example:
      > say setdiff(foo baz gleep bar, bar moof gleep)
      You say, "baz foo"
! 
! See also: setinter(), setunion()
  & SETINTER()
    setinter(<list1>, <list2>[,<delimiter>[, <sort type>[,<osep>]]])
   
***************
*** 3312,3324 ****
    elements that are in both <list1> and <list2>. The list that is
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in help sorting. If used with exactly four arguments where the fourth
!   is not a sort type, it's treated instead as the output separator.
   
    Example:
      > say setinter(foo baz gleep bar, bar moof gleep)
     You say, "bar gleep"
  
  & SETQ()
  & SETR()
    setq(<register>, <string>[, ... ,<regN>, <stringN>])
--- 3339,3353 ----
    elements that are in both <list1> and <list2>. The list that is
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in "help sorting". If used with exactly four arguments where the
!   fourth is not a sort type, it's treated instead as the output
!   separator.
   
    Example:
      > say setinter(foo baz gleep bar, bar moof gleep)
     You say, "bar gleep"
  
+ See also: setdiff(), setunion()
  & SETQ()
  & SETR()
    setq(<register>, <string>[, ... ,<regN>, <stringN>])
***************
*** 3343,3349 ****
    Registers set via setq() or setr() can be accessed via the r() function,
    or via the %qN percent-substitution.
  
!   See "help SETQ2" for examples of its use.
  
  & SETQ2
  
--- 3372,3378 ----
    Registers set via setq() or setr() can be accessed via the r() function,
    or via the %qN percent-substitution.
  
!   See 'SETQ2' for examples of its use.
  
  & SETQ2
  
***************
*** 3369,3375 ****
  
    yields "foobar - barfoo".
  
!   See "help SETQ3" for scoping rules of setq().
  
  & SETQ3
    The registers set by setq() can be used in later commands in the same
--- 3398,3404 ----
  
    yields "foobar - barfoo".
  
!   See 'SETQ3' for scoping rules of setq().
  
  & SETQ3
    The registers set by setq() can be used in later commands in the same
***************
*** 3395,3410 ****
    elements. Think of it as CAT() without words duplicated.  The list
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in help sorting. If used with exactly four arguments where the fourth
!   is not a sort type, it's treated instead as the output separator.
   
!   Example:
      > say setunion(foo baz gleep bar, bar moof gleep)
      You say, "bar baz foo gleep moof"
      > say setunion(1.1 1.0, 1.000)
      You say, "1.0 1.000 1.1"
      > say setunion(1.1 1.0, 1.000, %b, f)
      You say, "1.0 1.1"
  & SHA0()
    sha0(<string>)
  
--- 3424,3442 ----
    elements. Think of it as CAT() without words duplicated.  The list
    returned is sorted. Normally, alphabetic sorting is done. You can
    change this with the fourth argument, which is a sort type as defined
!   in "help sorting". If used with exactly four arguments where the
!   fourth is not a sort type, it's treated instead as the output
!   separator.
   
!   Examples:
      > say setunion(foo baz gleep bar, bar moof gleep)
      You say, "bar baz foo gleep moof"
      > say setunion(1.1 1.0, 1.000)
      You say, "1.0 1.000 1.1"
      > say setunion(1.1 1.0, 1.000, %b, f)
      You say, "1.0 1.1"
+ 
+ See also: setdiff(), setinter()
  & SHA0()
    sha0(<string>)
  
***************
*** 3426,3432 ****
    This function shuffles the order of the items of a list, returning a
    random permutation of its elements. "[shuffle(foo bar baz gleep)]" 
    might evaluate to "baz foo gleep bar".
!   
  & SIGN()
    sign(<number>)
  
--- 3458,3465 ----
    This function shuffles the order of the items of a list, returning a
    random permutation of its elements. "[shuffle(foo bar baz gleep)]" 
    might evaluate to "baz foo gleep bar".
! 
! See also: scramble()
  & SIGN()
    sign(<number>)
  
***************
*** 3439,3445 ****
    Returns the sine of <angle>, which should be expressed in the
    given angle type, or radians by default.
  
!   See HELP CTU() for more on the angle type.
  & SORT()
    sort(<word1> [... <wordN>][, <sort type>[,<delimiter>[, <osep>]]])
    
--- 3472,3478 ----
    Returns the sine of <angle>, which should be expressed in the
    given angle type, or radians by default.
  
!   See 'HELP CTU()' for more on the angle type.
  & SORT()
    sort(<word1> [... <wordN>][, <sort type>[,<delimiter>[, <osep>]]])
    
***************
*** 3449,3455 ****
    the words are dbrefs, it will sort them in order of smallest to
    largest. Otherwise, it will perform a lexicographic sort.
  
!   The second argument is a sort type. See help sorting.
   
    The optional third argument gives the list's delimiter character.
    If not present, <delimiter> defaults to a space.
--- 3482,3488 ----
    the words are dbrefs, it will sort them in order of smallest to
    largest. Otherwise, it will perform a lexicographic sort.
  
!   The second argument is a sort type. See 'help sorting'.
   
    The optional third argument gives the list's delimiter character.
    If not present, <delimiter> defaults to a space.
***************
*** 3478,3484 ****
    this limit is exceeded, the function will fail _silently_. List and
    function sizes should be kept reasonable.
  
!   See also: anonymous attributes, sorting, sortkey()
  & SORTKEY()
    sortkey([<obj>/]<attrib>, <list>[, <sort type>[,<delimiter>[, <osep>]]])
  
--- 3511,3517 ----
    this limit is exceeded, the function will fail _silently_. List and
    function sizes should be kept reasonable.
  
! See also: anonymous attributes, sorting, sortkey()
  & SORTKEY()
    sortkey([<obj>/]<attrib>, <list>[, <sort type>[,<delimiter>[, <osep>]]])
  
***************
*** 3499,3505 ****
      > say sortkey(key_name,#1 #2 #3)
      You say, "#2 #3 #1"
  
!   See also: anonymous attributes, sorting, sortby
  & SORTING
    In functions where you can specify a sorting method, you can provide
    one of these sort types:
--- 3532,3538 ----
      > say sortkey(key_name,#1 #2 #3)
      You say, "#2 #3 #1"
  
! See also: anonymous attributes, sorting, sortby()
  & SORTING
    In functions where you can specify a sorting method, you can provide
    one of these sort types:
***************
*** 3526,3532 ****
    Whether or not the 'a' sort type is case-sensitive or not depends
    on the particular mush and its environment.
  
!   See also: sort(), sortby(), sortkey(), setunion(), setinter(), setdiff()
  & SOUNDEX()
    soundex(<word>)
  
--- 3559,3565 ----
    Whether or not the 'a' sort type is case-sensitive or not depends
    on the particular mush and its environment.
  
! See also: sort(), sortby(), sortkey(), setunion(), setinter(), setdiff()
  & SOUNDEX()
    soundex(<word>)
  
***************
*** 3538,3546 ****
    > think soundex(foobar)
    F160
  
!   For details of how the algorithm works, see help soundex2
  
!   See also: soundslike()
  & SOUNDEX2
    Here's how the soundex algorithm works:
    1. The first letter of the soundex code is the first letter of
--- 3571,3579 ----
    > think soundex(foobar)
    F160
  
!   For details of how the algorithm works, see 'help soundex2'.
  
! See also: soundslike()
  & SOUNDEX2
    Here's how the soundex algorithm works:
    1. The first letter of the soundex code is the first letter of
***************
*** 3567,3573 ****
    soundlike(<word>, <word>)
  
    The soundslike function returns 1 if the two words have the same
!   soundex code (see help soundex() for information), which means, 
    in general, if they sound alike. For example:
    
    > think soundslike(robin,robbyn)
--- 3600,3606 ----
    soundlike(<word>, <word>)
  
    The soundslike function returns 1 if the two words have the same
!   soundex code (see 'help soundex()' for information), which means,
    in general, if they sound alike. For example:
    
    > think soundslike(robin,robbyn)
***************
*** 3581,3586 ****
--- 3614,3621 ----
    Prints <number> number of spaces. Useful for times when you want to
    be able to use lots of spaces to separate things. For example,
    "a[space(5)]b  would print, "Amberyl says, "a     b"".
+ 
+ See also: repeat()
  & SPEAK()
    speak(<speaker>, <string>[, <say string>
                    [, [<transform obj>/]<transform attr>
***************
*** 3738,3753 ****
  
    Given a number, return its written-out representation in words.
  
!   See also: ordinal()
  & ORDINAL()
    ordinal(<integer>)
  
    Given an integer, return its written-out ordinal representation in words.
  
    > think ordinal(1)
    first
  
!   See also: spellnum()
  & SPLICE()
    splice(<list1>, <list2>, <word>[,<delimiter>])
    
--- 3773,3789 ----
  
    Given a number, return its written-out representation in words.
  
! See also: ordinal()
  & ORDINAL()
    ordinal(<integer>)
  
    Given an integer, return its written-out ordinal representation in words.
  
+   Example:
    > think ordinal(1)
    first
  
! See also: spellnum()
  & SPLICE()
    splice(<list1>, <list2>, <word>[,<delimiter>])
    
***************
*** 3761,3767 ****
    Example:
      > say [splice(foo bar baz,eek moof gleep,bar)]
      You say, "foo moof baz"
!  
  & MAPSQL()
    mapsql([<object>/]<attribute>, <query>[, <osep>[, <dofieldnames>]])
  
--- 3797,3804 ----
    Example:
      > say [splice(foo bar baz,eek moof gleep,bar)]
      You say, "foo moof baz"
! 
! See also: merge()
  & MAPSQL()
    mapsql([<object>/]<attribute>, <query>[, <osep>[, <dofieldnames>]])
  
***************
*** 3780,3787 ****
    If <dofieldnames> evaluates to a true boolean, then the first call
    will be with row number 0 and %1-%9 will be set to the field names.
  
!   See "Help mapsql2" for examples.
!   See also: sqlescape(), sql(), @sql
  & MAPSQL2
  
    > @@ Field, Type, Null?, Key?, Default, Extra
--- 3817,3825 ----
    If <dofieldnames> evaluates to a true boolean, then the first call
    will be with row number 0 and %1-%9 will be set to the field names.
  
!   See 'help mapsql2' for examples.
! 
! See also: anonymous attributes, sqlescape(), sql(), @sql
  & MAPSQL2
  
    > @@ Field, Type, Null?, Key?, Default, Extra
***************
*** 3819,3825 ****
       &SEL_GETID obj = SELECT id FROM mytable WHERE name = '[sqlescape(%0)]'
       &DOIT obj = $do *: ... [setq(0,sql(u(SEL_GETID,%0),~,|))] ...
  
!   See also: sqlescape(), mapsql(), @sql
  
  & SQLESCAPE()
    sqlescape(<string>)
--- 3857,3863 ----
       &SEL_GETID obj = SELECT id FROM mytable WHERE name = '[sqlescape(%0)]'
       &DOIT obj = $do *: ... [setq(0,sql(u(SEL_GETID,%0),~,|))] ...
  
! See also: sqlescape(), mapsql(), @sql
  
  & SQLESCAPE()
    sqlescape(<string>)
***************
*** 3837,3850 ****
  
    You must be a WIZARD or have the Sql_Ok power to use this function.
  
!   See also: sql(), mapsql(), @sql
  
  & SQRT()
    sqrt(<number>)
   
    Returns the square root of <number>. <number> cannot be negative.
  
!   See also: root()
  & SQUISH()
    squish(<string>[,<character>])
    
--- 3875,3888 ----
  
    You must be a WIZARD or have the Sql_Ok power to use this function.
  
! See also: sql(), mapsql(), @sql
  
  & SQRT()
    sqrt(<number>)
   
    Returns the square root of <number>. <number> cannot be negative.
  
! See also: root()
  & SQUISH()
    squish(<string>[,<character>])
    
***************
*** 3852,3858 ****
    and condenses all inter-word <character>s to a single <character>. If no
    character is given, uses space.
    
!   Example:
    
      > say [squish(  foo bar  baz blech   eek )]
      You say, "foo bar baz blech eek"
--- 3890,3896 ----
    and condenses all inter-word <character>s to a single <character>. If no
    character is given, uses space.
    
!   Examples:
    
      > say [squish(  foo bar  baz blech   eek )]
      You say, "foo bar baz blech eek"
***************
*** 3870,3883 ****
      > say starttime()
      You say "Sat Dec  7 00:09:13 1991
  
!   See also: convtime(), restarttime(), restarts()
  & RESTARTTIME()
    restarttime()
  
    Returns a string which is the time the MUSH last rebooted. The time
    is in the same format as the TIME() function returns.
  
!   See also: convtime(), starttime()
  & RESTARTS()
    restarts()
  
--- 3908,3921 ----
      > say starttime()
      You say "Sat Dec  7 00:09:13 1991
  
! See also: convtime(), restarttime(), restarts()
  & RESTARTTIME()
    restarttime()
  
    Returns a string which is the time the MUSH last rebooted. The time
    is in the same format as the TIME() function returns.
  
! See also: convtime(), starttime()
  & RESTARTS()
    restarts()
  
***************
*** 3891,3897 ****
    Players can check the SSL status of their own connection.
    The See_All power is required to check other connections. 
  
!   See also: terminfo()
  & STEP()
    step([<obj>/]<attr>, <list>, <step>[,<delim>, <outsep>])
  
--- 3929,3935 ----
    Players can check the SSL status of their own connection.
    The See_All power is required to check other connections. 
  
! See also: terminfo()
  & STEP()
    step([<obj>/]<attr>, <list>, <step>[,<delim>, <outsep>])
  
***************
*** 3911,3929 ****
    1 - 2 - 3
    4 - 5 -
  
!   See also: map(), iter(), anonymous attributes
  & STDDEV()
    stddev(<number>[, ... , <numberN>])
  
    Returns the sample standard deviation of its arguments.
  
!   See also: mean(), median()
  & STRCAT()
    strcat(<string1>[, ... , <stringN>])
   
    Concatenates strings together, with no space between them.
    For example, strcat(foo bar,baz blech) will return the string
    "foo barbaz blech".
  & STRINSERT()
    strinsert(<string>, <position>, <insert>)
  
--- 3949,3969 ----
    1 - 2 - 3
    4 - 5 -
  
! See also: map(), iter(), anonymous attributes
  & STDDEV()
    stddev(<number>[, ... , <numberN>])
  
    Returns the sample standard deviation of its arguments.
  
! See also: mean(), median()
  & STRCAT()
    strcat(<string1>[, ... , <stringN>])
   
    Concatenates strings together, with no space between them.
    For example, strcat(foo bar,baz blech) will return the string
    "foo barbaz blech".
+ 
+ See also: cat()
  & STRINSERT()
    strinsert(<string>, <position>, <insert>)
  
***************
*** 3931,3941 ****
    in <string>. Note that the first character in a string is numbered 0,
    not 1. 
  
!   Example:
    > think strinsert(barbaz, 0, foo)
    foobarbaz
    > think strinsert(Myname, 2, %b)
!   My name   
  & STRIPACCENTS()
    stripaccents(<string>)
  
--- 3971,3983 ----
    in <string>. Note that the first character in a string is numbered 0,
    not 1. 
  
!   Examples:
    > think strinsert(barbaz, 0, foo)
    foobarbaz
    > think strinsert(Myname, 2, %b)
!   My name
! 
! See also: strdelete()
  & STRIPACCENTS()
    stripaccents(<string>)
  
***************
*** 3949,3964 ****
    strlen(<string>)
  
    Returns the length of the string (The number of characters in it).
  & STRMATCH()
!   strmatch(<string>, <pattern>)
    
    This function is matches <pattern> against the entire <string>.
    It returns 1 if it matches and 0 if it doesn't. It is not
    case-sensitive, and <pattern> may contain wildcards.
   
    strmatch(Foo bar baz,*Baz) will return 1.
    strmatch(Foo bar baz,*Foo) will return 0.
    strmatch(Foo bar baz,*o*a*) will return 1.
  & STRREPLACE()
    strreplace(<string>, <start>, <length>, <text>)
  
--- 3991,4015 ----
    strlen(<string>)
  
    Returns the length of the string (The number of characters in it).
+ 
+ See also: words()
  & STRMATCH()
!   strmatch(<string>, <pattern>[, <register list>])
    
    This function is matches <pattern> against the entire <string>.
    It returns 1 if it matches and 0 if it doesn't. It is not
    case-sensitive, and <pattern> may contain wildcards.
+ 
+   If <register list> is given, there is a side-effect: Wildcards
+   and patterns are stored in q-registers, in the order they are
+   given. <register list> must be a space-separated list of digits
+   or letters.
   
    strmatch(Foo bar baz,*Baz) will return 1.
    strmatch(Foo bar baz,*Foo) will return 0.
    strmatch(Foo bar baz,*o*a*) will return 1.
+   strmatch(foo:bar,*:*,0 1) will return 1, and %q0 will be 'foo',
+                             %q1 will be 'bar'
  & STRREPLACE()
    strreplace(<string>, <start>, <length>, <text>)
  
***************
*** 3969,3974 ****
--- 4020,4027 ----
    Example:
    > think strreplace(Fix teh typo, 4, 3, the)
    Fix the typo
+ 
+ See also: delete(), strinsert()
  & SUB()
    sub(<num>, <num>)
   
***************
*** 3991,3997 ****
    is case-sensitive, reswitchi() is case-insensitive. The reswitchall versions
    evaluate every corresponding <list>, not just the first that matches a regexp.
  
!   See also: switch() 
  & SWITCH()
  & SWITCHALL()
  & CASE()
--- 4044,4050 ----
    is case-sensitive, reswitchi() is case-insensitive. The reswitchall versions
    evaluate every corresponding <list>, not just the first that matches a regexp.
  
! See also: switch() 
  & SWITCH()
  & SWITCHALL()
  & CASE()
***************
*** 4018,4025 ****
    <expr>ssions, without spaces between them, so they match similarly to
    @switch, while switch() and case() match more like @switch/first.
  
!   See HELP SWITCH WILDCARDS for more, and HELP SWITCH2 for examples
!   See also: reswitch(), if()
  & SWITCH2
    Examples of switch() and related functions:
      > say switch(test, *a*, foo, *b*, bar, *t*, neat, baz)
--- 4071,4079 ----
    <expr>ssions, without spaces between them, so they match similarly to
    @switch, while switch() and case() match more like @switch/first.
  
!   See 'HELP SWITCH WILDCARDS' for more, and see 'HELP SWITCH2' for examples.
! 
! See also: reswitch(), if()
  & SWITCH2
    Examples of switch() and related functions:
      > say switch(test, *a*, foo, *b*, bar, *t*, neat, baz)
***************
*** 4056,4068 ****
    \ when the argument is evaluated, and then that single \ will stop
    the greater/less than check).
  
!   See also: HELP WILDCARDS
  & T()
    t(<expression>)
  
    Returns a 0 if the expression is false, and 1 otherwise. 
    The definition of truth and falsehood depends on configuration settings;
!   see help BOOLEAN VALUES for details.
  
  & TABLE()
    table(<list>, <field width>, <line length>,<delimiter>, <osep>)
--- 4110,4122 ----
    \ when the argument is evaluated, and then that single \ will stop
    the greater/less than check).
  
! See also: WILDCARDS
  & T()
    t(<expression>)
  
    Returns a 0 if the expression is false, and 1 otherwise. 
    The definition of truth and falsehood depends on configuration settings;
!   see 'BOOLEAN VALUES' for details.
  
  & TABLE()
    table(<list>, <field width>, <line length>,<delimiter>, <osep>)
***************
*** 4090,4096 ****
    Returns the tangent of <angle>, which should be expressed in the
    given angle type, or radians by default.
  
!   See HELP CTU() for more on the angle type.
  & TEL()
    tel(<object>, <destination>[, <silent>[, <inside>]])
  
--- 4144,4150 ----
    Returns the tangent of <angle>, which should be expressed in the
    given angle type, or radians by default.
  
!   See 'HELP CTU()' for more on the angle type.
  & TEL()
    tel(<object>, <destination>[, <silent>[, <inside>]])
  
***************
*** 4100,4106 ****
    is an optional boolean that, if true, makes the function act like
    @tel/inside (some value for <silent> must also be specified).
  
!   See also: @tel
  & TERMINFO()
    terminfo(<player|descriptor>)
  
--- 4154,4160 ----
    is an optional boolean that, if true, makes the function act like
    @tel/inside (some value for <silent> must also be specified).
  
! See also: @tel
  & TERMINFO()
    terminfo(<player|descriptor>)
  
***************
*** 4132,4147 ****
  
    textentries() returns the topic names of matching entries. <entry>
    may include wildcards, in which case a list of matching topic names
!   will be return, separated by the <separator> string if one is
    provided, and space-separated otherwise.
  
!   Examples: 
    > say textfile(help,tan\()
    You say, "  tan(<angle>[,<angle type>])
      Returns the tangent of <angle>, which should be expressed in the
      given angle type, or radians by default.
  
!     See HELP CTU() for more on the angle type.
    "
  & TIME()
  & UTCTIME()
--- 4186,4201 ----
  
    textentries() returns the topic names of matching entries. <entry>
    may include wildcards, in which case a list of matching topic names
!   will be returned, separated by the <separator> string if one is
    provided, and space-separated otherwise.
  
!   Example: 
    > say textfile(help,tan\()
    You say, "  tan(<angle>[,<angle type>])
      Returns the tangent of <angle>, which should be expressed in the
      given angle type, or radians by default.
  
!     See 'HELP CTU()' for more on the angle type.
    "
  & TIME()
  & UTCTIME()
***************
*** 4165,4171 ****
    on a player will always return a time, and if TZ is not a number between
    -24 and +24 inclusive, the time returned is UTC.
  
!   See also: timefmt(), timestring(), convsecs(), convtime()
  & ETIMEFMT()
    etimefmt(<format>, <secs>)
  
--- 4219,4225 ----
    on a player will always return a time, and if TZ is not a number between
    -24 and +24 inclusive, the time returned is UTC.
  
! See also: timefmt(), timestring(), convsecs(), convtime()
  & ETIMEFMT()
    etimefmt(<format>, <secs>)
  
***************
*** 4184,4190 ****
    > think etimefmt($2mm $2ss, 500) - [timestring(500)]
     8m 20s -  8m 20s
  
!   See also: timestring(), timefmt()
  & ETIMEFMT2
    etimefmt()'s escape codes are similar to timefmt()'s.
    The time is broken up into days, hours, minutes, and seconds, and
--- 4238,4244 ----
    > think etimefmt($2mm $2ss, 500) - [timestring(500)]
     8m 20s -  8m 20s
  
! See also: timestring(), timefmt()
  & ETIMEFMT2
    etimefmt()'s escape codes are similar to timefmt()'s.
    The time is broken up into days, hours, minutes, and seconds, and
***************
*** 4253,4259 ****
    if the number of seconds is less than a day, hour, or minute.
    If <pad flag> is 2, all numbers will be 2 digits long.
  
!   Example:
    > say [timestring(301)]
    You say, " 5m  1s"
    > say [timestring(301,1)]
--- 4307,4313 ----
    if the number of seconds is less than a day, hour, or minute.
    If <pad flag> is 2, all numbers will be 2 digits long.
  
!   Examples:
    > say [timestring(301)]
    You say, " 5m  1s"
    > say [timestring(301,1)]
***************
*** 4261,4267 ****
    > say [timestring(301,2)]
    You say, "00d 00h 05m 01s"
  
!   See also: stringsecs()
  & STRINGSECS()
    stringsecs(<timestring>)
  
--- 4315,4321 ----
    > say [timestring(301,2)]
    You say, "00d 00h 05m 01s"
  
! See also: stringsecs()
  & STRINGSECS()
    stringsecs(<timestring>)
  
***************
*** 4272,4278 ****
    > say [stringsecs(5m 1s)]
    You say, "301"
  
!   See also: timestring(), etimefmt()
  & TR()
    tr(<string>, <find>, <replace>)
  
--- 4326,4332 ----
    > say [stringsecs(5m 1s)]
    You say, "301"
  
! See also: timestring(), etimefmt()
  & TR()
    tr(<string>, <find>, <replace>)
  
***************
*** 4292,4298 ****
        > say tr(uryyb, a-z, n-za-m)
         You say, "hello"
  
!   See also: MERGE()
  & TRIM()
  & TRIMPENN()
  & TRIMTINY()
--- 4346,4352 ----
        > say tr(uryyb, a-z, n-za-m)
         You say, "hello"
  
! See also: MERGE()
  & TRIM()
  & TRIMPENN()
  & TRIMTINY()
***************
*** 4332,4343 ****
    also be used to return the leading numeric prefix of a string, or
    "0" if there isn't one. For example, "val(101Dalmations)"  => 101.
    
!   See also: ceil(), floor(), bound(), round()
  & TYPE()
    type(<object>)
  
    This function returns the type of an object - PLAYER, THING, EXIT,
!   or ROOM. See "help types of objects" for more.
  & U()
  & UFUN()
  & ULAMBDA()
--- 4386,4397 ----
    also be used to return the leading numeric prefix of a string, or
    "0" if there isn't one. For example, "val(101Dalmations)"  => 101.
    
! See also: ceil(), floor(), bound(), round()
  & TYPE()
    type(<object>)
  
    This function returns the type of an object - PLAYER, THING, EXIT,
!   or ROOM. See 'types of objects' for more.
  & U()
  & UFUN()
  & ULAMBDA()
***************
*** 4351,4372 ****
    do not, the attribute will be read off the object that is
    evaluating the UFUN().
    
!   <arg 0>, <arg 1>, ... are the arguments that get passed to the
!   user function as v(0), v(1), etc. (as in @trigger).  You can pass
!   up to 10 arguments (v(0) through v(9)).
    
    This function is also known as U()  (alias for TinyMUSH compatibility).
  
    ulambda() also accepts anonymous attributes.
  
!   See "help UFUN2" for more.
    
  & U2
  & UFUN2
    Example:
    
!   > @va Object=$test *:"[ufun(testfun, v(0))]; @emit [v(0)]
!   > &testfun object=[strlen(v(0))] [ucstr(v(0))]
    > test string
    Object says, "6 STRING"
    string
--- 4405,4426 ----
    do not, the attribute will be read off the object that is
    evaluating the UFUN().
    
!   <arg0>, <arg1>, ... are the arguments that get passed to the
!   user function as %0, %1, etc. (as in @trigger).  You can pass
!   up to 10 arguments (%0 through %9).
    
    This function is also known as U()  (alias for TinyMUSH compatibility).
  
    ulambda() also accepts anonymous attributes.
  
!   See 'help UFUN2' for more.
    
  & U2
  & UFUN2
    Example:
    
!   > @va Object=$test *:say [ufun(testfun, %0)]; @emit %0
!   > &testfun object=[strlen(%0)] [ucstr(%0)]
    > test string
    Object says, "6 STRING"
    string
***************
*** 4377,4389 ****
    If the evaluation order doesn't quite seem right, adding escapes
    or breaking up the expression will probably help.
  
  & UCSTR()
    ucstr(<string>)
  
    Returns <string> with all letters converted to uppercase.
    Example: ucstr(Foo BAR baz) returns "FOO BAR BAZ"
  
!   See also: lcstr(), capstr()
  & UDEFAULT()
    udefault([<obj>/]<attr>, <default case>[, <arg0>[, ... , <arg9>]])
   
--- 4431,4463 ----
    If the evaluation order doesn't quite seem right, adding escapes
    or breaking up the expression will probably help.
  
+   See 'help UFUN3' for more.
+ 
+ & U3
+ & UFUN3
+   A user-defined function called with ufun() is always evaluated with the
+   permissions (powers and flags) of the object the function resides on.
+   An object may evaluate an attribute on any object it controls, the
+   attributes it owns on other objects, and publically accessible attributes.
+ 
+   Because the user-defined functions are evaluated with the permissions of
+   the object they're stored on, allowing arbitrary use of the ufun()
+   function can be insecure.
+ 
+   By default, the safer_ufun @config is enabled, preventing objects from
+   running code on objects more privileged than themselves. Being more
+   privileged means being higher up on the mortal/royalty/wizard hierarchy.
+   However, attributes with the 'public' flag can always be evaluated by
+   anyone. This is necessary for attributes such as 'describe', but should
+   not be used for code.
+ 
  & UCSTR()
    ucstr(<string>)
  
    Returns <string> with all letters converted to uppercase.
    Example: ucstr(Foo BAR baz) returns "FOO BAR BAZ"
  
! See also: lcstr(), capstr()
  & UDEFAULT()
    udefault([<obj>/]<attr>, <default case>[, <arg0>[, ... , <arg9>]])
   
***************
*** 4404,4416 ****
      > say udefault(me/Test,-- BOOM --,ACK)
      You say "-- BOOM --"
   
!   See also: get(), eval(), ufun(), uldefault(), default(), edefault()
  & ULDEFAULT()
!   uldefault([<obj>/]<attr>,<default case>[,<arg>]...)
  
    Just like UDEFAULT(), but it preserves registers like ULOCAL().
  
!   See also: u(), udefault(), ulocal(), setq()
  & ULOCAL()
    ulocal([<obj>/]<attr>[, <arg0>[, ... , <arg9>]])
   
--- 4478,4490 ----
      > say udefault(me/Test,-- BOOM --,ACK)
      You say "-- BOOM --"
   
! See also: get(), eval(), ufun(), uldefault(), default(), edefault()
  & ULDEFAULT()
!   uldefault([<obj>/]<attr>, <default case>[, <arg0>[, ... <arg9>]])
  
    Just like UDEFAULT(), but it preserves registers like ULOCAL().
  
! See also: u(), udefault(), ulocal(), setq()
  & ULOCAL()
    ulocal([<obj>/]<attr>[, <arg0>[, ... , <arg9>]])
   
***************
*** 4432,4441 ****
    calls arbitrary u() functions, where global register values need to be
    preserved from accidental user clobbering.
   
!   See "help ulocal2" for examples.
   
  & ULOCAL2
!   Example of ulocal():
      > &FRUIT me=apples bananas oranges pears
      > &SUB-FUNCTION me=[setq(0,v(FRUIT))][extract(%q0,match(%q0,%0),1)]
      > &TOP-FUNCTION me=[setq(0,are delicious!)][ulocal(SUB-FUNCTION,%0)] %q0
--- 4506,4515 ----
    calls arbitrary u() functions, where global register values need to be
    preserved from accidental user clobbering.
   
!   See 'help ulocal2' for examples.
   
  & ULOCAL2
!   Examples of ulocal():
      > &FRUIT me=apples bananas oranges pears
      > &SUB-FUNCTION me=[setq(0,v(FRUIT))][extract(%q0,match(%q0,%0),1)]
      > &TOP-FUNCTION me=[setq(0,are delicious!)][ulocal(SUB-FUNCTION,%0)] %q0
***************
*** 4452,4465 ****
    %q0, this is what was printed. In the first example, ulocal() reset the
    value of %q0 to its original "are delicious!"
   
!   See also: u(), setq(), r()
  
  & UNIQUE()
    unique(<list>[, <sort type>[,<sep>[, <osep>]]])
  
    unique() returns a copy of <list> with consecutive duplicate items
    removed. It does not sort the list. The optional <sort type> describes
!   what type of data is in the list; see help sorting for details. If
    no type is given, the elements are compared as strings. The optional
    third and fourth arguments are the list delimiter and output seperator.
  
--- 4526,4539 ----
    %q0, this is what was printed. In the first example, ulocal() reset the
    value of %q0 to its original "are delicious!"
   
! See also: u(), setq(), r()
  
  & UNIQUE()
    unique(<list>[, <sort type>[,<sep>[, <osep>]]])
  
    unique() returns a copy of <list> with consecutive duplicate items
    removed. It does not sort the list. The optional <sort type> describes
!   what type of data is in the list; see 'help sorting' for details. If
    no type is given, the elements are compared as strings. The optional
    third and fourth arguments are the list delimiter and output seperator.
  
***************
*** 4471,4477 ****
    > think unique(1|2|3|3, n, |, _)
      1_2_3
  
!   See also: setunion()
  
  & V()
  & V-FUNCTION
--- 4545,4551 ----
    > think unique(1|2|3|3, n, |, _)
      1_2_3
  
! See also: setunion()
  
  & V()
  & V-FUNCTION
***************
*** 4490,4496 ****
         v(!) is equivalent to %!
         v(3) is equivalent to %3
  
!   See also: SUBSTITUTIONS, get(), ATTRIBUTES
  
  & VADD()
    vadd(<vector>, <vector>[,<delimiter>])
--- 4564,4570 ----
         v(!) is equivalent to %!
         v(3) is equivalent to %3
  
! See also: SUBSTITUTIONS, get(), ATTRIBUTES
  
  & VADD()
    vadd(<vector>, <vector>[,<delimiter>])
***************
*** 4690,4695 ****
--- 4764,4771 ----
    words(<list>[,<delimiter>])
  
    words() returns the number of elements in <list>.
+ 
+ See also: strlen()
  & WRAP()
    wrap(<string>, <width>[, <first line width>[, <line separator>]])
  
***************
*** 4719,4740 ****
    attributes are listed _after_ child attributes, not sorted
    alphabetically.
  
!   See also: nattr(), lattr()
  & XGET()
    xget(<object>, <attribute>)
    
    This function is identical to get() in purpose, but a comma instead of
    a slash separates object and attribute. There is no real advantage to
!   using this instead of get(). Please see "help get()" for more details
    on the use of this function.
    
  & XOR()
!   xor(<boolean value>, <boolean value>)
  
!   Takes two booleans, and returns a 1 if one, and only one of the two
!   inputs is equivalent to true(1).  See BOOLEAN VALUES.
  
!   See also: and(), or(), not(), nor()
  & XVCON()
  & XCON()
    xcon(<object>, <start>, <count>)
--- 4795,4816 ----
    attributes are listed _after_ child attributes, not sorted
    alphabetically.
  
! See also: nattr(), lattr()
  & XGET()
    xget(<object>, <attribute>)
    
    This function is identical to get() in purpose, but a comma instead of
    a slash separates object and attribute. There is no real advantage to
!   using this instead of get(). Please see 'help get()' for more details
    on the use of this function.
    
  & XOR()
!   xor(<boolean value 1>, <boolean value 2>[, ... , <boolean value N>])
  
!   Takes two or more booleans and returns a 1 if one, and only one, of
!   the inputs is equivalent to true(1).
  
! See also: BOOLEAN VALUES, and(), or(), not(), nor()
  & XVCON()
  & XCON()
    xcon(<object>, <start>, <count>)
***************
*** 4749,4755 ****
    xvcon() is identical, except it follows the restrictions of
    lvcon()
  
!   See also: ncon(), lcon(), lvcon()
  & XVEXITS()
  & XEXITS()
    xexits(<room>, <start>, <count>)
--- 4825,4831 ----
    xvcon() is identical, except it follows the restrictions of
    lvcon()
  
! See also: ncon(), lcon(), lvcon()
  & XVEXITS()
  & XEXITS()
    xexits(<room>, <start>, <count>)
***************
*** 4765,4771 ****
    xvexits() is identical, except it follows the restrictions of
    lvexits()
  
!   See also: nexits(), lexits(), lvexits()
  & XVPLAYERS()
  & XPLAYERS()
    xplayers(<object>, <start>, <count>)
--- 4841,4847 ----
    xvexits() is identical, except it follows the restrictions of
    lvexits()
  
! See also: nexits(), lexits(), lvexits()
  & XVPLAYERS()
  & XPLAYERS()
    xplayers(<object>, <start>, <count>)
***************
*** 4780,4786 ****
    xvplayers() is identical, except it follows the restrictions of
    lvplayers()
  
!   See also: nplayers(), lplayers(), lvplayers()
  & XVTHINGS()
  & XTHINGS()
    xthings(<object>, <start>, <count>)
--- 4856,4862 ----
    xvplayers() is identical, except it follows the restrictions of
    lvplayers()
  
! See also: nplayers(), lplayers(), lvplayers()
  & XVTHINGS()
  & XTHINGS()
    xthings(<object>, <start>, <count>)
***************
*** 4795,4801 ****
    xvthings() is identical, except it follows the restrictions of
    lvthings()
  
!   See also: nthings(), lthings(), lvthings()
  & XWHO()
  & XWHOID()
  & XMWHO()
--- 4871,4877 ----
    xvthings() is identical, except it follows the restrictions of
    lvthings()
  
! See also: nthings(), lthings(), lvthings()
  & XWHO()
  & XWHOID()
  & XMWHO()
***************
*** 4816,4822 ****
  
    xwhoid() and xmwhoid() return objids instead of dbrefs.
  
!   See also: lwho(), mwho(), nwho()
  
  & ZMWHO()
    zmwho(<object>)
--- 4892,4898 ----
  
    xwhoid() and xmwhoid() return objids instead of dbrefs.
  
! See also: lwho(), mwho(), nwho()
  
  & ZMWHO()
    zmwho(<object>)
***************
*** 4828,4834 ****
    The viewer must either have see_all privileges or pass the zone
    lock of the zone to use the function.
  
!   See also: zwho()
  
  & ZWHO()
    zwho(<object>[, <viewer>])
--- 4904,4910 ----
    The viewer must either have see_all privileges or pass the zone
    lock of the zone to use the function.
  
! See also: zwho()
  
  & ZWHO()
    zwho(<object>[, <viewer>])
***************
*** 4842,4848 ****
    If <viewer> is given by a privileged user, zwho() returns a dbref list
    using <viewer>'s privileges.
  
!   See also: zmwho()
  & ZEMIT()
  & NSZEMIT()
    zemit(<zone>, <message>)
--- 4918,4924 ----
    If <viewer> is given by a privileged user, zwho() returns a dbref list
    using <viewer>'s privileges.
  
! See also: zmwho()
  & ZEMIT()
  & NSZEMIT()
    zemit(<zone>, <message>)
***************
*** 4867,4873 ****
    4. The ZMO is set VISUAL.
    5. The attribute being checked is set VISUAL.
   
!   See the help for UFUN() for more details on user-defined functions.
  & ZONE()
    zone(<object>[, <new zone>])
   
--- 4943,4949 ----
    4. The ZMO is set VISUAL.
    5. The attribute being checked is set VISUAL.
   
!   See 'help UFUN()' for more details on user-defined functions.
  & ZONE()
    zone(<object>[, <new zone>])
   
***************
*** 4875,4878 ****
    which defines the zone.  If the second argument is specified, the
    function tries to change the zone on the object before reporting it.
  
!   See also: ZONES
--- 4951,4954 ----
    which defines the zone.  If the second argument is specified, the
    function tries to change the zone on the object before reporting it.
  
! See also: ZONES
*** 1_8_2p1/game/txt/hlp/pennmail.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennmail.hlp	Sat Jan 27 02:21:10 2007
***************
*** 115,121 ****
          This command moves all messages in msg-list from the current
          folder to a new folder, <folder#>.
  
!   See also: @mailfilter
  & @mailfilter
  & mailfilter
    The @mailfilter attribute specifies automatic filing of incoming
--- 115,121 ----
          This command moves all messages in msg-list from the current
          folder to a new folder, <folder#>.
  
! See also: @mailfilter
  & @mailfilter
  & mailfilter
    The @mailfilter attribute specifies automatic filing of incoming
***************
*** 135,141 ****
    Example: Filter urgent messages into folder 1
    > @mailfilter me=if(strmatch(%3,*U*),1)
  
!   See also: mail-folders
  & mail-admin
    
    The @mail command can also take the following switches:
--- 135,141 ----
    Example: Filter urgent messages into folder 1
    > @mailfilter me=if(strmatch(%3,*U*),1)
  
! See also: mail-folders
  & mail-admin
    
    The @mail command can also take the following switches:
*** 1_8_2p1/game/txt/hlp/pennpueb.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennpueb.hlp	Sat Jan 27 02:21:10 2007
***************
*** 29,35 ****
  - Object lists (like the ones found in 'examine') have links
  - Conversion of ANSI sequences to <FONT> tags.
  
! See 'HTML', 'HTML Functions' and '@VRML_URL' for more help.
  
  & @VRML_URL
  & VRML
--- 29,35 ----
  - Object lists (like the ones found in 'examine') have links
  - Conversion of ANSI sequences to <FONT> tags.
  
! See also: 'HTML', 'HTML Functions' and '@VRML_URL' for more help.
  
  & @VRML_URL
  & VRML
***************
*** 45,51 ****
  To learn about the VRML Format, have a look at the Pueblo Help, which
  mentions several good sites for learning.
  
! See also 'HTML'.
  
  & HTML
  Hyper Text Markup Language (http://www.w3.org)
--- 45,51 ----
  To learn about the VRML Format, have a look at the Pueblo Help, which
  mentions several good sites for learning.
  
! See also: 'HTML'.
  
  & HTML
  Hyper Text Markup Language (http://www.w3.org)
***************
*** 56,62 ****
  
  HTML tags are stripped when sent to non-HTML capable players.
  
! See 'HTML Functions'.
  & HTML FUNCTIONS
  HTML Functions are used to output HTML tags to HTML capable
  users. These tags will be stripped by the system for anything
--- 56,62 ----
  
  HTML tags are stripped when sent to non-HTML capable players.
  
! See also: 'HTML Functions'.
  & HTML FUNCTIONS
  HTML Functions are used to output HTML tags to HTML capable
  users. These tags will be stripped by the system for anything
*** 1_8_2p1/game/txt/hlp/penntop.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/penntop.hlp	Sat Jan 27 02:21:10 2007
***************
*** 111,117 ****
    which forces a re-insertion into the queue such as @switch or
    @dolist.
  
!   Actions in an action list are separated by semicolons. Each action is
    simply a separate MUSH command. If part of the action (such as the text
    in an @emit, for example) contains a semi-colon or comma, you may need
    to enclose that part in curly braces {}. You can also nest action lists
--- 111,117 ----
    which forces a re-insertion into the queue such as @switch or
    @dolist.
  
!   Actions in an action list are separated by semicolons. Each action is 
    simply a separate MUSH command. If part of the action (such as the text
    in an @emit, for example) contains a semi-colon or comma, you may need
    to enclose that part in curly braces {}. You can also nest action lists
***************
*** 140,146 ****
      Test 2, success.
      Test 3, success.
  
!   See also: ATTRIBUTES, SUBSTITUTION, @asuccess, @dolist
  & ANCESTORS
    ANCESTORS
  
--- 140,146 ----
      Test 2, success.
      Test 3, success.
  
! See also: ATTRIBUTES, SUBSTITUTION, @asuccess, @dolist
  & ANCESTORS
    ANCESTORS
  
***************
*** 153,160 ****
    parent chain. There is one ancestor for each object type (room, exit,
    thing, player), and @config lists the dbref of each ancestor object
    (@config ancestor_room, etc.) Under normal circumstances, if an attribute
!   can't be retrieve from an object or any of its explicit parents,
!   the attribute will be looked on on the appropriate ancestor.
    The ORPHAN flag may be set on an object to cause lookups on that 
    object to ignore ancestors (like the pre-ancestor behavior).
  
--- 153,160 ----
    parent chain. There is one ancestor for each object type (room, exit,
    thing, player), and @config lists the dbref of each ancestor object
    (@config ancestor_room, etc.) Under normal circumstances, if an attribute
!   can't be retrieved from an object or any of its explicit parents,
!   the attribute will be looked for on the appropriate ancestor.
    The ORPHAN flag may be set on an object to cause lookups on that 
    object to ignore ancestors (like the pre-ancestor behavior).
  
***************
*** 167,173 ****
    ^-commands; you should use the master room for global commands,
    instead.
  
!   See also: PARENTS, ORPHAN
  & ANONYMOUS ATTRIBUTES
  & LAMBDA
  & #LAMBDA
--- 167,173 ----
    ^-commands; you should use the master room for global commands,
    instead.
  
! See also: PARENTS, ORPHAN
  & ANONYMOUS ATTRIBUTES
  & LAMBDA
  & #LAMBDA
***************
*** 184,190 ****
    pieces of code. Anything long or complicated should go in an
    actual attribute, for readability and maintainability.
  
!   See HELP ANONYMOUS2 for examples.
  & ANONYMOUS2
    A typical usage of anonymous attributes would be to convert
    a list of dbrefs to names, as so:
--- 184,190 ----
    pieces of code. Anything long or complicated should go in an
    actual attribute, for readability and maintainability.
  
!   See 'HELP ANONYMOUS2' for examples.
  & ANONYMOUS2
    A typical usage of anonymous attributes would be to convert
    a list of dbrefs to names, as so:
***************
*** 207,213 ****
    filter(). However, this can lead to problems with evaluating un-trusted
    code. Use secure() or escape() where neccessary.
  
!   See HELP ANONYMOUS3 for another example.
  & ANONYMOUS3
    
    You can also use lit() to avoid having the code evaluated twice, if
--- 207,213 ----
    filter(). However, this can lead to problems with evaluating un-trusted
    code. Use secure() or escape() where neccessary.
  
!   See 'HELP ANONYMOUS3' for another example.
  & ANONYMOUS3
    
    You can also use lit() to avoid having the code evaluated twice, if
***************
*** 222,233 ****
    characters. However, it also makes it harder to build the code string on
    the fly. Use what's most appropriate.
       
!   See HELP ANONYMOUS4 for a list of functions that support anonymous
    attributes.
  & ANONYMOUS4
    The following functions support anonymous attributes:
    
!   filter()    filterbool()   fold()      foreach()   map()
    mix()       munge()        sortby()    sortkey()   step()
  & ATTRIB-OWNERSHIP
    ATTRIBUTE OWNERSHIP
--- 222,233 ----
    characters. However, it also makes it harder to build the code string on
    the fly. Use what's most appropriate.
       
!   See 'HELP ANONYMOUS4' for a list of functions that support anonymous
    attributes.
  & ANONYMOUS4
    The following functions support anonymous attributes:
    
!   filter()    filterbool()   fold()      foreach()   map()      mapsql()
    mix()       munge()        sortby()    sortkey()   step()
  & ATTRIB-OWNERSHIP
    ATTRIBUTE OWNERSHIP
***************
*** 243,249 ****
  
    You must control an object in order to set attributes on it.
  
!   See also: @atrlock, @atrchown, ATTRIBUTES
  & ATTRIBUTES
  & ATTRIBUTES LIST
  & ATTRIBUTE LIST
--- 243,249 ----
  
    You must control an object in order to set attributes on it.
  
! See also: @atrlock, @atrchown, ATTRIBUTES
  & ATTRIBUTES
  & ATTRIBUTES LIST
  & ATTRIBUTE LIST
***************
*** 280,286 ****
      &<attribute name> <object>=<content>
  
    It is also possible to have non-standard attributes, which can be named 
!   anything you like. Please see help NON-STANDARD ATTRIBUTES for more 
    information on those. 
  
  (continued in help attributes3)
--- 280,286 ----
      &<attribute name> <object>=<content>
  
    It is also possible to have non-standard attributes, which can be named 
!   anything you like. Please see 'help NON-STANDARD ATTRIBUTES' for more 
    information on those. 
  
  (continued in help attributes3)
***************
*** 315,335 ****
    longer and descriptive names for attributes, which makes it much easier
    to examine and work on objects.
  
!   See also: ATTRIB-OWNERSHIP, @set, examine, @atrchown, @atrlock, hasattr()
!     get(), v(), NON-STANDARD ATTRIBUTES, SETTING-ATTRIBUTES, ATTRIBUTE TREES
  
  & BEING KILLED
   
    Getting killed is no big deal. If you are killed, you return to
    your home, and  all things you carry return to their homes. You 
    also collect 50 pennies in insurance money (unless you have >= 10000 
!   pennies or you were killed via the Wizard slay command). See MONEY.  
    Generally, killing is not encouraged unless absolutely necessary.
    It can be extremely rude and annoying.
  
    Many MUSHes choose to disable the kill command.
  
!   See also: kill, slay, @death
  & BOOLEAN VALUES 
  
    A boolean variable, for those of you not familiar with programming, 
--- 315,335 ----
    longer and descriptive names for attributes, which makes it much easier
    to examine and work on objects.
  
! See also: ATTRIB-OWNERSHIP, @set, examine, @atrchown, @atrlock, hasattr(),
!   get(), v(), NON-STANDARD ATTRIBUTES, SETTING-ATTRIBUTES, ATTRIBUTE TREES
  
  & BEING KILLED
   
    Getting killed is no big deal. If you are killed, you return to
    your home, and  all things you carry return to their homes. You 
    also collect 50 pennies in insurance money (unless you have >= 10000 
!   pennies or you were killed via the Wizard slay command). See 'MONEY'.  
    Generally, killing is not encouraged unless absolutely necessary.
    It can be extremely rude and annoying.
  
    Many MUSHes choose to disable the kill command.
  
! See also: kill, slay, @death
  & BOOLEAN VALUES 
  
    A boolean variable, for those of you not familiar with programming, 
***************
*** 361,366 ****
--- 361,367 ----
    non-zero number       TRUE                    TRUE 
    #<non-negative>       TRUE                    FALSE               *
    #<negative>           FALSE                   FALSE                
+ 
    null string           FALSE                   FALSE
    0<non-numbers..>      TRUE                    FALSE               *
    <non-numbers...>      TRUE                    FALSE               *
***************
*** 379,385 ****
    (note: These rules only apply when a function expects a Boolean
    value, not for strings that expect other values.)
  
!   See also: BOOLEAN FUNCTIONS, not(), t()
  & CLIENTS
    Clients are special software programs that you can use to connect to 
    MUSHes. They are usually much nicer to use than raw telnet and give you
--- 380,386 ----
    (note: These rules only apply when a function expects a Boolean
    value, not for strings that expect other values.)
  
! See also: BOOLEAN FUNCTIONS, not(), t()
  & CLIENTS
    Clients are special software programs that you can use to connect to 
    MUSHes. They are usually much nicer to use than raw telnet and give you
***************
*** 425,431 ****
    There's also one special case: anyone can @link an unlinked exit
    (at which point the exit is @chowned to the linker).
  
!   See also: controls(), TRUST, MISTRUST, ZONES, SHARED PLAYERS
  & COSTS
    These are usually:
    
--- 426,432 ----
    There's also one special case: anyone can @link an unlinked exit
    (at which point the exit is @chowned to the linker).
  
! See also: controls(), TRUST, MISTRUST, ZONES, SHARED PLAYERS
  & COSTS
    These are usually:
    
***************
*** 442,448 ****
    
    Type '@config/list costs' to get the costs for the MUSH you are on.
  
!   See also: MONEY, money(), score
  & CREDITS
    Maintainer: Raevnos [SW]
    Developers: Javelin, Talek [TAP], Ervin Hearn III [EEH], 
--- 443,449 ----
    
    Type '@config/list costs' to get the costs for the MUSH you are on.
  
! See also: MONEY, money(), score
  & CREDITS
    Maintainer: Raevnos [SW]
    Developers: Javelin, Talek [TAP], Ervin Hearn III [EEH], 
***************
*** 465,471 ****
    and Rhost [Rhost] servers, as well as to the players of Belgariad MUSH,
    DuneMUSH, and M*U*S*H, and everyone else using this server!
  
!   See also: help code, help license
  & DATABASE
  & DBREFS
  & DBREF NUMBER
--- 466,472 ----
    and Rhost [Rhost] servers, as well as to the players of Belgariad MUSH,
    DuneMUSH, and M*U*S*H, and everyone else using this server!
  
! See also: help code, help license
  & DATABASE
  & DBREFS
  & DBREF NUMBER
***************
*** 505,511 ****
    flag is 'O' (o), which looks like '0' (zero) on some clients. Make sure 
    you have the right number before using it in your code!
  
!   See also: MYOPIC, OPAQUE, MUSHCODE
  & DROP-TOS
  
    When you use the @link command on a room, it sets another room or
--- 506,512 ----
    flag is 'O' (o), which looks like '0' (zero) on some clients. Make sure 
    you have the right number before using it in your code!
  
! See also: MYOPIC, OPAQUE, MUSHCODE
  & DROP-TOS
  
    When you use the @link command on a room, it sets another room or
***************
*** 525,531 ****
  
    Drop-tos are useful for keeping rooms uncluttered. 
  
!   See also: @link, STICKY, LINK_OK, @lock
  & %#
  & %N
  & %~
--- 526,532 ----
  
    Drop-tos are useful for keeping rooms uncluttered. 
  
! See also: @link, STICKY, LINK_OK, @lock
  & %#
  & %N
  & %~
***************
*** 548,554 ****
    replaced with the name of the enactor (the person who typed 'get <object>'
    in this case). 
    
!   See also: EXECUTOR, SUBSTITUTION, DBREF
  & EVALUATION ORDER
    Whenever some text is entered by an object or thing, the MUSH program
    attempts to match it against a valid game command in the following 
--- 549,555 ----
    replaced with the name of the enactor (the person who typed 'get <object>'
    in this case). 
    
! See also: EXECUTOR, SUBSTITUTION, DBREF
  & EVALUATION ORDER
    Whenever some text is entered by an object or thing, the MUSH program
    attempts to match it against a valid game command in the following 
***************
*** 607,613 ****
    command on the box, so Cyclonus was still the enactor, but the box was
    the object that was actually doing the @emit, and was thus the executor.
  
!   See also: ENACTOR, SUBSTITUTION
  & EXITS
    An exit is a one-way link that takes you from its source room to its 
    destination room. To open an exit from a room, you must control that room.
--- 608,614 ----
    command on the box, so Cyclonus was still the enactor, but the box was
    the object that was actually doing the @emit, and was thus the executor.
  
! See also: ENACTOR, SUBSTITUTION
  & EXITS
    An exit is a one-way link that takes you from its source room to its 
    destination room. To open an exit from a room, you must control that room.
***************
*** 649,655 ****
    Anyone can create variable exits, but the destinations must be to places
    that the exit can normally @link to.
  
!   See also: @link, @open, link_ok, CLOUDY, TRANSPARENT, @firstexit
  & FAILURE
    FAILURE  
  
--- 650,656 ----
    Anyone can create variable exits, but the destinations must be to places
    that the exit can normally @link to.
  
! See also: @link, @open, link_ok, CLOUDY, TRANSPARENT, @firstexit
  & FAILURE
    FAILURE  
  
***************
*** 670,676 ****
    more information. However, there are failure messages at this time
    only for the above.
  
!   See also: @lock, @fail, @efail, @lfail
  & GENDER
  & SEX
    Gender on a MUSH is entirely up to you. You can set yourself (or any
--- 671,677 ----
    more information. However, there are failure messages at this time
    only for the above.
  
! See also: @lock, @fail, @efail, @lfail
  & GENDER
  & SEX
    Gender on a MUSH is entirely up to you. You can set yourself (or any
***************
*** 678,686 ****
    is in the SEX attribute is not recognizable, the MUSH will assume 
    the object is neuter. Setting a gender attribute will enable 
    pronoun substitution by the MUSH. The SEX attribute is visual to
!   anyone who wants to see it. 
  
!   See also: @sex, SUBSTITUTION
  & GLOBALS
  & GLOBAL COMMANDS
    A command is "global" if it can be used anywhere in the world of the
--- 679,687 ----
    is in the SEX attribute is not recognizable, the MUSH will assume 
    the object is neuter. Setting a gender attribute will enable 
    pronoun substitution by the MUSH. The SEX attribute is visual to
!   anyone who wants to see it.
  
! See also: @sex, SUBSTITUTION
  & GLOBALS
  & GLOBAL COMMANDS
    A command is "global" if it can be used anywhere in the world of the
***************
*** 690,696 ****
    MUSH, but you can usually find MUSH-specific help on them by
    typing "+help". 
  
!   See also: MASTER ROOM, USER-DEFINED COMMANDS, EVALUATION
  & HERE
    The word 'here' refers to the room you are in. For example,
    to rename the room  you're in (if you control it), you could enter 
--- 691,697 ----
    MUSH, but you can usually find MUSH-specific help on them by
    typing "+help". 
  
! See also: MASTER ROOM, USER-DEFINED COMMANDS, EVALUATION
  & HERE
    The word 'here' refers to the room you are in. For example,
    to rename the room  you're in (if you control it), you could enter 
***************
*** 713,719 ****
    You can set the drop-to in a room to home by doing:
          @link <room dbref or "here">=home
  
!   See also: DROP-TOS, @link, STICKY, LINK_OK, FIXED, EXITS
  & INTERIORS
    Here's a quick description of how to make things that can be entered:
          
--- 714,720 ----
    You can set the drop-to in a room to home by doing:
          @link <room dbref or "here">=home
  
! See also: DROP-TOS, @link, STICKY, LINK_OK, FIXED, EXITS
  & INTERIORS
    Here's a quick description of how to make things that can be entered:
          
***************
*** 746,752 ****
    messages and people on the inside from seeing the 'ox' messages which
    is a good thing.)
  
!   See also: enter, leave, @prefix, @filter, AUDIBLE, @listen
  & LAST & LASTLOGOUT
    LAST and LASTLOGOUT
  
--- 747,753 ----
    messages and people on the inside from seeing the 'ox' messages which
    is a good thing.)
  
! See also: enter, leave, @prefix, @filter, AUDIBLE, @listen
  & LAST & LASTLOGOUT
    LAST and LASTLOGOUT
  
***************
*** 766,772 ****
    objects or yourself to that  room if it is set ABODE, and can set 
    the destination of exits to that room if it is LINK_OK.
  
!   See also: LINK_OK, ABODE, @link
  & LISTENING
    
    There are two basic ways to trigger action on the MUSH. The basic way
--- 767,773 ----
    objects or yourself to that  room if it is set ABODE, and can set 
    the destination of exits to that room if it is LINK_OK.
  
! See also: LINK_OK, ABODE, @link
  & LISTENING
    
    There are two basic ways to trigger action on the MUSH. The basic way
***************
*** 822,829 ****
  
    Listen patterns are checked after the object's normal @listen attribute.
  
!   See also: @listen, @ahear, @amhear, @aahear, MONITOR, 
!           USER-DEFINED COMMANDS, LISTEN_PARENT
  
  & LISTS
    The word "list" is used in the help files to refer to a string that
--- 823,830 ----
  
    Listen patterns are checked after the object's normal @listen attribute.
  
! See also: @listen, @ahear, @amhear, @aahear, MONITOR, LISTEN_PARENT,
!   USER-DEFINED COMMANDS
  
  & LISTS
    The word "list" is used in the help files to refer to a string that
***************
*** 841,847 ****
    is made up of similar items (so the fourth list in the example is NOT a 
    typical one).
  
!   See also: STRINGS, List Functions
  & LOOPING
    Looping in an object can have its good parts and its bad parts.
    The good part is when you activate part of a program multiple times
--- 842,848 ----
    is made up of similar items (so the fourth list in the example is NOT a 
    typical one).
  
! See also: STRINGS, List Functions
  & LOOPING
    Looping in an object can have its good parts and its bad parts.
    The good part is when you activate part of a program multiple times
***************
*** 855,861 ****
    machine that isn't @halt'd will drain your pennies while you are away
    from the mush!
  
!   See also: @ps, HALT, COSTS, @trigger
  & MASTER ROOM
    
    The Master Room enables global commands and exits. Exits in the Master
--- 856,862 ----
    machine that isn't @halt'd will drain your pennies while you are away
    from the mush!
  
! See also: @ps, HALT, COSTS, @trigger
  & MASTER ROOM
    
    The Master Room enables global commands and exits. Exits in the Master
***************
*** 866,872 ****
    a global command that you would like to see enabled for the MUSH, speak
    to a wizard.
    
!   See also: EVALUATION, GLOBAL COMMANDS
  & ME
    The word 'me' refers to yourself. Some things to do when 
    starting out: 
--- 867,873 ----
    a global command that you would like to see enabled for the MUSH, speak
    to a wizard.
    
! See also: EVALUATION, GLOBAL COMMANDS
  & ME
    The word 'me' refers to yourself. Some things to do when 
    starting out: 
***************
*** 875,881 ****
    3) lock yourself:                     @lock me==me
    4) set your gender:                   @sex me=<male|female|neuter|plural>
  
!   See also: help newbie, help @lock, help @describe, help @sex
  & MONEY
    The MUSH has a built-in money system, which gives a starting amount
    of money to new players and hands out a daily allowance thereafter.
--- 876,882 ----
    3) lock yourself:                     @lock me==me
    4) set your gender:                   @sex me=<male|female|neuter|plural>
  
! See also: help newbie, help @lock, help @describe, help @sex
  & MONEY
    The MUSH has a built-in money system, which gives a starting amount
    of money to new players and hands out a daily allowance thereafter.
***************
*** 891,897 ****
    pays the object by giving it the right number of pennies, the attributes
    are triggered.
  
!   See also: COSTS, give, @cost, @pay, @opay, @apay
  & MUSHCODE
  & SOFTCODE
  
--- 892,898 ----
    pays the object by giving it the right number of pennies, the attributes
    are triggered.
  
! See also: COSTS, give, @cost, @pay, @opay, @apay
  & MUSHCODE
  & SOFTCODE
  
***************
*** 942,949 ****
    All attributes can be used in attribute locks and can be 'owned' 
    independent of object ownership. 
    
!   See also: ATTRIBUTES, ATTRIB-OWNERSHIP, Attribute Functions, 
!      ATTRIBUTE TREES
  & PARENT
  & PARENTS
  & OBJECT PARENTS
--- 943,949 ----
    All attributes can be used in attribute locks and can be 'owned' 
    independent of object ownership. 
    
! See also: ATTRIBUTES, ATTRIB-OWNERSHIP, Attribute Functions, ATTRIBUTE TREES
  & PARENT
  & PARENTS
  & OBJECT PARENTS
***************
*** 1034,1040 ****
    that you want to make impossible to read, it's safe to allow the
    purchasers of your object to @chown their copy.
  
!   See also: @parent, $-COMMANDS, ATTRIBUTES, ANCESTORS
  & POWERS LIST
    Powers can be granted only by wizards, using the @power command. 
    Powers cannot be granted to guest characters or players who are set
--- 1034,1040 ----
    that you want to make impossible to read, it's safe to allow the
    purchasers of your object to @chown their copy.
  
! See also: @parent, $-COMMANDS, ATTRIBUTES, ANCESTORS
  & POWERS LIST
    Powers can be granted only by wizards, using the @power command. 
    Powers cannot be granted to guest characters or players who are set
***************
*** 1077,1083 ****
    unkillable            Can not be killed
    can_nspemit           Can use @nspemit and nspemit()
  
!   See also: help @power, and especially @power/list
  & PUPPETS
    A thing is turned into a puppet by setting the PUPPET flag on it.
    A puppet object is an extension of its owner and relays everything
--- 1077,1083 ----
    unkillable            Can not be killed
    can_nspemit           Can use @nspemit and nspemit()
  
! See also: help @power, and especially @power/list
  & PUPPETS
    A thing is turned into a puppet by setting the PUPPET flag on it.
    A puppet object is an extension of its owner and relays everything
***************
*** 1115,1121 ****
    #18 :waves hello
    Punch> Punch waves hello
  
!   See also: PUPPET, @force, DBREF
  & QUEUE
    QUEUE
  
--- 1115,1121 ----
    #18 :waves hello
    Punch> Punch waves hello
  
! See also: PUPPET, @force, DBREF
  & QUEUE
    QUEUE
  
***************
*** 1132,1138 ****
    (wizards, you, and your objects) or unless you are VISUAL.  It 
    tracks how many active commands you have in the queue.
  
!   See also: @ps, LOOPING
  & REGEXP
  & REGEXPS
    (This help text is largely from TinyMUSH 2.2.4, with permission)
--- 1132,1138 ----
    (wizards, you, and your objects) or unless you are VISUAL.  It 
    tracks how many active commands you have in the queue.
  
! See also: @ps, LOOPING
  & REGEXP
  & REGEXPS
    (This help text is largely from TinyMUSH 2.2.4, with permission)
***************
*** 1407,1422 ****
  
    The registers can also be accessed using the V-function (v(0) through v(9)).
  
!   Please see help setq() for more information about the setq registers.
  
!   See also: SUBSTITUTIONS, @trigger, USER-DEFINED COMMANDS, setq()
  & RQUOTA
    RQUOTA
  
    This attribute tracks remaining building quota if it is implemented.  
    It is settable in-game only by a wizard, and is only visible to wizards.
  
!   See also: @quota, @squota
  & SEMAPHORES
    The most complicated thing about semaphores is their name. Before you try
    to use semaphores, you should first be familiar with the "@wait" command.
--- 1407,1422 ----
  
    The registers can also be accessed using the V-function (v(0) through v(9)).
  
!   Please see 'help setq()' for more information about the setq registers.
  
! See also: SUBSTITUTIONS, @trigger, USER-DEFINED COMMANDS, setq()
  & RQUOTA
    RQUOTA
  
    This attribute tracks remaining building quota if it is implemented.  
    It is settable in-game only by a wizard, and is only visible to wizards.
  
! See also: @quota, @squota
  & SEMAPHORES
    The most complicated thing about semaphores is their name. Before you try
    to use semaphores, you should first be familiar with the "@wait" command.
***************
*** 1476,1482 ****
    [ 30 seconds passes. ]
    Wizard waits 30 seconds.
   
!   See also: @wait, @drain, @notify
  (continued in help semaphores4)
  & SEMAPHORES4
    Semaphores can be used to enforce mutual exclusion - to prevent
--- 1476,1482 ----
    [ 30 seconds passes. ]
    Wizard waits 30 seconds.
   
! See also: @wait, @drain, @notify
  (continued in help semaphores4)
  & SEMAPHORES4
    Semaphores can be used to enforce mutual exclusion - to prevent
***************
*** 1507,1513 ****
   attributes can be used, as long as they follow a few simple rules:
   If the attribute is already set, it has to have the same owner (God)
   and flags as the SEMAPHORE attribute would (typically no_inherit, no_clone,
!  and locked - see help @set and @atrlock), and have a numeric or empty
   value. If it's not set, it can't be one of the built in attributes
   (See @list attribs) unless, naturally, it is SEMAPHORE.
  
--- 1507,1513 ----
   attributes can be used, as long as they follow a few simple rules:
   If the attribute is already set, it has to have the same owner (God)
   and flags as the SEMAPHORE attribute would (typically no_inherit, no_clone,
!  and locked - see 'help @set' and '@atrlock'), and have a numeric or empty
   value. If it's not set, it can't be one of the built in attributes
   (See @list attribs) unless, naturally, it is SEMAPHORE.
  
***************
*** 1553,1559 ****
    Empty attributes retain their flags and atrlock status. Wiped attributes
    are gone forever.
    
!   See also ATTRIBUTES, NON-STANDARD ATTRIBUTES, @set, @wipe, attrib_set(), 
    set(), wipe()
  & SPOOFING
    Spoofing is the act of making other characters think that a person
--- 1553,1559 ----
    Empty attributes retain their flags and atrlock status. Wiped attributes
    are gone forever.
    
! See also: ATTRIBUTES, NON-STANDARD ATTRIBUTES, @set, @wipe, attrib_set(),
    set(), wipe()
  & SPOOFING
    Spoofing is the act of making other characters think that a person
***************
*** 1564,1577 ****
    want to know who is doing it, you can set yourself NOSPOOF and you will
    be notified who is making the @emits.
  
!   See also: @emit, @pemit, @remit, @oemit, and NOSPOOF.
  & STACK
    For those unfamiliar with the term stack, it refers to a programming
    data structure that follows a LIFO (Last-In-First-Out) principle. The
    stack in MUSH holds the ten REGISTERS, which can be accessed via the 
    V-function (v(0) through v(9)) or via %-substitution (%0 through %9).
    
!   See also: REGISTERS
  & STRINGS
    A string is simply a bunch of characters.  A word is a string that begins
    and ends with the space character.  A sentence is a string made up of 
--- 1564,1577 ----
    want to know who is doing it, you can set yourself NOSPOOF and you will
    be notified who is making the @emits.
  
! See also: @emit, @pemit, @remit, @oemit, NOSPOOF
  & STACK
    For those unfamiliar with the term stack, it refers to a programming
    data structure that follows a LIFO (Last-In-First-Out) principle. The
    stack in MUSH holds the ten REGISTERS, which can be accessed via the 
    V-function (v(0) through v(9)) or via %-substitution (%0 through %9).
    
! See also: REGISTERS
  & STRINGS
    A string is simply a bunch of characters.  A word is a string that begins
    and ends with the space character.  A sentence is a string made up of 
***************
*** 1582,1588 ****
  
          Foozle 09blert bar baz foo.
  
!   See also: string functions
  & %
  & SUBSTITUTIONS
    The % symbol is used in MUSH commands to indicate a substitution -- some
--- 1582,1588 ----
  
          Foozle 09blert bar baz foo.
  
! See also: string functions
  & %
  & SUBSTITUTIONS
    The % symbol is used in MUSH commands to indicate a substitution -- some
***************
*** 1651,1657 ****
    %! = #11
    %L = #13
  
!   See also: EVALUATION, ENACTOR, EXECUTOR, DBREFS, v()
  & SUCCESS
    A "success" normally occurs when you attempt to do something that is
    restricted by an @lock and you pass the @lock. (Note that if no lock
--- 1651,1657 ----
    %! = #11
    %L = #13
  
! See also: EVALUATION, ENACTOR, EXECUTOR, DBREFS, v()
  & SUCCESS
    A "success" normally occurs when you attempt to do something that is
    restricted by an @lock and you pass the @lock. (Note that if no lock
***************
*** 1665,1671 ****
    more information. Many of these actions have standard attributes that
    you can set messages in for when someone succeeds.
  
!   See also: FAILURE, @lock, VERBS, ATTRIBUTES, @success, @asuccess, @osuccess
  & SWITCHES
    SWITCHES
   
--- 1665,1671 ----
    more information. Many of these actions have standard attributes that
    you can set messages in for when someone succeeds.
  
! See also: FAILURE, @lock, VERBS, ATTRIBUTES, @success, @asuccess, @osuccess
  & SWITCHES
    SWITCHES
   
***************
*** 1685,1691 ****
  
    Help on the switches available for a command is available in the help
    file for that command.
!   (If you are looking for information on @switch, see help @switch instead.)
  & TYPES OF OBJECTS
    
    Everything on a MUSH is an object in the MUSH database. There are four
--- 1685,1691 ----
  
    Help on the switches available for a command is available in the help
    file for that command.
!   (If you are looking for information on @switch, see 'help @switch'.)
  & TYPES OF OBJECTS
    
    Everything on a MUSH is an object in the MUSH database. There are four
***************
*** 1717,1723 ****
      commands. Can send @mail as themselves. You can have $-commands and 
      ^-patterns on things. Things can carry, be carried, and can follow.
  
!   See also: EXITS, USER-DEFINED COMMANDS, LISTENING, GLOBALS
  & $-COMMANDS
  & MACROS
  & USER-DEFINED COMMANDS
--- 1717,1723 ----
      commands. Can send @mail as themselves. You can have $-commands and 
      ^-patterns on things. Things can carry, be carried, and can follow.
  
! See also: EXITS, USER-DEFINED COMMANDS, LISTENING, GLOBALS
  & $-COMMANDS
  & MACROS
  & USER-DEFINED COMMANDS
***************
*** 1767,1773 ****
  
    *BE SURE TO @LOCK/USE ME==ME IF YOU SET MACROS ON YOURSELF!*
  
!   See also: STACK, SUBSTITUTIONS, @lock
  & VERBS
    For most verbs there are three forms: Verb (what the Enactor sees),
    Overb (what others in the area see) and Averb (the action to be 
--- 1767,1773 ----
  
    *BE SURE TO @LOCK/USE ME==ME IF YOU SET MACROS ON YOURSELF!*
  
! See also: STACK, SUBSTITUTIONS, @lock
  & VERBS
    For most verbs there are three forms: Verb (what the Enactor sees),
    Overb (what others in the area see) and Averb (the action to be 
***************
*** 1832,1838 ****
  
    A backslash (\) can be used to escape * and ? if you want to match 
    a literal asterisk or question mark.
!   See also: USER-DEFINED COMMANDS, REGEXP
  & ZONE MASTER ROOMS
  & ZMR
    
--- 1832,1838 ----
  
    A backslash (\) can be used to escape * and ? if you want to match 
    a literal asterisk or question mark.
! See also: USER-DEFINED COMMANDS, REGEXP
  & ZONE MASTER ROOMS
  & ZMR
    
***************
*** 1850,1856 ****
    exits, or for zones with restricted commands that can go on a separate
    use-locked object from general ones.
  
!   See also: ZONES, MASTER ROOM, EVALUATION
  & ZONE MASTERS
  & ZMP
  & SHARED PLAYERS
--- 1850,1856 ----
    exits, or for zones with restricted commands that can go on a separate
    use-locked object from general ones.
  
! See also: ZONES, MASTER ROOM, EVALUATION
  & ZONE MASTERS
  & ZMP
  & SHARED PLAYERS
***************
*** 1918,1924 ****
    ZMO, and the players @chzoned to it so they can use the commands
    anywhere.
  
!   See also: @chzone, SHARED PLAYERS
  & matching
    Matching is the process the MUSH uses to determine which object you
    mean when you try to do something with an object. Different commands
--- 1918,1924 ----
    ZMO, and the players @chzoned to it so they can use the commands
    anywhere.
  
! See also: @chzone, SHARED PLAYERS
  & matching
    Matching is the process the MUSH uses to determine which object you
    mean when you try to do something with an object. Different commands
*** 1_8_2p1/game/txt/hlp/pennv181.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennv181.hlp	Sat Jan 27 02:21:10 2007
***************
*** 1,6 ****
  & 1.8.1p10
  Version 1.8.1 patchlevel 10                     September 13, 2006
- 
  Fixes:
    * Cleaned up some compiler warnings. [SW]
    * The AAHEAR and AMHEAR attribute flags work on listening parents. 
--- 1,5 ----
***************
*** 10,16 ****
    * Win32 lacks the LC_MESSAGES setlocale() category. Reported by Intrevis.
    * Trying to ignore signals could crash on Win32. Reported by Intrevis.
    * Fixed parse errors in non-C99 compilers. Reported by Intrevis.
!   * Help fixes by Talvo, Sketch.
    * Fixes for 64-bit platforms [SW]
    * @dump/paranoid produced corrupt databases. 
      Reported by Marvin@M*U*S*H. [SW]
--- 9,15 ----
    * Win32 lacks the LC_MESSAGES setlocale() category. Reported by Intrevis.
    * Trying to ignore signals could crash on Win32. Reported by Intrevis.
    * Fixed parse errors in non-C99 compilers. Reported by Intrevis.
!   * Help fixes by Talvo, Sketch
    * Fixes for 64-bit platforms [SW]
    * @dump/paranoid produced corrupt databases. 
      Reported by Marvin@M*U*S*H. [SW]
***************
*** 28,34 ****
      an entry. [SW]
    * The above is used in db.c, function.c and plyrlist.c [SW]
    * @config compile reports if MySQL support is present. (This does not 
!     mean the game is configured to use it.)
    * restart copies log files from the last time the mush was running to
      game/save/ instead of deleting them. [SW]
  
--- 27,33 ----
      an entry. [SW]
    * The above is used in db.c, function.c and plyrlist.c [SW]
    * @config compile reports if MySQL support is present. (This does not 
!     mean the game is configured to use it.) [SW]
    * restart copies log files from the last time the mush was running to
      game/save/ instead of deleting them. [SW]
  
***************
*** 171,177 ****
      named captures. [SW]
    * NT_TCP code has been removed from the server. It was deemed
      less useful than keeping the networking code unitary and
! <    the ability to @shutdown/reboot. Patch by Nathan Baum.
  Flags:
    * New LOUD flag (admin-settable) causes an object to bypass
      speech (including chat) and interaction locks. Admin and others
--- 170,176 ----
      named captures. [SW]
    * NT_TCP code has been removed from the server. It was deemed
      less useful than keeping the networking code unitary and
!     the ability to @shutdown/reboot. Patch by Nathan Baum.
  Flags:
    * New LOUD flag (admin-settable) causes an object to bypass
      speech (including chat) and interaction locks. Admin and others
*** 1_8_2p1/game/txt/hlp/pennv182.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennv182.hlp	Sat Jan 27 02:21:10 2007
***************
*** 1,16 ****
! & 1.8.2p1
! & changes
! This is a list of changes in this patchlevel which are probably of
! interest to players. More information about new commands and functions
! can probably be gotten via 'help <name of whatever>'. 'help credits'
! lists the [initials] of developers and porters that are used in the list 
! of changes.
! 
! Information about changes in prior releases can be found under
! help topics named for each release (e.g. 'help 1.7.2p30').
! A list of the patchlevels associated with each release can
! be read in 'help patchlevels'.
  
  Version 1.8.2 patchlevel 1                      November 26, 2006
  
  Fixes:
--- 1,24 ----
! & 1.8.2p2
! Version 1.8.2 patchlevel 2                      January 27, 2007
  
+ Fixes:
+   * Vector functions with an empty vector didn't return anything.
+     Reported by Talvo. [SW]
+   * Several typos in server messages. Fixed by Sketch, Stoko.
+   * Help fixes by Malix, [GM], Talvo, Sketch, and others.
+   * Crash bug in lmath() fixed. [GM]
+   * Crash bug in list functions fixed. [GM]
+   * list2arr in C now removes markup. list2arr_ansi() was added. [GM]
+   * Compilation problems on IRIX and similar OSes. [SW]
+   * Matcher bug with multiple wildcards fixed. Reported by Ian. [SW]
+   * Garbled output of locks from examine. Reported by Intrevis. [SW].
+   * regraballi() couldn't use its output seperator argument. Reported
+     by Jules. [SW]
+   * Looking at an object used the looker, not the lookee, as the origin
+     of the name for @ahear/@aahear/@amhear. [SW]
+   * Fixed the distribution of random numbers with a huge range. Reported
+     by Luke.
+ & 1.8.2p1
  Version 1.8.2 patchlevel 1                      November 26, 2006
  
  Fixes:
*** 1_8_2p1/game/txt/hlp/pennv183.hlp	Wed Dec 31 16:00:00 1969
--- 1_8_3p0/game/txt/hlp/pennv183.hlp	Sat Jan 27 02:21:10 2007
***************
*** 0 ****
--- 1,25 ----
+ & 1.8.3p0
+ & changes
+ This is a list of changes in this patchlevel which are probably of
+ interest to players. More information about new commands and functions
+ can probably be gotten via 'help <name of whatever>'. 'help credits'
+ lists the [initials] of developers and porters that are used in the list 
+ of changes.
+ 
+ Information about changes in prior releases can be found under
+ help topics named for each release (e.g. 'help 1.7.2p30').
+ A list of the patchlevels associated with each release can
+ be read in 'help patchlevels'.
+ 
+ Version 1.8.3 patchlevel 0                      January 27, 2007
+ 
+ Major changes:
+   *  Rewrite of color handling. [GM]
+ 
+ Minor changes:
+   * Cleaned up the internals of @wipe. [SW]
+   * strmatch() now takes a third argument, to store wildcard captures. [GM]
+   * Termination of OS/2 support. [SW]
+ 
+ Fixes:
+   * Fixes included from versions up to 1.8.2p2.
*** 1_8_2p1/game/txt/hlp/pennvOLD.hlp	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/game/txt/hlp/pennvOLD.hlp	Sat Jan 27 02:21:10 2007
***************
*** 4417,4423 ****
  For information on a specific patchlevel of one of the versions listed,
  type 'help <version>p<patchlevel>'. For example, 'help 1.7.2p3'
  
! 1.8.1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
  1.8.0: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
  1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
         19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
--- 4417,4425 ----
  For information on a specific patchlevel of one of the versions listed,
  type 'help <version>p<patchlevel>'. For example, 'help 1.7.2p3'
  
! 1.8.3: 0
! 1.8.2: 0, 1
! 1.8.1: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
  1.8.0: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
  1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
         19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
*** 1_8_2p1/hdrs/ansi.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/ansi.h	Sat Jan 27 05:24:13 2007
***************
*** 16,69 ****
  #ifndef __ANSI_H
  #define __ANSI_H
  
! #define ANSI_BLACK_V    (30)
! #define ANSI_RED_V      (31)
! #define ANSI_GREEN_V    (32)
! #define ANSI_YELLOW_V   (33)
! #define ANSI_BLUE_V     (34)
! #define ANSI_MAGENTA_V  (35)
! #define ANSI_CYAN_V     (36)
! #define ANSI_WHITE_V    (37)
  
  #define BEEP_CHAR     '\a'
  #define ESC_CHAR      '\x1B'
  
! #define ANSI_BEGIN   "\x1B["
! 
! #define ANSI_NORMAL   "\x1B[0m"
! 
! #define ANSI_HILITE   "\x1B[1m"
! #define ANSI_INVERSE  "\x1B[7m"
! #define ANSI_BLINK    "\x1B[5m"
! #define ANSI_UNDERSCORE "\x1B[4m"
! 
! #define ANSI_INV_BLINK         "\x1B[7;5m"
! #define ANSI_INV_HILITE        "\x1B[1;7m"
! #define ANSI_BLINK_HILITE      "\x1B[1;5m"
! #define ANSI_INV_BLINK_HILITE  "\x1B[1;5;7m"
  
  /* Foreground colors */
  
! #define ANSI_BLACK      "\x1B[30m"
! #define ANSI_RED        "\x1B[31m"
! #define ANSI_GREEN      "\x1B[32m"
! #define ANSI_YELLOW     "\x1B[33m"
! #define ANSI_BLUE       "\x1B[34m"
! #define ANSI_MAGENTA    "\x1B[35m"
! #define ANSI_CYAN       "\x1B[36m"
! #define ANSI_WHITE      "\x1B[37m"
  
  /* Background colors */
  
! #define ANSI_BBLACK     "\x1B[40m"
! #define ANSI_BRED       "\x1B[41m"
! #define ANSI_BGREEN     "\x1B[42m"
! #define ANSI_BYELLOW    "\x1B[43m"
! #define ANSI_BBLUE      "\x1B[44m"
! #define ANSI_BMAGENTA   "\x1B[45m"
! #define ANSI_BCYAN      "\x1B[46m"
! #define ANSI_BWHITE     "\x1B[47m"
! 
! #define ANSI_END        "m"
  
  #endif				/* __ANSI_H */
--- 16,171 ----
  #ifndef __ANSI_H
  #define __ANSI_H
  
! #include "mushtype.h"
! #include "pcre.h"
  
  #define BEEP_CHAR     '\a'
  #define ESC_CHAR      '\x1B'
  
! #define TAG_START     '\002'
! #define TAG_END       '\003'
! #define MARKUP_START     "\002"
! #define MARKUP_END       "\003"
! 
! #define ANSI_HILITE      MARKUP_START "ch" MARKUP_END
! #define ANSI_INVERSE     MARKUP_START "ci" MARKUP_END
! #define ANSI_BLINK       MARKUP_START "cf" MARKUP_END
! #define ANSI_UNDERSCORE  MARKUP_START "cu" MARKUP_END
! 
! #define ANSI_INV_BLINK         MARKUP_START "cfi" MARKUP_END
! #define ANSI_INV_HILITE        MARKUP_START "chi" MARKUP_END
! #define ANSI_BLINK_HILITE      MARKUP_START "cfh" MARKUP_END
! #define ANSI_INV_BLINK_HILITE  MARKUP_START "cifh" MARKUP_END
  
  /* Foreground colors */
  
! #define ANSI_PLAIN      MARKUP_START "n" MARKUP_END
! #define ANSI_BLACK      MARKUP_START "cx" MARKUP_END
! #define ANSI_RED        MARKUP_START "cr" MARKUP_END
! #define ANSI_GREEN      MARKUP_START "cg" MARKUP_END
! #define ANSI_YELLOW     MARKUP_START "cy" MARKUP_END
! #define ANSI_BLUE       MARKUP_START "cb" MARKUP_END
! #define ANSI_MAGENTA    MARKUP_START "cm" MARKUP_END
! #define ANSI_CYAN       MARKUP_START "cc" MARKUP_END
! #define ANSI_WHITE      MARKUP_START "cw" MARKUP_END
! 
! #define ANSI_HIBLACK      MARKUP_START "chx" MARKUP_END
! #define ANSI_HIRED        MARKUP_START "chr" MARKUP_END
! #define ANSI_HIGREEN      MARKUP_START "chg" MARKUP_END
! #define ANSI_HIYELLOW     MARKUP_START "chy" MARKUP_END
! #define ANSI_HIBLUE       MARKUP_START "chb" MARKUP_END
! #define ANSI_HIMAGENTA    MARKUP_START "chm" MARKUP_END
! #define ANSI_HICYAN       MARKUP_START "chc" MARKUP_END
! #define ANSI_HIWHITE      MARKUP_START "chw" MARKUP_END
  
  /* Background colors */
  
! #define ANSI_BBLACK     MARKUP_START "cX" MARKUP_END
! #define ANSI_BRED       MARKUP_START "cR" MARKUP_END
! #define ANSI_BGREEN     MARKUP_START "cG" MARKUP_END
! #define ANSI_BYELLOW    MARKUP_START "cY" MARKUP_END
! #define ANSI_BBLUE      MARKUP_START "cB" MARKUP_END
! #define ANSI_BMAGENTA   MARKUP_START "cM" MARKUP_END
! #define ANSI_BCYAN      MARKUP_START "cC" MARKUP_END
! #define ANSI_BWHITE     MARKUP_START "cW" MARKUP_END
! 
! #define ANSI_END        MARKUP_START "c/" MARKUP_END
! #define ANSI_ENDALL     MARKUP_START "c/a" MARKUP_END
! 
! #define ANSI_NORMAL     ANSI_ENDALL
! 
! void init_ansi_codes(void);
! 
! struct ansi_data {
!   char bits;
!   char offbits;
!   char fore;
!   char back;
! };
! 
! int read_raw_ansi_data(struct ansi_data *store, const char *codes);
! int write_ansi_data(struct ansi_data *cur, char *buff, char **bp);
! 
! int write_raw_ansi_data(struct ansi_data *old, struct ansi_data *cur,
! 			char *buff, char **bp);
! 
! 
! void define_ansi_data(struct ansi_data *store, const char *str);
! void nest_ansi_data(struct ansi_data *old, struct ansi_data *cur);
! 
! #define MARKUP_COLOR 'c'
! #define MARKUP_COLOR_STR "c"
! #define MARKUP_COLOR_OLD 'a'
! #define MARKUP_HTML 'p'
! #define MARKUP_HTML_STR "p"
! 
! /* Markup information necessary for ansi_string */
! typedef struct _markup_information {
!   char *start_code;
!   char *stop_code;
!   struct ansi_data ansi;
!   char type;
!   int start;
!   int end;
!   int priority;
! } markup_information;
! 
! /** A string, with ansi attributes broken out from the text */
! typedef struct _ansi_string {
!   char text[BUFFER_LEN];	/**< Text of the string */
!   markup_information markup[BUFFER_LEN]; /**< The markup_information list */
!   int nmarkups;
!   int len;		/**< Length of text */
!   int optimized;	      /**< Has this ansi-string been optimized? */
! } ansi_string;
! 
! int ansi_strcmp(const char *astr, const char *bstr);
! extern char *remove_markup(const char *orig, size_t * stripped_len);
! extern char *skip_leading_ansi(const char *p);
! 
! extern ansi_string *
! parse_ansi_string(const char *src)
!   __attribute_malloc__;
!     extern ansi_string *parse_ansi_string_real(const char *src, int oldstyle)
!  __attribute_malloc__;
!     extern void flip_ansi_string(ansi_string *as);
!     extern void free_ansi_string(ansi_string *as);
! 
! /* Append X characters to the end of a string, taking ansi and html codes into
!    account. */
!     extern int safe_ansi_string(ansi_string *as, int start, int len,
! 				char *buff, char **bp);
! 
! /* Modifying ansi strings */
!     int ansi_string_delete(ansi_string *as, int start, int count);
!     int ansi_string_insert(ansi_string *dst, int loc,
! 			   ansi_string *src, int start, int count);
! 
!     int ansi_string_replace(ansi_string *dst, int loc, int size,
! 			    ansi_string *src, int start, int count);
!     void optimize_ansi_string(ansi_string *as);
! 
! /* Dump the penn code required to recreate the ansi_string */
!     extern int dump_ansi_string(ansi_string *as, char *buff, char **bp);
! 
!     int ansi_pcre_copy_substring(ansi_string *as, int *ovector, int stringcount,
! 				 int stringnumber, int nonempty, char *buffer,
! 				 char **bp);
! 
!     int ansi_pcre_copy_named_substring(const pcre * code, ansi_string *as,
! 				       int *ovector, int stringcount,
! 				       const char *stringname, int nonempty,
! 				       char *buffer, char **bp);
! 
! /* Pueblo stuff */
! #define open_tag(x) tprintf("%c%c%s%c",TAG_START,MARKUP_HTML,x,TAG_END)
! #define close_tag(x) tprintf("%c%c/%s%c",TAG_START,MARKUP_HTML,x,TAG_END)
! #define wrap_tag(x,y) tprintf("%c%c%s%c%s%c%c/%s%c", \
!                               TAG_START,MARKUP_HTML,x,TAG_END, \
!                               y, TAG_START,MARKUP_HTML,x,TAG_END)
!     int safe_tag(char const *a_tag, char *buf, char **bp);
!     int safe_tag_cancel(char const *a_tag, char *buf, char **bp);
!     int safe_tag_wrap(char const *a_tag, char const *params,
! 		      char const *data, char *buf, char **bp, dbref player);
  
  #endif				/* __ANSI_H */
*** 1_8_2p1/hdrs/attrib.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/attrib.h	Sat Jan 27 02:21:10 2007
***************
*** 39,44 ****
--- 39,45 ----
  extern int atr_add(dbref thing, char const *RESTRICT atr,
  		   char const *RESTRICT s, dbref player, int flags);
  extern int atr_clr(dbref thing, char const *atr, dbref player);
+ extern int wipe_atr(dbref thing, char const *atr, dbref player);
  extern ATTR *atr_get(dbref thing, char const *atr);
  extern ATTR *atr_get_noparent(dbref thing, char const *atr);
  typedef int (*aig_func) (dbref, dbref, dbref, const char *, ATTR *, void *);
***************
*** 110,118 ****
  /* external predefined attributes. */
      extern ATTR attr[];
  
- /* external @wipe indicator (changes atr_clr() behaviour) */
-     extern int we_are_wiping;
- 
  #define AL_ATTR(alist)          (alist)
  #define AL_NAME(alist)          ((alist)->name)
  #define AL_STR(alist)           (atr_get_compressed_data((alist)))
--- 111,116 ----
***************
*** 121,126 ****
--- 119,136 ----
  #define AL_FLAGS(alist)         ((alist)->flags)
  #define AL_DEREFS(alist)        ((alist)->data?chunk_derefs((alist)->data):0)
  
+ 
+ /* Errors from attribute setting/clearing functions */
+ /** Attribute error - too many attribs */
+ #define AE_TOOMANY -4
+ /** Attribute error - invalid name */
+ #define AE_BADNAME -3
+ /** Attribute error - attempt to overwrite a safe attribute */
+ #define AE_SAFE -2
+ /** Attribute error - general failure */
+ #define AE_ERROR -1
+ 
+ 
  /* Errors from ok_player_alias */
  #define OPAE_SUCCESS    1
  #define OPAE_INVALID    -1
*** 1_8_2p1/hdrs/dbio.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/dbio.h	Sat Jan 27 02:21:10 2007
***************
*** 28,34 ****
  extern int db_paranoid_write(FILE * f, int flag);
  
  /* Input functions */
! extern const char *getstring_noalloc(FILE * f);
  extern long getref(FILE * f);
  extern void db_read_this_labeled_string(FILE * f, const char *label,
  					char **val);
--- 28,34 ----
  extern int db_paranoid_write(FILE * f, int flag);
  
  /* Input functions */
! extern char *getstring_noalloc(FILE * f);
  extern long getref(FILE * f);
  extern void db_read_this_labeled_string(FILE * f, const char *label,
  					char **val);
*** 1_8_2p1/hdrs/externs.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/externs.h	Sat Jan 27 02:21:10 2007
***************
*** 29,34 ****
--- 29,35 ----
  #include "mushtype.h"
  #include "dbdefs.h"
  #include "confmagic.h"
+ #include "pcre.h"
  #ifndef HAS_STRCASECMP
  #ifdef WIN32
  #define strcasecmp(s1,s2) _stricmp((s1), (s2))
***************
*** 171,176 ****
--- 172,184 ----
  #define notify_noecho(p,m)    notify_anything(orator, na_one, &(p), NULL, NA_NORELAY | NA_PUPPET, m)
  /** Notify player with message if they're not set QUIET */
  #define quiet_notify(p,m)     if (!IsQuiet(p)) notify(p,m)
+ /** Notify player but don't send \n */
+ #define notify_noenter_by(t,a,b) notify_anything(t, na_one, &(a), NULL, NA_NOENTER, b)
+ #define notify_noenter(a,b) notify_noenter_by(GOD, a, b)
+ /** Notify player but don't send <BR> if they're using Pueblo */
+ #define notify_nopenter_by(t,a,b) notify_anything(t, na_one, &(a), NULL, NA_NOPENTER, b)
+ #define notify_nopenter(a,b) notify_nopenter_by(GOD, a, b)
+ /* Notify with a printf-style format */
  extern void notify_format(dbref player, const char *fmt, ...)
    __attribute__ ((__format__(__printf__, 2, 3)));
  
***************
*** 197,202 ****
--- 205,211 ----
  #endif
  
  /* From cque.c */
+ struct _ansi_string;
  struct real_pcre;
  struct eval_context {
    char *wenv[10];		  /**< working environment (%0-%9) */
***************
*** 212,218 ****
    struct real_pcre *re_code;		  /**< The compiled re */
    int re_subpatterns;	      /**< The number of re subpatterns */
    int *re_offsets;	      /**< The offsets for the subpatterns */
!   char *re_from;	      /**< The positions of the subpatterns */
  };
  
  typedef struct eval_context EVAL_CONTEXT;
--- 221,227 ----
    struct real_pcre *re_code;		  /**< The compiled re */
    int re_subpatterns;	      /**< The number of re subpatterns */
    int *re_offsets;	      /**< The offsets for the subpatterns */
!   struct _ansi_string *re_from;		    /**< The positions of the subpatterns */
  };
  
  typedef struct eval_context EVAL_CONTEXT;
***************
*** 413,434 ****
  #define vsnprintf _vsnprintf
  #endif
  #endif
-     extern char *remove_markup(const char *orig, size_t * stripped_len);
-     extern char *skip_leading_ansi(const char *s);
  
- /** A string, with ansi attributes broken out from the text */
-     typedef struct {
-       char text[BUFFER_LEN];	/**< Text of the string */
-       char *codes[BUFFER_LEN];	/**< Ansi codes associated with each char of text */
-       size_t len;	/**< Length of text */
-     } ansi_string;
- 
- 
-     extern ansi_string *parse_ansi_string(const char *src) __attribute_malloc__;
-     extern void flip_ansi_string(ansi_string *as);
-     extern void free_ansi_string(ansi_string *as);
-     extern void populate_codes(ansi_string *as);
-     extern void depopulate_codes(ansi_string *as);
  #ifdef WIN32
  #define strncoll(s1,s2,n) _strncoll((s1), (s2), (n))
  #define strcasecoll(s1,s2) _stricoll((s1), (s2))
--- 422,428 ----
***************
*** 467,478 ****
  /** Append a boolean to the end of a string */
  #define safe_boolean(x, buf, bufp) \
                  safe_chr((x) ? '1' : '0', (buf), (bufp))
- /* Append X characters to the end of a string, taking ansi and html codes into
-    account. */
-     extern int safe_ansi_string(ansi_string *as, size_t start, size_t len,
- 				char *buff, char **bp);
-     extern int safe_ansi_string2(ansi_string *as, size_t start, size_t len,
- 				 char *buff, char **bp);
  /* Append N copies of the character X to the end of a string */
      extern int safe_fill(char x, size_t n, char *buff, char **bp);
  /* Append an accented string */
--- 461,466 ----
***************
*** 575,580 ****
--- 563,571 ----
  				  const char *RESTRICT d, int cs);
      extern int wild_match_case(const char *RESTRICT s, const char *RESTRICT d,
  			       int cs);
+     extern int wild_match_case_r(const char *RESTRICT s,
+ 				 const char *RESTRICT d, int cs,
+ 				 char **ary, int max);
      extern int quick_wild(const char *RESTRICT tsr, const char *RESTRICT dstr);
      extern int atr_wild(const char *RESTRICT tstr, const char *RESTRICT dstr);
  /** Default (case-sensitive) regex match */
*** 1_8_2p1/hdrs/mushdb.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/mushdb.h	Sat Jan 27 02:21:10 2007
***************
*** 149,154 ****
--- 149,155 ----
  #define DBF_NEW_POWERS          0x40000
  #define DBF_POWERS_LOGGED       0x80000
  #define DBF_LABELS              0x100000
+ #define DBF_SPIFFY_AF_ANSI      0x200000
  
  /* Reboot DB flag macros - these should be defined whether or not the
   * corresponding system option is defined 
*** 1_8_2p1/hdrs/parse.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/parse.h	Sat Jan 27 02:21:10 2007
***************
*** 80,88 ****
  extern int list2arr(char *r[], int max, char *list, char sep);
  /* The reverse */
  extern void arr2list(char *r[], int max, char *list, char **lp, char *sep);
! 
! 
! 
  
  /* All function declarations follow the format: */
  #ifndef HAVE_FUN_DEFINED
--- 80,91 ----
  extern int list2arr(char *r[], int max, char *list, char sep);
  /* The reverse */
  extern void arr2list(char *r[], int max, char *list, char **lp, char *sep);
! /* Split a sep-delimited string into individual elements.
!  * Uses mush_strdup, so freearr() is required on all
!  * list2arr_ansi()'d arrays (r) */
! extern int list2arr_ansi(char *r[], int max, char *list, char sep);
! /* Free an array generated by list2arr_ansi */
! extern void freearr(char *r[], int size);
  
  /* All function declarations follow the format: */
  #ifndef HAVE_FUN_DEFINED
*** 1_8_2p1/hdrs/pueblo.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/pueblo.h	Sat Jan 27 02:21:10 2007
***************
*** 20,46 ****
  #define PEND \
         *pp=0;
  
- #define notify_nopenter_by(t,a,b) notify_anything(t, na_one, &(a), NULL, NA_NOPENTER, b)
- #define notify_nopenter(a,b) notify_nopenter_by(GOD, a, b)
- #define notify_noenter_by(t,a,b) notify_anything(t, na_one, &(a), NULL, NA_NOENTER, b)
- #define notify_noenter(a,b) notify_noenter_by(GOD, a, b)
- 
  #define tag_wrap(a,b,c) safe_tag_wrap(a,b,c,pbuff,&pp,NOTHING)
  #define tag(a) safe_tag(a,pbuff,&pp)
  #define tag_cancel(a) safe_tag_cancel(a,pbuff,&pp)
  
- #define notify_pueblo(a,b) notify_anything(GOD, na_one, &(a), NULL, NA_PONLY | NA_NOPENTER | NA_NOLISTEN, b)
- 
- int safe_tag(char const *a_tag, char *buf, char **bp);
- int safe_tag_cancel(char const *a_tag, char *buf, char **bp);
- int safe_tag_wrap(char const *a_tag, char const *params,
- 		  char const *data, char *buf, char **bp, dbref player);
- 
  /* Please STAY SANE when modifying. 
   * Making this something like 'x' and 'y' is a BAD IDEA 
   */
  
- #define TAG_START '\02'
- #define TAG_END '\03'
- 
  #endif
--- 20,31 ----
*** 1_8_2p1/hdrs/version.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/hdrs/version.h	Sat Jan 27 02:21:10 2007
***************
*** 1,4 ****
! #define VERSION "1.8.2"
! #define PATCHLEVEL "1"
! #define PATCHDATE "[11/26/2006]"
! #define NUMVERSION 1008002001
--- 1,4 ----
! #define VERSION "1.8.3"
! #define PATCHLEVEL "0"
! #define PATCHDATE "[01/27/2007]"
! #define NUMVERSION 1008003000
*** 1_8_2p1/hints/irix_6.sh	Sun Nov 26 17:44:52 2006
--- 1_8_3p0/hints/irix_6.sh	Sat Jan 27 02:21:09 2007
***************
*** 1,4 ****
- ccflags="-mabi=n32"
  loclibpth="/usr/local/lib /usr/gnu/lib /usr/lib32 /usr/lib /lib"
  libs="-lc -lm"
  has_sigchld='define'
--- 1,3 ----
*** 1_8_2p1/src/Makefile.SH	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/Makefile.SH	Sat Jan 27 02:21:10 2007
***************
*** 64,72 ****
  	extmail.c filecopy.c flaglocal.c flags.c funcrypt.c function.c \
  	fundb.c funlist.c funlocal.c funmath.c funmisc.c funstr.c funtime.c \
  	funufun.c game.c help.c htab.c ident.c local.c lock.c log.c look.c \
! 	malias.c match.c memcheck.c move.c mycrypt.c mymalloc.c mysocket.c \
! 	myrlimit.c myssl.c notify.c parse.c pcre.c player.c plyrlist.c \
! 	predicat.c privtab.c \
  	ptab.c rob.c services.c set.c shs.c sig.c speech.c sql.c \
  	strdup.c strtree.c \
  	strutil.c tables.c timer.c unparse.c utils.c version.c warnings.c \
--- 64,72 ----
  	extmail.c filecopy.c flaglocal.c flags.c funcrypt.c function.c \
  	fundb.c funlist.c funlocal.c funmath.c funmisc.c funstr.c funtime.c \
  	funufun.c game.c help.c htab.c ident.c local.c lock.c log.c look.c \
!       malias.c markup.c match.c memcheck.c move.c mycrypt.c mymalloc.c \
!       mysocket.c myrlimit.c myssl.c notify.c parse.c pcre.c player.c \
!       plyrlist.c predicat.c privtab.c \
  	ptab.c rob.c services.c set.c shs.c sig.c speech.c sql.c \
  	strdup.c strtree.c \
  	strutil.c tables.c timer.c unparse.c utils.c version.c warnings.c \
***************
*** 79,86 ****
  	  ../hdrs/externs.h ../hdrs/extmail.h ../hdrs/flags.h \
  	  ../hdrs/function.h ../hdrs/game.h ../hdrs/getpgsiz.h ../hdrs/help.h \
  	  ../hdrs/htab.h ../hdrs/htab.h ../hdrs/ident.h ../hdrs/lock.h \
! 	  ../hdrs/log.h ../hdrs/log.h ../hdrs/malias.h ../hdrs/match.h \
! 	  ../hdrs/mushdb.h ../hdrs/mushtype.h \
  	  ../hdrs/mymalloc.h ../hdrs/mysocket.h ../hdrs/myssl.h \
  	  ../hdrs/parse.h ../hdrs/pcre.h ../hdrs/privtab.h ../hdrs/ptab.h \
  	  ../hdrs/strtree.h ../hdrs/version.h ../options.h
--- 79,86 ----
  	  ../hdrs/externs.h ../hdrs/extmail.h ../hdrs/flags.h \
  	  ../hdrs/function.h ../hdrs/game.h ../hdrs/getpgsiz.h ../hdrs/help.h \
  	  ../hdrs/htab.h ../hdrs/htab.h ../hdrs/ident.h ../hdrs/lock.h \
!         ../hdrs/log.h ../hdrs/log.h ../hdrs/malias.h \
!         ../hdrs/match.h ../hdrs/mushdb.h ../hdrs/mushtype.h \
  	  ../hdrs/mymalloc.h ../hdrs/mysocket.h ../hdrs/myssl.h \
  	  ../hdrs/parse.h ../hdrs/pcre.h ../hdrs/privtab.h ../hdrs/ptab.h \
  	  ../hdrs/strtree.h ../hdrs/version.h ../options.h
***************
*** 92,99 ****
  	extmail.o filecopy.o flaglocal.o flags.o funcrypt.o function.o \
  	fundb.o funlist.o funlocal.o funmath.o funmisc.o funstr.o funtime.o \
  	funufun.o game.o help.o htab.o ident.o local.o lock.o log.o look.o \
! 	malias.o match.o memcheck.o move.o mycrypt.o mymalloc.o mysocket.o \
! 	myrlimit.o myssl.o notify.o parse.o pcre.o player.o plyrlist.o \
  	predicat.o privtab.o \
  	ptab.o rob.o services.o set.o shs.o sig.o speech.o sql.o \
  	strdup.o strtree.o \
--- 92,100 ----
  	extmail.o filecopy.o flaglocal.o flags.o funcrypt.o function.o \
  	fundb.o funlist.o funlocal.o funmath.o funmisc.o funstr.o funtime.o \
  	funufun.o game.o help.o htab.o ident.o local.o lock.o log.o look.o \
!       malias.o markup.o match.o memcheck.o move.o mycrypt.o mymalloc.o \
!       mysocket.o myrlimit.o myssl.o notify.o parse.o pcre.o player.o \
!       plyrlist.o \
  	predicat.o privtab.o \
  	ptab.o rob.o services.o set.o shs.o sig.o speech.o sql.o \
  	strdup.o strtree.o \
***************
*** 260,265 ****
--- 261,267 ----
  access.o: ../hdrs/chunk.h
  access.o: ../hdrs/attrib.h
  access.o: ../confmagic.h
+ access.o: ../hdrs/pcre.h
  access.o: ../hdrs/access.h
  access.o: ../hdrs/mymalloc.h
  access.o: ../hdrs/match.h
***************
*** 280,285 ****
--- 282,288 ----
  atr_tab.o: ../hdrs/chunk.h
  atr_tab.o: ../hdrs/attrib.h
  atr_tab.o: ../confmagic.h
+ atr_tab.o: ../hdrs/pcre.h
  atr_tab.o: ../hdrs/atr_tab.h
  atr_tab.o: ../hdrs/privtab.h
  atr_tab.o: ../hdrs/mymalloc.h
***************
*** 300,305 ****
--- 303,309 ----
  attrib.o: ../hdrs/chunk.h
  attrib.o: ../hdrs/attrib.h
  attrib.o: ../confmagic.h
+ attrib.o: ../hdrs/pcre.h
  attrib.o: ../hdrs/match.h
  attrib.o: ../hdrs/parse.h
  attrib.o: ../hdrs/privtab.h
***************
*** 324,329 ****
--- 328,334 ----
  boolexp.o: ../hdrs/chunk.h
  boolexp.o: ../hdrs/attrib.h
  boolexp.o: ../confmagic.h
+ boolexp.o: ../hdrs/pcre.h
  boolexp.o: ../hdrs/lock.h
  boolexp.o: ../hdrs/boolexp.h
  boolexp.o: ../hdrs/parse.h
***************
*** 346,351 ****
--- 351,357 ----
  bsd.o: ../hdrs/chunk.h
  bsd.o: ../hdrs/attrib.h
  bsd.o: ../confmagic.h
+ bsd.o: ../hdrs/pcre.h
  bsd.o: ../hdrs/lock.h
  bsd.o: ../hdrs/boolexp.h
  bsd.o: ../hdrs/help.h
***************
*** 362,368 ****
  bsd.o: ../hdrs/ident.h
  bsd.o: ../hdrs/strtree.h
  bsd.o: ../hdrs/log.h
- bsd.o: ../hdrs/pcre.h
  bsd.o: ../hdrs/myssl.h
  bsd.o: ../hdrs/mymalloc.h
  bsd.o: ../hdrs/extmail.h
--- 368,373 ----
***************
*** 383,388 ****
--- 388,394 ----
  bufferq.o: ../hdrs/chunk.h
  bufferq.o: ../hdrs/attrib.h
  bufferq.o: ../confmagic.h
+ bufferq.o: ../hdrs/pcre.h
  bufferq.o: ../hdrs/bufferq.h
  bufferq.o: ../hdrs/mymalloc.h
  bufferq.o: ../hdrs/log.h
***************
*** 401,406 ****
--- 407,413 ----
  chunk.o: ../hdrs/chunk.h
  chunk.o: ../hdrs/attrib.h
  chunk.o: ../confmagic.h
+ chunk.o: ../hdrs/pcre.h
  chunk.o: ../hdrs/command.h
  chunk.o: ../hdrs/switches.h
  chunk.o: ../hdrs/intrface.h
***************
*** 421,426 ****
--- 428,434 ----
  cmdlocal.o: ../hdrs/chunk.h
  cmdlocal.o: ../hdrs/attrib.h
  cmdlocal.o: ../confmagic.h
+ cmdlocal.o: ../hdrs/pcre.h
  cmdlocal.o: ../hdrs/parse.h
  cmdlocal.o: ../hdrs/command.h
  cmdlocal.o: ../hdrs/switches.h
***************
*** 440,445 ****
--- 448,454 ----
  cmds.o: ../hdrs/chunk.h
  cmds.o: ../hdrs/attrib.h
  cmds.o: ../confmagic.h
+ cmds.o: ../hdrs/pcre.h
  cmds.o: ../hdrs/match.h
  cmds.o: ../hdrs/game.h
  cmds.o: ../hdrs/extmail.h
***************
*** 469,474 ****
--- 478,484 ----
  command.o: ../hdrs/chunk.h
  command.o: ../hdrs/attrib.h
  command.o: ../confmagic.h
+ command.o: ../hdrs/pcre.h
  command.o: ../hdrs/game.h
  command.o: ../hdrs/match.h
  command.o: ../hdrs/extmail.h
***************
*** 488,506 ****
  compress.o: ../hdrs/mushtype.h
  compress.o: ../hdrs/copyrite.h
  compress.o: ../hdrs/log.h
- compress.o: comp_h.c
- compress.o: ../hdrs/conf.h
- compress.o: ../hdrs/htab.h
- compress.o: ../hdrs/externs.h
- compress.o: ../hdrs/compile.h
- compress.o: ../hdrs/dbdefs.h
- compress.o: ../hdrs/mushdb.h
- compress.o: ../hdrs/flags.h
- compress.o: ../hdrs/ptab.h
- compress.o: ../hdrs/chunk.h
- compress.o: ../hdrs/attrib.h
- compress.o: ../confmagic.h
- compress.o: ../hdrs/mymalloc.h
  conf.o: ../hdrs/copyrite.h
  conf.o: ../config.h
  conf.o: ../hdrs/conf.h
--- 498,503 ----
***************
*** 516,521 ****
--- 513,520 ----
  conf.o: ../hdrs/chunk.h
  conf.o: ../hdrs/attrib.h
  conf.o: ../confmagic.h
+ conf.o: ../hdrs/pcre.h
+ conf.o: ../hdrs/ansi.h
  conf.o: ../hdrs/pueblo.h
  conf.o: ../hdrs/parse.h
  conf.o: ../hdrs/command.h
***************
*** 542,547 ****
--- 541,547 ----
  cque.o: ../hdrs/chunk.h
  cque.o: ../hdrs/attrib.h
  cque.o: ../confmagic.h
+ cque.o: ../hdrs/pcre.h
  cque.o: ../hdrs/parse.h
  cque.o: ../hdrs/strtree.h
  cque.o: ../hdrs/mymalloc.h
***************
*** 562,567 ****
--- 562,568 ----
  create.o: ../hdrs/chunk.h
  create.o: ../hdrs/attrib.h
  create.o: ../confmagic.h
+ create.o: ../hdrs/pcre.h
  create.o: ../hdrs/match.h
  create.o: ../hdrs/extchat.h
  create.o: ../hdrs/boolexp.h
***************
*** 588,593 ****
--- 589,595 ----
  db.o: ../hdrs/chunk.h
  db.o: ../hdrs/attrib.h
  db.o: ../confmagic.h
+ db.o: ../hdrs/pcre.h
  db.o: ../hdrs/mymalloc.h
  db.o: ../hdrs/game.h
  db.o: ../hdrs/lock.h
***************
*** 597,602 ****
--- 599,605 ----
  db.o: ../hdrs/parse.h
  db.o: ../hdrs/privtab.h
  db.o: ../hdrs/extmail.h
+ db.o: ../hdrs/ansi.h
  destroy.o: ../config.h
  destroy.o: ../hdrs/copyrite.h
  destroy.o: ../hdrs/conf.h
***************
*** 613,618 ****
--- 616,622 ----
  destroy.o: ../hdrs/chunk.h
  destroy.o: ../hdrs/attrib.h
  destroy.o: ../confmagic.h
+ destroy.o: ../hdrs/pcre.h
  destroy.o: ../hdrs/log.h
  destroy.o: ../hdrs/game.h
  destroy.o: ../hdrs/extmail.h
***************
*** 634,639 ****
--- 638,644 ----
  extchat.o: ../hdrs/chunk.h
  extchat.o: ../hdrs/attrib.h
  extchat.o: ../confmagic.h
+ extchat.o: ../hdrs/pcre.h
  extchat.o: ../hdrs/match.h
  extchat.o: ../hdrs/extchat.h
  extchat.o: ../hdrs/boolexp.h
***************
*** 665,676 ****
--- 670,683 ----
  extmail.o: ../hdrs/chunk.h
  extmail.o: ../hdrs/attrib.h
  extmail.o: ../confmagic.h
+ extmail.o: ../hdrs/pcre.h
  extmail.o: ../hdrs/match.h
  extmail.o: ../hdrs/extmail.h
  extmail.o: ../hdrs/function.h
  extmail.o: ../hdrs/malias.h
  extmail.o: ../hdrs/parse.h
  extmail.o: ../hdrs/mymalloc.h
+ extmail.o: ../hdrs/ansi.h
  extmail.o: ../hdrs/pueblo.h
  extmail.o: ../hdrs/log.h
  extmail.o: ../hdrs/lock.h
***************
*** 694,699 ****
--- 701,707 ----
  filecopy.o: ../hdrs/chunk.h
  filecopy.o: ../hdrs/attrib.h
  filecopy.o: ../confmagic.h
+ filecopy.o: ../hdrs/pcre.h
  filecopy.o: ../hdrs/mymalloc.h
  filecopy.o: ../hdrs/log.h
  flaglocal.o: ../hdrs/copyrite.h
***************
*** 711,716 ****
--- 719,725 ----
  flaglocal.o: ../hdrs/chunk.h
  flaglocal.o: ../hdrs/attrib.h
  flaglocal.o: ../confmagic.h
+ flaglocal.o: ../hdrs/pcre.h
  flags.o: ../config.h
  flags.o: ../hdrs/conf.h
  flags.o: ../hdrs/copyrite.h
***************
*** 726,731 ****
--- 735,741 ----
  flags.o: ../hdrs/chunk.h
  flags.o: ../hdrs/attrib.h
  flags.o: ../confmagic.h
+ flags.o: ../hdrs/pcre.h
  flags.o: ../hdrs/command.h
  flags.o: ../hdrs/switches.h
  flags.o: ../hdrs/parse.h
***************
*** 753,758 ****
--- 763,769 ----
  funcrypt.o: ../hdrs/chunk.h
  funcrypt.o: ../hdrs/attrib.h
  funcrypt.o: ../confmagic.h
+ funcrypt.o: ../hdrs/pcre.h
  funcrypt.o: ../hdrs/version.h
  funcrypt.o: ../hdrs/extchat.h
  funcrypt.o: ../hdrs/boolexp.h
***************
*** 779,784 ****
--- 790,796 ----
  function.o: ../hdrs/chunk.h
  function.o: ../hdrs/attrib.h
  function.o: ../confmagic.h
+ function.o: ../hdrs/pcre.h
  function.o: ../hdrs/function.h
  function.o: ../hdrs/match.h
  function.o: ../hdrs/parse.h
***************
*** 787,792 ****
--- 799,805 ----
  function.o: ../hdrs/game.h
  function.o: ../hdrs/mymalloc.h
  function.o: ../hdrs/funs.h
+ function.o: ../hdrs/ansi.h
  fundb.o: ../hdrs/copyrite.h
  fundb.o: ../config.h
  fundb.o: ../hdrs/conf.h
***************
*** 802,807 ****
--- 815,821 ----
  fundb.o: ../hdrs/chunk.h
  fundb.o: ../hdrs/attrib.h
  fundb.o: ../confmagic.h
+ fundb.o: ../hdrs/pcre.h
  fundb.o: ../hdrs/match.h
  fundb.o: ../hdrs/parse.h
  fundb.o: ../hdrs/command.h
***************
*** 815,823 ****
  funlist.o: ../hdrs/copyrite.h
  funlist.o: ../config.h
  funlist.o: ../hdrs/ansi.h
- funlist.o: ../hdrs/conf.h
- funlist.o: ../options.h
  funlist.o: ../hdrs/mushtype.h
  funlist.o: ../hdrs/htab.h
  funlist.o: ../hdrs/case.h
  funlist.o: ../hdrs/externs.h
--- 829,838 ----
  funlist.o: ../hdrs/copyrite.h
  funlist.o: ../config.h
  funlist.o: ../hdrs/ansi.h
  funlist.o: ../hdrs/mushtype.h
+ funlist.o: ../options.h
+ funlist.o: ../hdrs/pcre.h
+ funlist.o: ../hdrs/conf.h
  funlist.o: ../hdrs/htab.h
  funlist.o: ../hdrs/case.h
  funlist.o: ../hdrs/externs.h
***************
*** 832,838 ****
  funlist.o: ../hdrs/parse.h
  funlist.o: ../hdrs/function.h
  funlist.o: ../hdrs/mymalloc.h
- funlist.o: ../hdrs/pcre.h
  funlist.o: ../hdrs/match.h
  funlist.o: ../hdrs/command.h
  funlist.o: ../hdrs/switches.h
--- 847,852 ----
***************
*** 853,858 ****
--- 867,873 ----
  funlocal.o: ../hdrs/chunk.h
  funlocal.o: ../hdrs/attrib.h
  funlocal.o: ../confmagic.h
+ funlocal.o: ../hdrs/pcre.h
  funlocal.o: ../hdrs/parse.h
  funlocal.o: ../hdrs/function.h
  funmath.o: ../hdrs/copyrite.h
***************
*** 870,875 ****
--- 885,891 ----
  funmath.o: ../hdrs/chunk.h
  funmath.o: ../hdrs/attrib.h
  funmath.o: ../confmagic.h
+ funmath.o: ../hdrs/pcre.h
  funmath.o: ../hdrs/parse.h
  funmisc.o: ../hdrs/copyrite.h
  funmisc.o: ../config.h
***************
*** 887,899 ****
--- 903,919 ----
  funmisc.o: ../hdrs/chunk.h
  funmisc.o: ../hdrs/attrib.h
  funmisc.o: ../confmagic.h
+ funmisc.o: ../hdrs/pcre.h
  funmisc.o: ../hdrs/version.h
+ funmisc.o: ../hdrs/lock.h
+ funmisc.o: ../hdrs/boolexp.h
  funmisc.o: ../hdrs/match.h
  funmisc.o: ../hdrs/parse.h
  funmisc.o: ../hdrs/function.h
  funmisc.o: ../hdrs/command.h
  funmisc.o: ../hdrs/switches.h
  funmisc.o: ../hdrs/game.h
+ funmisc.o: ../hdrs/ansi.h
  funstr.o: ../hdrs/copyrite.h
  funstr.o: ../config.h
  funstr.o: ../hdrs/conf.h
***************
*** 901,906 ****
--- 921,927 ----
  funstr.o: ../hdrs/mushtype.h
  funstr.o: ../hdrs/htab.h
  funstr.o: ../hdrs/ansi.h
+ funstr.o: ../hdrs/pcre.h
  funstr.o: ../hdrs/externs.h
  funstr.o: ../hdrs/compile.h
  funstr.o: ../hdrs/dbdefs.h
***************
*** 931,936 ****
--- 952,958 ----
  funtime.o: ../hdrs/chunk.h
  funtime.o: ../hdrs/attrib.h
  funtime.o: ../confmagic.h
+ funtime.o: ../hdrs/pcre.h
  funtime.o: ../hdrs/parse.h
  funtime.o: ../hdrs/log.h
  funtime.o: ../hdrs/match.h
***************
*** 949,954 ****
--- 971,977 ----
  funufun.o: ../hdrs/chunk.h
  funufun.o: ../hdrs/attrib.h
  funufun.o: ../confmagic.h
+ funufun.o: ../hdrs/pcre.h
  funufun.o: ../hdrs/match.h
  funufun.o: ../hdrs/parse.h
  funufun.o: ../hdrs/mymalloc.h
***************
*** 969,974 ****
--- 992,998 ----
  game.o: ../hdrs/chunk.h
  game.o: ../hdrs/attrib.h
  game.o: ../confmagic.h
+ game.o: ../hdrs/pcre.h
  game.o: ../hdrs/game.h
  game.o: ../hdrs/match.h
  game.o: ../hdrs/case.h
***************
*** 1004,1009 ****
--- 1028,1034 ----
  help.o: ../hdrs/chunk.h
  help.o: ../hdrs/attrib.h
  help.o: ../confmagic.h
+ help.o: ../hdrs/pcre.h
  help.o: ../hdrs/command.h
  help.o: ../hdrs/switches.h
  help.o: ../hdrs/help.h
***************
*** 1027,1032 ****
--- 1052,1058 ----
  htab.o: ../hdrs/chunk.h
  htab.o: ../hdrs/attrib.h
  htab.o: ../confmagic.h
+ htab.o: ../hdrs/pcre.h
  htab.o: ../hdrs/mymalloc.h
  ident.o: ../config.h
  ident.o: ../hdrs/conf.h
***************
*** 1043,1048 ****
--- 1069,1075 ----
  ident.o: ../hdrs/chunk.h
  ident.o: ../hdrs/attrib.h
  ident.o: ../confmagic.h
+ ident.o: ../hdrs/pcre.h
  ident.o: ../hdrs/ident.h
  ident.o: ../hdrs/mysocket.h
  ident.o: ../hdrs/mymalloc.h
***************
*** 1062,1067 ****
--- 1089,1095 ----
  local.o: ../hdrs/chunk.h
  local.o: ../hdrs/attrib.h
  local.o: ../confmagic.h
+ local.o: ../hdrs/pcre.h
  local.o: ../hdrs/parse.h
  local.o: ../hdrs/command.h
  local.o: ../hdrs/switches.h
***************
*** 1080,1085 ****
--- 1108,1114 ----
  lock.o: ../hdrs/chunk.h
  lock.o: ../hdrs/attrib.h
  lock.o: ../confmagic.h
+ lock.o: ../hdrs/pcre.h
  lock.o: ../hdrs/boolexp.h
  lock.o: ../hdrs/lock.h
  lock.o: ../hdrs/match.h
***************
*** 1103,1108 ****
--- 1132,1138 ----
  log.o: ../hdrs/chunk.h
  log.o: ../hdrs/attrib.h
  log.o: ../confmagic.h
+ log.o: ../hdrs/pcre.h
  log.o: ../hdrs/bufferq.h
  log.o: ../hdrs/log.h
  look.o: ../config.h
***************
*** 1120,1125 ****
--- 1150,1156 ----
  look.o: ../hdrs/chunk.h
  look.o: ../hdrs/attrib.h
  look.o: ../confmagic.h
+ look.o: ../hdrs/pcre.h
  look.o: ../hdrs/lock.h
  look.o: ../hdrs/boolexp.h
  look.o: ../hdrs/match.h
***************
*** 1148,1153 ****
--- 1179,1185 ----
  malias.o: ../hdrs/chunk.h
  malias.o: ../hdrs/attrib.h
  malias.o: ../confmagic.h
+ malias.o: ../hdrs/pcre.h
  malias.o: ../hdrs/match.h
  malias.o: ../hdrs/parse.h
  malias.o: ../hdrs/malias.h
***************
*** 1156,1161 ****
--- 1188,1215 ----
  malias.o: ../hdrs/pueblo.h
  malias.o: ../hdrs/log.h
  malias.o: ../hdrs/dbio.h
+ markup.o: ../config.h
+ markup.o: ../hdrs/copyrite.h
+ markup.o: ../hdrs/conf.h
+ markup.o: ../options.h
+ markup.o: ../hdrs/mushtype.h
+ markup.o: ../hdrs/htab.h
+ markup.o: ../hdrs/case.h
+ markup.o: ../hdrs/ansi.h
+ markup.o: ../hdrs/pcre.h
+ markup.o: ../hdrs/pueblo.h
+ markup.o: ../hdrs/parse.h
+ markup.o: ../confmagic.h
+ markup.o: ../hdrs/externs.h
+ markup.o: ../hdrs/compile.h
+ markup.o: ../hdrs/dbdefs.h
+ markup.o: ../hdrs/mushdb.h
+ markup.o: ../hdrs/flags.h
+ markup.o: ../hdrs/ptab.h
+ markup.o: ../hdrs/chunk.h
+ markup.o: ../hdrs/attrib.h
+ markup.o: ../hdrs/mymalloc.h
+ markup.o: ../hdrs/log.h
  match.o: ../hdrs/copyrite.h
  match.o: ../config.h
  match.o: ../hdrs/conf.h
***************
*** 1171,1176 ****
--- 1225,1231 ----
  match.o: ../hdrs/chunk.h
  match.o: ../hdrs/attrib.h
  match.o: ../confmagic.h
+ match.o: ../hdrs/pcre.h
  match.o: ../hdrs/case.h
  match.o: ../hdrs/match.h
  match.o: ../hdrs/parse.h
***************
*** 1189,1194 ****
--- 1244,1250 ----
  memcheck.o: ../hdrs/chunk.h
  memcheck.o: ../hdrs/attrib.h
  memcheck.o: ../confmagic.h
+ memcheck.o: ../hdrs/pcre.h
  memcheck.o: ../hdrs/mymalloc.h
  memcheck.o: ../hdrs/log.h
  move.o: ../hdrs/copyrite.h
***************
*** 1206,1211 ****
--- 1262,1268 ----
  move.o: ../hdrs/chunk.h
  move.o: ../hdrs/attrib.h
  move.o: ../confmagic.h
+ move.o: ../hdrs/pcre.h
  move.o: ../hdrs/match.h
  move.o: ../hdrs/lock.h
  move.o: ../hdrs/boolexp.h
***************
*** 1240,1248 ****
--- 1297,1323 ----
  mysocket.o: ../hdrs/chunk.h
  mysocket.o: ../hdrs/attrib.h
  mysocket.o: ../confmagic.h
+ mysocket.o: ../hdrs/pcre.h
  mysocket.o: ../hdrs/mymalloc.h
  mysocket.o: ../hdrs/mysocket.h
  mysocket.o: ../hdrs/ident.h
+ myrlimit.o: ../hdrs/copyrite.h
+ myrlimit.o: ../config.h
+ myrlimit.o: ../hdrs/conf.h
+ myrlimit.o: ../options.h
+ myrlimit.o: ../hdrs/mushtype.h
+ myrlimit.o: ../hdrs/htab.h
+ myrlimit.o: ../hdrs/version.h
+ myrlimit.o: ../hdrs/externs.h
+ myrlimit.o: ../hdrs/compile.h
+ myrlimit.o: ../hdrs/dbdefs.h
+ myrlimit.o: ../hdrs/mushdb.h
+ myrlimit.o: ../hdrs/flags.h
+ myrlimit.o: ../hdrs/ptab.h
+ myrlimit.o: ../hdrs/chunk.h
+ myrlimit.o: ../hdrs/attrib.h
+ myrlimit.o: ../confmagic.h
+ myrlimit.o: ../hdrs/pcre.h
  myssl.o: ../hdrs/copyrite.h
  myssl.o: ../config.h
  myssl.o: ../hdrs/conf.h
***************
*** 1259,1264 ****
--- 1334,1340 ----
  myssl.o: ../hdrs/ptab.h
  myssl.o: ../hdrs/chunk.h
  myssl.o: ../hdrs/attrib.h
+ myssl.o: ../hdrs/pcre.h
  myssl.o: ../hdrs/myssl.h
  myssl.o: ../hdrs/log.h
  myssl.o: ../hdrs/parse.h
***************
*** 1277,1282 ****
--- 1353,1359 ----
  notify.o: ../hdrs/chunk.h
  notify.o: ../hdrs/attrib.h
  notify.o: ../confmagic.h
+ notify.o: ../hdrs/pcre.h
  notify.o: ../hdrs/lock.h
  notify.o: ../hdrs/boolexp.h
  notify.o: ../hdrs/help.h
***************
*** 1311,1322 ****
  parse.o: ../hdrs/chunk.h
  parse.o: ../hdrs/attrib.h
  parse.o: ../confmagic.h
  parse.o: ../hdrs/ansi.h
  parse.o: ../hdrs/function.h
  parse.o: ../hdrs/case.h
  parse.o: ../hdrs/match.h
  parse.o: ../hdrs/parse.h
- parse.o: ../hdrs/pcre.h
  parse.o: ../hdrs/log.h
  parse.o: ../hdrs/mymalloc.h
  pcre.o: ../config.h
--- 1388,1399 ----
  parse.o: ../hdrs/chunk.h
  parse.o: ../hdrs/attrib.h
  parse.o: ../confmagic.h
+ parse.o: ../hdrs/pcre.h
  parse.o: ../hdrs/ansi.h
  parse.o: ../hdrs/function.h
  parse.o: ../hdrs/case.h
  parse.o: ../hdrs/match.h
  parse.o: ../hdrs/parse.h
  parse.o: ../hdrs/log.h
  parse.o: ../hdrs/mymalloc.h
  pcre.o: ../config.h
***************
*** 1337,1342 ****
--- 1414,1420 ----
  player.o: ../hdrs/chunk.h
  player.o: ../hdrs/attrib.h
  player.o: ../confmagic.h
+ player.o: ../hdrs/pcre.h
  player.o: ../hdrs/access.h
  player.o: ../hdrs/mymalloc.h
  player.o: ../hdrs/log.h
***************
*** 1359,1364 ****
--- 1437,1443 ----
  plyrlist.o: ../hdrs/chunk.h
  plyrlist.o: ../hdrs/attrib.h
  plyrlist.o: ../confmagic.h
+ plyrlist.o: ../hdrs/pcre.h
  predicat.o: ../hdrs/copyrite.h
  predicat.o: ../config.h
  predicat.o: ../hdrs/conf.h
***************
*** 1374,1379 ****
--- 1453,1459 ----
  predicat.o: ../hdrs/chunk.h
  predicat.o: ../hdrs/attrib.h
  predicat.o: ../confmagic.h
+ predicat.o: ../hdrs/pcre.h
  predicat.o: ../hdrs/lock.h
  predicat.o: ../hdrs/boolexp.h
  predicat.o: ../hdrs/match.h
***************
*** 1397,1402 ****
--- 1477,1483 ----
  privtab.o: ../hdrs/ptab.h
  privtab.o: ../hdrs/chunk.h
  privtab.o: ../hdrs/attrib.h
+ privtab.o: ../hdrs/pcre.h
  ptab.o: ../config.h
  ptab.o: ../hdrs/copyrite.h
  ptab.o: ../hdrs/conf.h
***************
*** 1412,1417 ****
--- 1493,1499 ----
  ptab.o: ../hdrs/chunk.h
  ptab.o: ../hdrs/attrib.h
  ptab.o: ../confmagic.h
+ ptab.o: ../hdrs/pcre.h
  rob.o: ../config.h
  rob.o: ../hdrs/copyrite.h
  rob.o: ../hdrs/conf.h
***************
*** 1427,1432 ****
--- 1509,1515 ----
  rob.o: ../hdrs/chunk.h
  rob.o: ../hdrs/attrib.h
  rob.o: ../confmagic.h
+ rob.o: ../hdrs/pcre.h
  rob.o: ../hdrs/match.h
  rob.o: ../hdrs/parse.h
  rob.o: ../hdrs/log.h
***************
*** 1449,1454 ****
--- 1532,1538 ----
  set.o: ../hdrs/chunk.h
  set.o: ../hdrs/attrib.h
  set.o: ../confmagic.h
+ set.o: ../hdrs/pcre.h
  set.o: ../hdrs/match.h
  set.o: ../hdrs/ansi.h
  set.o: ../hdrs/command.h
***************
*** 1475,1480 ****
--- 1559,1565 ----
  sig.o: ../hdrs/chunk.h
  sig.o: ../hdrs/attrib.h
  sig.o: ../confmagic.h
+ sig.o: ../hdrs/pcre.h
  speech.o: ../hdrs/copyrite.h
  speech.o: ../config.h
  speech.o: ../hdrs/conf.h
***************
*** 1482,1487 ****
--- 1567,1573 ----
  speech.o: ../hdrs/mushtype.h
  speech.o: ../hdrs/htab.h
  speech.o: ../hdrs/ansi.h
+ speech.o: ../hdrs/pcre.h
  speech.o: ../hdrs/externs.h
  speech.o: ../hdrs/compile.h
  speech.o: ../hdrs/dbdefs.h
***************
*** 1497,1503 ****
  speech.o: ../hdrs/match.h
  speech.o: ../hdrs/parse.h
  speech.o: ../hdrs/game.h
- speech.o: ../hdrs/pcre.h
  sql.o: ../hdrs/copyrite.h
  sql.o: ../config.h
  sql.o: ../hdrs/conf.h
--- 1583,1588 ----
***************
*** 1513,1523 ****
--- 1598,1610 ----
  sql.o: ../hdrs/chunk.h
  sql.o: ../hdrs/attrib.h
  sql.o: ../confmagic.h
+ sql.o: ../hdrs/pcre.h
  sql.o: ../hdrs/log.h
  sql.o: ../hdrs/parse.h
  sql.o: ../hdrs/command.h
  sql.o: ../hdrs/switches.h
  sql.o: ../hdrs/function.h
+ sql.o: ../hdrs/ansi.h
  strdup.o: ../config.h
  strdup.o: ../hdrs/conf.h
  strdup.o: ../hdrs/copyrite.h
***************
*** 1541,1546 ****
--- 1628,1634 ----
  strtree.o: ../hdrs/chunk.h
  strtree.o: ../hdrs/attrib.h
  strtree.o: ../confmagic.h
+ strtree.o: ../hdrs/pcre.h
  strtree.o: ../hdrs/strtree.h
  strutil.o: ../config.h
  strutil.o: ../hdrs/copyrite.h
***************
*** 1550,1555 ****
--- 1638,1644 ----
  strutil.o: ../hdrs/htab.h
  strutil.o: ../hdrs/case.h
  strutil.o: ../hdrs/ansi.h
+ strutil.o: ../hdrs/pcre.h
  strutil.o: ../hdrs/pueblo.h
  strutil.o: ../hdrs/parse.h
  strutil.o: ../confmagic.h
***************
*** 1578,1583 ****
--- 1667,1673 ----
  timer.o: ../hdrs/chunk.h
  timer.o: ../hdrs/attrib.h
  timer.o: ../confmagic.h
+ timer.o: ../hdrs/pcre.h
  timer.o: ../hdrs/lock.h
  timer.o: ../hdrs/boolexp.h
  timer.o: ../hdrs/extmail.h
***************
*** 1602,1607 ****
--- 1692,1698 ----
  unparse.o: ../hdrs/chunk.h
  unparse.o: ../hdrs/attrib.h
  unparse.o: ../confmagic.h
+ unparse.o: ../hdrs/pcre.h
  unparse.o: ../hdrs/lock.h
  unparse.o: ../hdrs/boolexp.h
  unparse.o: ../hdrs/ansi.h
***************
*** 1613,1618 ****
--- 1704,1711 ----
  utils.o: ../options.h
  utils.o: ../hdrs/mushtype.h
  utils.o: ../hdrs/htab.h
+ utils.o: ../hdrs/ansi.h
+ utils.o: ../hdrs/pcre.h
  utils.o: ../hdrs/match.h
  utils.o: ../hdrs/externs.h
  utils.o: ../hdrs/compile.h
***************
*** 1625,1630 ****
--- 1718,1724 ----
  utils.o: ../confmagic.h
  utils.o: ../hdrs/mymalloc.h
  utils.o: ../hdrs/log.h
+ utils.o: ../hdrs/parse.h
  utils.o: ../hdrs/lock.h
  utils.o: ../hdrs/boolexp.h
  version.o: ../config.h
***************
*** 1642,1647 ****
--- 1736,1742 ----
  version.o: ../hdrs/chunk.h
  version.o: ../hdrs/attrib.h
  version.o: ../confmagic.h
+ version.o: ../hdrs/pcre.h
  version.o: ../hdrs/version.h
  version.o: ../hdrs/patches.h
  version.o: ../hdrs/buildinf.h
***************
*** 1660,1673 ****
  warnings.o: ../hdrs/chunk.h
  warnings.o: ../hdrs/attrib.h
  warnings.o: ../confmagic.h
  warnings.o: ../hdrs/lock.h
  warnings.o: ../hdrs/boolexp.h
  warnings.o: ../hdrs/match.h
  wild.o: ../config.h
  wild.o: ../hdrs/copyrite.h
- wild.o: ../hdrs/conf.h
  wild.o: ../options.h
! wild.o: ../hdrs/mushtype.h
  wild.o: ../hdrs/htab.h
  wild.o: ../hdrs/case.h
  wild.o: ../hdrs/externs.h
--- 1755,1771 ----
  warnings.o: ../hdrs/chunk.h
  warnings.o: ../hdrs/attrib.h
  warnings.o: ../confmagic.h
+ warnings.o: ../hdrs/pcre.h
  warnings.o: ../hdrs/lock.h
  warnings.o: ../hdrs/boolexp.h
  warnings.o: ../hdrs/match.h
  wild.o: ../config.h
+ wild.o: ../hdrs/ansi.h
+ wild.o: ../hdrs/mushtype.h
  wild.o: ../hdrs/copyrite.h
  wild.o: ../options.h
! wild.o: ../hdrs/pcre.h
! wild.o: ../hdrs/conf.h
  wild.o: ../hdrs/htab.h
  wild.o: ../hdrs/case.h
  wild.o: ../hdrs/externs.h
***************
*** 1681,1687 ****
  wild.o: ../confmagic.h
  wild.o: ../hdrs/mymalloc.h
  wild.o: ../hdrs/parse.h
- wild.o: ../hdrs/pcre.h
  wiz.o: ../hdrs/copyrite.h
  wiz.o: ../config.h
  wiz.o: ../hdrs/conf.h
--- 1779,1784 ----
***************
*** 1697,1702 ****
--- 1794,1800 ----
  wiz.o: ../hdrs/chunk.h
  wiz.o: ../hdrs/attrib.h
  wiz.o: ../confmagic.h
+ wiz.o: ../hdrs/pcre.h
  wiz.o: ../hdrs/match.h
  wiz.o: ../hdrs/access.h
  wiz.o: ../hdrs/parse.h
***************
*** 1708,1713 ****
--- 1806,1815 ----
  wiz.o: ../hdrs/command.h
  wiz.o: ../hdrs/switches.h
  wiz.o: ../hdrs/extmail.h
+ ../hdrs/ansi.o: ../hdrs/mushtype.h
+ ../hdrs/ansi.o: ../hdrs/copyrite.h
+ ../hdrs/ansi.o: ../options.h
+ ../hdrs/ansi.o: ../hdrs/pcre.h
  ../hdrs/atr_tab.o: ../hdrs/attrib.h
  ../hdrs/atr_tab.o: ../hdrs/mushtype.h
  ../hdrs/atr_tab.o: ../hdrs/copyrite.h
***************
*** 1765,1770 ****
--- 1867,1873 ----
  ../hdrs/externs.o: ../hdrs/chunk.h
  ../hdrs/externs.o: ../hdrs/attrib.h
  ../hdrs/externs.o: ../confmagic.h
+ ../hdrs/externs.o: ../hdrs/pcre.h
  ../hdrs/flags.o: ../hdrs/mushtype.h
  ../hdrs/flags.o: ../hdrs/copyrite.h
  ../hdrs/flags.o: ../options.h
*** 1_8_2p1/src/attrib.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/attrib.c	Sat Jan 27 02:21:10 2007
***************
*** 31,45 ****
  #pragma warning( disable : 4761)	/* disable warning re conversion */
  #endif
  
- /** Attribute error - too many attribs */
- #define AE_TOOMANY -4
- /** Attribute error - invalid name */
- #define AE_BADNAME -3
- /** Attribute error - attempt to overwrite a safe attribute */
- #define AE_SAFE -2
- /** Attribute error - general failure */
- #define AE_ERROR -1
- 
  /** A string tree of attribute names in use, to save us memory since
   * many are duplicated.
   */
--- 31,36 ----
***************
*** 48,61 ****
  extern PRIV attr_privs_set[];
  extern PRIV attr_privs_view[];
  
- /** A flag to show if we're in the middle of a @wipe (this changes
-  * behaviour for atr_clr()).  Yes, this is gross and ugly, but it
-  * seemed like a better idea than propogating signature changes
-  * for atr_clr() and do_set_atr() through the entire codebase.  If
-  * you come up with a better way, PLEASE fix this...
-  */
- int we_are_wiping;
- 
  /** A string to hold the name of a missing prefix branch, set by
   * can_write_attr_internal.  Again, gross and ugly.  Please fix.
   */
--- 39,44 ----
***************
*** 75,80 ****
--- 58,66 ----
    ATTR atrs[ATTRS_PER_PAGE];	/**< Array of attribute structures */
  } ATTRPAGE;
  
+ static int real_atr_clr(dbref thinking, char const *atr, dbref player,
+ 			int we_are_wiping);
+ 
  static ATTR *atr_free_list;
  static ATTR *get_atr_free_list(void);
  static ATTR *find_atr_pos_in_list(ATTR ***pos, char const *name);
***************
*** 681,698 ****
    return 1;
  }
  
! /** Remove an attribute from an object.
!  * This function clears an attribute from an object. 
!  * Permission is denied if the attribute is a branch, not a leaf.
!  * \param thing object to clear attribute from.
!  * \param atr name of attribute to remove.
!  * \param player enactor attempting to remove attribute.
!  * \retval 0 no attribute found to reset
!  * \retval AE_SAFE attribute is safe
!  * \retval AE_ERROR other failure
!  */
! int
! atr_clr(dbref thing, char const *atr, dbref player)
  {
    ATTR *ptr, **prev, *sub;
    size_t len;
--- 667,674 ----
    return 1;
  }
  
! static int
! real_atr_clr(dbref thing, char const *atr, dbref player, int we_are_wiping)
  {
    ATTR *ptr, **prev, *sub;
    size_t len;
***************
*** 750,755 ****
--- 726,766 ----
    return 1;
  }
  
+ 
+ /** Remove an attribute from an object.
+  * This function clears an attribute from an object. 
+  * Permission is denied if the attribute is a branch, not a leaf.
+  * \param thing object to clear attribute from.
+  * \param atr name of attribute to remove.
+  * \param player enactor attempting to remove attribute.
+  * \retval 0 no attribute found to reset
+  * \retval AE_SAFE attribute is safe
+  * \retval AE_ERROR other failure
+  */
+ int
+ atr_clr(dbref thing, char const *atr, dbref player)
+ {
+   return real_atr_clr(thing, atr, player, 0);
+ }
+ 
+ 
+ /** @wipe an attribute (And any leafs) from an object.
+  * This function clears an attribute from an object. 
+  * Permission is denied if the attribute is a branch, not a leaf.
+  * \param thing object to clear attribute from.
+  * \param atr name of attribute to remove.
+  * \param player enactor attempting to remove attribute.
+  * \retval 0 no attribute found to reset
+  * \retval AE_SAFE attribute is safe
+  * \retval AE_ERROR other failure
+  */
+ int
+ wipe_atr(dbref thing, char const *atr, dbref player)
+ {
+   return real_atr_clr(thing, atr, player, 1);
+ }
+ 
+ 
  /** Retrieve an attribute from an object or its ancestors.
   * This function retrieves an attribute from an object, or from its
   * parent chain, returning a pointer to the first attribute that
*** 1_8_2p1/src/boolexp.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/boolexp.c	Sat Jan 27 02:21:10 2007
***************
*** 643,648 ****
--- 643,651 ----
    }
  }
  
+ /** True if unparse_boolexp() is being evaluated. */
+ int unparsing_boolexp = 0;
+ 
  /** Display a boolexp.
   * This function returns the textual representation of the boolexp.
   * \param player The object wanting the decompiled boolexp.
***************
*** 657,662 ****
--- 660,667 ----
    char *buftop = boolexp_buf;
    unsigned char *bytecode = NULL;
  
+   unparsing_boolexp = 1;
+ 
    if (b == TRUE_BOOLEXP)
      safe_str("*UNLOCKED*", boolexp_buf, &buftop);
    else {
***************
*** 775,780 ****
--- 780,786 ----
    }
  done:
    *buftop++ = '\0';
+   unparsing_boolexp = 0;
  
    return boolexp_buf;
  }
*** 1_8_2p1/src/bsd.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/bsd.c	Sat Jan 27 02:21:10 2007
***************
*** 3174,3180 ****
        strcpy(poll_msg, "Doing");
      if (ShowAnsi(call_by->player))
        sprintf(tbuf2, "%-16s %10s %6s  %s%s\n",
! 	      T("Player Name"), T("On For"), T("Idle"), poll_msg, ANSI_NORMAL);
      else
        sprintf(tbuf2, "%-16s %10s %6s  %s\n",
  	      T("Player Name"), T("On For"), T("Idle"), poll_msg);
--- 3174,3180 ----
        strcpy(poll_msg, "Doing");
      if (ShowAnsi(call_by->player))
        sprintf(tbuf2, "%-16s %10s %6s  %s%s\n",
! 	      T("Player Name"), T("On For"), T("Idle"), poll_msg, ANSI_END);
      else
        sprintf(tbuf2, "%-16s %10s %6s  %s\n",
  	      T("Player Name"), T("On For"), T("Idle"), poll_msg);
*** 1_8_2p1/src/conf.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/conf.c	Sat Jan 27 02:21:10 2007
***************
*** 23,28 ****
--- 23,29 ----
  
  #include "conf.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "pueblo.h"
  #include "mushdb.h"
  #include "parse.h"
***************
*** 1318,1324 ****
        if (!cp->overridden) {
  	do_rawlog(LT_ERR,
  		  T
! 		  ("CONFIG: local directive '%s' missing from cnf file. using default value."),
  		  cp->name);
        }
      }
--- 1319,1325 ----
        if (!cp->overridden) {
  	do_rawlog(LT_ERR,
  		  T
! 		  ("CONFIG: local directive '%s' missing from cnf file. Using default value."),
  		  cp->name);
        }
      }
***************
*** 1370,1376 ****
    PENNCONF *cp;
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
    if (type && *type) {
      /* Look up the type in the group table */
      int found = 0;
--- 1371,1377 ----
    PENNCONF *cp;
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, open_tag("SAMP"));
    if (type && *type) {
      /* Look up the type in the group table */
      int found = 0;
***************
*** 1436,1442 ****
      }
    }
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END));
  }
  
  /** Lowercase a string if we've been asked to */
--- 1437,1443 ----
      }
    }
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, close_tag("SAMP"));
  }
  
  /** Lowercase a string if we've been asked to */
*** 1_8_2p1/src/db.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/db.c	Sat Jan 27 02:21:10 2007
***************
*** 35,40 ****
--- 35,41 ----
  #include "htab.h"
  #include "extmail.h"
  #include "confmagic.h"
+ #include "ansi.h"
  
  #ifdef WIN32
  #pragma warning( disable : 4761)	/* disable warning re conversion */
***************
*** 670,675 ****
--- 671,677 ----
    dbflag += DBF_NEW_POWERS;
    dbflag += DBF_POWERS_LOGGED;
    dbflag += DBF_LABELS;
+   dbflag += DBF_SPIFFY_AF_ANSI;
  
    OUTPUT(fprintf(f, "+V%d\n", dbflag * 256 + 2));
  
***************
*** 865,870 ****
--- 867,873 ----
    dbflag += DBF_NEW_POWERS;
    dbflag += DBF_POWERS_LOGGED;
    dbflag += DBF_LABELS;
+   dbflag += DBF_SPIFFY_AF_ANSI;
  
    do_rawlog(LT_CHECK, "PARANOID WRITE BEGINNING...\n");
  
***************
*** 921,927 ****
   * \param f file pointer to read from.
   * \return pointer to static buffer containing string read.
   */
! const char *
  getstring_noalloc(FILE * f)
  {
    static char buf[BUFFER_LEN];
--- 924,930 ----
   * \param f file pointer to read from.
   * \return pointer to static buffer containing string read.
   */
! char *
  getstring_noalloc(FILE * f)
  {
    static char buf[BUFFER_LEN];
***************
*** 1124,1129 ****
--- 1127,1135 ----
    int flags;
    int count = 0;
    unsigned char derefs;
+   ansi_string *as;
+   char tbuf2[BUFFER_LEN];
+   char *tb2;
  
    List(i) = NULL;
    tbuf1[0] = '\0';
***************
*** 1168,1174 ****
         * since we haven't loaded the whole db and can't really tell
         * if it is or not. We'll fix this up at the end of the load 
         */
!       atr_new_add(i, tbuf1, getstring_noalloc(f), atoi(p), flags, derefs);
        count++;
        /* Check removed for atoi(q) == 0  (which results in NOTHING for that
         * parameter, and thus no flags), since this eliminates 'visual'
--- 1174,1190 ----
         * since we haven't loaded the whole db and can't really tell
         * if it is or not. We'll fix this up at the end of the load 
         */
!       tb2 = getstring_noalloc(f);
!       if (strchr(tb2, TAG_START) || strchr(tb2, ESC_CHAR)) {
! 	as = parse_ansi_string_real(tb2, 1);
! 	tb2 = tbuf2;
! 	safe_ansi_string(as, 0, as->len, tbuf2, &tb2);
! 	*(tb2) = '\0';
! 	tb2 = tbuf2;
! 	free_ansi_string(as);
!       }
! 
!       atr_new_add(i, tbuf1, tb2, atoi(p), flags, derefs);
        count++;
        /* Check removed for atoi(q) == 0  (which results in NOTHING for that
         * parameter, and thus no flags), since this eliminates 'visual'
***************
*** 1221,1226 ****
--- 1237,1243 ----
    int flags;
    char *tmp;
    int found = 0;
+   ansi_string *as;
  
    List(i) = NULL;
  
***************
*** 1243,1248 ****
--- 1260,1274 ----
      db_read_this_labeled_number(f, "derefs", &derefs);
      db_read_this_labeled_string(f, "value", &tmp);
      strcpy(value, tmp);
+     if (!(globals.indb_flags & DBF_SPIFFY_AF_ANSI)) {
+       if (strchr(value, ESC_CHAR) || strchr(value, TAG_START)) {
+ 	char *vp = value;
+ 	as = parse_ansi_string_real(value, 1);
+ 	safe_ansi_string(as, 0, as->len, value, &vp);
+ 	*vp = '\0';
+ 	free_ansi_string(as);
+       }
+     }
      atr_new_add(i, name, value, owner, flags, derefs);
    }
  
*** 1_8_2p1/src/extchat.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/extchat.c	Sat Jan 27 02:21:10 2007
***************
*** 1526,1536 ****
    case POSE_TOKEN:
      arg1 = arg1 + 1;
      channel_broadcast(chan, player, 0, "<%s> %s%s%s%s%s%s", ChanName(chan),
! 		      title ? title : "", title ? ANSI_NORMAL : "",
  		      (title && name) ? " " : "", name ? name : "", gap, arg1);
      if (!canhear)
        notify_format(player, T("To channel %s: %s%s%s%s%s%s"), ChanName(chan),
! 		    title ? title : "", title ? ANSI_NORMAL : "",
  		    (title && name) ? " " : "", name ? name : "", gap, arg1);
      break;
    default:
--- 1526,1536 ----
    case POSE_TOKEN:
      arg1 = arg1 + 1;
      channel_broadcast(chan, player, 0, "<%s> %s%s%s%s%s%s", ChanName(chan),
! 		      title ? title : "", title ? ANSI_ENDALL : "",
  		      (title && name) ? " " : "", name ? name : "", gap, arg1);
      if (!canhear)
        notify_format(player, T("To channel %s: %s%s%s%s%s%s"), ChanName(chan),
! 		    title ? title : "", title ? ANSI_ENDALL : "",
  		    (title && name) ? " " : "", name ? name : "", gap, arg1);
      break;
    default:
***************
*** 1538,1550 ****
        arg1 = arg1 + 1;
      channel_broadcast(chan, player, 0, T("<%s> %s%s%s%s says, \"%s\""),
  		      ChanName(chan), title ? title : "",
! 		      title ? ANSI_NORMAL : "", (title && name) ? " " : "",
  		      name ? name : "", arg1);
      if (!canhear)
        notify_format(player,
  		    T("To channel %s: %s%s%s%s says, \"%s\""),
  		    ChanName(chan), title ? title : "",
! 		    title ? ANSI_NORMAL : "", (title && name) ? " " : "",
  		    name ? name : "", arg1);
      break;
    }
--- 1538,1550 ----
        arg1 = arg1 + 1;
      channel_broadcast(chan, player, 0, T("<%s> %s%s%s%s says, \"%s\""),
  		      ChanName(chan), title ? title : "",
! 		      title ? ANSI_ENDALL : "", (title && name) ? " " : "",
  		      name ? name : "", arg1);
      if (!canhear)
        notify_format(player,
  		    T("To channel %s: %s%s%s%s says, \"%s\""),
  		    ChanName(chan), title ? title : "",
! 		    title ? ANSI_ENDALL : "", (title && name) ? " " : "",
  		    name ? name : "", arg1);
      break;
    }
***************
*** 2042,2048 ****
    int numblanks;
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
    notify_format(player, "%-30s %-5s %8s %-16s %-8s %-3s",
  		"Name", "Users", "Msgs", T("Chan Type"), "Status", "Buf");
    for (c = channels; c; c = c->next) {
--- 2042,2048 ----
    int numblanks;
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, open_tag("SAMP"));
    notify_format(player, "%-30s %-5s %8s %-16s %-8s %-3s",
  		"Name", "Users", "Msgs", T("Chan Type"), "Status", "Buf");
    for (c = channels; c; c = c->next) {
***************
*** 2051,2059 ****
        u = onchannel(player, c);
        if (SUPPORT_PUEBLO)
  	sprintf(numusers,
! 		"%cA XCH_CMD=\"@channel/who %s\" XCH_HINT=\"See who's on this channel now\"%c%5ld%c/A%c",
! 		TAG_START, cleanname, TAG_END, ChanNumUsers(c),
! 		TAG_START, TAG_END);
        else
  	sprintf(numusers, "%5ld", ChanNumUsers(c));
        /* Display length is strlen(cleanname), but actual length is
--- 2051,2059 ----
        u = onchannel(player, c);
        if (SUPPORT_PUEBLO)
  	sprintf(numusers,
! 		"%c%cA XCH_CMD=\"@channel/who %s\" XCH_HINT=\"See who's on this channel now\"%c%5ld%c%c/A%c",
! 		TAG_START, MARKUP_HTML, cleanname, TAG_END, ChanNumUsers(c),
! 		TAG_START, MARKUP_HTML, TAG_END);
        else
  	sprintf(numusers, "%5ld", ChanNumUsers(c));
        /* Display length is strlen(cleanname), but actual length is
***************
*** 2100,2106 ****
      }
    }
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END));
  }
  
  static char *
--- 2100,2106 ----
      }
    }
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, close_tag("SAMP"));
  }
  
  static char *
*** 1_8_2p1/src/extmail.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/extmail.c	Sat Jan 27 02:21:10 2007
***************
*** 82,87 ****
--- 82,88 ----
  #include "attrib.h"
  #include "parse.h"
  #include "mymalloc.h"
+ #include "ansi.h"
  #include "pueblo.h"
  #include "flags.h"
  #include "log.h"
***************
*** 532,542 ****
  	/* Read it */
  	j++;
  	if (SUPPORT_PUEBLO) {
! 	  notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
  	  sprintf(folderheader,
! 		  "%cA XCH_HINT=\"List messages in this folder\" XCH_CMD=\"@mail/list %d:1-\"%c%s%c/A%c",
! 		  TAG_START, Folder(mp), TAG_END, T("Folder:"), TAG_START,
! 		  TAG_END);
  	} else
  	  strcpy(folderheader, T("Folder:"));
  	notify(player, DASH_LINE);
--- 533,543 ----
  	/* Read it */
  	j++;
  	if (SUPPORT_PUEBLO) {
! 	  notify_noenter(player, open_tag("SAMP"));
  	  sprintf(folderheader,
! 		  "%c%cA XCH_HINT=\"List messages in this folder\" XCH_CMD=\"@mail/list %d:1-\"%c%s%c%c/A%c",
! 		  TAG_START, MARKUP_HTML, Folder(mp), TAG_END, T("Folder:"),
! 		  TAG_START, MARKUP_HTML, TAG_END);
  	} else
  	  strcpy(folderheader, T("Folder:"));
  	notify(player, DASH_LINE);
***************
*** 553,564 ****
  	notify_format(player, T("Subject: %s"), get_subject(mp));
  	notify(player, DASH_LINE);
  	if (SUPPORT_PUEBLO)
! 	  notify_noenter(player, tprintf("%c/SAMP%c", TAG_START, TAG_END));
  	strcpy(tbuf1, get_message(mp));
  	notify(player, tbuf1);
  	if (SUPPORT_PUEBLO)
! 	  notify_format(player, "%cSAMP%c%s%c/SAMP%c", TAG_START, TAG_END,
! 			DASH_LINE, TAG_START, TAG_END);
  	else
  	  notify(player, DASH_LINE);
  	if (Unread(mp))
--- 554,564 ----
  	notify_format(player, T("Subject: %s"), get_subject(mp));
  	notify(player, DASH_LINE);
  	if (SUPPORT_PUEBLO)
! 	  notify_noenter(player, close_tag("SAMP"));
  	strcpy(tbuf1, get_message(mp));
  	notify(player, tbuf1);
  	if (SUPPORT_PUEBLO)
! 	  notify(player, wrap_tag("SAMP", DASH_LINE));
  	else
  	  notify(player, DASH_LINE);
  	if (Unread(mp))
***************
*** 596,602 ****
    j = 0;
    folder = AllInFolder(ms) ? player_folder(player) : MSFolder(ms);
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
    notify_format(player,
  		T
  		("---------------------------  MAIL (folder %2d)  ------------------------------"),
--- 596,602 ----
    j = 0;
    folder = AllInFolder(ms) ? player_folder(player) : MSFolder(ms);
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, open_tag("SAMP"));
    notify_format(player,
  		T
  		("---------------------------  MAIL (folder %2d)  ------------------------------"),
***************
*** 610,617 ****
  	if (SUPPORT_PUEBLO)
  	  notify_noenter(player,
  			 tprintf
! 			 ("%cA XCH_CMD=\"@mail/read %d:%d\" XCH_HINT=\"Read message %d in folder %d\"%c",
! 			  TAG_START, Folder(mp), i[Folder(mp)],
  			  i[Folder(mp)], Folder(mp), TAG_END));
  	strcpy(subj, chopstr(get_subject(mp), 28));
  	strcpy(sender, chopstr(get_sender(mp, 0), 12));
--- 610,617 ----
  	if (SUPPORT_PUEBLO)
  	  notify_noenter(player,
  			 tprintf
! 			 ("%c%cA XCH_CMD=\"@mail/read %d:%d\" XCH_HINT=\"Read message %d in folder %d\"%c",
! 			  TAG_START, MARKUP_HTML, Folder(mp), i[Folder(mp)],
  			  i[Folder(mp)], Folder(mp), TAG_END));
  	strcpy(subj, chopstr(get_subject(mp), 28));
  	strcpy(sender, chopstr(get_sender(mp, 0), 12));
***************
*** 623,635 ****
  		       ? '*' : ' '), sender, 30, subj,
  		      mail_list_time(show_time(mp->time, 0), 1));
  	if (SUPPORT_PUEBLO)
! 	  notify_noenter(player, tprintf("%c/A%c", TAG_START, TAG_END));
        }
      }
    }
    notify(player, DASH_LINE);
    if (SUPPORT_PUEBLO)
!     notify_format(player, "%c/SAMP%c", TAG_START, TAG_END);
    return;
  }
  
--- 623,636 ----
  		       ? '*' : ' '), sender, 30, subj,
  		      mail_list_time(show_time(mp->time, 0), 1));
  	if (SUPPORT_PUEBLO)
! 	  notify_noenter(player, tprintf("%c%c/A%c", TAG_START,
! 					 MARKUP_HTML, TAG_END));
        }
      }
    }
    notify(player, DASH_LINE);
    if (SUPPORT_PUEBLO)
!     notify_format(player, close_tag("SAMP"));
    return;
  }
  
*** 1_8_2p1/src/funcrypt.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/funcrypt.c	Sat Jan 27 02:21:10 2007
***************
*** 133,144 ****
  
  FUNCTION(fun_encrypt)
  {
!   safe_str(crypt_code(args[1], args[0], 1), buff, bp);
  }
  
  FUNCTION(fun_decrypt)
  {
!   safe_str(crypt_code(args[1], args[0], 0), buff, bp);
  }
  
  FUNCTION(fun_checkpass)
--- 133,168 ----
  
  FUNCTION(fun_encrypt)
  {
!   char tbuff[BUFFER_LEN], *tp;
!   char *pass;
!   size_t len;
!   ansi_string *as = parse_ansi_string(args[0]);
! 
!   pass = remove_markup(args[1], &len);
! 
!   tp = tbuff;
!   safe_str(crypt_code(pass, as->text, 1), tbuff, &tp);
!   *tp = '\0';
!   memcpy(as->text, tbuff, as->len);
!   safe_ansi_string(as, 0, as->len, buff, bp);
!   free_ansi_string(as);
  }
  
  FUNCTION(fun_decrypt)
  {
!   char tbuff[BUFFER_LEN], *tp;
!   char *pass;
!   size_t len;
!   ansi_string *as = parse_ansi_string(args[0]);
! 
!   pass = remove_markup(args[1], &len);
! 
!   tp = tbuff;
!   safe_str(crypt_code(pass, as->text, 0), tbuff, &tp);
!   *tp = '\0';
!   memcpy(as->text, tbuff, as->len + 1);
!   safe_ansi_string(as, 0, as->len, buff, bp);
!   free_ansi_string(as);
  }
  
  FUNCTION(fun_checkpass)
*** 1_8_2p1/src/function.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/function.c	Sat Jan 27 02:21:10 2007
***************
*** 27,32 ****
--- 27,33 ----
  #include "mymalloc.h"
  #include "funs.h"
  #include "confmagic.h"
+ #include "ansi.h"
  
  static void func_hash_insert(const char *name, FUN *func);
  extern void local_functions(void);
***************
*** 282,288 ****
    {"ANDLFLAGS", fun_andlflags, 2, 2, FN_REG},
    {"ANDLPOWERS", fun_andlflags, 2, 2, FN_REG},
    {"ANDPOWERS", fun_andflags, 2, 2, FN_REG},
!   {"ANSI", fun_ansi, 2, -2, FN_NOPARSE},
    {"APOSS", fun_aposs, 1, 1, FN_REG},
    {"ART", fun_art, 1, 1, FN_REG},
    {"ATRLOCK", fun_atrlock, 1, 2, FN_REG},
--- 283,292 ----
    {"ANDLFLAGS", fun_andlflags, 2, 2, FN_REG},
    {"ANDLPOWERS", fun_andlflags, 2, 2, FN_REG},
    {"ANDPOWERS", fun_andflags, 2, 2, FN_REG},
!   {"ANSI", fun_ansi, 2, -2, FN_REG},
! #ifdef ANSI_DEBUG
!   {"ANSIINSPECT", fun_ansiinspect, 1, 2, FN_REG},
! #endif
    {"APOSS", fun_aposs, 1, 1, FN_REG},
    {"ART", fun_art, 1, 1, FN_REG},
    {"ATRLOCK", fun_atrlock, 1, 2, FN_REG},
***************
*** 564,570 ****
    {"REGMATCHI", fun_regmatch, 2, 3, FN_REG},
    {"REGRAB", fun_regrab, 2, 4, FN_REG},
    {"REGRABALL", fun_regrab, 2, 4, FN_REG},
!   {"REGRABALLI", fun_regrab, 2, 3, FN_REG},
    {"REGRABI", fun_regrab, 2, 3, FN_REG},
    {"REGREP", fun_regrep, 3, 3, FN_REG},
    {"REGREPI", fun_regrep, 3, 3, FN_REG},
--- 568,574 ----
    {"REGMATCHI", fun_regmatch, 2, 3, FN_REG},
    {"REGRAB", fun_regrab, 2, 4, FN_REG},
    {"REGRABALL", fun_regrab, 2, 4, FN_REG},
!   {"REGRABALLI", fun_regrab, 2, 4, FN_REG},
    {"REGRABI", fun_regrab, 2, 3, FN_REG},
    {"REGREP", fun_regrep, 3, 3, FN_REG},
    {"REGREPI", fun_regrep, 3, 3, FN_REG},
***************
*** 626,632 ****
    {"STRIPACCENTS", fun_stripaccents, 1, 1, FN_REG},
    {"STRIPANSI", fun_stripansi, 1, -1, FN_REG},
    {"STRLEN", fun_strlen, 1, -1, FN_REG},
!   {"STRMATCH", fun_strmatch, 2, 2, FN_REG},
    {"STRREPLACE", fun_strreplace, 4, 4, FN_REG},
    {"SUB", fun_sub, 2, 2, FN_REG},
    {"SUBJ", fun_subj, 1, 1, FN_REG},
--- 630,636 ----
    {"STRIPACCENTS", fun_stripaccents, 1, 1, FN_REG},
    {"STRIPANSI", fun_stripansi, 1, -1, FN_REG},
    {"STRLEN", fun_strlen, 1, -1, FN_REG},
!   {"STRMATCH", fun_strmatch, 2, 3, FN_REG},
    {"STRREPLACE", fun_strreplace, 4, 4, FN_REG},
    {"SUB", fun_sub, 2, 2, FN_REG},
    {"SUBJ", fun_subj, 1, 1, FN_REG},
*** 1_8_2p1/src/funlist.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/funlist.c	Sat Jan 27 02:21:10 2007
***************
*** 10,19 ****
  #include "config.h"
  #include <string.h>
  #include <ctype.h>
- #include "ansi.h"
  #include "conf.h"
  #include "case.h"
  #include "externs.h"
  #include "parse.h"
  #include "function.h"
  #include "mymalloc.h"
--- 10,19 ----
  #include "config.h"
  #include <string.h>
  #include <ctype.h>
  #include "conf.h"
  #include "case.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "parse.h"
  #include "function.h"
  #include "mymalloc.h"
***************
*** 71,77 ****
  
  /** Convert list to array.
   * Chops up a list of words into an array of words. The list is
!  * destructively modified.
   * \param r pointer to array to store words.
   * \param max maximum number of words to split out.
   * \param list list of words as a string.
--- 71,78 ----
  
  /** Convert list to array.
   * Chops up a list of words into an array of words. The list is
!  * destructively modified. The array returned consists of
!  * mush_strdup'd strings.
   * \param r pointer to array to store words.
   * \param max maximum number of words to split out.
   * \param list list of words as a string.
***************
*** 79,85 ****
   * \return number of words split out.
   */
  int
! list2arr(char *r[], int max, char *list, char sep)
  {
    char *p, *lp;
    int i;
--- 80,86 ----
   * \return number of words split out.
   */
  int
! list2arr_ansi(char *r[], int max, char *list, char sep)
  {
    char *p, *lp;
    int i;
***************
*** 96,109 ****
    p = split_token(&aptr, sep);
    first = 0;
    for (i = 0; p && (i < max); i++, p = split_token(&aptr, sep)) {
!     r[i] = lp;
      safe_ansi_string(as, p - (as->text), strlen(p), list, &lp);
!     *(lp++) = '\0';
    }
    free_ansi_string(as);
    return i;
  }
  
  /** Convert array to list.
   * Takes an array of words and concatenates them into a string,
   * using our safe string functions.
--- 97,141 ----
    p = split_token(&aptr, sep);
    first = 0;
    for (i = 0; p && (i < max); i++, p = split_token(&aptr, sep)) {
!     lp = list;
      safe_ansi_string(as, p - (as->text), strlen(p), list, &lp);
!     *lp = '\0';
!     r[i] = mush_strdup(list, "list2arr_item");
    }
    free_ansi_string(as);
    return i;
  }
  
+ /** Convert list to array.
+  * Chops up a list of words into an array of words. The list is
+  * destructively modified.
+  * \param r pointer to array to store words.
+  * \param max maximum number of words to split out.
+  * \param list list of words as a string.
+  * \param sep separator character between list items.
+  * \return number of words split out.
+  */
+ int
+ list2arr(char *r[], int max, char *list, char sep)
+ {
+   char *p, *lp;
+   int i;
+   int first;
+   char *aptr;
+ 
+   memcpy(list, remove_markup(list, NULL), BUFFER_LEN);
+ 
+   aptr = trim_space_sep(list, sep);
+ 
+   lp = list;
+   p = split_token(&aptr, sep);
+   first = 0;
+   for (i = 0; p && (i < max); i++, p = split_token(&aptr, sep)) {
+     r[i] = p;
+   }
+   return i;
+ }
+ 
  /** Convert array to list.
   * Takes an array of words and concatenates them into a string,
   * using our safe string functions.
***************
*** 133,138 ****
--- 165,184 ----
    **lp = '\0';
  }
  
+ /** Free an array generated by list2arr.
+  * Takes an array of words and frees it.
+  * \param r pointer to array of words.
+  * \param size The number of items in the list.
+  */
+ void
+ freearr(char *r[], int size)
+ {
+   int i;
+   for (i = 0; i < size; i++) {
+     mush_free(r[i], "list2arr_item");
+   }
+ }
+ 
  /* ARGSUSED */
  FUNCTION(fun_munge)
  {
***************
*** 147,152 ****
--- 193,199 ----
  
    char list1[BUFFER_LEN], *lp, rlist[BUFFER_LEN], *rp;
    char **ptrs1, **ptrs2, **results;
+   char **ptrs3;
    int i, j, nptrs1, nptrs2, nresults;
    dbref thing;
    ATTR *attrib;
***************
*** 187,201 ****
  
    ptrs1 = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
    ptrs2 = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
    if (!ptrs1 || !ptrs2)
      mush_panic("Unable to allocate memory in fun_munge");
!   nptrs1 = list2arr(ptrs1, MAX_SORTSIZE, args[1], sep);
!   nptrs2 = list2arr(ptrs2, MAX_SORTSIZE, args[2], sep);
  
    if (nptrs1 != nptrs2) {
      safe_str(T("#-1 LISTS MUST BE OF EQUAL SIZE"), buff, bp);
      mush_free((Malloc_t) ptrs1, "ptrarray");
      mush_free((Malloc_t) ptrs2, "ptrarray");
      free_anon_attrib(attrib);
      return;
    }
--- 234,257 ----
  
    ptrs1 = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
    ptrs2 = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
+ 
+   /* ptrs3 is destructively modified, but it's a copy of ptrs2, so we
+    * make it a straight copy of ptrs2 and freearr() on ptrs2. */
+   ptrs3 = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
+ 
    if (!ptrs1 || !ptrs2)
      mush_panic("Unable to allocate memory in fun_munge");
!   nptrs1 = list2arr_ansi(ptrs1, MAX_SORTSIZE, args[1], sep);
!   nptrs2 = list2arr_ansi(ptrs2, MAX_SORTSIZE, args[2], sep);
!   memcpy(ptrs3, ptrs2, MAX_SORTSIZE * sizeof(char *));
  
    if (nptrs1 != nptrs2) {
      safe_str(T("#-1 LISTS MUST BE OF EQUAL SIZE"), buff, bp);
+     freearr(ptrs1, nptrs1);
+     freearr(ptrs2, nptrs2);
      mush_free((Malloc_t) ptrs1, "ptrarray");
      mush_free((Malloc_t) ptrs2, "ptrarray");
+     mush_free((Malloc_t) ptrs3, "ptrarray");
      free_anon_attrib(attrib);
      return;
    }
***************
*** 217,240 ****
    results = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
    if (!results)
      mush_panic("Unable to allocate memory in fun_munge");
!   nresults = list2arr(results, MAX_SORTSIZE, rlist, sep);
  
    first = 1;
    for (i = 0; i < nresults; i++) {
      for (j = 0; j < nptrs1; j++) {
!       if (ptrs2[j] && !strcmp(results[i], ptrs1[j])) {
  	if (first)
  	  first = 0;
  	else
  	  safe_str(osep, buff, bp);
! 	safe_str(ptrs2[j], buff, bp);
! 	ptrs2[j] = NULL;
  	break;
        }
      }
    }
    mush_free((Malloc_t) ptrs1, "ptrarray");
    mush_free((Malloc_t) ptrs2, "ptrarray");
    mush_free((Malloc_t) results, "ptrarray");
    free_anon_attrib(attrib);
  }
--- 273,300 ----
    results = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
    if (!results)
      mush_panic("Unable to allocate memory in fun_munge");
!   nresults = list2arr_ansi(results, MAX_SORTSIZE, rlist, sep);
  
    first = 1;
    for (i = 0; i < nresults; i++) {
      for (j = 0; j < nptrs1; j++) {
!       if (ptrs3[j] && !strcmp(results[i], ptrs1[j])) {
  	if (first)
  	  first = 0;
  	else
  	  safe_str(osep, buff, bp);
! 	safe_str(ptrs3[j], buff, bp);
! 	ptrs3[j] = NULL;
  	break;
        }
      }
    }
+   freearr(ptrs1, nptrs1);
+   freearr(ptrs2, nptrs2);
+   freearr(results, nresults);
    mush_free((Malloc_t) ptrs1, "ptrarray");
    mush_free((Malloc_t) ptrs2, "ptrarray");
+   mush_free((Malloc_t) ptrs3, "ptrarray");
    mush_free((Malloc_t) results, "ptrarray");
    free_anon_attrib(attrib);
  }
***************
*** 270,276 ****
  
    /* Turn the first list into an array. */
    strcpy(wordlist, args[0]);
!   nwords = list2arr(ptrs, MAX_SORTSIZE, wordlist, sep);
  
    s = trim_space_sep(args[1], ' ');
  
--- 330,336 ----
  
    /* Turn the first list into an array. */
    strcpy(wordlist, args[0]);
!   nwords = list2arr_ansi(ptrs, MAX_SORTSIZE, wordlist, sep);
  
    s = trim_space_sep(args[1], ' ');
  
***************
*** 290,295 ****
--- 350,356 ----
        safe_str(ptrs[cur], buff, bp);
      }
    }
+   freearr(ptrs, nwords);
    mush_free((Malloc_t) ptrs, "ptrarray");
    mush_free((Malloc_t) wordlist, "string");
  }
***************
*** 539,545 ****
     * Will take an optional delimiter argument.
     */
  
!   char *words[BUFFER_LEN / 2];
    int n, i, j;
    char sep;
    char *osep, osepd[2] = { '\0', '\0' };
--- 600,606 ----
     * Will take an optional delimiter argument.
     */
  
!   char *words[MAX_SORTSIZE];
    int n, i, j;
    char sep;
    char *osep, osepd[2] = { '\0', '\0' };
***************
*** 557,563 ****
    /* split the list up, or return if the list is empty */
    if (!*args[0])
      return;
!   n = list2arr(words, BUFFER_LEN / 2, args[0], sep);
  
    /* shuffle it */
    for (i = 0; i < n; i++) {
--- 618,624 ----
    /* split the list up, or return if the list is empty */
    if (!*args[0])
      return;
!   n = list2arr_ansi(words, MAX_SORTSIZE, args[0], sep);
  
    /* shuffle it */
    for (i = 0; i < n; i++) {
***************
*** 569,574 ****
--- 630,636 ----
    }
  
    arr2list(words, n, buff, bp, osep);
+   freearr(words, n);
  }
  
  typedef enum {
***************
*** 722,728 ****
  GENRECORD(gen_alphanum)
  {
    size_t len;
!   if (strchr(rec->val, ESC_CHAR)) {
      rec->str = mush_strdup(remove_markup(rec->val, &len), "genrecord");
      rec->freestr = 1;
    } else {
--- 784,790 ----
  GENRECORD(gen_alphanum)
  {
    size_t len;
!   if (strchr(rec->val, ESC_CHAR) || strchr(rec->val, TAG_START)) {
      rec->str = mush_strdup(remove_markup(rec->val, &len), "genrecord");
      rec->freestr = 1;
    } else {
***************
*** 1068,1077 ****
    } else
      strcpy(outsep, args[3]);
  
!   nptrs = list2arr(ptrs, MAX_SORTSIZE, args[0], sep);
    sort_type = get_list_type(args, nargs, 2, ptrs, nptrs);
    do_gensort(executor, ptrs, NULL, nptrs, sort_type);
    arr2list(ptrs, nptrs, buff, bp, outsep);
  }
  
  static void
--- 1130,1140 ----
    } else
      strcpy(outsep, args[3]);
  
!   nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[0], sep);
    sort_type = get_list_type(args, nargs, 2, ptrs, nptrs);
    do_gensort(executor, ptrs, NULL, nptrs, sort_type);
    arr2list(ptrs, nptrs, buff, bp, outsep);
+   freearr(ptrs, nptrs);
  }
  
  static void
***************
*** 1185,1191 ****
    *tp = '\0';
  
  
!   nptrs = list2arr(ptrs, MAX_SORTSIZE, args[1], sep);
  
    /* Now we make a list of keys */
    for (i = 0; i < nptrs; i++) {
--- 1248,1254 ----
    *tp = '\0';
  
  
!   nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[1], sep);
  
    /* Now we make a list of keys */
    for (i = 0; i < nptrs; i++) {
***************
*** 1202,1207 ****
--- 1265,1271 ----
    sort_type = get_list_type(args, nargs, 3, keys, nptrs);
    do_gensort(executor, keys, ptrs, nptrs, sort_type);
    arr2list(ptrs, nptrs, buff, bp, outsep);
+   freearr(ptrs, nptrs);
    for (i = 0; i < nptrs; i++) {
      mush_free(keys[i], "sortkey");
    }
***************
*** 1252,1262 ****
    save_global_env("sortby", tptr);
  
    /* Split up the list, sort it, reconstruct it. */
!   nptrs = list2arr(ptrs, MAX_SORTSIZE, args[1], sep);
    if (nptrs > 1)		/* pointless to sort less than 2 elements */
      sane_qsort((void *) ptrs, 0, nptrs - 1, u_comp);
  
    arr2list(ptrs, nptrs, buff, bp, osep);
  
    restore_global_env("sortby", tptr);
    free_anon_attrib(attrib);
--- 1316,1327 ----
    save_global_env("sortby", tptr);
  
    /* Split up the list, sort it, reconstruct it. */
!   nptrs = list2arr_ansi(ptrs, MAX_SORTSIZE, args[1], sep);
    if (nptrs > 1)		/* pointless to sort less than 2 elements */
      sane_qsort((void *) ptrs, 0, nptrs - 1, u_comp);
  
    arr2list(ptrs, nptrs, buff, bp, osep);
+   freearr(ptrs, nptrs);
  
    restore_global_env("sortby", tptr);
    free_anon_attrib(attrib);
***************
*** 1285,1292 ****
      mush_panic("Unable to allocate memory in fun_inter");
  
    /* make arrays out of the lists */
!   n1 = list2arr(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
--- 1350,1357 ----
      mush_panic("Unable to allocate memory in fun_inter");
  
    /* make arrays out of the lists */
!   n1 = list2arr_ansi(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr_ansi(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
***************
*** 1320,1325 ****
--- 1385,1392 ----
      if (val < 0) {
        x1++;
        if (x1 >= n1) {
+ 	freearr(a1, n1);
+ 	freearr(a2, n2);
  	mush_free((Malloc_t) a1, "ptrarray");
  	mush_free((Malloc_t) a2, "ptrarray");
  	return;
***************
*** 1327,1332 ****
--- 1394,1401 ----
      } else {
        x2++;
        if (x2 >= n2) {
+ 	freearr(a1, n1);
+ 	freearr(a2, n2);
  	mush_free((Malloc_t) a1, "ptrarray");
  	mush_free((Malloc_t) a2, "ptrarray");
  	return;
***************
*** 1337,1342 ****
--- 1406,1413 ----
    while (!gencomp(executor, a1[x1], a2[x2], sort_type)) {
      x1++;
      if (x1 >= n1) {
+       freearr(a1, n1);
+       freearr(a2, n2);
        mush_free((Malloc_t) a1, "ptrarray");
        mush_free((Malloc_t) a2, "ptrarray");
        return;
***************
*** 1349,1354 ****
--- 1420,1427 ----
        if (val < 0) {
  	x1++;
  	if (x1 >= n1) {
+ 	  freearr(a1, n1);
+ 	  freearr(a2, n2);
  	  mush_free((Malloc_t) a1, "ptrarray");
  	  mush_free((Malloc_t) a2, "ptrarray");
  	  return;
***************
*** 1356,1361 ****
--- 1429,1436 ----
        } else {
  	x2++;
  	if (x2 >= n2) {
+ 	  freearr(a1, n1);
+ 	  freearr(a2, n2);
  	  mush_free((Malloc_t) a1, "ptrarray");
  	  mush_free((Malloc_t) a2, "ptrarray");
  	  return;
***************
*** 1367,1378 ****
--- 1442,1457 ----
      while (!gencomp(executor, a1[x1], a2[x2], sort_type)) {
        x1++;
        if (x1 >= n1) {
+ 	freearr(a1, n1);
+ 	freearr(a2, n2);
  	mush_free((Malloc_t) a1, "ptrarray");
  	mush_free((Malloc_t) a2, "ptrarray");
  	return;
        }
      }
    }
+   freearr(a1, n1);
+   freearr(a2, n2);
    mush_free((Malloc_t) a1, "ptrarray");
    mush_free((Malloc_t) a2, "ptrarray");
  }
***************
*** 1401,1408 ****
      mush_panic("Unable to allocate memory in fun_setunion");
  
    /* make arrays out of the lists */
!   n1 = list2arr(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
--- 1480,1487 ----
      mush_panic("Unable to allocate memory in fun_setunion");
  
    /* make arrays out of the lists */
!   n1 = list2arr_ansi(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr_ansi(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
***************
*** 1510,1515 ****
--- 1589,1596 ----
        }
      }
    }
+   freearr(a1, n1);
+   freearr(a2, n2);
    mush_free((Malloc_t) a1, "ptrarray");
    mush_free((Malloc_t) a2, "ptrarray");
  }
***************
*** 1537,1544 ****
      mush_panic("Unable to allocate memory in fun_diff");
  
    /* make arrays out of the lists */
!   n1 = list2arr(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
--- 1618,1625 ----
      mush_panic("Unable to allocate memory in fun_diff");
  
    /* make arrays out of the lists */
!   n1 = list2arr_ansi(a1, MAX_SORTSIZE, args[0], sep);
!   n2 = list2arr_ansi(a2, MAX_SORTSIZE, args[1], sep);
  
    if (nargs < 4) {
      osepd[0] = sep;
***************
*** 1578,1583 ****
--- 1659,1666 ----
      if (!val) {
        x1++;
        if (x1 >= n1) {
+ 	freearr(a1, n1);
+ 	freearr(a2, n2);
  	mush_free((Malloc_t) a1, "ptrarray");
  	mush_free((Malloc_t) a2, "ptrarray");
  	return;
***************
*** 1588,1593 ****
--- 1671,1678 ----
    do {
      x1++;
      if (x1 >= n1) {
+       freearr(a1, n1);
+       freearr(a2, n2);
        mush_free((Malloc_t) a1, "ptrarray");
        mush_free((Malloc_t) a2, "ptrarray");
        return;
***************
*** 1604,1609 ****
--- 1689,1696 ----
        do {
  	x1++;
  	if (x1 >= n1) {
+ 	  freearr(a1, n1);
+ 	  freearr(a2, n2);
  	  mush_free((Malloc_t) a1, "ptrarray");
  	  mush_free((Malloc_t) a2, "ptrarray");
  	  return;
***************
*** 1622,1627 ****
--- 1709,1716 ----
        x1++;
      } while ((x1 < n1) && !gencomp(executor, a1[x1], a1[x1 - 1], sort_type));
    }
+   freearr(a1, n1);
+   freearr(a2, n2);
    mush_free((Malloc_t) a1, "ptrarray");
    mush_free((Malloc_t) a2, "ptrarray");
  }
***************
*** 1650,1656 ****
      mush_panic("Unable to allocate memory in fun_unique");
  
    /* make array out of the list */
!   n1 = list2arr(a1, MAX_SORTSIZE, args[0], sep);
  
    a2 = mush_malloc(n1 * sizeof(char *), "ptrarray");
    if (!a2)
--- 1739,1745 ----
      mush_panic("Unable to allocate memory in fun_unique");
  
    /* make array out of the list */
!   n1 = list2arr_ansi(a1, MAX_SORTSIZE, args[0], sep);
  
    a2 = mush_malloc(n1 * sizeof(char *), "ptrarray");
    if (!a2)
***************
*** 1687,1692 ****
--- 1776,1784 ----
      safe_str(a2[x1], buff, bp);
    }
  
+   freearr(a1, n1);
+   /* We don't freearr(a2) since it wasn't generated by
+    * list2arr, and is instead just pointers into a1 */
    mush_free(a1, "ptrarray");
    mush_free(a2, "ptrarray");
  
***************
*** 2020,2034 ****
    char *s, *r;
    char sep;
    int wcount = 1;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
  
    /* Walk the wordstring, until we find the word we want. */
!   s = trim_space_sep(args[0], sep);
    do {
      r = split_token(&s, sep);
!     if (quick_wild(args[1], r)) {
        safe_integer(wcount, buff, bp);
        return;
      }
--- 2112,2130 ----
    char *s, *r;
    char sep;
    int wcount = 1;
+   size_t len;
+   char needle[BUFFER_LEN];
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
  
+   strncpy(needle, remove_markup(args[1], &len), BUFFER_LEN);
+ 
    /* Walk the wordstring, until we find the word we want. */
!   s = trim_space_sep(remove_markup(args[0], &len), sep);
    do {
      r = split_token(&s, sep);
!     if (quick_wild(needle, r)) {
        safe_integer(wcount, buff, bp);
        return;
      }
***************
*** 2462,2492 ****
  /* ARGSUSED */
  FUNCTION(fun_before)
  {
!   char *p;
  
!   if (!*args[1])
!     p = strchr(args[0], ' ');
!   else
!     p = strstr(args[0], args[1]);
!   if (p) {
!     safe_strl(args[0], p - args[0], buff, bp);
!   } else
      safe_strl(args[0], arglens[0], buff, bp);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_after)
  {
!   char *p;
  
    if (!*args[1]) {
      args[1][0] = ' ';
      args[1][1] = '\0';
      arglens[1] = 1;
    }
!   p = strstr(args[0], args[1]);
!   if (p)
!     safe_str(p + arglens[1], buff, bp);
  }
  
  /* ARGSUSED */
--- 2558,2604 ----
  /* ARGSUSED */
  FUNCTION(fun_before)
  {
!   const char *p, *q;
!   ansi_string *as;
!   size_t len;
!   p = remove_markup(args[1], &len);
  
!   if (!*p)
!     p = " ";
!   as = parse_ansi_string(args[0]);
!   q = strstr(as->text, p);
!   if (q) {
!     safe_ansi_string(as, 0, q - as->text, buff, bp);
!   } else {
      safe_strl(args[0], arglens[0], buff, bp);
+   }
+   free_ansi_string(as);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_after)
  {
!   ansi_string *as;
!   char *p, *delim;
!   size_t len, count;
!   size_t start;
  
    if (!*args[1]) {
      args[1][0] = ' ';
      args[1][1] = '\0';
      arglens[1] = 1;
    }
!   delim = remove_markup(args[1], &len);
!   len--;
!   as = parse_ansi_string(args[0]);
! 
!   p = strstr(as->text, delim);
!   if (p) {
!     start = p - as->text + len;
!     count = as->len - start;
!     safe_ansi_string(as, start, count, buff, bp);
!   }
!   free_ansi_string(as);
  }
  
  /* ARGSUSED */
***************
*** 2509,2515 ****
  
    words = (char **) mush_malloc(sizeof(char *) * BUFFER_LEN, "wordlist");
  
!   count = list2arr(words, BUFFER_LEN, args[0], sep);
    if (count == 0) {
      mush_free((Malloc_t) words, "wordlist");
      return;
--- 2621,2627 ----
  
    words = (char **) mush_malloc(sizeof(char *) * BUFFER_LEN, "wordlist");
  
!   count = list2arr_ansi(words, BUFFER_LEN, args[0], sep);
    if (count == 0) {
      mush_free((Malloc_t) words, "wordlist");
      return;
***************
*** 2520,2525 ****
--- 2632,2638 ----
      safe_str(osep, buff, bp);
      safe_str(words[--count], buff, bp);
    }
+   freearr(words, count);
    mush_free((Malloc_t) words, "wordlist");
  }
  
***************
*** 2537,2577 ****
  FUNCTION(fun_splice)
  {
    /* like MERGE(), but does it for a word */
! 
!   char *s0, *s1, *s2;
!   char *p0, *p1;
    char sep;
  
    if (!delim_check(buff, bp, nargs, args, 4, &sep))
      return;
  
!   s0 = trim_space_sep(args[0], sep);
!   s1 = trim_space_sep(args[1], sep);
!   s2 = trim_space_sep(args[2], sep);
  
!   /* length checks */
!   if (!*args[2]) {
      safe_str(T("#-1 NEED A WORD"), buff, bp);
      return;
    }
!   if (do_wordcount(s2, sep) != 1) {
      safe_str(T("#-1 TOO MANY WORDS"), buff, bp);
      return;
    }
!   if (do_wordcount(s0, sep) != do_wordcount(s1, sep)) {
      safe_str(T("#-1 NUMBER OF WORDS MUST BE EQUAL"), buff, bp);
      return;
    }
!   /* loop through the two lists */
!   p0 = split_token(&s0, sep);
!   p1 = split_token(&s1, sep);
!   safe_str(strcmp(p0, s2) ? p0 : p1, buff, bp);
!   while (s0) {
!     p0 = split_token(&s0, sep);
!     p1 = split_token(&s1, sep);
!     safe_chr(sep, buff, bp);
!     safe_str(strcmp(p0, s2) ? p0 : p1, buff, bp);
    }
  }
  
  /* ARGSUSED */
--- 2650,2706 ----
  FUNCTION(fun_splice)
  {
    /* like MERGE(), but does it for a word */
!   char **orig;
!   char **repl;
!   char haystack[BUFFER_LEN];
!   int ocount, rcount;
!   int i;
    char sep;
+   char osep[2];
  
    if (!delim_check(buff, bp, nargs, args, 4, &sep))
      return;
  
!   osep[0] = sep;
!   osep[1] = '\0';
  
!   orig = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
!   repl = (char **) mush_malloc(MAX_SORTSIZE * sizeof(char *), "ptrarray");
!   /* Turn them into lists */
!   ocount = list2arr(orig, MAX_SORTSIZE, args[0], sep);
!   rcount = list2arr(repl, MAX_SORTSIZE, args[1], sep);
! 
!   strncpy(haystack, remove_markup(args[2], NULL), BUFFER_LEN);
!   if (!*haystack) {
      safe_str(T("#-1 NEED A WORD"), buff, bp);
+     mush_free((Malloc_t) orig, "ptrarray");
+     mush_free((Malloc_t) repl, "ptrarray");
      return;
    }
!   if (do_wordcount(haystack, sep) != 1) {
      safe_str(T("#-1 TOO MANY WORDS"), buff, bp);
+     mush_free((Malloc_t) orig, "ptrarray");
+     mush_free((Malloc_t) repl, "ptrarray");
      return;
    }
! 
!   if (ocount != rcount) {
      safe_str(T("#-1 NUMBER OF WORDS MUST BE EQUAL"), buff, bp);
+     mush_free((Malloc_t) orig, "ptrarray");
+     mush_free((Malloc_t) repl, "ptrarray");
      return;
    }
! 
!   for (i = 0; i < ocount; i++) {
!     if (!ansi_strcmp(orig[i], haystack)) {
!       orig[i] = repl[i];
!     }
    }
+ 
+   arr2list(orig, ocount, buff, bp, osep);
+ 
+   mush_free((Malloc_t) orig, "ptrarray");
+   mush_free((Malloc_t) repl, "ptrarray");
  }
  
  /* ARGSUSED */
***************
*** 3053,3071 ****
    int subpatterns;
    int offsets[99];
    int erroffset;
!   const char *r, *obp;
!   char *start, *oldbp;
!   char tbuf[BUFFER_LEN], *tbp;
!   char abuf[BUFFER_LEN], *abp;
!   char prebuf[BUFFER_LEN], *prep;
!   char postbuf[BUFFER_LEN], *postp;
    pcre *old_re_code;
    int old_re_subpatterns;
    int *old_re_offsets;
!   char *old_re_from;
!   int flags = 0, all = 0, match_offset = 0, len, funccount;
    int i;
  
    old_re_code = global_eval_context.re_code;
    old_re_subpatterns = global_eval_context.re_subpatterns;
    old_re_offsets = global_eval_context.re_offsets;
--- 3182,3207 ----
    int subpatterns;
    int offsets[99];
    int erroffset;
!   int flags = 0, all = 0, match_offset = 0;
! 
    pcre *old_re_code;
    int old_re_subpatterns;
    int *old_re_offsets;
!   ansi_string *old_re_from;
! 
    int i;
+   const char *r, *obp;
+   char *start, *oldbp;
+   char tbuf[BUFFER_LEN], *tbp;
+   char prebuf[BUFFER_LEN];
+   char postbuf[BUFFER_LEN], *postp;
+   ansi_string *orig, *repl;
+   int search;
+   int prelen;
+   size_t searchlen;
+   int funccount;
  
+   /* Save old regexp contexts */
    old_re_code = global_eval_context.re_code;
    old_re_subpatterns = global_eval_context.re_subpatterns;
    old_re_offsets = global_eval_context.re_offsets;
***************
*** 3077,3135 ****
    if (string_prefix(called_as, "REGEDITALL"))
      all = 1;
  
!   abp = abuf;
    r = args[0];
!   process_expression(abuf, &abp, &r, executor, caller, enactor, PE_DEFAULT,
  		     PT_DEFAULT, pe_info);
-   *abp = '\0';
- 
-   postp = postbuf;
-   safe_str(abuf, postbuf, &postp);
    *postp = '\0';
  
!   for (i = 1; i < nargs - 1; i += 2) {
!     /* old postbuf is new prebuf */
!     prep = prebuf;
!     safe_str(postbuf, prebuf, &prep);
!     *prep = '\0';
      postp = postbuf;
!     *postp = '\0';
      tbp = tbuf;
      r = args[i];
      process_expression(tbuf, &tbp, &r, executor, caller, enactor, PE_DEFAULT,
! 		       PT_DEFAULT, pe_info);
      *tbp = '\0';
  
!     if ((re = pcre_compile(tbuf, flags, &errptr, &erroffset, tables)) == NULL) {
        /* Matching error. */
        safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
        safe_str(errptr, buff, bp);
        return;
      }
!     add_check("pcre");
!     /* If we're going to match the pattern multiple times, let's
!        study it. */
      if (all) {
        study = pcre_study(re, 0, &errptr);
        if (errptr != NULL) {
! 	mush_free((Malloc_t) re, "pcre");
! 	safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
! 	safe_str(errptr, buff, bp);
! 	return;
        }
        if (study != NULL)
! 	add_check("pcre.extra");
      }
!     len = strlen(prebuf);
      start = prebuf;
!     subpatterns = pcre_exec(re, study, prebuf, len, 0, 0, offsets, 99);
  
      /* Match wasn't found... we're done */
      if (subpatterns < 0) {
        safe_str(prebuf, postbuf, &postp);
        mush_free((Malloc_t) re, "pcre");
        if (study)
! 	mush_free((Malloc_t) study, "pcre.extra");
        continue;
      }
  
--- 3213,3283 ----
    if (string_prefix(called_as, "REGEDITALL"))
      all = 1;
  
!   /* Build orig */
!   postp = postbuf;
    r = args[0];
!   process_expression(postbuf, &postp, &r, executor, caller, enactor, PE_DEFAULT,
  		     PT_DEFAULT, pe_info);
    *postp = '\0';
  
!   /* Ansi-less regedits */
!   for (i=1 ; i < nargs - 1; i+= 2) {
!     /* If this string has ANSI, switch to using ansi only */
!     if (strchr(postbuf,TAG_START)) break;
! 
!     memcpy(prebuf,postbuf,BUFFER_LEN);
!     prelen = strlen(prebuf);
! 
      postp = postbuf;
! 
!     orig = parse_ansi_string(prebuf);
! 
!     /* Get the needle */
      tbp = tbuf;
      r = args[i];
      process_expression(tbuf, &tbp, &r, executor, caller, enactor, PE_DEFAULT,
!                        PT_DEFAULT, pe_info);
      *tbp = '\0';
  
!     if ((re = pcre_compile(remove_markup(tbuf, &searchlen),
!                            flags, &errptr, &erroffset, tables)) == NULL) {
        /* Matching error. */
        safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
        safe_str(errptr, buff, bp);
+       free_ansi_string(orig);
        return;
      }
!     add_check("pcre");		/* re */
!     if (searchlen)
!       searchlen--;
! 
!     /* If we're doing a lot, study the regexp to make sure it's good */
      if (all) {
        study = pcre_study(re, 0, &errptr);
        if (errptr != NULL) {
!         mush_free((Malloc_t) re, "pcre");
!         safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
!         safe_str(errptr, buff, bp);
!         free_ansi_string(orig);
!         return;
        }
        if (study != NULL)
!         /* study */
!         add_check("pcre.extra");
      }
! 
!     search = 0;
!     /* Do all the searches and replaces we can */
! 
      start = prebuf;
!     subpatterns = pcre_exec(re, study, prebuf, prelen, 0, 0, offsets, 99);
  
      /* Match wasn't found... we're done */
      if (subpatterns < 0) {
        safe_str(prebuf, postbuf, &postp);
        mush_free((Malloc_t) re, "pcre");
        if (study)
!         mush_free((Malloc_t) study, "pcre.extra");
        continue;
      }
  
***************
*** 3142,3159 ****
        prebuf[offsets[0]] = '\0';
        safe_str(start, postbuf, &postp);
        prebuf[offsets[0]] = tmp;
- 
        /* Now copy in the replacement, putting in captured sub-expressions */
        obp = args[i + 1];
        global_eval_context.re_code = re;
!       global_eval_context.re_from = prebuf;
        global_eval_context.re_offsets = offsets;
        global_eval_context.re_subpatterns = subpatterns;
        process_expression(postbuf, &postp, &obp, executor, caller, enactor,
! 			 PE_DEFAULT | PE_DOLLAR, PT_DEFAULT, pe_info);
        if ((*bp == (buff + BUFFER_LEN - 1))
! 	  && (pe_info->fun_invocations == funccount))
! 	break;
  
        oldbp = postp;
        funccount = pe_info->fun_invocations;
--- 3290,3306 ----
        prebuf[offsets[0]] = '\0';
        safe_str(start, postbuf, &postp);
        prebuf[offsets[0]] = tmp;
        /* Now copy in the replacement, putting in captured sub-expressions */
        obp = args[i + 1];
        global_eval_context.re_code = re;
!       global_eval_context.re_from = orig;
        global_eval_context.re_offsets = offsets;
        global_eval_context.re_subpatterns = subpatterns;
        process_expression(postbuf, &postp, &obp, executor, caller, enactor,
!           PE_DEFAULT | PE_DOLLAR, PT_DEFAULT, pe_info);
        if ((*bp == (buff + BUFFER_LEN - 1))
!           && (pe_info->fun_invocations == funccount))
!         break;
  
        oldbp = postp;
        funccount = pe_info->fun_invocations;
***************
*** 3162,3188 ****
        match_offset = offsets[1];
        /* Make sure we advance at least 1 char */
        if (offsets[0] == match_offset)
! 	match_offset++;
!     } while (all && match_offset < len && (subpatterns =
! 					   pcre_exec(re, study, prebuf, len,
! 						     match_offset, 0, offsets,
! 						     99)) >= 0);
! 
  
-     /* Now copy everything after the matched bit */
      safe_str(start, postbuf, &postp);
      *postp = '\0';
      mush_free((Malloc_t) re, "pcre");
!     if (study)
        mush_free((Malloc_t) study, "pcre.extra");
  
!     global_eval_context.re_code = old_re_code;
!     global_eval_context.re_offsets = old_re_offsets;
!     global_eval_context.re_subpatterns = old_re_subpatterns;
!     global_eval_context.re_from = old_re_from;
    }
  
!   safe_str(postbuf, buff, bp);
  }
  
  /** array of indexes for %q registers during regexp matching */
--- 3309,3426 ----
        match_offset = offsets[1];
        /* Make sure we advance at least 1 char */
        if (offsets[0] == match_offset)
!         match_offset++;
!     } while (all && match_offset < prelen &&
!                   (subpatterns = pcre_exec(re, study, prebuf, prelen,
!                    match_offset, 0, offsets, 99)) >= 0);
  
      safe_str(start, postbuf, &postp);
      *postp = '\0';
+ 
      mush_free((Malloc_t) re, "pcre");
!     if (study != NULL)
        mush_free((Malloc_t) study, "pcre.extra");
+     free_ansi_string(orig);
+   }
+ 
+   /* We get to this point if there is ansi in an 'orig' string */
+   if (i < nargs - 1) {
  
!     orig = parse_ansi_string(postbuf);
! 
!     /* For each search/replace pair, compare them against orig */
!     for (; i < nargs - 1; i += 2) {
! 
!       /* Get the needle */
!       tbp = tbuf;
!       r = args[i];
!       process_expression(tbuf, &tbp, &r, executor, caller, enactor, PE_DEFAULT,
!                          PT_DEFAULT, pe_info);
!       *tbp = '\0';
! 
!       if ((re = pcre_compile(remove_markup(tbuf, &searchlen),
!                              flags, &errptr, &erroffset, tables)) == NULL) {
!         /* Matching error. */
!         safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
!         safe_str(errptr, buff, bp);
!         free_ansi_string(orig);
!         return;
!       }
!       add_check("pcre");		/* re */
!       if (searchlen)
!         searchlen--;
! 
!       /* If we're doing a lot, study the regexp to make sure it's good */
!       if (all) {
!         study = pcre_study(re, 0, &errptr);
!         if (errptr != NULL) {
!           mush_free((Malloc_t) re, "pcre");
!           safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
!           safe_str(errptr, buff, bp);
!           free_ansi_string(orig);
!           return;
!         }
!         if (study != NULL)
!           /* study */
!           add_check("pcre.extra");
!       }
!       search = 0;
!       /* Do all the searches and replaces we can */
!       do {
!         subpatterns =
!           pcre_exec(re, study, orig->text, orig->len, search, 0, offsets, 99);
!         if (subpatterns >= 0) {
!           /* We have a match */
!           /* Process the replacement */
!           r = args[i + 1];
!           global_eval_context.re_code = re;
!           global_eval_context.re_from = orig;
!           global_eval_context.re_offsets = offsets;
!           global_eval_context.re_subpatterns = subpatterns;
!           tbp = tbuf;
!           process_expression(tbuf, &tbp, &r, executor, caller, enactor,
!                              PE_DEFAULT | PE_DOLLAR, PT_DEFAULT, pe_info);
!           *tbp = '\0';
!           if (offsets[0] >= search) {
!             repl = parse_ansi_string(tbuf);
! 
!             /* Do the replacement */
!             ansi_string_replace(orig, offsets[0], offsets[1] - offsets[0], repl,
!                                 0, repl->len);
! 
!             /* Advance search */
!             if (search == offsets[1]) {
!               search = offsets[0] + repl->len;
!               search++;
!             } else {
!               search = offsets[0] + repl->len;
!             }
!             /* if (offsets[0] < 1) search++; */
! 
!             free_ansi_string(repl);
!             if (search >= orig->len)
!               break;
!           } else {
!             break;
!           }
!         }
!       } while (subpatterns >= 0 && all);
!       mush_free((Malloc_t) re, "pcre");
!       if (study != NULL)
!         mush_free((Malloc_t) study, "pcre.extra");
!     }
!     safe_ansi_string(orig, 0, orig->len, buff, bp);
!     free_ansi_string(orig);
!   } else {
!     safe_str(postbuf, buff, bp);
    }
  
!   /* Restore old regexp contexts */
!   global_eval_context.re_code = old_re_code;
!   global_eval_context.re_offsets = old_re_offsets;
!   global_eval_context.re_subpatterns = old_re_subpatterns;
!   global_eval_context.re_from = old_re_from;
! 
  }
  
  /** array of indexes for %q registers during regexp matching */
***************
*** 3208,3230 ****
    int subpatterns;
    int flags = 0;
    int qindex;
  
    if (strcmp(called_as, "REGMATCHI") == 0)
      flags = PCRE_CASELESS;
  
    if (nargs == 2) {		/* Don't care about saving sub expressions */
!     safe_boolean(quick_regexp_match(args[1], args[0], flags ? 0 : 1), buff, bp);
      return;
    }
  
!   if ((re = pcre_compile(args[1], flags, &errptr, &erroffset, tables)) == NULL) {
      /* Matching error. */
      safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
      safe_str(errptr, buff, bp);
      return;
    }
    add_check("pcre");
!   subpatterns = pcre_exec(re, NULL, args[0], arglens[0], 0, 0, offsets, 99);
    safe_integer(subpatterns >= 0, buff, bp);
  
    /* We need to parse the list of registers.  Anything that we don't parse
--- 3446,3479 ----
    int subpatterns;
    int flags = 0;
    int qindex;
+   ansi_string *as;
+   char *txt;
+   char *qptr;
+   char *needle;
+   size_t len;
  
    if (strcmp(called_as, "REGMATCHI") == 0)
      flags = PCRE_CASELESS;
  
+   needle = remove_markup(args[1], &len);
+ 
+   as = parse_ansi_string(args[0]);
+   txt = as->text;
    if (nargs == 2) {		/* Don't care about saving sub expressions */
!     safe_boolean(quick_regexp_match(needle, txt, flags ? 0 : 1), buff, bp);
!     free_ansi_string(as);
      return;
    }
  
!   if ((re = pcre_compile(needle, flags, &errptr, &erroffset, tables)) == NULL) {
      /* Matching error. */
      safe_str(T("#-1 REGEXP ERROR: "), buff, bp);
      safe_str(errptr, buff, bp);
+     free_ansi_string(as);
      return;
    }
    add_check("pcre");
!   subpatterns = pcre_exec(re, NULL, txt, arglens[0], 0, 0, offsets, 99);
    safe_integer(subpatterns >= 0, buff, bp);
  
    /* We need to parse the list of registers.  Anything that we don't parse
***************
*** 3235,3240 ****
--- 3484,3491 ----
    if (subpatterns == 0)
      subpatterns = 33;
    nqregs = list2arr(qregs, NUMQ, args[2], ' ');
+ 
+   /* Initialize every q-register used to '' */
    for (i = 0; i < nqregs; i++) {
      char *regname;
      char *named_subpattern = NULL;
***************
*** 3259,3276 ****
        curq = -1;
      if (curq < 0 || curq >= NUMQ)
        continue;
  
!     if (subpatterns < 0)
!       global_eval_context.renv[curq][0] = '\0';
!     else if (named_subpattern)
!       pcre_copy_named_substring(re, args[0], offsets, subpatterns,
! 				named_subpattern,
! 				global_eval_context.renv[curq], BUFFER_LEN);
      else
!       pcre_copy_substring(args[0], offsets, subpatterns, subpattern,
! 			  global_eval_context.renv[curq], BUFFER_LEN);
    }
    mush_free((Malloc_t) re, "pcre");
  }
  
  
--- 3510,3563 ----
        curq = -1;
      if (curq < 0 || curq >= NUMQ)
        continue;
+     *(global_eval_context.renv[curq]) = '\0';
+   }
+   /* Now, only for those that have a pattern, copy text */
+   for (i = 0; i < nqregs; i++) {
+     char *regname;
+     char *named_subpattern = NULL;
+     int subpattern = 0;
+     if ((regname = strchr(qregs[i], ':'))) {
+       /* subexpr:register */
+       *regname++ = '\0';
+       if (is_strict_integer(qregs[i]))
+ 	subpattern = parse_integer(qregs[i]);
+       else
+ 	named_subpattern = qregs[i];
+     } else {
+       /* Get subexper by position in list */
+       subpattern = i;
+       regname = qregs[i];
+     }
  
!     if (regname && regname[0] && !regname[1] &&
! 	((qindex = qreg_indexes[(unsigned char) regname[0]]) != -1))
!       curq = qindex;
      else
!       curq = -1;
!     if (curq < 0 || curq >= NUMQ)
!       continue;
! 
!     if (subpatterns < 0) {
!       global_eval_context.renv[curq][0] = '\0';
!     } else if (named_subpattern) {
!       qptr = global_eval_context.renv[curq];
!       ansi_pcre_copy_named_substring(re, as, offsets, subpatterns,
! 				     named_subpattern, 1,
! 				     global_eval_context.renv[curq], &qptr);
! 
!       if (qptr != global_eval_context.renv[curq])
! 	*qptr = '\0';
!     } else {
!       qptr = global_eval_context.renv[curq];
!       ansi_pcre_copy_substring(as, offsets, subpatterns, subpattern, 1,
! 			       global_eval_context.renv[curq], &qptr);
!       if (qptr != global_eval_context.renv[curq])
! 	*qptr = '\0';
!     }
    }
    mush_free((Malloc_t) re, "pcre");
+   free_ansi_string(as);
  }
  
  
***************
*** 3348,3357 ****
  {
    struct regrep_data *reharg = args;
    char const *str;
    int offsets[99];
  
!   str = atr_value(atr);
!   if (pcre_exec(reharg->re, reharg->study, str, strlen(str), 0, 0, offsets, 99)
        >= 0) {
      if (reharg->first != 0)
        safe_chr(' ', reharg->buff, reharg->bp);
--- 3635,3645 ----
  {
    struct regrep_data *reharg = args;
    char const *str;
+   size_t slen;
    int offsets[99];
  
!   str = remove_markup(atr_value(atr), &slen);
!   if (pcre_exec(reharg->re, reharg->study, str, slen - 1, 0, 0, offsets, 99)
        >= 0) {
      if (reharg->first != 0)
        safe_chr(' ', reharg->buff, reharg->bp);
***************
*** 3368,3373 ****
--- 3656,3662 ----
  FUNCTION(fun_regrab)
  {
    char *r, *s, *b, sep;
+   size_t rlen;
    pcre *re;
    pcre_extra *study;
    const char *errptr;
***************
*** 3414,3421 ****
      add_check("pcre.extra");
  
    do {
!     r = split_token(&s, sep);
!     if (pcre_exec(re, study, r, strlen(r), 0, 0, offsets, 99) >= 0) {
        if (all && *bp != b)
  	safe_str(osep, buff, bp);
        safe_str(r, buff, bp);
--- 3703,3710 ----
      add_check("pcre.extra");
  
    do {
!     r = remove_markup(split_token(&s, sep), &rlen);
!     if (pcre_exec(re, study, r, rlen - 1, 0, 0, offsets, 99) >= 0) {
        if (all && *bp != b)
  	safe_str(osep, buff, bp);
        safe_str(r, buff, bp);
*** 1_8_2p1/src/funmath.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/funmath.c	Sat Jan 27 02:21:10 2007
***************
*** 433,440 ****
    NVAL a, b;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 433,442 ----
    NVAL a, b;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 442,449 ****
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2)
      return;
  
    /* max the vectors */
    start = *bp;
--- 444,453 ----
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    /* max the vectors */
    start = *bp;
***************
*** 475,482 ****
    NVAL a, b;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 479,488 ----
    NVAL a, b;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 517,524 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 523,532 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 526,533 ****
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2)
      return;
  
    /* add the vectors */
    start = *bp;
--- 534,543 ----
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    /* add the vectors */
    start = *bp;
***************
*** 556,563 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 566,575 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 565,572 ****
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2)
      return;
  
    /* subtract the vectors */
    start = *bp;
--- 577,586 ----
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    /* subtract the vectors */
    start = *bp;
***************
*** 595,602 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 609,618 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 604,611 ****
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2)
      return;
  
    /* multiply the vectors */
    start = *bp;
--- 620,629 ----
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    /* multiply the vectors */
    start = *bp;
***************
*** 652,659 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
--- 670,679 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0] || !args[1]) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep))
      return;
***************
*** 661,668 ****
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2)
      return;
  
    /* multiply the vectors */
    product = 0;
--- 681,690 ----
    p2 = trim_space_sep(args[1], sep);
  
    /* return if a list is empty */
!   if (!*p1 || !*p2) {
!     safe_str(T("#-1 VECTORS MUST BE SAME DIMENSIONS"), buff, bp);
      return;
+   }
  
    /* multiply the vectors */
    product = 0;
***************
*** 685,700 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
    p1 = trim_space_sep(args[0], sep);
  
    /* return if a list is empty */
!   if (!*p1)
      return;
  
    /* sum the squares */
    num = parse_number(split_token(&p1, sep));
--- 707,726 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0]) {
!     safe_str(T("#-1 VECTOR MUST NOT BE EMPTY"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
    p1 = trim_space_sep(args[0], sep);
  
    /* return if a list is empty */
!   if (!*p1) {
!     safe_str(T("#-1 VECTOR MUST NOT BE EMPTY"), buff, bp);
      return;
+   }
  
    /* sum the squares */
    num = parse_number(split_token(&p1, sep));
***************
*** 716,731 ****
    char sep;
  
    /* return if a list is empty */
!   if (!args[0])
      return;
  
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
    p1 = trim_space_sep(args[0], sep);
  
    /* return if a list is empty */
!   if (!*p1)
      return;
  
    /* copy the vector, since we have to walk it twice... */
    strcpy(tbuf, p1);
--- 742,761 ----
    char sep;
  
    /* return if a list is empty */
!   if (!args[0]) {
!     safe_str(T("#-1 VECTOR MUST NOT BE EMPTY"), buff, bp);
      return;
+   }
  
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
    p1 = trim_space_sep(args[0], sep);
  
    /* return if a list is empty */
!   if (!*p1) {
!     safe_str(T("#-1 VECTOR MUST NOT BE EMPTY"), buff, bp);
      return;
+   }
  
    /* copy the vector, since we have to walk it twice... */
    strcpy(tbuf, p1);
***************
*** 1652,1665 ****
    char **ptr;
    MATH *op;
  
-   /* Allocate memory */
-   ptr = (char **) mush_malloc(BUFFER_LEN, "string");
- 
    if (!delim_check(buff, bp, nargs, args, 3, &sep)) {
-     mush_free((Malloc_t) ptr, "string");
      return;
    }
  
    nptr = list2arr(ptr, BUFFER_LEN, args[1], sep);
  
    op = math_hash_lookup(args[0]);
--- 1682,1694 ----
    char **ptr;
    MATH *op;
  
    if (!delim_check(buff, bp, nargs, args, 3, &sep)) {
      return;
    }
  
+   /* Allocate memory */
+   ptr = (char **) mush_malloc(sizeof(char *) * BUFFER_LEN, "string");
+ 
    nptr = list2arr(ptr, BUFFER_LEN, args[1], sep);
  
    op = math_hash_lookup(args[0]);
*** 1_8_2p1/src/funmisc.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/funmisc.c	Sat Jan 27 02:21:10 2007
***************
*** 27,32 ****
--- 27,33 ----
  #include "game.h"
  #include "attrib.h"
  #include "confmagic.h"
+ #include "ansi.h"
  
  #ifdef WIN32
  #pragma warning( disable : 4761)	/* NJG: disable warning re conversion */
*** 1_8_2p1/src/funstr.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/funstr.c	Sat Jan 27 05:24:13 2007
***************
*** 13,20 ****
  #include <limits.h>
  #include <locale.h>
  #include "conf.h"
- #include "ansi.h"
  #include "externs.h"
  #include "case.h"
  #include "match.h"
  #include "parse.h"
--- 13,20 ----
  #include <limits.h>
  #include <locale.h>
  #include "conf.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "case.h"
  #include "match.h"
  #include "parse.h"
***************
*** 32,39 ****
  #pragma warning( disable : 4761)	/* NJG: disable warning re conversion */
  #endif
  
- HASHTAB htab_tag;  /**< Hash table of safe html tags */
- 
  #define MAX_COLS 32  /**< Maximum number of columns for align() */
  static int wraplen(char *str, int maxlen);
  static int align_one_line(char *buff, char **bp, int ncols,
--- 32,37 ----
***************
*** 42,50 ****
  			  int linenum, char *fieldsep, int fslen, char *linesep,
  			  int lslen, char filler);
  static int comp_gencomp(dbref executor, char *left, char *right, char *type);
- void init_tag_hashtab(void);
  void init_pronouns(void);
  
  /** Return an indicator of a player's gender.
   * \param player player whose gender is to be checked.
   * \retval 0 neuter.
--- 40,49 ----
  			  int linenum, char *fieldsep, int fslen, char *linesep,
  			  int lslen, char filler);
  static int comp_gencomp(dbref executor, char *left, char *right, char *type);
  void init_pronouns(void);
  
+ extern signed char qreg_indexes[UCHAR_MAX + 1];
+ 
  /** Return an indicator of a player's gender.
   * \param player player whose gender is to be checked.
   * \retval 0 neuter.
***************
*** 353,362 ****
    }
  
    as = parse_ansi_string(args[0]);
!   if ((size_t) len > as->len)
      safe_strl(args[0], arglens[0], buff, bp);
    else
!     safe_ansi_string(as, as->len - len, as->len, buff, bp);
    free_ansi_string(as);
  }
  
--- 352,361 ----
    }
  
    as = parse_ansi_string(args[0]);
!   if (len > as->len)
      safe_strl(args[0], arglens[0], buff, bp);
    else
!     safe_ansi_string(as, as->len - len, len, buff, bp);
    free_ansi_string(as);
  }
  
***************
*** 364,370 ****
  FUNCTION(fun_strinsert)
  {
    /* Insert a string into another */
!   ansi_string *as;
    int pos;
  
    if (!is_integer(args[1])) {
--- 363,369 ----
  FUNCTION(fun_strinsert)
  {
    /* Insert a string into another */
!   ansi_string *dst, *src;
    int pos;
  
    if (!is_integer(args[1])) {
***************
*** 378,398 ****
      return;
    }
  
!   as = parse_ansi_string(args[0]);
! 
!   if ((size_t) pos > as->len) {
!     /* Fast special case - concatenate args[2] to args[0] */
      safe_strl(args[0], arglens[0], buff, bp);
      safe_strl(args[2], arglens[0], buff, bp);
!     free_ansi_string(as);
      return;
    }
  
!   safe_ansi_string(as, 0, pos, buff, bp);
!   safe_strl(args[2], arglens[2], buff, bp);
!   safe_ansi_string(as, pos, as->len, buff, bp);
!   free_ansi_string(as);
  
  }
  
  /* ARGSUSED */
--- 377,398 ----
      return;
    }
  
!   dst = parse_ansi_string(args[0]);
!   if (pos > dst->len) {
      safe_strl(args[0], arglens[0], buff, bp);
      safe_strl(args[2], arglens[0], buff, bp);
!     free_ansi_string(dst);
      return;
    }
  
!   src = parse_ansi_string(args[2]);
! 
!   ansi_string_insert(dst, pos, src, 0, src->len);
  
+   safe_ansi_string(dst, 0, dst->len, buff, bp);
+ 
+   free_ansi_string(dst);
+   free_ansi_string(src);
  }
  
  /* ARGSUSED */
***************
*** 416,422 ****
  
    as = parse_ansi_string(args[0]);
  
!   if ((size_t) pos > as->len || num == 0) {
      safe_strl(args[0], arglens[0], buff, bp);
      free_ansi_string(as);
      return;
--- 416,422 ----
  
    as = parse_ansi_string(args[0]);
  
!   if (pos > as->len || num == 0) {
      safe_strl(args[0], arglens[0], buff, bp);
      free_ansi_string(as);
      return;
***************
*** 430,447 ****
    } else
      pos2 = pos + num;
  
! 
! 
!   safe_ansi_string(as, 0, pos, buff, bp);
!   safe_ansi_string(as, pos2, as->len, buff, bp);
    free_ansi_string(as);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_strreplace)
  {
!   ansi_string *as, *anew;
!   int start, len, end;
  
    if (!is_integer(args[1]) || !is_integer(args[2])) {
      safe_str(T(e_ints), buff, bp);
--- 430,445 ----
    } else
      pos2 = pos + num;
  
!   ansi_string_delete(as, pos, num);
!   safe_ansi_string(as, 0, as->len, buff, bp);
    free_ansi_string(as);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_strreplace)
  {
!   ansi_string *dst, *src;
!   int start, len;
  
    if (!is_integer(args[1]) || !is_integer(args[2])) {
      safe_str(T(e_ints), buff, bp);
***************
*** 456,475 ****
      return;
    }
  
!   as = parse_ansi_string(args[0]);
!   anew = parse_ansi_string(args[3]);
! 
!   safe_ansi_string(as, 0, start, buff, bp);
!   safe_ansi_string(anew, 0, anew->len, buff, bp);
! 
!   end = start + len;
! 
!   if ((size_t) end < as->len)
!     safe_ansi_string(as, end, as->len - end, buff, bp);
! 
!   free_ansi_string(as);
!   free_ansi_string(anew);
  
  }
  
  static int
--- 454,467 ----
      return;
    }
  
!   dst = parse_ansi_string(args[0]);
!   src = parse_ansi_string(args[3]);
  
+   ansi_string_delete(dst, start, len);
+   ansi_string_insert(dst, start, src, 0, src->len);
+   safe_ansi_string(dst, 0, dst->len, buff, bp);
+   free_ansi_string(dst);
+   free_ansi_string(src);
  }
  
  static int
***************
*** 577,583 ****
    int first = 1;
  
    if (args[1][0])
!     c = args[1][0];
  
    pos = remove_markup(args[0], &len);
    for (n = 0; n < len; n++)
--- 569,575 ----
    int first = 1;
  
    if (args[1][0])
!     c = remove_markup(args[1], &len)[0];
  
    pos = remove_markup(args[0], &len);
    for (n = 0; n < len; n++)
***************
*** 595,607 ****
  FUNCTION(fun_strmatch)
  {
    char tbuf[BUFFER_LEN];
    char *t;
    size_t len;
    /* matches a wildcard pattern for an _entire_ string */
  
    t = remove_markup(args[0], &len);
    memcpy(tbuf, t, len);
!   safe_boolean(quick_wild(remove_markup(args[1], NULL), tbuf), buff, bp);
  }
  
  /* ARGSUSED */
--- 587,621 ----
  FUNCTION(fun_strmatch)
  {
    char tbuf[BUFFER_LEN];
+   char *ret[36];
    char *t;
    size_t len;
+   int matches;
+   int i;
+   char *qregs[NUMQ];
+   int nqregs;
+   int qindex;
+ 
    /* matches a wildcard pattern for an _entire_ string */
  
    t = remove_markup(args[0], &len);
    memcpy(tbuf, t, len);
!   matches = wild_match_case_r(remove_markup(args[1], NULL), tbuf, 0, ret, 36);
!   safe_boolean(matches, buff, bp);
! 
!   if (nargs > 2) {
!     /* Now, assign the captures if desired */
!     nqregs = list2arr(qregs, NUMQ, args[2], ' ');
! 
!     for (i = 0; i < nqregs; i++) {
!       if (ret[i] && qregs[i][0] && !qregs[i][1]) {
! 	qindex = qreg_indexes[(unsigned char) qregs[i][0]];
! 	if (qindex >= 0 && global_eval_context.renv[qindex]) {
! 	  strcpy(global_eval_context.renv[qindex], ret[i]);
! 	}
!       }
!     }
!   }
  }
  
  /* ARGSUSED */
***************
*** 632,645 ****
     * char in s2.
     */
  
!   char *str, *rep;
    char matched[UCHAR_MAX + 1];
! 
!   /* do length checks first */
!   if (arglens[0] != arglens[1]) {
!     safe_str(T("#-1 STRING LENGTHS MUST BE EQUAL"), buff, bp);
!     return;
!   }
  
    memset(matched, 0, sizeof matched);
  
--- 646,656 ----
     * char in s2.
     */
  
!   int i, j;
!   size_t len;
!   char *ptr = args[0];
    char matched[UCHAR_MAX + 1];
!   ansi_string *as;
  
    memset(matched, 0, sizeof matched);
  
***************
*** 648,662 ****
      matched[(unsigned char) ' '] = 1;
    else {
      unsigned char *p;
!     for (p = (unsigned char *) args[2]; p && *p; p++)
        matched[*p] = 1;
    }
  
    /* walk strings, copy from the appropriate string */
!   for (str = args[0], rep = args[1]; *str && *rep; str++, rep++) {
!     *str = matched[(unsigned char) *str] ? *rep : *str;
    }
!   safe_str(args[0], buff, bp);
  }
  
  /* ARGSUSED */
--- 659,728 ----
      matched[(unsigned char) ' '] = 1;
    else {
      unsigned char *p;
!     for (p = (unsigned char *) remove_markup(args[2], &len); p && *p; p++)
        matched[*p] = 1;
    }
  
+   as = parse_ansi_string(args[1]);
+ 
+   /* do length checks first */
+   if (as->len != ansi_strlen(ptr)) {
+     safe_str(T("#-1 STRING LENGTHS MUST BE EQUAL"), buff, bp);
+     free_ansi_string(as);
+     return;
+   }
+ 
    /* walk strings, copy from the appropriate string */
!   i = 0;
!   ptr = args[0];
!   while (*ptr) {
!     switch (*ptr) {
!     case ESC_CHAR:
!       while (*ptr && *ptr != 'm') {
! 	safe_chr(*(ptr++), buff, bp);
!       }
!       safe_chr(*(ptr++), buff, bp);
!       break;
!     case TAG_START:
!       while (*ptr && *ptr != TAG_END) {
! 	safe_chr(*(ptr++), buff, bp);
!       }
!       safe_chr(*(ptr++), buff, bp);
!       break;
!     default:
!       if (matched[(unsigned char) *ptr]) {
! 	j = 0;
! 	while (*ptr && matched[(unsigned char) *ptr]) {
! 	  ptr++;
! 	  j++;
! 	  switch (*ptr) {
! 	  case ESC_CHAR:
! 	    safe_ansi_string(as, i, j, buff, bp);
! 	    i += j;
! 	    j = 0;
! 	    while (*ptr && *ptr != 'm')
! 	      safe_chr(*(ptr++), buff, bp);
! 	    break;
! 	  case TAG_START:
! 	    safe_ansi_string(as, i, j, buff, bp);
! 	    i += j;
! 	    j = 0;
! 	    while (*ptr && *ptr != TAG_END)
! 	      safe_chr(*(ptr++), buff, bp);
! 	    break;
! 	  }
! 	}
! 	if (j != 0) {
! 	  safe_ansi_string(as, i, j, buff, bp);
! 	  i += j;
! 	}
!       } else {
! 	i++;
! 	safe_chr(*(ptr++), buff, bp);
!       }
!     }
    }
!   free_ansi_string(as);
  }
  
  /* ARGSUSED */
***************
*** 668,737 ****
  
    char charmap[256];
    char instr[BUFFER_LEN], outstr[BUFFER_LEN];
-   char rawstr[BUFFER_LEN];
    char *ip, *op;
    size_t i, len;
    char *c;
    ansi_string *as;
  
!   /* No ansi allowed in find or replace lists */
!   c = remove_markup(args[1], &len);
!   memcpy(rawstr, c, len);
! 
!   /* do length checks first */
! 
    for (i = 0; i < 256; i++) {
      charmap[i] = (char) i;
    }
  
    ip = instr;
!   op = outstr;
! 
!   for (i = 0; i < len; i++) {
!     safe_chr(rawstr[i], instr, &ip);
!     /* Handle a range of characters */
!     if (i != len - 1 && rawstr[i + 1] == '-' && i != len - 2) {
!       int dir, sentinel, cur;
! 
!       if (rawstr[i] < rawstr[i + 2])
! 	dir = 1;
!       else
! 	dir = -1;
! 
!       sentinel = rawstr[i + 2] + dir;
!       cur = rawstr[i] + dir;
! 
!       while (cur != sentinel) {
! 	safe_chr((char) cur, instr, &ip);
! 	cur += dir;
        }
!       i += 2;
      }
    }
  
!   c = remove_markup(args[2], &len);
!   memcpy(rawstr, c, len);
!   for (i = 0; i < len; i++) {
!     safe_chr(rawstr[i], outstr, &op);
!     /* Handle a range of characters */
!     if (i != len - 1 && rawstr[i + 1] == '-' && i != len - 2) {
!       int dir, sentinel, cur;
! 
!       if (rawstr[i] < rawstr[i + 2])
! 	dir = 1;
!       else
! 	dir = -1;
! 
!       sentinel = rawstr[i + 2] + dir;
!       cur = rawstr[i] + dir;
! 
!       while (cur != sentinel) {
! 	safe_chr((char) cur, outstr, &op);
! 	cur += dir;
        }
!       i += 2;
      }
    }
  
    if ((ip - instr) != (op - outstr)) {
      safe_str(T("#-1 STRING LENGTHS MUST BE EQUAL"), buff, bp);
--- 734,827 ----
  
    char charmap[256];
    char instr[BUFFER_LEN], outstr[BUFFER_LEN];
    char *ip, *op;
    size_t i, len;
+   unsigned char cur, dest;
    char *c;
    ansi_string *as;
  
!   /* Initialize */
    for (i = 0; i < 256; i++) {
      charmap[i] = (char) i;
    }
  
+ #define goodchr(x) (isprint(x) || (x == '\n'))
+   /* Convert ranges in input string, and check that
+    * we don't receive a nonprinting char such as
+    * beep() */
    ip = instr;
!   c = remove_markup(args[1], NULL);
!   while (*c) {
!     cur = (unsigned char) *c;
!     if (!goodchr(cur)) {
!       safe_str(T("#-1 TR CANNOT ACCEPT NONPRINTING CHARS"), buff, bp);
!       return;
!     }
!     /* Tack it onto the string */
!     /* Do we have a range? */
!     if (*(c + 1) && *(c + 1) == '-' && *(c + 2)) {
!       dest = (unsigned char) *(c + 2);
!       if (!goodchr(dest)) {
! 	safe_str(T("#-1 TR CANNOT ACCEPT NONPRINTING CHARS"), buff, bp);
! 	return;
        }
!       if (dest > cur) {
! 	for (; cur <= dest; cur++) {
! 	  if (goodchr(cur))
! 	    safe_chr(cur, instr, &ip);
! 	}
!       } else {
! 	for (; cur >= dest; cur--) {
! 	  if (goodchr(cur))
! 	    safe_chr(cur, instr, &ip);
! 	}
!       }
!       c += 3;
!     } else {
!       safe_chr(cur, instr, &ip);
!       c++;
      }
    }
+   *ip = '\0';
  
!   /* Convert ranges in output string, and check that
!    * we don't receive a nonprinting char such as
!    * beep() */
!   op = outstr;
!   c = remove_markup(args[2], NULL);
!   while (*c) {
!     cur = (unsigned char) *c;
!     if (!goodchr(cur)) {
!       safe_str(T("#-1 TR CANNOT ACCEPT NONPRINTING CHARS"), buff, bp);
!       return;
!     }
!     /* Tack it onto the string */
!     /* Do we have a range? */
!     if (*(c + 1) && *(c + 1) == '-' && *(c + 2)) {
!       dest = (unsigned char) *(c + 2);
!       if (!goodchr(dest)) {
! 	safe_str(T("#-1 TR CANNOT ACCEPT NONPRINTING CHARS"), buff, bp);
! 	return;
        }
!       if (dest > cur) {
! 	for (; cur <= dest; cur++) {
! 	  if (goodchr(cur))
! 	    safe_chr(cur, outstr, &op);
! 	}
!       } else {
! 	for (; cur >= dest; cur--) {
! 	  if (goodchr(cur))
! 	    safe_chr(cur, outstr, &op);
! 	}
!       }
!       c += 3;
!     } else {
!       safe_chr(cur, outstr, &op);
!       c++;
      }
    }
+   *op = '\0';
+ #undef goodchr
  
    if ((ip - instr) != (op - outstr)) {
      safe_str(T("#-1 STRING LENGTHS MUST BE EQUAL"), buff, bp);
***************
*** 745,751 ****
  
    /* walk the string, translating characters */
    as = parse_ansi_string(args[0]);
-   populate_codes(as);
    len = as->len;
    for (i = 0; i < len; i++) {
      as->text[i] = charmap[(unsigned char) as->text[i]];
--- 835,840 ----
***************
*** 843,867 ****
  {
    int n, i, j;
    ansi_string *as;
  
    if (!*args[0])
      return;
  
    as = parse_ansi_string(args[0]);
!   populate_codes(as);
    n = as->len;
    for (i = 0; i < n; i++) {
!     char t, *tcode;
!     j = get_random_long(i, n - 1);
!     t = as->text[j];
!     as->text[j] = as->text[i];
!     as->text[i] = t;
!     tcode = as->codes[j];
!     as->codes[j] = as->codes[i];
!     as->codes[i] = tcode;
    }
-   safe_ansi_string(as, 0, as->len, buff, bp);
    free_ansi_string(as);
  }
  
  /* ARGSUSED */
--- 932,974 ----
  {
    int n, i, j;
    ansi_string *as;
+   ansi_string *dst;
+   int pos[BUFFER_LEN];
+   char tmp[BUFFER_LEN];
  
    if (!*args[0])
      return;
  
+   /* Set up the new ansi_string */
+   memset(tmp, 0, BUFFER_LEN);
+   dst = parse_ansi_string(tmp);
+ 
+   /* Read the current one */
    as = parse_ansi_string(args[0]);
! 
!   for (i = 0; i < as->len; i++)
!     pos[i] = i;
! 
    n = as->len;
    for (i = 0; i < n; i++) {
!     int t;
!     j = get_random_long(0, n - 1);
!     t = pos[i];
!     pos[i] = pos[j];
!     pos[j] = t;
!   }
! 
!   for (i = 0; i < n; i++) {
!     ansi_string_insert(dst, dst->len, as, pos[i], 1);
!     if ((i % 100) == 99) {
!       optimize_ansi_string(dst);
!     }
    }
    free_ansi_string(as);
+ 
+   /* Now optimize the ansi string */
+   safe_ansi_string(dst, 0, dst->len, buff, bp);
+   free_ansi_string(dst);
  }
  
  /* ARGSUSED */
***************
*** 1161,1169 ****
     * takes a delimiter argument and trim style.
     */
  
!   char *p, *q, sep;
    int trim;
    int trim_style_arg, trim_char_arg;
  
    /* Alas, PennMUSH and TinyMUSH used different orders for the arguments.
     * We'll give the users an option about it
--- 1268,1278 ----
     * takes a delimiter argument and trim style.
     */
  
!   char sep;
    int trim;
    int trim_style_arg, trim_char_arg;
+   ansi_string *as;
+   int i;
  
    /* Alas, PennMUSH and TinyMUSH used different orders for the arguments.
     * We'll give the users an option about it
***************
*** 1207,1225 ****
     */
  
    /* If necessary, skip over the leading stuff. */
!   p = args[0];
    if (trim != 2) {
!     while (*p == sep)
!       p++;
    }
    /* Cut off the trailing stuff, if appropriate. */
!   if ((trim != 1) && (*p != '\0')) {
!     q = args[0] + arglens[0] - 1;
!     while ((q > p) && (*q == sep))
!       q--;
!     q[1] = '\0';
    }
!   safe_str(p, buff, bp);
  }
  
  /* ARGSUSED */
--- 1316,1339 ----
     */
  
    /* If necessary, skip over the leading stuff. */
!   as = parse_ansi_string(args[0]);
    if (trim != 2) {
!     for (i = 0; i < as->len; i++) {
!       if (as->text[i] != sep)
! 	break;
!       as->text[i] = '\0';
!     }
    }
    /* Cut off the trailing stuff, if appropriate. */
!   if ((trim != 1)) {
!     for (i = as->len - 1; i >= 0; i--) {
!       if (as->text[i] != sep)
! 	break;
!       as->text[i] = '\0';
!     }
    }
!   safe_ansi_string(as, 0, as->len, buff, bp);
!   free_ansi_string(as);
  }
  
  /* ARGSUSED */
***************
*** 1239,1270 ****
     * never going to end up with a longer string.
     */
  
!   char *tp;
    char sep;
  
    /* Figure out the character to squish */
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
  
    /* get rid of trailing spaces first, so we don't have to worry about
     * them later.
     */
!   tp = args[0] + arglens[0] - 1;
!   while ((tp > args[0]) && (*tp == sep))
!     tp--;
!   tp[1] = '\0';
! 
!   for (tp = args[0]; *tp == sep; tp++)	/* skip leading spaces */
!     ;
! 
!   while (*tp) {
!     safe_chr(*tp, buff, bp);
!     if (*tp == sep)
!       while (*tp == sep)
! 	tp++;
      else
!       tp++;
    }
  }
  
  /* ARGSUSED */
--- 1353,1390 ----
     * never going to end up with a longer string.
     */
  
!   int i;
    char sep;
+   int insep = 1;
+   ansi_string *as;
  
    /* Figure out the character to squish */
    if (!delim_check(buff, bp, nargs, args, 2, &sep))
      return;
  
+   as = parse_ansi_string(args[0]);
+ 
    /* get rid of trailing spaces first, so we don't have to worry about
     * them later.
     */
!   for (i = as->len - 1; i >= 0; i--) {
!     if (as->text[i] == sep)
!       as->text[i] = '\0';
      else
!       break;
    }
+   /* Now trim leading and sequences */
+   for (i = 0; i < as->len; i++) {
+     if (as->text[i] == sep) {
+       if (insep)
+ 	as->text[i] = '\0';
+       insep = 1;
+     } else {
+       insep = 0;
+     }
+   }
+   safe_ansi_string(as, 0, as->len, buff, bp);
+   free_ansi_string(as);
  }
  
  /* ARGSUSED */
***************
*** 1307,1350 ****
    safe_fill(BEEP_CHAR, k, buff, bp);
  }
  
- /** Initialize the html tag hash table with all the safe tags from HTML 4.0 */
- void
- init_tag_hashtab(void)
- {
-   static char dummy = 1;
-   int i = 0;
-   static const char *safetags[] = { "A", "B", "I", "U", "STRONG", "EM",
-     "ADDRESS", "BLOCKQUOTE", "CENTER", "DEL", "DIV",
-     "H1", "H2", "H3", "H4", "H5", "H6", "HR", "INS",
-     "P", "PRE", "DIR", "DL", "DT", "DD", "LI", "MENU", "OL", "UL",
-     "TABLE", "CAPTION", "COLGROUP", "COL", "THEAD", "TFOOT",
-     "TBODY", "TR", "TD", "TH",
-     "BR", "FONT", "IMG", "SPAN", "SUB", "SUP",
-     "ABBR", "ACRONYM", "CITE", "CODE", "DFN", "KBD", "SAMP", "VAR",
-     "BIG", "S", "SMALL", "STRIKE", "TT",
-     NULL
-   };
-   hashinit(&htab_tag, 64, 1);
-   while (safetags[i]) {
-     hashadd(safetags[i], (void *) &dummy, &htab_tag);
-     i++;
-   }
- }
- 
  FUNCTION(fun_ord)
  {
    char *m;
    size_t len = 0;
    if (!args[0] || !args[0][0]) {
      safe_str(T("#-1 FUNCTION (ORD) EXPECTS ONE CHARACTER"), buff, bp);
      return;
    }
    m = remove_markup(args[0], &len);
  
    if (len != 2)			/* len includes trailing nul */
      safe_str(T("#-1 FUNCTION (ORD) EXPECTS ONE CHARACTER"), buff, bp);
!   else if (isprint((unsigned char) *m))
!     safe_integer((unsigned char) *m, buff, bp);
    else
      safe_str(T("#-1 UNPRINTABLE CHARACTER"), buff, bp);
  }
--- 1427,1448 ----
    safe_fill(BEEP_CHAR, k, buff, bp);
  }
  
  FUNCTION(fun_ord)
  {
    char *m;
    size_t len = 0;
+   unsigned char what;
    if (!args[0] || !args[0][0]) {
      safe_str(T("#-1 FUNCTION (ORD) EXPECTS ONE CHARACTER"), buff, bp);
      return;
    }
    m = remove_markup(args[0], &len);
+   what = (unsigned char) *m;
  
    if (len != 2)			/* len includes trailing nul */
      safe_str(T("#-1 FUNCTION (ORD) EXPECTS ONE CHARACTER"), buff, bp);
!   else if (isprint(what) || what == '\n')
!     safe_integer(what, buff, bp);
    else
      safe_str(T("#-1 UNPRINTABLE CHARACTER"), buff, bp);
  }
***************
*** 1388,1722 ****
  }
  
  /* ARGSUSED */
- FUNCTION(fun_html)
- {
-   if (!Wizard(executor))
-     safe_str(T(e_perm), buff, bp);
-   else
-     safe_tag(args[0], buff, bp);
- }
- 
- /* ARGSUSED */
- FUNCTION(fun_tag)
- {
-   int i;
-   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0])))
-     safe_str("#-1", buff, bp);
-   else {
-     safe_chr(TAG_START, buff, bp);
-     safe_strl(args[0], arglens[0], buff, bp);
-     for (i = 1; i < nargs; i++) {
-       if (ok_tag_attribute(executor, args[i])) {
- 	safe_chr(' ', buff, bp);
- 	safe_strl(args[i], arglens[i], buff, bp);
-       }
-     }
-     safe_chr(TAG_END, buff, bp);
-   }
- }
- 
- /* ARGSUSED */
- FUNCTION(fun_endtag)
- {
-   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0])))
-     safe_str("#-1", buff, bp);
-   else
-     safe_tag_cancel(args[0], buff, bp);
- }
- 
- /* ARGSUSED */
- FUNCTION(fun_tagwrap)
- {
-   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0])))
-     safe_str("#-1", buff, bp);
-   else {
-     if (nargs == 2)
-       safe_tag_wrap(args[0], NULL, args[1], buff, bp, executor);
-     else
-       safe_tag_wrap(args[0], args[1], args[2], buff, bp, executor);
-   }
- }
- 
- #define COL_FLASH       (1)	/**< ANSI flash attribute bit */
- #define COL_HILITE      (2)	/**< ANSI hilite attribute bit */
- #define COL_INVERT      (4)	/**< ANSI inverse attribute bit */
- #define COL_UNDERSCORE  (8)	/**< ANSI underscore attribute bit */
- 
- #define VAL_FLASH       (5)	/**< ANSI flag attribute value */
- #define VAL_HILITE      (1)	/**< ANSI hilite attribute value */
- #define VAL_INVERT      (7)	/**< ANSI inverse attribute value */
- #define VAL_UNDERSCORE  (4)	/**< ANSI underscore attribute value */
- 
- #define COL_BLACK       (30)	/**< ANSI color black */
- #define COL_RED         (31)	/**< ANSI color red */
- #define COL_GREEN       (32)	/**< ANSI color green */
- #define COL_YELLOW      (33)	/**< ANSI color yellow */
- #define COL_BLUE        (34)	/**< ANSI color blue */
- #define COL_MAGENTA     (35)	/**< ANSI color magenta */
- #define COL_CYAN        (36)	/**< ANSI color cyan */
- #define COL_WHITE       (37)	/**< ANSI color white */
- 
- /** The ansi attributes associated with a character. */
- typedef struct {
-   char flags;		/**< Ansi text attributes */
-   char fore;		/**< Ansi foreground color */
-   char back;		/**< Ansi background color */
- } ansi_data;
- 
- static void dump_ansi_codes(ansi_data * ad, char *buff, char **bp);
- 
- /** If we're adding y to x, do we need to add z as well? */
- #define EDGE_UP(x,y,z)  (((y) & (z)) && !((x) & (z)))
- 
- static void
- dump_ansi_codes(ansi_data * ad, char *buff, char **bp)
- {
-   static ansi_data old_ad = { 0, 0, 0 };
-   int f = 0;
- 
-   if ((old_ad.fore && !ad->fore)
-       || (old_ad.back && !ad->back)
-       || ((old_ad.flags & ad->flags) != old_ad.flags)) {
-     safe_str(ANSI_NORMAL, buff, bp);
-     old_ad.flags = 0;
-     old_ad.fore = 0;
-     old_ad.back = 0;
-   }
- 
-   if ((old_ad.fore == ad->fore)
-       && (old_ad.back == ad->back)
-       && (old_ad.flags == ad->flags))
-     /* If nothing has changed, don't bother doing anything.
-      * This stops the entirely pointless \e[m being generated. */
-     return;
- 
-   safe_str(ANSI_BEGIN, buff, bp);
- 
-   if (EDGE_UP(old_ad.flags, ad->flags, COL_FLASH)) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(VAL_FLASH, buff, bp);
-   }
- 
-   if (EDGE_UP(old_ad.flags, ad->flags, COL_HILITE)) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(VAL_HILITE, buff, bp);
-   }
- 
-   if (EDGE_UP(old_ad.flags, ad->flags, COL_INVERT)) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(VAL_INVERT, buff, bp);
-   }
- 
-   if (EDGE_UP(old_ad.flags, ad->flags, COL_UNDERSCORE)) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(VAL_UNDERSCORE, buff, bp);
-   }
- 
-   if (ad->fore != old_ad.fore) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(ad->fore, buff, bp);
-   }
- 
-   if (ad->back != old_ad.back) {
-     if (f++)
-       safe_chr(';', buff, bp);
-     safe_integer(ad->back + 10, buff, bp);
-   }
- 
-   safe_str(ANSI_END, buff, bp);
- 
-   old_ad = *ad;
- 
- }
- 
- 
- /* ARGSUSED */
- FUNCTION(fun_ansi)
- {
-   static char tbuff[BUFFER_LEN];
-   static ansi_data stack[1024] = { {0, 0, 0} }, *sp = stack;
-   char const *arg0, *arg1;
-   char *tbp;
- 
-   tbp = tbuff;
-   arg0 = args[0];
-   process_expression(tbuff, &tbp, &arg0, executor, caller, enactor,
- 		     PE_DEFAULT, PT_DEFAULT, pe_info);
-   *tbp = '\0';
- 
-   sp[1] = sp[0];
-   sp++;
- 
-   for (tbp = tbuff; *tbp; tbp++) {
-     switch (*tbp) {
-     case 'n':			/* normal */
-       sp->flags = 0;
-       sp->fore = 0;
-       sp->back = 0;
-       break;
-     case 'f':			/* flash */
-       sp->flags |= COL_FLASH;
-       break;
-     case 'h':			/* hilite */
-       sp->flags |= COL_HILITE;
-       break;
-     case 'i':			/* inverse */
-       sp->flags |= COL_INVERT;
-       break;
-     case 'u':			/* underscore */
-       sp->flags |= COL_UNDERSCORE;
-       break;
-     case 'F':			/* flash */
-       sp->flags &= ~COL_FLASH;
-       break;
-     case 'H':			/* hilite */
-       sp->flags &= ~COL_HILITE;
-       break;
-     case 'I':			/* inverse */
-       sp->flags &= ~COL_INVERT;
-       break;
-     case 'U':			/* underscore */
-       sp->flags &= ~COL_UNDERSCORE;
-       break;
-     case 'b':			/* blue fg */
-       sp->fore = COL_BLUE;
-       break;
-     case 'c':			/* cyan fg */
-       sp->fore = COL_CYAN;
-       break;
-     case 'g':			/* green fg */
-       sp->fore = COL_GREEN;
-       break;
-     case 'm':			/* magenta fg */
-       sp->fore = COL_MAGENTA;
-       break;
-     case 'r':			/* red fg */
-       sp->fore = COL_RED;
-       break;
-     case 'w':			/* white fg */
-       sp->fore = COL_WHITE;
-       break;
-     case 'x':			/* black fg */
-       sp->fore = COL_BLACK;
-       break;
-     case 'y':			/* yellow fg */
-       sp->fore = COL_YELLOW;
-       break;
-     case 'B':			/* blue bg */
-       sp->back = COL_BLUE;
-       break;
-     case 'C':			/* cyan bg */
-       sp->back = COL_CYAN;
-       break;
-     case 'G':			/* green bg */
-       sp->back = COL_GREEN;
-       break;
-     case 'M':			/* magenta bg */
-       sp->back = COL_MAGENTA;
-       break;
-     case 'R':			/* red bg */
-       sp->back = COL_RED;
-       break;
-     case 'W':			/* white bg */
-       sp->back = COL_WHITE;
-       break;
-     case 'X':			/* black bg */
-       sp->back = COL_BLACK;
-       break;
-     case 'Y':			/* yellow bg */
-       sp->back = COL_YELLOW;
-       break;
-     }
-   }
- 
-   dump_ansi_codes(sp, buff, bp);
- 
-   arg1 = args[1];
-   process_expression(buff, bp, &arg1, executor, caller, enactor,
- 		     PE_DEFAULT, PT_DEFAULT, pe_info);
- 
-   dump_ansi_codes(--sp, buff, bp);
- 
- }
- 
- /* ARGSUSED */
- FUNCTION(fun_stripansi)
- {
-   /* Strips ANSI codes away from a given string of text. Starts by
-    * finding the '\x' character and stripping until it hits an 'm'.
-    */
- 
-   char *cp;
- 
-   cp = remove_markup(args[0], NULL);
-   safe_str(cp, buff, bp);
- }
- 
- /* ARGSUSED */
  FUNCTION(fun_edit)
  {
!   int i;
!   char *f, *r, *raw;
!   ansi_string *prebuf;
!   char postbuf[BUFFER_LEN], lastbuf[BUFFER_LEN], *postp;
!   size_t rlen, flen;
  
!   prebuf = parse_ansi_string(args[0]);
!   raw = args[0];
!   for (i = 1; i < nargs - 1; i += 2) {
  
!     postp = postbuf;
!     f = args[i];		/* find this */
!     r = args[i + 1];		/* replace it with this */
!     flen = arglens[i];
!     rlen = arglens[i + 1];
! 
!     /* Check for nothing to avoid infinite loop */
!     if (!*f && !*r)
!       continue;
! 
!     if (flen == 1 && *f == '$') {
!       /* append */
!       safe_str(raw, postbuf, &postp);
!       safe_strl(r, rlen, postbuf, &postp);
!     } else if (flen == 1 && *f == '^') {
!       /* prepend */
!       safe_strl(r, rlen, postbuf, &postp);
!       safe_str(raw, postbuf, &postp);
!     } else if (!*f) {
!       /* insert between every character */
!       size_t last;
!       safe_strl(r, rlen, postbuf, &postp);
!       for (last = 0; last < prebuf->len; last++) {
! 	safe_ansi_string(prebuf, last, 1, postbuf, &postp);
! 	safe_strl(r, rlen, postbuf, &postp);
        }
      } else {
!       char *p;
!       size_t last = 0;
! 
!       while (last < prebuf->len && (p = strstr(prebuf->text + last, f)) != NULL) {
! 	safe_ansi_string(prebuf, last, p - (prebuf->text + last),
! 			 postbuf, &postp);
! 	safe_strl(r, rlen, postbuf, &postp);
! 	last = p - prebuf->text + flen;
!       }
!       if (last < prebuf->len)
! 	safe_ansi_string(prebuf, last, prebuf->len, postbuf, &postp);
!     }
!     *postp = '\0';
!     free_ansi_string(prebuf);
!     prebuf = parse_ansi_string(postbuf);
!     strcpy(lastbuf, postbuf);
!     raw = lastbuf;
    }
!   safe_ansi_string(prebuf, 0, prebuf->len, buff, bp);
!   free_ansi_string(prebuf);
  }
  
  FUNCTION(fun_brackets)
--- 1486,1530 ----
  }
  
  /* ARGSUSED */
  FUNCTION(fun_edit)
  {
!   int i, j;
!   char *needle;
!   size_t nlen;
!   char *search, *ptr;
!   ansi_string *orig, *repl;
  
!   orig = parse_ansi_string(args[0]);
  
!   for (i = 1; i < nargs - 1; i += 2) {
!     needle = remove_markup(args[i], &nlen);
!     nlen--;
!     repl = parse_ansi_string(args[i + 1]);
!     if (strcmp(needle, "$") == 0) {
!       ansi_string_insert(orig, orig->len, repl, 0, repl->len);
!     } else if (strcmp(needle, "^") == 0) {
!       ansi_string_insert(orig, 0, repl, 0, repl->len);
!     } else if (nlen == 0) {
!       /* Annoying. Stick repl between each character */
!       /* Since this is inserts, we're working *backwards* */
!       for (j = orig->len - 1; j > 0; j--) {
! 	ansi_string_insert(orig, j, repl, 0, repl->len);
        }
      } else {
!       search = orig->text;
!       /* Find each occurrence */
!       while ((ptr = strstr(search, needle)) != NULL) {
! 	/* Perform the replacement */
! 	ansi_string_replace(orig, ptr - orig->text, nlen, repl, 0, repl->len);
! 	search = ptr + repl->len;
!       }
!     }
!     free_ansi_string(repl);
!     optimize_ansi_string(orig);
    }
! 
!   safe_ansi_string(orig, 0, orig->len, buff, bp);
!   free_ansi_string(orig);
  }
  
  FUNCTION(fun_brackets)
***************
*** 1864,1875 ****
  
      if (ansilen < 0) {
        /* word doesn't fit on one line, so cut it */
!       safe_ansi_string2(as, pstr - as->text, ansiwidth - 1, buff, bp);
        safe_chr('-', buff, bp);
        pstr += ansiwidth - 1;	/* move to start of next line */
      } else {
        /* normal line */
!       safe_ansi_string2(as, pstr - as->text, ansilen, buff, bp);
        if (pstr[ansilen] == '\r')
  	++ansilen;
        pstr += ansilen + 1;	/* move to start of next line */
--- 1672,1683 ----
  
      if (ansilen < 0) {
        /* word doesn't fit on one line, so cut it */
!       safe_ansi_string(as, pstr - as->text, ansiwidth - 1, buff, bp);
        safe_chr('-', buff, bp);
        pstr += ansiwidth - 1;	/* move to start of next line */
      } else {
        /* normal line */
!       safe_ansi_string(as, pstr - as->text, ansilen, buff, bp);
        if (pstr[ansilen] == '\r')
  	++ansilen;
        pstr += ansilen + 1;	/* move to start of next line */
***************
*** 1932,1938 ****
      sp = segment;
      if (!*ptr) {
        if (len > 0) {
! 	safe_ansi_string2(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr;
      } else if (*ptr == '\n') {
--- 1740,1746 ----
      sp = segment;
      if (!*ptr) {
        if (len > 0) {
! 	safe_ansi_string(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr;
      } else if (*ptr == '\n') {
***************
*** 1940,1946 ****
  	   *tptr && tptr >= ptrs[i] && isspace((unsigned char) *tptr); tptr--) ;
        len = (tptr - ptrs[i]) + 1;
        if (len > 0) {
! 	safe_ansi_string2(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr + 1;
        ptr = tptr;
--- 1748,1754 ----
  	   *tptr && tptr >= ptrs[i] && isspace((unsigned char) *tptr); tptr--) ;
        len = (tptr - ptrs[i]) + 1;
        if (len > 0) {
! 	safe_ansi_string(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr + 1;
        ptr = tptr;
***************
*** 1951,1962 ****
  	   *tptr && tptr >= ptrs[i] && isspace((unsigned char) *tptr); tptr--) ;
        len = (tptr - ptrs[i]) + 1;
        if (len > 0) {
! 	safe_ansi_string2(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = lastspace;
      } else {
        if (len > 0) {
! 	safe_ansi_string2(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr;
      }
--- 1759,1770 ----
  	   *tptr && tptr >= ptrs[i] && isspace((unsigned char) *tptr); tptr--) ;
        len = (tptr - ptrs[i]) + 1;
        if (len > 0) {
! 	safe_ansi_string(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = lastspace;
      } else {
        if (len > 0) {
! 	safe_ansi_string(as[i], ptrs[i] - (as[i]->text), len, segment, &sp);
        }
        ptrs[i] = ptr;
      }
*** 1_8_2p1/src/game.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/game.c	Sat Jan 27 02:21:10 2007
***************
*** 68,73 ****
--- 68,74 ----
  #include "help.h"
  #include "dbio.h"
  #include "pcre.h"
+ #include "ansi.h"
  
  #ifdef hpux
  #include <sys/syscall.h>
***************
*** 724,729 ****
--- 725,731 ----
    init_func_hashtab();
    init_math_hashtab();
    init_tag_hashtab();
+   init_ansi_codes();
    init_aname_table();
    init_atr_name_tree();
    init_locks();
*** 1_8_2p1/src/help.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/help.c	Sat Jan 27 02:21:10 2007
***************
*** 212,224 ****
    /* ANSI topics */
    if (ShowAnsi(player)) {
      char ansi_topic[LINE_SIZE + 10];
!     sprintf(ansi_topic, "%s%s%s", ANSI_HILITE, the_topic, ANSI_NORMAL);
      notify(player, ansi_topic);
    } else
      notify(player, the_topic);
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, tprintf("%cSAMP%c", TAG_START, TAG_END));
    for (n = 0; n < BUFFER_LEN; n++) {
      if (fgets(line, LINE_SIZE, fp) == NULL)
        break;
--- 212,224 ----
    /* ANSI topics */
    if (ShowAnsi(player)) {
      char ansi_topic[LINE_SIZE + 10];
!     sprintf(ansi_topic, "%s%s%s", ANSI_HILITE, the_topic, ANSI_END);
      notify(player, ansi_topic);
    } else
      notify(player, the_topic);
  
    if (SUPPORT_PUEBLO)
!     notify_noenter(player, open_tag("SAMP"));
    for (n = 0; n < BUFFER_LEN; n++) {
      if (fgets(line, LINE_SIZE, fp) == NULL)
        break;
***************
*** 234,240 ****
      }
    }
    if (SUPPORT_PUEBLO)
!     notify_format(player, "%c/SAMP%c", TAG_START, TAG_END);
    fclose(fp);
    if (n >= BUFFER_LEN)
      notify_format(player, T("%s output truncated."), help_dat->command);
--- 234,240 ----
      }
    }
    if (SUPPORT_PUEBLO)
!     notify(player, close_tag("SAMP"));
    fclose(fp);
    if (n >= BUFFER_LEN)
      notify_format(player, T("%s output truncated."), help_dat->command);
*** 1_8_2p1/src/lock.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/lock.c	Sat Jan 27 02:21:10 2007
***************
*** 153,158 ****
--- 153,160 ----
  static lock_list *next_free_lock(void);
  static void free_lock(lock_list *ll);
  
+ extern int unparsing_boolexp;
+ 
  /** Return a list of all available locks
   * \param buff the buffer
   * \param bp a pointer to the current position in the buffer.
***************
*** 825,831 ****
  eval_lock(dbref player, dbref thing, lock_type ltype)
  {
    boolexp b = getlock(thing, ltype);
!   log_activity(LA_LOCK, thing, unparse_boolexp(player, b, UB_DBREF));
    return eval_boolexp(player, b, thing);
  }
  
--- 827,835 ----
  eval_lock(dbref player, dbref thing, lock_type ltype)
  {
    boolexp b = getlock(thing, ltype);
!   /* Prevent overwriting a static buffer in unparse_boolexp() */
!   if (!unparsing_boolexp)
!     log_activity(LA_LOCK, thing, unparse_boolexp(player, b, UB_DBREF));
    return eval_boolexp(player, b, thing);
  }
  
*** 1_8_2p1/src/log.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/log.c	Sat Jan 27 02:21:10 2007
***************
*** 77,83 ****
  static void
  start_log(FILE ** fp, const char *filename)
  {
-   char newfilename[256] = "\0";
    static int ht_initialized = 0;
    FILE *f;
  
--- 77,82 ----
***************
*** 93,104 ****
        *fp = f;
      } else {
  
!       /* Must use a buffer for MacOS file path conversion */
!       strncpy(newfilename, filename, 256);
! 
!       *fp = fopen(newfilename, "a");
        if (*fp == NULL) {
! 	fprintf(stderr, T("WARNING: cannot open log %s\n"), newfilename);
  	*fp = stderr;
        } else {
  	hashadd(strupper(filename), (void *) *fp, &htab_logfiles);
--- 92,100 ----
        *fp = f;
      } else {
  
!       *fp = fopen(filename, "a");
        if (*fp == NULL) {
! 	fprintf(stderr, T("WARNING: cannot open log %s\n"), filename);
  	*fp = stderr;
        } else {
  	hashadd(strupper(filename), (void *) *fp, &htab_logfiles);
*** 1_8_2p1/src/look.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/look.c	Sat Jan 27 02:21:10 2007
***************
*** 51,58 ****
  		     int skipdef, const char *prefix);
  static char *parent_chain(dbref player, dbref thing);
  
- static void insert_spaces(int count, int dospace, char *buff, char **bp);
- 
  extern PRIV attr_privs_view[];
  
  static void
--- 51,56 ----
***************
*** 179,185 ****
        if (Transparented(loc) && !(Opaque(thing))) {
  	if (SUPPORT_PUEBLO && !texits) {
  	  texits = 1;
! 	  notify_noenter_by(loc, player, tprintf("%cUL%c", TAG_START, TAG_END));
  	}
  	s1 = tbuf1;
  	safe_tag("LI", tbuf1, &s1);
--- 177,183 ----
        if (Transparented(loc) && !(Opaque(thing))) {
  	if (SUPPORT_PUEBLO && !texits) {
  	  texits = 1;
! 	  notify_noenter_by(loc, player, open_tag("UL"));
  	}
  	s1 = tbuf1;
  	safe_tag("LI", tbuf1, &s1);
***************
*** 341,351 ****
        if (GoodObject(parent))
  	notify_format(player,
  		      "%s#%d/%s [#%d%s]%s is veiled", ANSI_HILITE, parent,
! 		      AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL);
        else
  	notify_format(player,
  		      "%s%s [#%d%s]%s is veiled", ANSI_HILITE, AL_NAME(atr),
! 		      Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL);
      } else {
        if (GoodObject(parent))
  	notify_format(player,
--- 339,349 ----
        if (GoodObject(parent))
  	notify_format(player,
  		      "%s#%d/%s [#%d%s]%s is veiled", ANSI_HILITE, parent,
! 		      AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_END);
        else
  	notify_format(player,
  		      "%s%s [#%d%s]%s is veiled", ANSI_HILITE, AL_NAME(atr),
! 		      Owner(AL_CREATOR(atr)), fbuf, ANSI_END);
      } else {
        if (GoodObject(parent))
  	notify_format(player,
***************
*** 362,373 ****
        if (GoodObject(parent))
  	notify_format(player,
  		      "%s#%d/%s [#%d%s]:%s %s", ANSI_HILITE, parent,
! 		      AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL,
! 		      r);
        else
  	notify_format(player,
  		      "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr),
! 		      Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL, r);
      } else {
        if (GoodObject(parent))
  	notify_format(player, "#%d/%s [#%d%s]: %s", parent, AL_NAME(atr),
--- 360,370 ----
        if (GoodObject(parent))
  	notify_format(player,
  		      "%s#%d/%s [#%d%s]:%s %s", ANSI_HILITE, parent,
! 		      AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_END, r);
        else
  	notify_format(player,
  		      "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr),
! 		      Owner(AL_CREATOR(atr)), fbuf, ANSI_END, r);
      } else {
        if (GoodObject(parent))
  	notify_format(player, "#%d/%s [#%d%s]: %s", parent, AL_NAME(atr),
***************
*** 403,413 ****
      if (GoodObject(parent))
        notify_format(player,
  		    "%s#%d/%s [#%d%s]:%s %s", ANSI_HILITE, parent,
! 		    AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL, r);
      else
        notify_format(player,
  		    "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr),
! 		    Owner(AL_CREATOR(atr)), fbuf, ANSI_NORMAL, r);
    } else {
      if (GoodObject(parent))
        notify_format(player, "#%d/%s [#%d%s]: %s", parent, AL_NAME(atr),
--- 400,410 ----
      if (GoodObject(parent))
        notify_format(player,
  		    "%s#%d/%s [#%d%s]:%s %s", ANSI_HILITE, parent,
! 		    AL_NAME(atr), Owner(AL_CREATOR(atr)), fbuf, ANSI_END, r);
      else
        notify_format(player,
  		    "%s%s [#%d%s]:%s %s", ANSI_HILITE, AL_NAME(atr),
! 		    Owner(AL_CREATOR(atr)), fbuf, ANSI_END, r);
    } else {
      if (GoodObject(parent))
        notify_format(player, "#%d/%s [#%d%s]: %s", parent, AL_NAME(atr),
***************
*** 462,468 ****
    PUSE;
    tag_wrap("FONT", "SIZE=+2", unparse_object_myopic(player, thing));
    PEND;
!   notify(player, pbuff);
    look_description(player, thing, T("You see nothing special."), "DESCRIBE",
  		   "DESCFORMAT");
    did_it(player, thing, NULL, NULL, "ODESCRIBE", NULL, "ADESCRIBE", NOTHING);
--- 459,465 ----
    PUSE;
    tag_wrap("FONT", "SIZE=+2", unparse_object_myopic(player, thing));
    PEND;
!   notify_by(thing, player, pbuff);
    look_description(player, thing, T("You see nothing special."), "DESCRIBE",
  		   "DESCFORMAT");
    did_it(player, thing, NULL, NULL, "ODESCRIBE", NULL, "ADESCRIBE", NOTHING);
***************
*** 1391,1648 ****
    int skipdef;		/**< Skip default flags on attributes if true */
  };
  
- extern char escaped_chars[UCHAR_MAX + 1];
- 
- static void
- insert_spaces(int count, int dospace, char *buff, char **bp)
- {
-   if (count) {
-     if (count >= 5) {
-       safe_str("[space(", buff, bp);
-       safe_number(count, buff, bp);
-       safe_str(")]", buff, bp);
-     } else if (count == 1) {
-       if (dospace) {
- 	safe_str("%b", buff, bp);
-       } else {
- 	safe_chr(' ', buff, bp);
-       }
-     } else {
-       /* Take care of the final %b */
-       count--;
-       if (dospace) {
- 	safe_str("%b", buff, bp);
- 	count--;
-       }
-       while (count) {
- 	safe_chr(' ', buff, bp);
- 	count--;
- 	if (count) {
- 	  count--;
- 	  safe_str("%b", buff, bp);
- 	}
-       }
-       safe_str("%b", buff, bp);
-     }
-   }
- }
- 
  char *
  decompose_str(char *what)
  {
    static char value[BUFFER_LEN];
!   char *ptr, *s, *codestart;
!   char ansi_letter = '\0';
!   int len;
!   int dospace;
!   int spaces;
!   int flag_depth, ansi_depth;
!   int digits;
! 
!   len = strlen(what);
!   flag_depth = 0;
!   ansi_depth = 0;
! 
!   /* Go through the string, escaping characters and
!    * turning every other space into %b. */
! 
!   s = value;
!   ptr = what;
! #ifdef NEVER
!   /* Put a \ at the beginning if it won't already be put there,
!    * unless it's a space, which would require %b, %r, or %t anyway */
!   if (!escaped_chars[(unsigned int) *what] && !isspace((unsigned char) *what)) {
!     safe_chr('\\', value, &s);
!   }
! #endif
! 
!   dospace = 1;
!   spaces = 0;
!   for (; *ptr; ptr++) {
!     switch (*ptr) {
!     case ' ':
!       spaces++;
!       break;
!     case '\n':
!       insert_spaces(spaces, dospace, value, &s);
!       spaces = 0;
!       dospace = 0;
!       safe_str("%r", value, &s);
!       break;
!     case '\t':
!       insert_spaces(spaces, dospace, value, &s);
!       spaces = 0;
!       dospace = 0;
!       safe_str("%t", value, &s);
!       break;
!     case ESC_CHAR:
!       insert_spaces(spaces, dospace, value, &s);
!       spaces = 0;
!       ptr++;
!       if (!*ptr) {
! 	ptr--;
! 	break;
!       }
!       /* Check if this is any sort of useful code. */
!       if (*ptr == '[' && *(ptr + 1) && *(ptr + 2)) {
! 	codestart = ptr;	/* Save the address of the escape code. */
! 	ptr++;
! 	digits = 0;		/* Digit count is zero. */
! 	/* The following code works in this way:
! 	 * 1) If a character is a ;, we are allowed to count 2 more digits
! 	 * 2) If the digits count is 3, break out of the "ansi" code.
! 	 * 3) If the character is not a number or ;, break out.
! 	 * 4) If an 'm' is encountered, the for-loop exits.
! 	 * The only non-breaking exit occurs when the code ends with "m".
! 	 * Otherwise, we break out with the ptr pointing to the end of
! 	 * the invalid code, causing decompose() to ignore it entirely.
! 	 */
! 	for (; *ptr && (*ptr != 'm'); ptr++) {
! 	  if (*ptr == ';') {
! 	    if (digits == 0) {	/* No double-;s are allowed. */
! 	      digits = 3;
! 	      break;
! 	    }
! 	    digits = 0;
! 	  } else if (digits >= 2) {
! 	    digits = 3;		/* If we encounter a 3-number code, break out. */
! 	    break;
! 	  } else if (isdigit((unsigned char) *ptr)) {
! 	    digits++;		/* Count the numbers we encounter. */
! 	  } else {
! 	    digits = 3;
! 	    break;
! 	  }
! 	}
  
! 	/* 3 is the break-code. 0 means there's no ANSI at all! */
! 	if (!*ptr || digits == 3 || digits == 0) {
! 	  break;
! 	}
! 
! 	/* It IS an ansi code! It ends in "m" anyway.
! 	 * Set ptr to point to the first digit in the code. We are
! 	 * promised at this point that ptr+1 is not NUL. 
! 	 */
! 	ptr = codestart + 1;
! 
! 	/* Check if the first part of the code is two-digit (color) */
! 	if (*(ptr + 1) >= '0' && *(ptr + 1) <= '7') {
! 	  if (flag_depth < ansi_depth) {
! 	    safe_str(")]", value, &s);
! 	    ansi_depth--;
! 	  }
! 	} else {		/* ansi "flag", inverse, flash, underline, hilight */
! 	  flag_depth = ansi_depth + 1;
! 	}
! 	/* Check to see if this is an 'ansi-reset' code. */
! 	if (*ptr == '0' && *(ptr + 1) == 'm') {
! 	  for (; ansi_depth > 0; ansi_depth--) {
! 	    safe_str(")]", value, &s);
! 	  }
! 	  flag_depth = 0;
! 	  ptr++;
! 	  break;
! 	}
  
- 	ansi_depth++;
- 	safe_str("[ansi(", value, &s);
- 	dospace = 1;
- 
- 	/* code for decompiling ansi */
- 	for (; isdigit((unsigned char) *ptr) || *ptr == ';'; ptr++) {
- 	  if (*ptr == ';')	/* Yes, it is necessary to do it this way. */
- 	    ptr++;
- 	  /* Break if there is an 'm' here. */
- 	  if (!*ptr || !isdigit((unsigned char) *ptr))
- 	    break;
- 	  /* Check to see if the code is one character long. */
- 	  if (*(ptr + 1) == ';' || *(ptr + 1) == 'm') {
- 	    /* ANSI flag */
- 	    switch (*ptr) {
- 	    case '1':
- 	      safe_chr('h', value, &s);
- 	      break;
- 	    case '4':
- 	      safe_chr('u', value, &s);
- 	      break;
- 	    case '5':
- 	      safe_chr('f', value, &s);
- 	      break;
- 	    case '7':
- 	      safe_chr('i', value, &s);
- 	      break;
- 	    default:		/* Not a valid code. */
- 	      break;
- 	    }
- 	  } else {
- 	    if (!*(ptr + 1))
- 	      break;		/* Sudden string end or lack of real color code. */
- 	    ptr++;
- 
- 	    /* Check if this could be a real color (starts with 3 or 4) */
- 	    if (*(ptr - 1) == '3' || *(ptr - 1) == '4') {
- 	      switch (*ptr) {
- 	      case '0':
- 		ansi_letter = 'x';
- 		break;
- 	      case '1':
- 		ansi_letter = 'r';
- 		break;
- 	      case '2':
- 		ansi_letter = 'g';
- 		break;
- 	      case '3':
- 		ansi_letter = 'y';
- 		break;
- 	      case '4':
- 		ansi_letter = 'b';
- 		break;
- 	      case '5':
- 		ansi_letter = 'm';
- 		break;
- 	      case '6':
- 		ansi_letter = 'c';
- 		break;
- 	      case '7':
- 		ansi_letter = 'w';
- 		break;
- 	      default:
- 		break;		/* Not a valid color. */
- 	      }
- 	      /* If background color, change the letter to a capital. */
- 	      if (*(ptr - 1) == '4')
- 		ansi_letter = toupper(ansi_letter);
- 	      safe_chr(ansi_letter, value, &s);
- 	    }
- 	    /* No "else" here: If a two-digit code
- 	     * doesn't start with 3 or 4, is isn't ANSI. */
- 	  }
- 	}
- 	safe_chr(',', value, &s);
-       } else {
- 	ptr--;
- 	/* This shouldn't happen if we only have ansi codes
- 	 * So if more dirty things must be added later... */
-       }
-       break;
-     default:
-       insert_spaces(spaces, dospace, value, &s);
-       spaces = 0;
-       if (escaped_chars[(unsigned int) *ptr]) {
- 	safe_chr('\\', value, &s);
-       }
-       safe_chr(*ptr, value, &s);
-       dospace = 0;
-     }
-   }
-   /* Now check the last space. */
-   insert_spaces(spaces, dospace, value, &s);
-   spaces = 0;
-   for (; ansi_depth > 0; ansi_depth--) {
-     safe_str(")]", value, &s);
-   }
-   *s = '\0';
    return value;
  }
  
--- 1388,1405 ----
    int skipdef;		/**< Skip default flags on attributes if true */
  };
  
  char *
  decompose_str(char *what)
  {
    static char value[BUFFER_LEN];
!   char *vp = value;
!   ansi_string *as;
  
!   as = parse_ansi_string(what);
!   dump_ansi_string(as, value, &vp);
!   *vp = '\0';
!   free_ansi_string(as);
  
    return value;
  }
  
*** 1_8_2p1/src/notify.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/notify.c	Sat Jan 27 02:21:10 2007
***************
*** 87,92 ****
--- 87,93 ----
  #include "game.h"
  #include "confmagic.h"
  
+ #define ANSI_RAW_NORMAL   "\x1B[0m"
  
  static int under_limit = 1;
  
***************
*** 177,185 ****
    int made;			/**< True if message has been rendered. */
  };
  
- static void fillstate(int state[], const unsigned char **f);
- static void ansi_change_state(char *t, char **o, int color, int *state,
- 			      int *newstate);
  static enum na_type notify_type(DESC *d);
  static void free_strings(struct notify_strings messages[]);
  static void zero_strings(struct notify_strings messages[]);
--- 178,183 ----
***************
*** 187,295 ****
  					struct notify_strings messages[],
  					enum na_type type, int flags);
  
- 
- static void
- fillstate(int state[6], const unsigned char **f)
- {
-   const unsigned char *p;
-   int i;
-   int n;
-   p = *f;
-   p++;
-   if (*p != '[') {
-     while (*p && *p != 'm')
-       p++;
-   } else {
-     p++;
-     while (*p && *p != 'm') {
-       if ((*p > '9') || (*p < '0')) {
- 	/* Nada */
-       } else if (!(*(p + 1)) || (*(p + 1) == 'm') || (*(p + 1) == ';')) {
- 	/* ShortCode */ ;
- 	switch (*p) {
- 	case '0':
- 	  for (i = 0; i < 6; i++)
- 	    state[i] = 0;
- 	  break;
- 	case '1':
- 	  state[TA_BOLD] = 1;
- 	  break;
- 	case '7':
- 	  state[TA_REV] = 1;
- 	  break;
- 	case '5':
- 	  state[TA_BLINK] = 1;
- 	  break;
- 	case '4':
- 	  state[TA_ULINE] = 1;
- 	  break;
- 	}
-       } else {
- 	n = (*p - '0') * 10;
- 	p++;
- 	n += (*p - '0');
- 	if ((n >= 30) && (n <= 37))
- 	  state[TA_FGC] = n - 29;
- 	else if ((n >= 40) && (n <= 47))
- 	  state[TA_BGC] = n - 39;
-       }
-       p++;
-     }
-   }
-   if ((p != *f) && (*p != 'm'))
-     p--;
-   *f = p;
- }
- 
- /** Add an ansi tag if it's needed here */
- #define add_ansi_if(x,c) \
-   do { \
-     if (newstate[(x)] && (newstate[(x)]!=state[(x)])) { \
-       if (i) safe_chr(';',t,o); \
-       safe_str((c),t,o); \
-       i=1; \
-     } \
-   } while (0)
- 
- static void
- ansi_change_state(char *t, char **o, int color, int *state, int *newstate)
- {
-   int i, n;
-   if ((state[TA_BOLD] && !newstate[TA_BOLD]) ||
-       (state[TA_REV] && !newstate[TA_REV]) ||
-       (state[TA_BLINK] && !newstate[TA_BLINK]) ||
-       (state[TA_ULINE] && !newstate[TA_ULINE]) ||
-       (color && state[TA_FGC] && !newstate[TA_FGC]) ||
-       (color && state[TA_BGC] && !newstate[TA_BGC])) {
-     for (n = 0; n < 6; n++)
-       state[n] = 0;
-     safe_str(ANSI_NORMAL, t, o);
-   }
-   if ((newstate[TA_BOLD] && (newstate[TA_BOLD] != state[TA_BOLD])) ||
-       (newstate[TA_REV] && (newstate[TA_REV] != state[TA_REV])) ||
-       (newstate[TA_BLINK] && (newstate[TA_BLINK] != state[TA_BLINK])) ||
-       (newstate[TA_ULINE] && (newstate[TA_ULINE] != state[TA_ULINE])) ||
-       (color && newstate[TA_FGC] && (newstate[TA_FGC] != state[TA_FGC])) ||
-       (color && newstate[TA_BGC] && (newstate[TA_BGC] != state[TA_BGC]))) {
-     safe_chr(ESC_CHAR, t, o);
-     safe_chr('[', t, o);
-     i = 0;
-     add_ansi_if(TA_BOLD, "1");
-     add_ansi_if(TA_REV, "7");
-     add_ansi_if(TA_BLINK, "5");
-     add_ansi_if(TA_ULINE, "4");
-     if (color) {
-       add_ansi_if(TA_FGC, unparse_integer(newstate[TA_FGC] + 29));
-       add_ansi_if(TA_BGC, unparse_integer(newstate[TA_BGC] + 39));
-     }
-     safe_chr('m', t, o);
-   }
-   for (n = 0; n < 6; n++)
-     state[n] = newstate[n];
- }
- 
- #undef add_ansi_if
- 
  static void
  zero_strings(struct notify_strings messages[])
  {
--- 185,190 ----
***************
*** 310,315 ****
--- 205,273 ----
        mush_free(messages[n].message, "string");
  }
  
+ static int
+ output_ansichange(struct ansi_data *states, int *ansi_ptr,
+ 		  const unsigned char **ptr, char *buff, char **bp)
+ {
+   const unsigned char *p = *ptr;
+   int newaptr = *ansi_ptr;
+   int retval = 0;
+   int changed = 0;
+   struct ansi_data cur = states[*ansi_ptr];
+ 
+   /* This is color */
+   while (*p &&
+ 	 ((*p == TAG_START && *(p + 1) == MARKUP_COLOR) || (*p == ESC_CHAR))) {
+     switch (*p) {
+     case TAG_START:
+       p += 2;
+       if (*p != '/') {
+ 	newaptr++;
+ 	define_ansi_data(&(states[newaptr]), (const char *) p);
+ 	/* The new ansi state inherits ansi from the old one that it doesn't
+ 	 * have or disable  */
+ 	nest_ansi_data(&(states[newaptr - 1]), &(states[newaptr]));
+       } else {
+ 	if (*(p + 1) == 'a') {
+ 	  newaptr = 0;
+ 	} else {
+ 	  newaptr--;
+ 	}
+ 	if (newaptr < 0)
+ 	  newaptr = 0;
+ 	if (newaptr < *ansi_ptr)
+ 	  changed = 1;
+       }
+       while (*p && *p != TAG_END)
+ 	p++;
+       /* Advance past TAG_END */
+       if (*p && ((*(p + 1) == TAG_START && *(p + 2) == MARKUP_COLOR) ||
+ 		 (*(p + 1) == ESC_CHAR)))
+ 	p++;
+       break;
+     case ESC_CHAR:
+       /* I really hate daling with this. Consider it a nesting code. */
+       newaptr++;
+       read_raw_ansi_data(&states[newaptr], (const char *) p);
+       nest_ansi_data(&states[newaptr - 1], &states[newaptr]);
+       while (*p && *p != 'm')
+ 	p++;
+       /* Advance past the 'm' if there's a tag or  */
+       if (*p && ((*(p + 1) == TAG_START && *(p + 2) == MARKUP_COLOR) ||
+ 		 (*(p + 1) == ESC_CHAR)))
+ 	p++;
+       break;
+     }
+   }
+   /* Do we print anything? */
+   if (changed || newaptr != *ansi_ptr) {
+     retval = write_raw_ansi_data(&cur, &(states[newaptr]), buff, bp);
+     *(ansi_ptr) = newaptr;
+   }
+   *ptr = p;
+   return retval;
+ }
+ 
  static unsigned char *
  notify_makestring(const char *message, struct notify_strings messages[],
  		  enum na_type type, int flags)
***************
*** 317,330 ****
    char *o;
    const unsigned char *p;
    char *t;
-   int state[6] = { 0, 0, 0, 0, 0, 0 };
-   int newstate[6] = { 0, 0, 0, 0, 0, 0 };
    int changed = 0;
    int color = 0;
    int strip = 0;
    int pueblo = 0;
    static char tbuf[BUFFER_LEN];
    char *bp;
  
    if (messages[type].made) {
      if (flags & NA_PROMPT) {
--- 275,296 ----
    char *o;
    const unsigned char *p;
    char *t;
    int changed = 0;
    int color = 0;
    int strip = 0;
    int pueblo = 0;
    static char tbuf[BUFFER_LEN];
    char *bp;
+   static struct ansi_data states[BUFFER_LEN];
+   int ansi_ptr, ansifix;
+ 
+   ansi_ptr = 0;
+   ansifix = 0;
+   /* Everything is explicitly off by default */
+   states[0].bits = 0;
+   states[0].offbits = 0xFF;
+   states[0].fore = 0;
+   states[0].back = 0;
  
    if (messages[type].made) {
      if (flags & NA_PROMPT) {
***************
*** 446,452 ****
        case IAC:
  	if (changed) {
  	  changed = 0;
! 	  ansi_change_state(t, &o, color, state, newstate);
  	}
  	if (type == NA_TANSI || type == NA_TCOLOR)
  	  safe_str("\xFF\xFF", t, &o);
--- 412,418 ----
        case IAC:
  	if (changed) {
  	  changed = 0;
! 	  ansifix += write_raw_ansi_data(&states[ansi_ptr], &states[0], t, &o);
  	}
  	if (type == NA_TANSI || type == NA_TCOLOR)
  	  safe_str("\xFF\xFF", t, &o);
***************
*** 456,474 ****
  	  safe_chr((char) IAC, t, &o);
  	break;
        case TAG_START:
! 	if (pueblo) {
  	  safe_chr('<', t, &o);
! 	  p++;
  	  while ((*p) && (*p != TAG_END)) {
  	    safe_chr(*p, t, &o);
  	    p++;
  	  }
  	  safe_chr('>', t, &o);
  	} else {
- 	  /* Non-pueblo */
  	  while (*p && *p != TAG_END)
  	    p++;
  	}
  	break;
        case TAG_END:
  	/* Should never be seen alone */
--- 422,443 ----
  	  safe_chr((char) IAC, t, &o);
  	break;
        case TAG_START:
! 	if (pueblo && (*(p + 1) == MARKUP_HTML)) {
  	  safe_chr('<', t, &o);
! 	  /* Skip over the 'p' for Pueblo */
! 	  p += 2;
  	  while ((*p) && (*p != TAG_END)) {
  	    safe_chr(*p, t, &o);
  	    p++;
  	  }
  	  safe_chr('>', t, &o);
+ 	} else if (*(p + 1) == MARKUP_COLOR) {
+ 	  ansifix += output_ansichange(states, &ansi_ptr, &p, t, &o);
  	} else {
  	  while (*p && *p != TAG_END)
  	    p++;
  	}
+ 	changed = 1;
  	break;
        case TAG_END:
  	/* Should never be seen alone */
***************
*** 476,489 ****
        case '\r':
  	break;
        case ESC_CHAR:
! 	fillstate(newstate, &p);
  	changed = 1;
  	break;
        default:
- 	if (changed) {
- 	  changed = 0;
- 	  ansi_change_state(t, &o, color, state, newstate);
- 	}
  	if (pueblo) {
  	  if (strip) {
  	    /* Even if we're NOACCENTS, we must still translate a few things */
--- 445,456 ----
        case '\r':
  	break;
        case ESC_CHAR:
! 	/* After the ansi changes, I really hope we don't encounter this. */
! 	/* because it borks how proper ansi handling works */
! 	ansifix += output_ansichange(states, &ansi_ptr, &p, t, &o);
  	changed = 1;
  	break;
        default:
  	if (pueblo) {
  	  if (strip) {
  	    /* Even if we're NOACCENTS, we must still translate a few things */
***************
*** 516,527 ****
  	    safe_chr(*p, t, &o);
  	}
        }
!       p++;
      }
-     if (state[TA_BOLD] || state[TA_REV] ||
- 	state[TA_BLINK] || state[TA_ULINE] ||
- 	(color && (state[TA_FGC] || state[TA_BGC])))
-       safe_str(ANSI_NORMAL, t, &o);
  
      break;
    }
--- 483,514 ----
  	    safe_chr(*p, t, &o);
  	}
        }
!       if (*p)
! 	p++;
!     }
!     /* We possibly have some unclosed ansi. Force an
!      * ANSI_NORMAL for now. */
!     if (ansifix || (ansi_ptr && safe_str(ANSI_RAW_NORMAL, t, &o))) {
!       int sub = 7;
!       char *ptr;
!       int q;
!       if (flags & NA_PROMPT)
! 	sub += 2;
!       ptr = t + BUFFER_LEN - sub;
!       for (q = 20; q > 0 && *ptr != ESC_CHAR; q--, ptr--) ;
!       if (pueblo) {
! 	for (q = 20; q > 0 && *ptr != ESC_CHAR && *ptr != '<'; q--, ptr--) ;
!       } else {
! 	for (q = 20; q > 0 && *ptr != ESC_CHAR; q--, ptr--) ;
!       }
!       if (q > 0) {
! 	o = ptr;
!       } else {
! 	o = t + BUFFER_LEN - sub;
!       }
!       safe_str(ANSI_RAW_NORMAL, t, &o);
!       *o = '\0';
      }
  
      break;
    }
***************
*** 1160,1259 ****
    free((Malloc_t) orig);
  }
  
- /** Safely add a tag into a buffer.
-  * If we support pueblo, this function adds the tag start token,
-  * the tag, and the tag end token. If not, it does nothing.
-  * If we can't fit the tag in, we don't put any of it in.
-  * \param a_tag the html tag to add.
-  * \param buf the buffer to append to.
-  * \param bp pointer to address in buf to insert.
-  * \retval 0, successfully added.
-  * \retval 1, tag wouldn't fit in buffer.
-  */
- int
- safe_tag(char const *a_tag, char *buf, char **bp)
- {
-   int result = 0;
-   char *save = buf;
- 
-   if (SUPPORT_PUEBLO) {
-     result = safe_chr(TAG_START, buf, bp);
-     result = safe_str(a_tag, buf, bp);
-     result = safe_chr(TAG_END, buf, bp);
-   }
-   /* If it didn't all fit, rewind. */
-   if (result)
-     *bp = save;
- 
-   return result;
- }
- 
- /** Safely add a closing tag into a buffer.
-  * If we support pueblo, this function adds the tag start token,
-  * a slash, the tag, and the tag end token. If not, it does nothing.
-  * If we can't fit the tag in, we don't put any of it in.
-  * \param a_tag the html tag to add.
-  * \param buf the buffer to append to.
-  * \param bp pointer to address in buf to insert.
-  * \retval 0, successfully added.
-  * \retval 1, tag wouldn't fit in buffer.
-  */
- int
- safe_tag_cancel(char const *a_tag, char *buf, char **bp)
- {
-   int result = 0;
-   char *save = buf;
- 
-   if (SUPPORT_PUEBLO) {
-     result = safe_chr(TAG_START, buf, bp);
-     result = safe_chr('/', buf, bp);
-     result = safe_str(a_tag, buf, bp);
-     result = safe_chr(TAG_END, buf, bp);
-   }
-   /* If it didn't all fit, rewind. */
-   if (result)
-     *bp = save;
- 
-   return result;
- }
- 
- /** Safely add a tag, some text, and a matching closing tag into a buffer.
-  * If we can't fit the stuff, we don't put any of it in.
-  * \param a_tag the html tag to add.
-  * \param params tag parameters.
-  * \param data the text to wrap the tag around.
-  * \param buf the buffer to append to.
-  * \param bp pointer to address in buf to insert.
-  * \param player the player involved in all this, or NOTHING if internal.
-  * \retval 0, successfully added.
-  * \retval 1, tagged text wouldn't fit in buffer.
-  */
- int
- safe_tag_wrap(char const *a_tag, char const *params, char const *data,
- 	      char *buf, char **bp, dbref player)
- {
-   int result = 0;
-   char *save = buf;
- 
-   if (SUPPORT_PUEBLO) {
-     result = safe_chr(TAG_START, buf, bp);
-     result = safe_str(a_tag, buf, bp);
-     if (params && *params && ok_tag_attribute(player, params)) {
-       result = safe_chr(' ', buf, bp);
-       result = safe_str(params, buf, bp);
-     }
-     result = safe_chr(TAG_END, buf, bp);
-   }
-   result = safe_str(data, buf, bp);
-   if (SUPPORT_PUEBLO) {
-     result = safe_tag_cancel(a_tag, buf, bp);
-   }
-   /* If it didn't all fit, rewind. */
-   if (result)
-     *bp = save;
-   return result;
- }
- 
  /** Wrapper to notify a single player with a message, unconditionally.
   * \param player player to notify.
   * \param msg message to send.
--- 1147,1152 ----
*** 1_8_2p1/src/parse.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/parse.c	Sat Jan 27 02:21:10 2007
***************
*** 40,46 ****
  int global_fun_recursions;
  /* extern int re_subpatterns; */
  /* extern int *re_offsets; */
! /* extern char *re_from; */
  extern sig_atomic_t cpu_time_limit_hit;
  extern int cpu_limit_warning_sent;
  
--- 40,46 ----
  int global_fun_recursions;
  /* extern int re_subpatterns; */
  /* extern int *re_offsets; */
! /* extern ansi_string *re_from; */
  extern sig_atomic_t cpu_time_limit_hit;
  extern int cpu_limit_warning_sent;
  
***************
*** 257,268 ****
    static pcre *re = NULL;
    const char *errptr;
    int erroffset;
  
    if (!str)
      return 0;
    if (!re)
      re = pcre_compile("^#-?\\d+(?::\\d+)?$", 0, &errptr, &erroffset, NULL);
!   return (pcre_exec(re, NULL, str, strlen(str), 0, 0, NULL, 0) >= 0);
  }
  
  /** Is string an integer?
--- 257,271 ----
    static pcre *re = NULL;
    const char *errptr;
    int erroffset;
+   char *val;
+   size_t vlen;
  
    if (!str)
      return 0;
    if (!re)
      re = pcre_compile("^#-?\\d+(?::\\d+)?$", 0, &errptr, &erroffset, NULL);
!   val = remove_markup((const char *) str, &vlen);
!   return (pcre_exec(re, NULL, val, vlen - 1, 0, 0, NULL, 0) >= 0);
  }
  
  /** Is string an integer?
***************
*** 648,659 ****
  	  global_eval_context.re_subpatterns >= 0 &&
  	  global_eval_context.re_offsets != NULL &&
  	  global_eval_context.re_from != NULL) {
- 	char obuf[BUFFER_LEN];
  	int p = -1;
  	char subspace[BUFFER_LEN];
  	char *named_substring = NULL;
  
- 	obuf[0] = '\0';
  	(*str)++;
  	/* Check the first character after the $ for a number */
  	if (isdigit((unsigned char) **str)) {
--- 651,660 ----
***************
*** 677,695 ****
  	  break;
  	}
  
! 	if (named_substring) {
! 	  pcre_copy_named_substring(global_eval_context.re_code,
! 				    global_eval_context.re_from,
! 				    global_eval_context.re_offsets,
! 				    global_eval_context.re_subpatterns,
! 				    named_substring, obuf, BUFFER_LEN);
  	} else {
! 	  pcre_copy_substring(global_eval_context.re_from,
! 			      global_eval_context.re_offsets,
! 			      global_eval_context.re_subpatterns,
! 			      p, obuf, BUFFER_LEN);
  	}
- 	safe_str(obuf, buff, bp);
        } else {
  	safe_chr('$', buff, bp);
  	(*str)++;
--- 678,695 ----
  	  break;
  	}
  
! 	if (named_substring != NULL) {
! 	  ansi_pcre_copy_named_substring(global_eval_context.re_code,
! 					 global_eval_context.re_from,
! 					 global_eval_context.re_offsets,
! 					 global_eval_context.re_subpatterns,
! 					 named_substring, 0, buff, bp);
  	} else {
! 	  ansi_pcre_copy_substring(global_eval_context.re_from,
! 				   global_eval_context.re_offsets,
! 				   global_eval_context.re_subpatterns,
! 				   p, 0, buff, bp);
  	}
        } else {
  	safe_chr('$', buff, bp);
  	(*str)++;
*** 1_8_2p1/src/predicat.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/predicat.c	Sat Jan 27 02:21:10 2007
***************
*** 1364,1370 ****
        strncpy(buf, s, gh->len);
        buf[gh->len] = '\0';
        s += gh->len;
!       safe_format(tbuf1, &tbp, "%s%s%s", ANSI_HILITE, buf, ANSI_NORMAL);
      } else {
        safe_chr(*s, tbuf1, &tbp);
        s++;
--- 1364,1370 ----
        strncpy(buf, s, gh->len);
        buf[gh->len] = '\0';
        s += gh->len;
!       safe_format(tbuf1, &tbp, "%s%s%s", ANSI_HILITE, buf, ANSI_END);
      } else {
        safe_chr(*s, tbuf1, &tbp);
        s++;
***************
*** 1378,1384 ****
  		  ANSI_HILITE, AL_NAME(atr),
  		  Owner(AL_CREATOR(atr)),
  		  privs_to_letters(attr_privs_view, AL_FLAGS(atr)),
! 		  ANSI_NORMAL, tbuf1);
    return found;
  }
  
--- 1378,1384 ----
  		  ANSI_HILITE, AL_NAME(atr),
  		  Owner(AL_CREATOR(atr)),
  		  privs_to_letters(attr_privs_view, AL_FLAGS(atr)),
! 		  ANSI_END, tbuf1);
    return found;
  }
  
*** 1_8_2p1/src/set.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/set.c	Sat Jan 27 02:21:10 2007
***************
*** 342,348 ****
  	Royalty(thing) || Inherit(thing))
        notify_format(player,
  		    T
! 		    ("Warning: @CHOWN/PRESERVE on a object (#%d) with WIZ, ROY, INHERIT, or @power privileges."),
  		    thing);
    }
  }
--- 342,348 ----
  	Royalty(thing) || Inherit(thing))
        notify_format(player,
  		    T
! 		    ("Warning: @CHOWN/PRESERVE on an object (#%d) with WIZ, ROY, INHERIT, or @power privileges."),
  		    thing);
    }
  }
***************
*** 772,785 ****
      safe_str(r, tbuf1, &tbufp);
  
      if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", s, ANSI_HILITE, r,
! 		    ANSI_NORMAL))
        ansi_long_flag = 1;
    } else if (vlen == 1 && *val == '^') {
      /* prepend */
      safe_str(r, tbuf1, &tbufp);
      safe_str(s, tbuf1, &tbufp);
  
!     if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", ANSI_HILITE, r, ANSI_NORMAL,
  		    s))
        ansi_long_flag = 1;
    } else if (!*val) {
--- 772,785 ----
      safe_str(r, tbuf1, &tbufp);
  
      if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", s, ANSI_HILITE, r,
! 		    ANSI_END))
        ansi_long_flag = 1;
    } else if (vlen == 1 && *val == '^') {
      /* prepend */
      safe_str(r, tbuf1, &tbufp);
      safe_str(s, tbuf1, &tbufp);
  
!     if (safe_format(tbuf_ansi, &tbufap, "%s%s%s%s", ANSI_HILITE, r, ANSI_END,
  		    s))
        ansi_long_flag = 1;
    } else if (!*val) {
***************
*** 793,799 ****
      /* Add one at the start */
      if (!safe_strl(r, rlen, tbuf1, &tbufp)) {
        if (gargs->target != EDIT_FIRST) {
! 	for (last = 0; last < haystack->len; last++) {
  	  /* Add the next character */
  	  if (safe_ansi_string(haystack, last, 1, tbuf1, &tbufp)) {
  	    too_long = 1;
--- 793,799 ----
      /* Add one at the start */
      if (!safe_strl(r, rlen, tbuf1, &tbufp)) {
        if (gargs->target != EDIT_FIRST) {
! 	for (last = 0; last < (size_t) haystack->len; last++) {
  	  /* Add the next character */
  	  if (safe_ansi_string(haystack, last, 1, tbuf1, &tbufp)) {
  	    too_long = 1;
***************
*** 804,816 ****
  	      ansi_long_flag = 1;
  	  }
  	  /* Copy in r */
! 	  if (safe_strl(r, rlen, tbuf1, &tbufp)) {
  	    too_long = 1;
  	    break;
  	  }
  	  if (!ansi_long_flag) {
  	    if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r,
! 			    ANSI_NORMAL))
  	      ansi_long_flag = 1;
  	  }
  	}
--- 804,816 ----
  	      ansi_long_flag = 1;
  	  }
  	  /* Copy in r */
! 	  if (safe_str(r, tbuf1, &tbufp)) {
  	    too_long = 1;
  	    break;
  	  }
  	  if (!ansi_long_flag) {
  	    if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r,
! 			    ANSI_END))
  	      ansi_long_flag = 1;
  	  }
  	}
***************
*** 826,832 ****
  
      haystack = parse_ansi_string(s);
  
!     while (last < haystack->len
  	   && (p = strstr(haystack->text + last, val)) != NULL) {
        if (safe_ansi_string(haystack, last, p - (haystack->text + last),
  			   tbuf1, &tbufp)) {
--- 826,832 ----
  
      haystack = parse_ansi_string(s);
  
!     while (last < (size_t) haystack->len
  	   && (p = strstr(haystack->text + last, val)) != NULL) {
        if (safe_ansi_string(haystack, last, p - (haystack->text + last),
  			   tbuf1, &tbufp)) {
***************
*** 840,859 ****
        }
  
        /* Copy in r */
!       if (safe_strl(r, rlen, tbuf1, &tbufp)) {
  	too_long = 1;
  	break;
        }
        if (!ansi_long_flag) {
! 	if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r,
! 			ANSI_NORMAL))
  	  ansi_long_flag = 1;
        }
        last = p - haystack->text + vlen;
        if (gargs->target == EDIT_FIRST)
  	break;
      }
!     if (last < haystack->len && !too_long) {
        safe_ansi_string(haystack, last, haystack->len, tbuf1, &tbufp);
        if (!ansi_long_flag) {
  	if (safe_ansi_string(haystack, last, haystack->len, tbuf_ansi, &tbufap))
--- 840,858 ----
        }
  
        /* Copy in r */
!       if (safe_str(r, tbuf1, &tbufp)) {
  	too_long = 1;
  	break;
        }
        if (!ansi_long_flag) {
! 	if (safe_format(tbuf_ansi, &tbufap, "%s%s%s", ANSI_HILITE, r, ANSI_END))
  	  ansi_long_flag = 1;
        }
        last = p - haystack->text + vlen;
        if (gargs->target == EDIT_FIRST)
  	break;
      }
!     if (last < (size_t) haystack->len && !too_long) {
        safe_ansi_string(haystack, last, haystack->len, tbuf1, &tbufp);
        if (!ansi_long_flag) {
  	if (safe_ansi_string(haystack, last, haystack->len, tbuf_ansi, &tbufap))
***************
*** 1085,1091 ****
     */
    if (wildcard(pattern) && AF_Wizard(atr) && !God(player))
      return 0;
!   return do_set_atr(thing, AL_NAME(atr), NULL, player, 0) == 1;
  }
  
  /** Clear an attribute.
--- 1084,1103 ----
     */
    if (wildcard(pattern) && AF_Wizard(atr) && !God(player))
      return 0;
! 
!   switch (wipe_atr(thing, AL_NAME(atr), player)) {
!   case 0:
!     return 0;
!   case AE_SAFE:
!     notify_format(player, T("Attribute %s is SAFE. Set it !SAFE to modify it."),
! 		  AL_NAME(atr));
!     return 0;
!   case AE_ERROR:
!     notify_format(player, T("Unable to wipe attribute %s"), AL_NAME(atr));
!     return 0;
!   default:
!     return 1;
!   }
  }
  
  /** Clear an attribute.
***************
*** 1100,1105 ****
--- 1112,1118 ----
  {
    dbref thing;
    char *pattern;
+   int wiped;
  
    if ((pattern = strchr(name, '/')) != NULL)
      *pattern++ = '\0';
***************
*** 1120,1131 ****
      return;
    }
  
!   we_are_wiping = 1;
! 
!   if (!atr_iter_get(player, thing, pattern, 0, wipe_helper, NULL))
      notify(player, T("No attributes wiped."));
!   else
!     notify(player, T("Attributes wiped."));
! 
!   we_are_wiping = 0;
  }
--- 1133,1147 ----
      return;
    }
  
!   wiped = atr_iter_get(player, thing, pattern, 0, wipe_helper, NULL);
!   switch (wiped) {
!   case 0:
      notify(player, T("No attributes wiped."));
!     break;
!   case 1:
!     notify(player, T("One attribute wiped."));
!     break;
!   default:
!     notify_format(player, T("%d attributes wiped."), wiped);
!   }
  }
*** 1_8_2p1/src/speech.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/speech.c	Sat Jan 27 02:21:10 2007
***************
*** 13,20 ****
  #include <string.h>
  #include <stdlib.h>
  #include "conf.h"
- #include "ansi.h"
  #include "externs.h"
  #include "mushdb.h"
  #include "dbdefs.h"
  #include "lock.h"
--- 13,20 ----
  #include <string.h>
  #include <stdlib.h>
  #include "conf.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "mushdb.h"
  #include "dbdefs.h"
  #include "lock.h"
***************
*** 130,136 ****
    recurse = 1;			/* Protect use from recursive teach */
    notify_except(Contents(loc), NOTHING,
  		tprintf(T("%s types --> %s%s%s"), spname(player),
! 			ANSI_HILITE, tbuf1, ANSI_NORMAL), NA_INTER_HEAR);
    command = mush_strdup(tbuf1, "string");	/* process_command is destructive */
    process_command(player, command, cause, 1);
    mush_free(command, "string");
--- 130,136 ----
    recurse = 1;			/* Protect use from recursive teach */
    notify_except(Contents(loc), NOTHING,
  		tprintf(T("%s types --> %s%s%s"), spname(player),
! 			ANSI_HILITE, tbuf1, ANSI_END), NA_INTER_HEAR);
    command = mush_strdup(tbuf1, "string");	/* process_command is destructive */
    process_command(player, command, cause, 1);
    mush_free(command, "string");
*** 1_8_2p1/src/sql.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/sql.c	Sat Jan 27 02:21:10 2007
***************
*** 30,35 ****
--- 30,36 ----
  #include "dbdefs.h"
  #include "conf.h"
  #include "confmagic.h"
+ #include "ansi.h"
  
  #ifdef HAS_MYSQL
  static MYSQL *mysql_struct = NULL;
***************
*** 167,172 ****
--- 168,177 ----
    int funccount = 0;
    int do_fieldnames = 0;
    int i;
+   char buffs[9][BUFFER_LEN];
+   char *tbp;
+   char *cell;
+   ansi_string *as;
    MYSQL_FIELD *fields;
  
    if (!Sql_Ok(executor)) {
***************
*** 228,234 ****
      strncpy(numbuff, unparse_integer(rownum), 20);
      wenv[0] = numbuff;
      for (i = 0; (i < numfields) && (i < 9); i++) {
!       wenv[i + 1] = row_p[i];
        if (!wenv[i + 1])
  	wenv[i + 1] = (char *) "";
      }
--- 233,258 ----
      strncpy(numbuff, unparse_integer(rownum), 20);
      wenv[0] = numbuff;
      for (i = 0; (i < numfields) && (i < 9); i++) {
!       cell = row_p[i];
!       if (strchr(cell, ESC_CHAR)) {
! 	/* Old style ANSI string */
! 	tbp = buffs[i];
! 	as = parse_ansi_string_real(cell, 1);
! 	safe_ansi_string(as, 0, as->len, buffs[i], &tbp);
! 	*tbp = '\0';
! 	free_ansi_string(as);
! 	cell = buffs[i];
!       } else if (strchr(cell, TAG_START)) {
! 	/* Either old or new style ANSI string,
! 	 * We assume new style. */
! 	tbp = buffs[i];
! 	as = parse_ansi_string_real(cell, 2);
! 	safe_ansi_string(as, 0, as->len, buffs[i], &tbp);
! 	*tbp = '\0';
! 	free_ansi_string(as);
! 	cell = buffs[i];
!       }
!       wenv[i + 1] = cell;
        if (!wenv[i + 1])
  	wenv[i + 1] = (char *) "";
      }
***************
*** 249,258 ****
--- 273,285 ----
    MYSQL_ROW row_p;
    char *rowsep = (char *) " ";
    char *fieldsep = (char *) " ";
+   char tbuf[BUFFER_LEN], *tbp;
+   char *cell;
    int affected_rows;
    int rownum;
    int i;
    int numfields;
+   ansi_string *as;
  
    if (!Sql_Ok(executor)) {
      safe_str(T(e_perm), buff, bp);
***************
*** 296,304 ****
  	if (safe_str(fieldsep, buff, bp))
  	  goto finished;
        }
!       if (row_p[i] && *row_p[i])
! 	if (safe_str(row_p[i], buff, bp))
  	  goto finished;	/* We filled the buffer, best stop */
      }
    }
  finished:
--- 323,351 ----
  	if (safe_str(fieldsep, buff, bp))
  	  goto finished;
        }
!       if (row_p[i] && *row_p[i]) {
! 	cell = row_p[i];
! 	if (strchr(cell, ESC_CHAR)) {
! 	  /* Old style ANSI string */
! 	  tbp = tbuf;
! 	  as = parse_ansi_string_real(cell, 1);
! 	  safe_ansi_string(as, 0, as->len, tbuf, &tbp);
! 	  *tbp = '\0';
! 	  free_ansi_string(as);
! 	  cell = tbuf;
! 	} else if (strchr(cell, TAG_START)) {
! 	  /* Either old or new style ANSI string,
! 	   * We assume new style. */
! 	  tbp = tbuf;
! 	  as = parse_ansi_string_real(cell, 2);
! 	  safe_ansi_string(as, 0, as->len, tbuf, &tbp);
! 	  *tbp = '\0';
! 	  free_ansi_string(as);
! 	  cell = tbuf;
! 	}
! 	if (safe_str(cell, buff, bp))
  	  goto finished;	/* We filled the buffer, best stop */
+       }
      }
    }
  finished:
***************
*** 342,347 ****
--- 389,397 ----
    int numfields;
    char *cell;
    MYSQL_FIELD *fields;
+   char tbuf[BUFFER_LEN];
+   char *tbp;
+   ansi_string *as;
    int i;
  
    qres = sql_query(arg_left, &affected_rows);
***************
*** 367,372 ****
--- 417,442 ----
      if (numfields > 0) {
        for (i = 0; i < numfields; i++) {
  	cell = row_p[i];
+ 	if (cell && *cell) {
+ 	  if (strchr(cell, ESC_CHAR)) {
+ 	    /* Old style ANSI string */
+ 	    tbp = tbuf;
+ 	    as = parse_ansi_string_real(cell, 1);
+ 	    safe_ansi_string(as, 0, as->len, tbuf, &tbp);
+ 	    *tbp = '\0';
+ 	    free_ansi_string(as);
+ 	    cell = tbuf;
+ 	  } else if (strchr(cell, TAG_START)) {
+ 	    /* Either old or new style ANSI string,
+ 	     * We assume new style. */
+ 	    tbp = tbuf;
+ 	    as = parse_ansi_string_real(cell, 2);
+ 	    safe_ansi_string(as, 0, as->len, tbuf, &tbp);
+ 	    *tbp = '\0';
+ 	    free_ansi_string(as);
+ 	    cell = tbuf;
+ 	  }
+ 	}
  	notify_format(player, "Row %d, Field %s: %s",
  		      rownum, fields[i].name, (cell && *cell) ? cell : "NULL");
        }
*** 1_8_2p1/src/strutil.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/strutil.c	Sat Jan 27 02:21:10 2007
***************
*** 16,37 ****
  #include "copyrite.h"
  #include "conf.h"
  #include "case.h"
- #include "ansi.h"
  #include "pueblo.h"
  #include "parse.h"
  #include "externs.h"
  #include "mymalloc.h"
  #include "log.h"
  #include "confmagic.h"
  
  char *next_token(char *str, char sep);
  int format_long(long val, char *buff, char **bp, int maxlen, int base);
! static char *
  mush_strndup(const char *src, size_t len, const char *check)
    __attribute_malloc__;
  
  /* Duplicate the first len characters of s */
!     static char *mush_strndup(const char *src, size_t len, const char *check)
  {
    char *copy;
    size_t rlen = strlen(src);
--- 16,38 ----
  #include "copyrite.h"
  #include "conf.h"
  #include "case.h"
  #include "pueblo.h"
  #include "parse.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "mymalloc.h"
  #include "log.h"
  #include "confmagic.h"
  
  char *next_token(char *str, char sep);
  int format_long(long val, char *buff, char **bp, int maxlen, int base);
! 
! char *
  mush_strndup(const char *src, size_t len, const char *check)
    __attribute_malloc__;
  
  /* Duplicate the first len characters of s */
!     char *mush_strndup(const char *src, size_t len, const char *check)
  {
    char *copy;
    size_t rlen = strlen(src);
***************
*** 48,53 ****
--- 49,55 ----
    return copy;
  }
  
+ 
  /** Our version of strdup, with memory leak checking.
   * This should be used in preference to strdup, and in assocation
   * with mush_free().
***************
*** 1009,1016 ****
  {
    /* move pointer to start of the next token */
  
!   while (*str && (*str != sep))
      str++;
    if (!*str)
      return NULL;
    str++;
--- 1011,1032 ----
  {
    /* move pointer to start of the next token */
  
!   while (*str) {
!     if (*str == sep) {
!       break;
!     }
!     switch (*str) {
!     case TAG_START:
!       while (*str && *str != TAG_END)
! 	str++;
!       break;
!     case ESC_CHAR:
!       while (*str && *str != 'm')
! 	str++;
!       break;
!     }
      str++;
+   }
    if (!*str)
      return NULL;
    str++;
***************
*** 1070,1135 ****
    return n;
  }
  
- /** A version of strlen that ignores ansi and HTML sequences.
-  * \param p string to get length of.
-  * \return length of string p, not including ansi/html sequences.
-  */
- int
- ansi_strlen(const char *p)
- {
-   int i = 0;
- 
-   if (!p)
-     return 0;
- 
-   while (*p) {
-     if (*p == ESC_CHAR) {
-       while ((*p) && (*p != 'm'))
- 	p++;
-     } else if (*p == TAG_START) {
-       while ((*p) && (*p != TAG_END))
- 	p++;
-     } else {
-       i++;
-     }
-     p++;
-   }
-   return i;
- }
- 
- /** Returns the apparent length of a string, up to numchars visible 
-  * characters. The apparent length skips over nonprinting ansi and
-  * tags.
-  * \param p string.
-  * \param numchars maximum size to report.
-  * \return apparent length of string.
-  */
- int
- ansi_strnlen(const char *p, size_t numchars)
- {
-   int i = 0;
- 
-   if (!p)
-     return 0;
-   while (*p && numchars > 0) {
-     if (*p == ESC_CHAR) {
-       while ((*p) && (*p != 'm')) {
- 	p++;
- 	i++;
-       }
-     } else if (*p == TAG_START) {
-       while ((*p) && (*p != TAG_END)) {
- 	p++;
- 	i++;
-       }
-     } else
-       numchars--;
-     i++;
-     p++;
-   }
-   return i;
- }
- 
  /** Given a string, a word, and a separator, remove first occurence
   * of the word from the string. Destructive.
   * \param list a string containing a separated list.
--- 1086,1091 ----
***************
*** 1203,1252 ****
  
  }
  
- /** Strip all ansi and html markup from a string. As a side effect,
-  * stores the length of the stripped string in a provided address.
-  * NOTE! Length returned is length *including* the terminating NULL,
-  * because we usually memcpy the result.
-  * \param orig string to strip.
-  * \param s_len address to store length of stripped string, if provided.
-  * \return pointer to static buffer containing stripped string.
-  */
- char *
- remove_markup(const char *orig, size_t * s_len)
- {
-   static char buff[BUFFER_LEN];
-   char *bp = buff;
-   const char *q;
-   size_t len = 0;
- 
-   if (!orig) {
-     if (s_len)
-       *s_len = 0;
-     return NULL;
-   }
- 
-   for (q = orig; *q;) {
-     switch (*q) {
-     case ESC_CHAR:
-       /* Skip over ansi */
-       while (*q && *q++ != 'm') ;
-       break;
-     case TAG_START:
-       /* Skip over HTML */
-       while (*q && *q++ != TAG_END) ;
-       break;
-     default:
-       safe_chr(*q++, buff, &bp);
-       len++;
-     }
-   }
-   *bp = '\0';
-   if (s_len)
-     *s_len = len + 1;
-   return buff;
- }
- 
- 
  /** Safely append an int to a string. Returns a true value on failure.
   * This will someday take extra arguments for use with our version 
   * of snprintf. Please try not to use it.
--- 1159,1164 ----
***************
*** 1461,1874 ****
  }
  #endif				/* HAS_STRXFRM && !WIN32 */
  
- /** Return a string pointer past any ansi/html markup at the start.
-  * \param p a string.
-  * \return pointer to string after any initial ansi/html markup.
-  */
- char *
- skip_leading_ansi(const char *p)
- {
-   if (!p)
-     return NULL;
-   while (*p == ESC_CHAR || *p == TAG_START) {
-     if (*p == ESC_CHAR) {
-       while (*p && *p != 'm')
- 	p++;
-     } else {			/* TAG_START */
-       while (*p && *p != TAG_END)
- 	p++;
-     }
-     if (*p)
-       p++;
-   }
-   return (char *) p;
- 
- }
- 
- /** Convert a string into an ansi_string.
-  * This takes a string that may contain ansi/html markup codes and
-  * converts it to an ansi_string structure that separately stores
-  * the plain string and the markup codes for each character.
-  * \param src string to parse.
-  * \return pointer to an ansi_string structure representing the src string.
-  */
- ansi_string *
- parse_ansi_string(const char *src)
- {
-   ansi_string *data;
-   char *y, *current = NULL;
-   size_t p = 0;
- 
-   if (!src)
-     return NULL;
- 
-   data = mush_malloc(sizeof *data, "ansi_string");
-   if (!data)
-     return NULL;
- 
-   data->len = ansi_strlen(src);
- 
-   while (*src) {
-     y = skip_leading_ansi(src);
-     if (y != src) {
-       if (current)
- 	mush_free(current, "markup_codes");
-       current = mush_strndup(src, y - src, "markup_codes");
-       src = y;
-     }
-     if (current)
-       data->codes[p] = mush_strdup(current, "markup_codes");
-     else
-       data->codes[p] = NULL;
-     data->text[p] = *src;
-     if (*src)
-       src++;
-     p++;
-   }
-   data->text[p] = '\0';
- 
-   while (p <= data->len) {
-     data->codes[p] = NULL;
-     p++;
-   }
- 
-   if (current)
-     mush_free(current, "markup_codes");
- 
-   return data;
- }
- 
- 
- /** Fill up an ansi_string with codes so that when a code starts it
-  * applies to all the following characters until there's a new code.
-  * \param as pointer to an ansi_string to populate codes in.
-  */
- void
- populate_codes(ansi_string *as)
- {
-   size_t p;
-   char *current = NULL;
- 
-   if (!as)
-     return;
- 
-   for (p = 0; p < as->len; p++)
-     if (as->codes[p]) {
-       if (current)
- 	mush_free(current, "markup_codes");
-       current = mush_strdup(as->codes[p], "markup_codes");
-     } else {
-       if (!current)
- 	current = mush_strdup(ANSI_NORMAL, "markup_codes");
-       as->codes[p] = mush_strdup(current, "markup_codes");
-     }
-   if (current)
-     mush_free(current, "markup_codes");
- }
- 
- /** Strip out codes from an ansi_string, leaving in only the codes where
-  * they change.
-  * \param as pointer to an ansi_string.
-  */
- void
- depopulate_codes(ansi_string *as)
- {
-   size_t p, m;
-   int normal = 1;
- 
-   if (!as)
-     return;
- 
-   for (p = 0; p <= as->len; p++) {
-     if (as->codes[p]) {
-       if (normal) {
- 	if (strcmp(as->codes[p], ANSI_NORMAL) == 0) {
- 	  mush_free(as->codes[p], "markup_codes");
- 	  as->codes[p] = NULL;
- 	  continue;
- 	} else {
- 	  normal = 0;
- 	}
-       }
- 
-       m = p;
-       for (p++; p < as->len; p++) {
- 	if (as->codes[p] && strcmp(as->codes[p], as->codes[m]) == 0) {
- 	  mush_free(as->codes[p], "markup_codes");
- 	  as->codes[p] = NULL;
- 	} else {
- 	  p--;
- 	  break;
- 	}
-       }
-     }
-   }
- }
- 
- /** Reverse an ansi string, preserving its ansification.
-  * This function destructively modifies the ansi_string passed.
-  * \param as pointer to an ansi string.
-  */
- void
- flip_ansi_string(ansi_string *as)
- {
-   int p, n;
- 
-   populate_codes(as);
- 
-   for (p = 0, n = as->len - 1; p < n; p++, n--) {
-     char *tcode;
-     char t;
- 
-     tcode = as->codes[p];
-     t = as->text[p];
-     as->codes[p] = as->codes[n];
-     as->text[p] = as->text[n];
-     as->codes[n] = tcode;
-     as->text[n] = t;
-   }
- }
- 
- 
- static int is_ansi_code(const char *s);
- static int is_start_html_code(const char *s) __attribute__ ((__unused__));
- static int is_end_html_code(const char *s);
- /** Is s a string that signifies the end of ANSI codes? */
- #define is_end_ansi_code(s) (!strcmp((s),ANSI_NORMAL))
- 
- 
- static int
- is_ansi_code(const char *s)
- {
-   return s && *s == ESC_CHAR;
- }
- 
- static int
- is_start_html_code(const char *s)
- {
-   return s && *s == TAG_START && *(s + 1) != '/';
- }
- 
- static int
- is_end_html_code(const char *s)
- {
-   return s && *s == TAG_START && *(s + 1) == '/';
- }
- 
- /** Free an ansi_string.
-  * \param as pointer to ansi_string to free.
-  */
- void
- free_ansi_string(ansi_string *as)
- {
-   int p;
- 
-   if (!as)
-     return;
-   for (p = as->len; p >= 0; p--) {
-     if (as->codes[p])
-       mush_free(as->codes[p], "markup_codes");
-   }
-   mush_free(as, "ansi_string");
- }
- 
- /** Safely append an ansi_string into a buffer as a real string.
-  * \param as pointer to ansi_string to append.
-  * \param start position in as to start copying from.
-  * \param len length in characters to copy from as.
-  * \param buff buffer to insert into.
-  * \param bp pointer to pointer to insertion point of buff.
-  * \retval 0 success.
-  * \retval 1 failure.
-  */
- int
- safe_ansi_string(ansi_string *as, size_t start, size_t len, char *buff,
- 		 char **bp)
- {
-   int p, q;
-   int in_ansi = 0;
-   int in_html = 0;
- 
-   if (!as)
-     return 1;
- 
-   depopulate_codes(as);
- 
-   if (start > as->len || len == 0 || as->len == 0)
-     return safe_str("", buff, bp);
- 
-   /* Find the starting codes by working our way backward until we
-    * reach some opening codes, and then working our way back from there
-    * until we hit a non-opening code or non-code 
-    */
-   p = start;
-   while ((p >= 0) && (as->codes[p] == NULL))
-     p--;
-   /* p is now either <0 or pointing to a code */
-   if ((p >= 0) && !is_end_html_code(as->codes[p]) &&
-       !is_end_ansi_code(as->codes[p])) {
-     /* p is now pointing to a starting code */
-     q = p;
-     while ((q >= 0) && as->codes[q] && !is_end_html_code(as->codes[q]) &&
- 	   !is_end_ansi_code(as->codes[q])) {
-       if (is_ansi_code(as->codes[q]))
- 	in_ansi = 1;
-       else if (is_start_html_code(as->codes[q]))
- 	in_html++;
-       q--;
-     }
-     /* p is now pointing to the first starting code, and we know if we're
-      * in ansi, html, or both. We also know how many html tags have been
-      * opened.
-      */
-   }
- 
-   /* Copy the text. The right thing to do now would be to have a stack
-    * of open html tags and clear in_html once all of the tags have
-    * been closed. We don't quite do that, alas.
-    */
-   for (p = (int) start; p < (int) (start + len) && p < (int) as->len; p++) {
-     if (as->codes[p]) {
-       if (safe_str(as->codes[p], buff, bp))
- 	return 1;
-       if (is_end_ansi_code(as->codes[p]))
- 	in_ansi = 0;
-       else if (is_ansi_code(as->codes[p]))
- 	in_ansi = 1;
-       if (is_end_html_code(as->codes[p]))
- 	in_html--;
-       else if (is_start_html_code(as->codes[p]))
- 	in_html++;
-     }
-     if (safe_chr(as->text[p], buff, bp))
-       return 1;
-   }
- 
-   /* Output (only) closing codes if needed. */
-   while (p <= (int) as->len) {
-     if (!in_ansi && !in_html)
-       break;
-     if (as->codes[p]) {
-       if (is_end_ansi_code(as->codes[p])) {
- 	in_ansi = 0;
- 	if (safe_str(as->codes[p], buff, bp))
- 	  return 1;
-       } else if (is_end_html_code(as->codes[p])) {
- 	in_html--;
- 	if (safe_str(as->codes[p], buff, bp))
- 	  return 1;
-       }
-     }
-     p++;
-   }
-   if (in_ansi)
-     safe_str(ANSI_NORMAL, buff, bp);
-   return 0;
- }
- 
- /** Safely append an ansi_string into a buffer as a real string,
-  * with extra copying of starting tags (for wrap()/align()).
-  * \param as pointer to ansi_string to append.
-  * \param start position in as to start copying from.
-  * \param len length in characters to copy from as.
-  * \param buff buffer to insert into.
-  * \param bp pointer to pointer to insertion point of buff.
-  * \retval 0 success.
-  * \retval 1 failure.
-  */
- int
- safe_ansi_string2(ansi_string *as, size_t start, size_t len, char *buff,
- 		  char **bp)
- {
-   int p, q;
-   int in_ansi = 0;
-   int in_html = 0;
- 
-   if (!as)
-     return 1;
- 
-   depopulate_codes(as);
- 
-   if (start > as->len || len == 0 || as->len == 0)
-     return safe_str("", buff, bp);
- 
-   /* Find the starting codes by working our way backward until we
-    * reach some opening codes, and then working our way back from there
-    * until we hit a non-opening code or non-code 
-    */
-   p = start;
-   while ((p >= 0) && (as->codes[p] == NULL))
-     p--;
-   /* p is now either <0 or pointing to a code */
-   if ((p >= 0) && !is_end_html_code(as->codes[p]) &&
-       !is_end_ansi_code(as->codes[p])) {
-     /* p is now pointing to a starting code */
-     q = p;
-     while ((q >= 0) && as->codes[q] && !is_end_html_code(as->codes[q]) &&
- 	   !is_end_ansi_code(as->codes[q])) {
-       if (is_ansi_code(as->codes[q]))
- 	in_ansi = 1;
-       else if (is_start_html_code(as->codes[q]))
- 	in_html++;
-       q--;
-     }
-     /* p is now pointing to the first starting code, and we know if we're
-      * in ansi, html, or both. We also know how many html tags have been
-      * opened.
-      */
-     /* Except there's this one problem - Now we know it, we weren't
-      * doing anything with it.
-      */
-     for (q = q + 1; q <= p; q++) {
-       if (safe_str(as->codes[q], buff, bp))
- 	return 1;
-     }
-   }
- 
-   /* Copy the text. The right thing to do now would be to have a stack
-    * of open html tags and clear in_html once all of the tags have
-    * been closed. We don't quite do that, alas.
-    */
-   for (p = (int) start; p < (int) (start + len) && p < (int) as->len; p++) {
-     if (as->codes[p]) {
-       if (safe_str(as->codes[p], buff, bp))
- 	return 1;
-       if (is_end_ansi_code(as->codes[p]))
- 	in_ansi = 0;
-       else if (is_ansi_code(as->codes[p]))
- 	in_ansi = 1;
-       if (is_end_html_code(as->codes[p]))
- 	in_html--;
-       else if (is_start_html_code(as->codes[p]))
- 	in_html++;
-     }
-     if (safe_chr(as->text[p], buff, bp))
-       return 1;
-   }
- 
-   /* Output (only) closing codes if needed. */
-   while (p <= (int) as->len) {
-     if (!in_ansi && !in_html)
-       break;
-     if (as->codes[p]) {
-       if (is_end_ansi_code(as->codes[p])) {
- 	in_ansi = 0;
- 	if (safe_str(as->codes[p], buff, bp))
- 	  return 1;
-       } else if (is_end_html_code(as->codes[p])) {
- 	in_html--;
- 	if (safe_str(as->codes[p], buff, bp))
- 	  return 1;
-       }
-     }
-     p++;
-   }
-   if (in_ansi)
-     safe_str(ANSI_NORMAL, buff, bp);
-   return 0;
- }
- 
- 
  /** Safely append a list item to a buffer, possibly with punctuation
   * and conjunctions.
   * Given the current item number in a list, whether it's the last item
--- 1373,1378 ----
*** 1_8_2p1/src/unparse.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/unparse.c	Sat Jan 27 02:21:10 2007
***************
*** 151,157 ****
        bp = buf;
        if (ANSI_NAMES && ShowAnsi(player) && !got_nameformat)
  	safe_format(buf, &bp, "%s%s%s(#%d%s)", ANSI_HILITE, tbuf1,
! 		    ANSI_NORMAL, loc, unparse_flags(loc, player));
        else
  	safe_format(buf, &bp, "%s(#%d%s)", tbuf1, loc,
  		    unparse_flags(loc, player));
--- 151,157 ----
        bp = buf;
        if (ANSI_NAMES && ShowAnsi(player) && !got_nameformat)
  	safe_format(buf, &bp, "%s%s%s(#%d%s)", ANSI_HILITE, tbuf1,
! 		    ANSI_END, loc, unparse_flags(loc, player));
        else
  	safe_format(buf, &bp, "%s(#%d%s)", tbuf1, loc,
  		    unparse_flags(loc, player));
***************
*** 160,166 ****
        /* show only the name */
        if (ANSI_NAMES && ShowAnsi(player) && !got_nameformat) {
  	bp = buf;
! 	safe_format(buf, &bp, "%s%s%s", ANSI_HILITE, tbuf1, ANSI_NORMAL);
  	*bp = '\0';
        } else
  	strcpy(buf, tbuf1);
--- 160,166 ----
        /* show only the name */
        if (ANSI_NAMES && ShowAnsi(player) && !got_nameformat) {
  	bp = buf;
! 	safe_format(buf, &bp, "%s%s%s", ANSI_HILITE, tbuf1, ANSI_END);
  	*bp = '\0';
        } else
  	strcpy(buf, tbuf1);
*** 1_8_2p1/src/utils.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/utils.c	Sat Jan 27 02:21:10 2007
***************
*** 36,41 ****
--- 36,42 ----
  
  #include "match.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "mushdb.h"
  #include "mymalloc.h"
  #include "log.h"
***************
*** 49,55 ****
  dbref find_entrance(dbref door);
  void initialize_mt(void);
  static unsigned long genrand_int32(void);
- static long genrand_int31(void);
  static void init_genrand(unsigned long);
  static void init_by_array(unsigned long *, int);
  
--- 50,55 ----
***************
*** 258,264 ****
  
    int old_re_subpatterns;
    int *old_re_offsets;
!   char *old_re_from;
  
    old_re_subpatterns = global_eval_context.re_subpatterns;
    old_re_offsets = global_eval_context.re_offsets;
--- 258,264 ----
  
    int old_re_subpatterns;
    int *old_re_offsets;
!   ansi_string *old_re_from;
  
    old_re_subpatterns = global_eval_context.re_subpatterns;
    old_re_offsets = global_eval_context.re_offsets;
***************
*** 626,637 ****
    return y;
  }
  
- /* generates a random number on [0,0x7fffffff]-interval */
- static long
- genrand_int31(void)
- {
-   return (long) (genrand_int32() >> 1);
- }
  
  /** Get a uniform random long between low and high values, inclusive.
   * Based on MUX's RandomINT32()
--- 626,631 ----
***************
*** 677,683 ****
    n_limit = ULONG_MAX - (ULONG_MAX % x);
  
    do {
!     n = genrand_int31();
    } while (n >= n_limit);
  
    return low + (n % x);
--- 671,677 ----
    n_limit = ULONG_MAX - (ULONG_MAX % x);
  
    do {
!     n = genrand_int32();
    } while (n >= n_limit);
  
    return low + (n % x);
*** 1_8_2p1/src/wild.c	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/src/wild.c	Sat Jan 27 02:21:10 2007
***************
*** 30,35 ****
--- 30,36 ----
  #include "conf.h"
  #include "case.h"
  #include "externs.h"
+ #include "ansi.h"
  #include "mymalloc.h"
  #include "parse.h"
  #include "pcre.h"
***************
*** 51,58 ****
  
  static int wild1
    (const char *RESTRICT tstr, const char *RESTRICT dstr, int arg,
!    char *RESTRICT wbuf, int cs);
! static int wild(const char *RESTRICT s, const char *RESTRICT d, int p, int cs);
  static int check_literals(const char *RESTRICT tstr, const char *RESTRICT dstr,
  			  int cs);
  static char *strip_backslashes(const char *str);
--- 52,60 ----
  
  static int wild1
    (const char *RESTRICT tstr, const char *RESTRICT dstr, int arg,
!    char **RESTRICT wbuf, int cs, char **ary, int max);
! static int wild(const char *RESTRICT s, const char *RESTRICT d, int p, int cs,
! 		char **ary, int max);
  static int check_literals(const char *RESTRICT tstr, const char *RESTRICT dstr,
  			  int cs);
  static char *strip_backslashes(const char *str);
***************
*** 271,280 ****
   */
  static int
  wild1(const char *RESTRICT tstr, const char *RESTRICT dstr, int arg,
!       char *RESTRICT wbuf, int cs)
  {
    const char *datapos;
-   char *wnext;
    int argpos, numextra;
  
    while (*tstr != '*') {
--- 273,281 ----
   */
  static int
  wild1(const char *RESTRICT tstr, const char *RESTRICT dstr, int arg,
!       char **RESTRICT wbuf, int cs, char **ary, int max)
  {
    const char *datapos;
    int argpos, numextra;
  
    while (*tstr != '*') {
***************
*** 286,298 ****
        if (!*dstr)
  	return 0;
  
!       global_eval_context.wnxt[arg++] = wbuf;
!       *wbuf++ = *dstr;
!       *wbuf++ = '\0';
  
        /* Jump to the fast routine if we can. */
  
!       if (arg >= NUMARGS)
  	return quick_wild_new(tstr + 1, dstr + 1, cs);
        break;
      case '\\':
--- 287,299 ----
        if (!*dstr)
  	return 0;
  
!       ary[arg++] = *wbuf;
!       *(*wbuf)++ = *dstr;
!       *(*wbuf)++ = '\0';
  
        /* Jump to the fast routine if we can. */
  
!       if (arg >= max)
  	return quick_wild_new(tstr + 1, dstr + 1, cs);
        break;
      case '\\':
***************
*** 316,323 ****
  
    /* If at end of pattern, slurp the rest, and leave. */
    if (!tstr[1]) {
!     global_eval_context.wnxt[arg] = wbuf;
!     strcpy(wbuf, dstr);
      return 1;
    }
    /* Remember current position for filling in the '*' return. */
--- 317,325 ----
  
    /* If at end of pattern, slurp the rest, and leave. */
    if (!tstr[1]) {
!     ary[arg] = *wbuf;
!     strcpy(*wbuf, dstr);
!     *wbuf += strlen(dstr) + 2;
      return 1;
    }
    /* Remember current position for filling in the '*' return. */
***************
*** 330,350 ****
        /* Fill in arguments if someone put another '*'
         * before a fixed string.
         */
!       global_eval_context.wnxt[argpos++] = wbuf;
!       *wbuf++ = '\0';
  
        /* Jump to the fast routine if we can. */
!       if (argpos >= NUMARGS)
  	return quick_wild_new(tstr, dstr, cs);
  
        /* Fill in any intervening '?'s */
        while (argpos < arg) {
! 	global_eval_context.wnxt[argpos++] = wbuf;
! 	*wbuf++ = *datapos++;
! 	*wbuf++ = '\0';
  
  	/* Jump to the fast routine if we can. */
! 	if (argpos >= NUMARGS)
  	  return quick_wild_new(tstr, dstr, cs);
        }
      }
--- 332,352 ----
        /* Fill in arguments if someone put another '*'
         * before a fixed string.
         */
!       ary[argpos++] = *wbuf;
!       *(*wbuf)++ = '\0';
  
        /* Jump to the fast routine if we can. */
!       if (argpos >= max)
  	return quick_wild_new(tstr, dstr, cs);
  
        /* Fill in any intervening '?'s */
        while (argpos < arg) {
! 	ary[argpos++] = *wbuf;
! 	*(*wbuf)++ = *datapos++;
! 	*(*wbuf)++ = '\0';
  
  	/* Jump to the fast routine if we can. */
! 	if (argpos >= max)
  	  return quick_wild_new(tstr, dstr, cs);
        }
      }
***************
*** 375,410 ****
      while (*dstr)
        dstr++;
    else {
-     wnext = wbuf;
-     wnext++;
      while (1) {
        if (EQUAL(cs, *dstr, *tstr) &&
! 	  ((arg < NUMARGS) ? wild1(tstr, dstr, arg, wnext, cs)
  	   : quick_wild_new(tstr, dstr, cs)))
  	break;
        if (!*dstr)
  	return 0;
        dstr++;
-       wnext++;
      }
    }
  
    /* Found a match!  Fill in all remaining arguments.
     * First do the '*'...
     */
!   global_eval_context.wnxt[argpos++] = wbuf;
!   strncpy(wbuf, datapos, (dstr - datapos) - numextra);
!   wbuf += (dstr - datapos) - numextra;
!   *wbuf++ = '\0';
    datapos = dstr - numextra;
  
    /* Fill in any trailing '?'s that are left. */
    while (numextra) {
!     if (argpos >= NUMARGS)
        return 1;
!     global_eval_context.wnxt[argpos++] = wbuf;
!     *wbuf++ = *datapos++;
!     *wbuf++ = '\0';
      numextra--;
    }
  
--- 377,409 ----
      while (*dstr)
        dstr++;
    else {
      while (1) {
        if (EQUAL(cs, *dstr, *tstr) &&
! 	  ((arg < max) ? wild1(tstr, dstr, arg, wbuf, cs, ary, max)
  	   : quick_wild_new(tstr, dstr, cs)))
  	break;
        if (!*dstr)
  	return 0;
        dstr++;
      }
    }
  
    /* Found a match!  Fill in all remaining arguments.
     * First do the '*'...
     */
!   ary[argpos++] = *wbuf;
!   strncpy(*wbuf, datapos, (dstr - datapos) - numextra);
!   *wbuf += (dstr - datapos) - numextra;
!   *(*wbuf)++ = '\0';
    datapos = dstr - numextra;
  
    /* Fill in any trailing '?'s that are left. */
    while (numextra) {
!     if (argpos >= max)
        return 1;
!     ary[argpos++] = *wbuf;
!     *(*wbuf)++ = *datapos++;
!     *(*wbuf)++ = '\0';
      numextra--;
    }
  
***************
*** 422,429 ****
   * Side Effect: this routine modifies the 'wnxt' global variable.
   */
  static int
! wild(const char *RESTRICT s, const char *RESTRICT d, int p, int cs)
  {
    /* Do fast match. */
    while ((*s != '*') && (*s != '?')) {
      if (*s == '\\')
--- 421,431 ----
   * Side Effect: this routine modifies the 'wnxt' global variable.
   */
  static int
! wild(const char *RESTRICT s, const char *RESTRICT d, int p, int cs,
!      char **ary, int max)
  {
+   char *buffer = wspace;
+ 
    /* Do fast match. */
    while ((*s != '*') && (*s != '?')) {
      if (*s == '\\')
***************
*** 441,447 ****
      return 0;
  
    /* Do the match. */
!   return wild1(s, d, p, wspace, cs);
  }
  
  /** Wildcard match, possibly case-sensitive, and remember the wild data.
--- 443,449 ----
      return 0;
  
    /* Do the match. */
!   return wild1(s, d, p, &buffer, cs, ary, max);
  }
  
  /** Wildcard match, possibly case-sensitive, and remember the wild data.
***************
*** 463,469 ****
      global_eval_context.wnxt[j] = (char *) NULL;
    for (j = 0; j < NUMQ; j++)
      global_eval_context.rnxt[j] = (char *) NULL;
!   return wild(s, d, 0, cs);
  }
  
  /** Regexp match, possibly case-sensitive, and remember matched subexpressions.
--- 465,491 ----
      global_eval_context.wnxt[j] = (char *) NULL;
    for (j = 0; j < NUMQ; j++)
      global_eval_context.rnxt[j] = (char *) NULL;
!   return wild(s, d, 0, cs, global_eval_context.wnxt, NUMARGS);
! }
! 
! /** Wildcard match, possibly case-sensitive, and remember the wild data.
!  *
!  * This routine will cause crashes if fed NULLs instead of strings.
!  *
!  * \param s pattern to match against.
!  * \param d string to check.
!  * \param cs if 1, case-sensitive; if 0, case-insensitive.
!  * \param ary An array to store the grabs in
!  * \param maxcount Maximum number of results ary can hold.
!  * \retval 1 d matches s.
!  * \retval 0 d doesn't match s.
!  */
! int
! wild_match_case_r(const char *RESTRICT s, const char *RESTRICT d, int cs,
! 		  char **ary, int max)
! {
!   /* Clear %0-%9 and r(0) - r(9) */
!   return wild(s, d, 0, cs, ary, max);
  }
  
  /** Regexp match, possibly case-sensitive, and remember matched subexpressions.
***************
*** 477,489 ****
   * \retval 0 d doesn't match s.
   */
  int
! regexp_match_case(const char *RESTRICT s, const char *RESTRICT d, int cs)
  {
    int j;
    pcre *re;
    int i;
    static char wtmp[NUMARGS][BUFFER_LEN];
    const char *errptr;
    int erroffset;
    int offsets[99];
    int subpatterns;
--- 499,513 ----
   * \retval 0 d doesn't match s.
   */
  int
! regexp_match_case(const char *RESTRICT s, const char *RESTRICT val, int cs)
  {
    int j;
    pcre *re;
    int i;
    static char wtmp[NUMARGS][BUFFER_LEN];
    const char *errptr;
+   const char *d;
+   size_t delenn;
    int erroffset;
    int offsets[99];
    int subpatterns;
***************
*** 498,508 ****
      return 0;
    }
    add_check("pcre");
    /*
     * Now we try to match the pattern. The relevant fields will
     * automatically be filled in by this.
     */
!   if ((subpatterns = pcre_exec(re, NULL, d, strlen(d), 0, 0, offsets, 99))
        < 0) {
      mush_free(re, "pcre");
      return 0;
--- 522,533 ----
      return 0;
    }
    add_check("pcre");
+   d = remove_markup(val, &delenn);
    /*
     * Now we try to match the pattern. The relevant fields will
     * automatically be filled in by this.
     */
!   if ((subpatterns = pcre_exec(re, NULL, d, delenn - 1, 0, 0, offsets, 99))
        < 0) {
      mush_free(re, "pcre");
      return 0;
***************
*** 552,557 ****
--- 577,584 ----
  quick_regexp_match(const char *RESTRICT s, const char *RESTRICT d, int cs)
  {
    pcre *re;
+   const char *sptr;
+   size_t slen;
    const char *errptr;
    int erroffset;
    int offsets[99];
***************
*** 573,583 ****
      return 0;
    }
    add_check("pcre");
    /*
     * Now we try to match the pattern. The relevant fields will
     * automatically be filled in by this.
     */
!   r = pcre_exec(re, NULL, d, strlen(d), 0, 0, offsets, 99);
  
    mush_free(re, "pcre");
  
--- 600,611 ----
      return 0;
    }
    add_check("pcre");
+   sptr = remove_markup(d, &slen);
    /*
     * Now we try to match the pattern. The relevant fields will
     * automatically be filled in by this.
     */
!   r = pcre_exec(re, NULL, sptr, slen - 1, 0, 0, offsets, 99);
  
    mush_free(re, "pcre");
  
*** 1_8_2p1/utils/mkcmds.sh.SH	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/utils/mkcmds.sh.SH	Sat Jan 27 02:21:10 2007
***************
*** 25,31 ****
  $startsh
  echo=$echo
  n=$n
! c=$c
  !GROK!THIS!
  
  : In the following dollars and backticks do not need the extra backslash.
--- 25,31 ----
  $startsh
  echo=$echo
  n=$n
! c="$c"
  !GROK!THIS!
  
  : In the following dollars and backticks do not need the extra backslash.
*** 1_8_2p1/win32/funs.h	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/win32/funs.h	Sat Jan 27 02:58:16 2007
***************
*** 14,19 ****
--- 14,20 ----
  FUNCTION_PROTO(fun_andflags);
  FUNCTION_PROTO(fun_andlflags);
  FUNCTION_PROTO(fun_ansi);
+ FUNCTION_PROTO(fun_ansiinspect);
  FUNCTION_PROTO(fun_aposs);
  FUNCTION_PROTO(fun_art);
  FUNCTION_PROTO(fun_asin);
*** 1_8_2p1/win32/msvc.net/pennmush.vcproj	Sun Nov 26 17:44:53 2006
--- 1_8_3p0/win32/msvc.net/pennmush.vcproj	Sat Jan 27 04:14:03 2007
***************
*** 80,86 ****
  				SuppressStartupBanner="true"
  				ProgramDatabaseFile=".\./game/pennmush.pdb"
  				SubSystem="1"
! 				StackReserveSize="8172"
  				TargetMachine="1"
  			/>
  			<Tool
--- 80,86 ----
  				SuppressStartupBanner="true"
  				ProgramDatabaseFile=".\./game/pennmush.pdb"
  				SubSystem="1"
! 				StackReserveSize="8388608"
  				TargetMachine="1"
  			/>
  			<Tool
***************
*** 176,182 ****
  				GenerateDebugInformation="true"
  				ProgramDatabaseFile=".\./game/pennmush.pdb"
  				SubSystem="1"
! 				StackReserveSize="8172"
  				TargetMachine="1"
  			/>
  			<Tool
--- 176,182 ----
  				GenerateDebugInformation="true"
  				ProgramDatabaseFile=".\./game/pennmush.pdb"
  				SubSystem="1"
! 				StackReserveSize="8388608"
  				TargetMachine="1"
  			/>
  			<Tool
***************
*** 715,720 ****
--- 715,724 ----
  			>
  		</File>
  		<File
+ 			RelativePath=".\src\markup.c"
+ 			>
+ 		</File>
+ 		<File
  			RelativePath="options.h"
  			>
  		</File>
*** 1_8_2p1/src/markup.c	Wed Dec 31 16:00:00 1969
--- 1_8_3p0/src/markup.c	Sat Jan 27 05:24:13 2007
***************
*** 0 ****
--- 1,2103 ----
+ /**
+  * \file strutil.c
+  *
+  * \brief String utilities for PennMUSH.
+  *
+  *
+  */
+ 
+ #include "config.h"
+ 
+ #include <ctype.h>
+ #include <string.h>
+ #include <stdlib.h>
+ #include <stdarg.h>
+ #include <limits.h>
+ #ifdef WIN32
+ #define snprintf(s1,n,s2,s3) sprintf_s((s1), (n), (s2), (s3))
+ #endif
+ #include "copyrite.h"
+ #include "conf.h"
+ #include "case.h"
+ #include "pueblo.h"
+ #include "parse.h"
+ #include "externs.h"
+ #include "ansi.h"
+ #include "mymalloc.h"
+ #include "log.h"
+ #include "game.h"
+ #include "confmagic.h"
+ 
+ #define ANSI_BLACK_V    (30)
+ #define ANSI_RED_V      (31)
+ #define ANSI_GREEN_V    (32)
+ #define ANSI_YELLOW_V   (33)
+ #define ANSI_BLUE_V     (34)
+ #define ANSI_MAGENTA_V  (35)
+ #define ANSI_CYAN_V     (36)
+ #define ANSI_WHITE_V    (37)
+ 
+ #define ANSI_BEGIN   "\x1B["
+ #define ANSI_FINISH  "m"
+ 
+ #define COL_NORMAL   "\x1B[0m"
+ 
+ /* COL_* and VAL_* defines */
+ 
+ #define CBIT_FLASH       (1)	 /**< ANSI flash attribute bit */
+ #define CBIT_HILITE      (2)	 /**< ANSI hilite attribute bit */
+ #define CBIT_INVERT      (4)	 /**< ANSI inverse attribute bit */
+ #define CBIT_UNDERSCORE  (8)	 /**< ANSI underscore attribute bit */
+ 
+ #define COL_HILITE      (1)	/**< ANSI hilite attribute value */
+ #define COL_UNDERSCORE  (4)	/**< ANSI underscore attribute value */
+ #define COL_FLASH       (5)	/**< ANSI flag attribute value */
+ #define COL_INVERT      (7)	/**< ANSI inverse attribute value */
+ 
+ #define COL_BLACK       (30)	/**< ANSI color black */
+ #define COL_RED         (31)	/**< ANSI color red */
+ #define COL_GREEN       (32)	/**< ANSI color green */
+ #define COL_YELLOW      (33)	/**< ANSI color yellow */
+ #define COL_BLUE        (34)	/**< ANSI color blue */
+ #define COL_MAGENTA     (35)	/**< ANSI color magenta */
+ #define COL_CYAN        (36)	/**< ANSI color cyan */
+ #define COL_WHITE       (37)	/**< ANSI color white */
+ 
+ /* Now the code */
+ 
+ HASHTAB htab_tag;  /**< Hash table of safe html tags */
+ 
+ static int write_ansi_close(char *buff, char **bp);
+ static int is_ansi_oldstyle(const char *str);
+ static int safe_markup(char const *a_tag, char *buf, char **bp, char type);
+ static int
+  safe_markup_cancel(char const *a_tag, char *buf, char **bp, char type);
+ static int
+  compare_starts(const void *a, const void *b);
+ 
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_stripansi)
+ {
+   /* Strips ANSI codes away from a given string of text. Starts by
+    * finding the '\x' character and stripping until it hits an 'm'.
+    */
+ 
+   char *cp;
+ 
+   cp = remove_markup(args[0], NULL);
+   safe_str(cp, buff, bp);
+ }
+ 
+ #ifdef ANSI_DEBUG
+ /* ARGSUSED */
+ void inspect_ansi_string(ansi_string *as, dbref who);
+ FUNCTION(fun_ansiinspect)
+ {
+   char *ptr;
+   ansi_string *as;
+   char choice;
+   if (nargs < 2 || !args[1] || !*args[1])
+     choice = 'r';
+   else
+     choice = *args[1];
+   switch (choice) {
+   case 'i':
+     as = parse_ansi_string(args[0]);
+     inspect_ansi_string(as, executor);
+     free_ansi_string(as);
+     break;
+   case 'r':
+     for (ptr = args[0]; *ptr; ptr++) {
+       if (*ptr == TAG_START)
+ 	*ptr = '<';
+       if (*ptr == TAG_END)
+ 	*ptr = '>';
+     }
+     safe_str(args[0], buff, bp);
+     break;
+   case 'l':
+     safe_integer(arglens[0], buff, bp);
+     break;
+   default:
+     safe_str("i: inspect r: raw l: length", buff, bp);
+     break;
+   }
+ }
+ #endif
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_ansi)
+ {
+   struct ansi_data colors;
+   char *save = *bp;
+   char *p;
+   int i;
+ 
+   /* Populate the colors struct */
+   define_ansi_data(&colors, args[0]);
+   if (!(colors.bits || colors.offbits || colors.fore || colors.back)) {
+     safe_str(args[1], buff, bp) || write_ansi_close(buff, bp);
+     return;
+   }
+ 
+   /* Write the colors to buff */
+   if (write_ansi_data(&colors, buff, bp)) {
+     *bp = save;
+     return;
+   }
+ 
+   /* If the contents overrun the buffer, we
+    * place an ANSI_ENDALL tag at the end */
+   if (safe_str(args[1], buff, bp) || write_ansi_close(buff, bp)) {
+     p = buff + BUFFER_LEN - 6;	/* <c/a> */
+     for (i = 10; i > 0 && *p != TAG_START; i--, p--) ;
+     if (i > 0) {
+       /* There's an extant tag, let's just replace that. */
+       *bp = p;
+       safe_str(ANSI_ENDALL, buff, bp);
+     } else {
+       *bp = buff + BUFFER_LEN - 6;
+       safe_str(ANSI_ENDALL, buff, bp);
+     }
+   }
+ }
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_html)
+ {
+   if (!Wizard(executor))
+     safe_str(T(e_perm), buff, bp);
+   else
+     safe_tag(args[0], buff, bp);
+ }
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_tag)
+ {
+   int i;
+   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0]))) {
+     safe_str("#-1", buff, bp);
+     return;
+   }
+   safe_chr(TAG_START, buff, bp);
+   safe_chr(MARKUP_HTML, buff, bp);
+   safe_strl(args[0], arglens[0], buff, bp);
+   for (i = 1; i < nargs; i++) {
+     if (ok_tag_attribute(executor, args[i])) {
+       safe_chr(' ', buff, bp);
+       safe_strl(args[i], arglens[i], buff, bp);
+     }
+   }
+   safe_chr(TAG_END, buff, bp);
+ }
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_endtag)
+ {
+   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0])))
+     safe_str("#-1", buff, bp);
+   else
+     safe_tag_cancel(args[0], buff, bp);
+ }
+ 
+ /* ARGSUSED */
+ FUNCTION(fun_tagwrap)
+ {
+   if (!Wizard(executor) && !hash_find(&htab_tag, strupper(args[0])))
+     safe_str("#-1", buff, bp);
+   else {
+     if (nargs == 2)
+       safe_tag_wrap(args[0], NULL, args[1], buff, bp, executor);
+     else
+       safe_tag_wrap(args[0], args[1], args[2], buff, bp, executor);
+   }
+ }
+ 
+ /** A version of strlen that ignores ansi and HTML sequences.
+  * \param p string to get length of.
+  * \return length of string p, not including ansi/html sequences.
+  */
+ int
+ ansi_strlen(const char *p)
+ {
+   int i = 0;
+ 
+   if (!p)
+     return 0;
+ 
+   while (*p) {
+     if (*p == ESC_CHAR) {
+       while ((*p) && (*p != 'm'))
+ 	p++;
+     } else if (*p == TAG_START) {
+       while ((*p) && (*p != TAG_END))
+ 	p++;
+     } else {
+       i++;
+     }
+     p++;
+   }
+   return i;
+ }
+ 
+ /** Compare two strings, ignoring  all ansi and html markup from a string.
+  *  Is *NOT* locale safe (a la strcoll)
+  * \param a string to compare to
+  * \param b Other string
+  * \return int - 0 is identical, -1 or 1 for difference.
+  */
+ int
+ ansi_strcmp(const char *astr, const char *bstr)
+ {
+   const char *a, *b;
+ 
+   for (a = astr, b = bstr; *a && *b;) {
+     a = skip_leading_ansi(a);
+     b = skip_leading_ansi(b);
+     if (*a != *b)
+       return (*a - *b);
+     b++;
+     a++;
+   }
+   if (*a)
+     a = skip_leading_ansi(a);
+   if (*b)
+     b = skip_leading_ansi(b);
+   return (*a - *b);
+ }
+ 
+ /** Returns the apparent length of a string, up to numchars visible
+  * characters. The apparent length skips over nonprinting ansi and
+  * tags.
+  * \param p string.
+  * \param numchars maximum size to report.
+  * \return apparent length of string.
+  */
+ int
+ ansi_strnlen(const char *p, size_t numchars)
+ {
+   size_t i = 0;
+ 
+   if (!p)
+     return 0;
+   while (*p && numchars > 0) {
+     if (*p == ESC_CHAR) {
+       while ((*p) && (*p != 'm')) {
+ 	p++;
+ 	i++;
+       }
+     } else if (*p == TAG_START) {
+       while ((*p) && (*p != TAG_END)) {
+ 	p++;
+ 	i++;
+       }
+     } else
+       numchars--;
+     i++;
+     p++;
+   }
+   return i;
+ }
+ 
+ /** Strip all ansi and html markup from a string. As a side effect,
+  * stores the length of the stripped string in a provided address.
+  * NOTE! Length returned is length *including* the terminating NULL,
+  * because we usually memcpy the result.
+  * \param orig string to strip.
+  * \param s_len address to store length of stripped string, if provided.
+  * \return pointer to static buffer containing stripped string.
+  */
+ char *
+ remove_markup(const char *orig, size_t * s_len)
+ {
+   static char buff[BUFFER_LEN];
+   char *bp = buff;
+   const char *q;
+   size_t len = 0;
+ 
+   if (!orig) {
+     if (s_len)
+       *s_len = 0;
+     return NULL;
+   }
+ 
+   for (q = orig; *q;) {
+     switch (*q) {
+     case ESC_CHAR:
+       /* Skip over ansi */
+       while (*q && *q++ != 'm') ;
+       break;
+     case TAG_START:
+       /* Skip over HTML */
+       while (*q && *q++ != TAG_END) ;
+       break;
+     default:
+       safe_chr(*q++, buff, &bp);
+       len++;
+     }
+   }
+   *bp = '\0';
+   if (s_len)
+     *s_len = len + 1;
+   return buff;
+ }
+ 
+ static char ansi_chars[50];
+ 
+ static int ansi_codes[255];
+ 
+ struct {
+   char flag;
+   int num;
+ } build_ansi_codes[] = {
+   {
+   'n', 0}, {
+   'f', COL_FLASH}, {
+   'h', COL_HILITE}, {
+   'i', COL_INVERT}, {
+   'u', COL_UNDERSCORE}, {
+   'x', COL_BLACK}, {
+   'X', COL_BLACK + 10}, {
+   'r', COL_RED}, {
+   'R', COL_RED + 10}, {
+   'g', COL_GREEN}, {
+   'G', COL_GREEN + 10}, {
+   'y', COL_YELLOW}, {
+   'Y', COL_YELLOW + 10}, {
+   'b', COL_BLUE}, {
+   'B', COL_BLUE + 10}, {
+   'm', COL_MAGENTA}, {
+   'M', COL_MAGENTA + 10}, {
+   'c', COL_CYAN}, {
+   'C', COL_CYAN + 10}, {
+   'w', COL_WHITE}, {
+   'W', COL_WHITE + 10}, {
+   '\0', 0}
+ };
+ 
+ void
+ init_ansi_codes(void)
+ {
+   int i;
+ 
+   memset(ansi_chars, 0, sizeof(ansi_chars));
+   memset(ansi_codes, 0, sizeof(ansi_codes));
+ 
+   for (i = 0; build_ansi_codes[i].flag; i++) {
+     ansi_chars[build_ansi_codes[i].num] = build_ansi_codes[i].flag;
+     ansi_codes[(unsigned char) build_ansi_codes[i].flag] =
+       build_ansi_codes[i].num;
+   }
+ }
+ 
+ int
+ read_raw_ansi_data(struct ansi_data *store, const char *codes)
+ {
+   int curnum;
+   if (codes == NULL || store == NULL)
+     return 0;
+   store->bits = 0;
+   store->offbits = 0;
+   store->fore = 0;
+   store->back = 0;
+ 
+   /* codes can point at either the ESC_CHAR or one
+    * following after. */
+ 
+   /* Skip to the first ansi number */
+   while (*codes && !isdigit(*codes) && *codes != 'm')
+     codes++;
+ 
+   memset(store, 0, sizeof(struct ansi_data));
+ 
+   while (*codes && (*codes != 'm')) {
+     curnum = atoi(codes);
+     if (curnum < 10) {
+       switch (curnum) {
+       case COL_HILITE:
+ 	store->bits ^= CBIT_HILITE;
+ 	break;
+       case COL_UNDERSCORE:
+ 	store->bits ^= CBIT_UNDERSCORE;
+ 	break;
+       case COL_FLASH:
+ 	store->bits ^= CBIT_FLASH;
+ 	break;
+       case COL_INVERT:
+ 	store->bits ^= CBIT_INVERT;
+ 	break;
+       case 0:
+ 	store->bits = 0;
+ 	store->offbits = 0;
+ 	store->fore = 'n';
+ 	store->back = 'n';
+ 	break;
+       }
+     } else if (curnum < 40) {
+       store->fore = ansi_chars[curnum];
+     } else if (curnum < 50) {
+       store->back = ansi_chars[curnum];
+     }
+     /* Skip current and find the nxt ansi number */
+     while (*codes && isdigit(*codes))
+       codes++;
+     while (*codes && !isdigit(*codes) && (*codes != 'm'))
+       codes++;
+   }
+   return 1;
+ }
+ 
+ static int
+ write_ansi_close(char *buff, char **bp)
+ {
+   int retval = 0;
+   retval += safe_chr(TAG_START, buff, bp);
+   retval += safe_chr(MARKUP_COLOR, buff, bp);
+   retval += safe_chr('/', buff, bp);
+   retval += safe_chr(TAG_END, buff, bp);
+   return retval;
+ }
+ 
+ 
+ int
+ write_ansi_data(struct ansi_data *cur, char *buff, char **bp)
+ {
+   int retval = 0;
+   retval += safe_chr(TAG_START, buff, bp);
+   retval += safe_chr(MARKUP_COLOR, buff, bp);
+   if (cur->fore == 'n') {
+     retval += safe_chr(cur->fore, buff, bp);
+   } else {
+ #define CBIT_SET(x,y) (x->bits & y)
+     if (CBIT_SET(cur, CBIT_FLASH))
+       retval += safe_chr('f', buff, bp);
+     if (CBIT_SET(cur, CBIT_HILITE))
+       retval += safe_chr('h', buff, bp);
+     if (CBIT_SET(cur, CBIT_INVERT))
+       retval += safe_chr('i', buff, bp);
+     if (CBIT_SET(cur, CBIT_UNDERSCORE))
+       retval += safe_chr('u', buff, bp);
+ #undef CBIT_SET
+ #define CBIT_SET(x,y) (x->offbits & y)
+     if (CBIT_SET(cur, CBIT_FLASH))
+       retval += safe_chr('F', buff, bp);
+     if (CBIT_SET(cur, CBIT_HILITE))
+       retval += safe_chr('H', buff, bp);
+     if (CBIT_SET(cur, CBIT_INVERT))
+       retval += safe_chr('I', buff, bp);
+     if (CBIT_SET(cur, CBIT_UNDERSCORE))
+       retval += safe_chr('U', buff, bp);
+ #undef CBIT_SET
+ 
+     if (cur->fore)
+       retval += safe_chr(cur->fore, buff, bp);
+     if (cur->back)
+       retval += safe_chr(cur->back, buff, bp);
+   }
+ 
+   retval += safe_chr(TAG_END, buff, bp);
+   return retval;
+ }
+ 
+ /* We need EDGE_UP to return 1 if:
+  * x has bit set and y's offbit does.
+  */
+ #define EDGE_UP(x,y,z) ((x->bits & z) != (y->bits & z))
+ 
+ static struct ansi_data ansi_normal = { 0, 0xFF, 'n', 0 };
+ 
+ void
+ nest_ansi_data(struct ansi_data *old, struct ansi_data *cur)
+ {
+   if (cur->fore != 'n') {
+     cur->bits |= old->bits;
+     cur->bits &= ~cur->offbits;
+     if (!cur->fore)
+       cur->fore = old->fore;
+     if (!cur->back)
+       cur->back = old->back;
+   }
+ }
+ 
+ int
+ write_raw_ansi_data(struct ansi_data *old, struct ansi_data *cur,
+ 		    char *buff, char **bp)
+ {
+   int f = 0;
+ 
+   if (cur->fore == 'n') {
+     if (old->bits || (old->fore && old->fore != 'n') || old->back) {
+       return safe_str(COL_NORMAL, buff, bp);
+     }
+   }
+   if (cur->fore == 'd')
+     cur->fore = 0;
+   if (cur->back == 'D')
+     cur->back = 0;
+ 
+   /* Do we *unset* anything in cur? */
+   if ((old->bits & ~(cur->bits)) ||
+       (old->fore && !cur->fore) || (old->back && !cur->back)) {
+     safe_str(COL_NORMAL, buff, bp);
+     old = &ansi_normal;
+   }
+ 
+   if (old->fore == cur->fore &&
+       old->back == cur->back &&
+       old->bits == cur->bits && old->offbits == cur->offbits)
+     return 0;
+ 
+   if (!(cur->fore || cur->back || cur->bits || cur->offbits)) {
+     return safe_str(COL_NORMAL, buff, bp);
+   }
+ 
+   safe_str(ANSI_BEGIN, buff, bp);
+   if (EDGE_UP(old, cur, CBIT_FLASH)) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes['f'], buff, bp);
+   }
+   if (EDGE_UP(old, cur, CBIT_HILITE)) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes['h'], buff, bp);
+   }
+   if (EDGE_UP(old, cur, CBIT_INVERT)) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes['i'], buff, bp);
+   }
+   if (EDGE_UP(old, cur, CBIT_UNDERSCORE)) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes['u'], buff, bp);
+   }
+ 
+   if (cur->fore && cur->fore != old->fore) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes[(unsigned char) cur->fore], buff, bp);
+   }
+   if (cur->back && cur->back != old->back) {
+     if (f++)
+       safe_chr(';', buff, bp);
+     safe_integer(ansi_codes[(unsigned char) cur->back], buff, bp);
+   }
+ 
+   return safe_str(ANSI_FINISH, buff, bp);
+ }
+ 
+ void
+ define_ansi_data(struct ansi_data *cur, const char *str)
+ {
+   cur->bits = 0;
+   cur->offbits = 0;
+   cur->fore = 0;
+   cur->back = 0;
+ 
+   for (; str && *str && (*str != TAG_END); str++) {
+     switch (*str) {
+     case 'n':			/* normal */
+       /* This is explicitly normal, it'll never be
+        * clored */
+       cur->bits = 0;
+       cur->fore = 'n';
+       cur->back = 0;
+       break;
+     case 'f':			/* flash */
+       cur->bits |= CBIT_FLASH;
+       break;
+     case 'h':			/* hilite */
+       cur->bits |= CBIT_HILITE;
+       break;
+     case 'i':			/* inverse */
+       cur->bits |= CBIT_INVERT;
+       break;
+     case 'u':			/* underscore */
+       cur->bits |= CBIT_UNDERSCORE;
+       break;
+     case 'F':			/* flash */
+       cur->offbits |= CBIT_FLASH;
+       break;
+     case 'H':			/* hilite */
+       cur->offbits |= CBIT_HILITE;
+       break;
+     case 'I':			/* inverse */
+       cur->offbits |= CBIT_INVERT;
+       break;
+     case 'U':			/* underscore */
+       cur->offbits |= CBIT_UNDERSCORE;
+       break;
+     case 'b':			/* blue fg */
+     case 'c':			/* cyan fg */
+     case 'g':			/* green fg */
+     case 'm':			/* magenta fg */
+     case 'r':			/* red fg */
+     case 'w':			/* white fg */
+     case 'x':			/* black fg */
+     case 'y':			/* yellow fg */
+     case 'd':			/* default fg */
+       cur->fore = *str;
+       break;
+     case 'B':			/* blue bg */
+     case 'C':			/* cyan bg */
+     case 'G':			/* green bg */
+     case 'M':			/* magenta bg */
+     case 'R':			/* red bg */
+     case 'W':			/* white bg */
+     case 'X':			/* black bg */
+     case 'Y':			/* yellow bg */
+     case 'D':			/* default fg */
+       cur->back = *str;
+       break;
+     }
+   }
+ }
+ 
+ /** Return a string pointer past any ansi/html markup at the start.
+  * \param p a string.
+  * \return pointer to string after any initial ansi/html markup.
+  */
+ 
+ char *
+ skip_leading_ansi(const char *p)
+ {
+   if (!p)
+     return NULL;
+   while (*p == ESC_CHAR || *p == TAG_START) {
+     if (*p == ESC_CHAR) {
+       while (*p && *p != 'm')
+ 	p++;
+     } else {			/* TAG_START */
+       while (*p && *p != TAG_END)
+ 	p++;
+     }
+     if (*p)
+       p++;
+   }
+   return (char *) p;
+ 
+ }
+ 
+ static char *
+ parse_tagname(const char *ptr)
+ {
+   static char tagname[BUFFER_LEN];
+   char *tag = tagname;
+   if (!ptr || !*ptr)
+     return NULL;
+   while (*ptr && !isspace(*ptr) && *ptr != TAG_END) {
+     *(tag++) = *(ptr++);
+   }
+   *tag = '\0';
+   return tagname;
+ }
+ 
+ static void
+ free_markup_info(markup_information * info)
+ {
+   if (info) {
+     if (info->start_code) {
+       mush_free(info->start_code, "markup_code");
+       info->start_code = NULL;
+     }
+     if (info->stop_code) {
+       mush_free(info->stop_code, "markup_code");
+       info->stop_code = NULL;
+     }
+   }
+ }
+ 
+ /* Is this string an old style format? */
+ static int
+ is_ansi_oldstyle(const char *source)
+ {
+   char *ptr;
+   /* First test: ESC_CHAR. If there's one this is old style. */
+   if (strchr(source, ESC_CHAR) != NULL)
+     return 1;
+   /* This usually means a TAG_START appears, but no ESC_CHAR. */
+   if (strstr(source, MARKUP_START MARKUP_HTML_STR "/") ||
+       strstr(source, MARKUP_START MARKUP_COLOR_STR "/")) {
+     /* There's a <p/ or <c/ - which is newstyle */
+     return 0;
+   }
+   /* Check for those that don't begin with p or c */
+   for (ptr = strchr(source, TAG_START); ptr; ptr = strchr(ptr + 1, TAG_START)) {
+     if ((*(ptr + 1) != MARKUP_HTML) && (*(ptr + 1) != MARKUP_COLOR)) {
+       return 1;
+     }
+   }
+   for (ptr = strchr(source, TAG_START); ptr; ptr = strchr(ptr + 1, TAG_START)) {
+     /* Check for <pre ...> */
+     if (strncasecmp(ptr + 1, "PRE", 3) == 0)
+       return 1;
+     /* And <p> or <p ...> */
+     if (!isalnum(*(ptr + 2)))
+       return 1;
+   }
+   return 0;
+ }
+ 
+ /** Convert a string into an ansi_string.
+  * This takes a string that may contain ansi/html markup codes and
+  * converts it to an ansi_string structure that separately stores
+  * the plain string and the markup codes for each character.
+  * \param source string to parse.
+  * \return pointer to an ansi_string structure representing the src string.
+  */
+ ansi_string *
+ parse_ansi_string(const char *source)
+ {
+   return parse_ansi_string_real(source, 0);
+ }
+ 
+ /** Convert a string into an ansi_string.
+  * This takes a string that may contain ansi/html markup codes and
+  * converts it to an ansi_string structure that separately stores
+  * the plain string and the markup codes for each character.
+  * \param source string to parse.
+  * \param oldstyle If true, treats it as an old style ansi string.
+  * \return pointer to an ansi_string structure representing the src string.
+  */
+ ansi_string *
+ parse_ansi_string_real(const char *source, int oldstyle)
+ {
+   ansi_string *data = NULL;
+   char src[BUFFER_LEN], *sptr;
+   char tagbuff[BUFFER_LEN];
+   char *ptr, *txt;
+   char *tmp;
+   char type;
+   int i, j;
+   int priority = 0;
+   markup_information *info;
+ 
+   if (!source)
+     return NULL;
+ 
+   if (oldstyle == 2)
+     oldstyle = is_ansi_oldstyle(source);
+   info = NULL;
+ 
+   sptr = src;
+   safe_str(source, src, &sptr);
+   *sptr = '\0';
+ 
+ 
+   data = mush_malloc(sizeof(ansi_string), "ansi_string");
+   if (!data)
+     return NULL;
+ 
+   /* Set it to zero */
+   memset(data, 0, sizeof(ansi_string));
+ 
+   txt = data->text;
+   i = 0;
+   for (ptr = src; *ptr;) {
+     /* Is this an ansi sequence? */
+     switch (*ptr) {
+     case TAG_START:
+       /* In modern Penn, this is both Pueblo/HTML and color defining code */
+       /* Find the end. */
+       for (tmp = ptr; *tmp && *tmp != TAG_END; tmp++) ;
+       if (*tmp) {
+ 	*(tmp) = '\0';
+       } else {
+ 	/* Point tmp at the end */
+ 	tmp--;
+       }
+       ptr++;
+       type = oldstyle ? MARKUP_HTML : *(ptr++);
+       switch (type) {
+       case MARKUP_HTML:
+ 	if (*ptr && *ptr != '/') {
+ 	  /* We're at the start tag. */
+ 	  info = &(data->markup[data->nmarkups++]);
+ 	  info->start_code = mush_strdup(ptr, "markup_code");
+ 	  snprintf(tagbuff, BUFFER_LEN, "/%s", parse_tagname(ptr));
+ 	  info->stop_code = mush_strdup(tagbuff, "markup_code");
+ 	  info->type = MARKUP_HTML;
+ 	  info->start = i;
+ 	  info->end = -1;
+ 	  info->priority = priority++;
+ 	} else if (*ptr) {
+ 	  /* Closing tag */
+ 	  for (j = data->nmarkups - 1; j >= 0; j--) {
+ 	    if (data->markup[j].end < 0 && data->markup[j].stop_code &&
+ 		strcasecmp(data->markup[j].stop_code, ptr) == 0) {
+ 	      break;
+ 	    }
+ 	  }
+ 	  if (j >= 0) {
+ 	    data->markup[j].end = i;
+ 	  } else {
+ 	    /* This is greviously wrong, we can't find the begin tag?
+ 	     * Consider this a standalone tag with no close tag. */
+ 	    info = &(data->markup[data->nmarkups++]);
+ 	    /* Start code is where we are */
+ 	    info->stop_code = mush_strdup(ptr, "markup_code");
+ 	    info->type = MARKUP_HTML;
+ 	    info->priority = priority++;
+ 	    info->start = -1;
+ 	    info->end = i;
+ 	  }
+ 	}
+ 	break;
+       case MARKUP_COLOR:
+ 	if (*ptr && *ptr != '/') {
+ 	  /* We're at the start tag. */
+ 	  j = data->nmarkups++;
+ 	  data->markup[j].start_code = NULL;
+ 	  data->markup[j].stop_code = NULL;
+ 	  data->markup[j].type = MARKUP_COLOR;
+ 	  data->markup[j].priority = priority++;
+ 	  define_ansi_data(&(data->markup[j].ansi), ptr);
+ 	  data->markup[j].start = i;
+ 	  data->markup[j].end = -1;
+ 	} else if (*ptr) {
+ 	  int endall = (*(ptr + 1) == 'a');
+ 	  /* Closing tag. For markup color, this means we
+ 	   * close the last opened tag. */
+ 	  for (j = (data->nmarkups) - 1; j >= 0; j--) {
+ 	    if (data->markup[j].end < 0 && data->markup[j].type == MARKUP_COLOR) {
+ 	      data->markup[j].end = i;
+ 	      /* If it's not ENDALL, break */
+ 	      if (!endall)
+ 		break;
+ 	    }
+ 	  }
+ 	}
+ 	break;
+       default:
+ 	/* This is a broken string. Are we near or at buffer_len? */
+ 	if (ptr - source < BUFFER_LEN - 4) {
+ 	  /* If we're not, this is broken in more ways than I can think */
+ 	  goto broken_string;
+ 	}
+ 	break;
+       }
+       ptr = tmp;
+       ptr++;
+       break;
+     case ESC_CHAR:
+       /* I'm getting rid of ESC_CHAR. Still, pretend it's
+        * a proper "opening" one, unless it's normal,
+        * in which case we ether close all extant, or
+        * open a 'normal'.
+        *
+        * If we open a new one, find all old open ones that
+        * it overwrites (rather than modifies) */
+       for (tmp = ptr; *tmp && *tmp != 'm'; tmp++) ;
+       if (strcmp(ptr, COL_NORMAL) != 0) {
+ 	struct ansi_data cur;
+ 	read_raw_ansi_data(&cur, ptr);
+ 	/* Close any we can */
+ 	for (j = (data->nmarkups) - 1; j >= 0; j--) {
+ 	  if (data->markup[j].type == MARKUP_COLOR_OLD) {
+ 	    cur.bits |= data->markup[j].ansi.bits;
+ 	    data->markup[j].type = MARKUP_COLOR;
+ 	    data->markup[j].end = i;
+ 	  }
+ 	}
+ 	/* We're at a start tag. */
+ 	j = data->nmarkups++;
+ 	data->markup[j].start_code = NULL;
+ 	data->markup[j].stop_code = NULL;
+ 	data->markup[j].type = MARKUP_COLOR_OLD;
+ 	data->markup[j].priority = priority++;
+ 	data->markup[j].ansi = cur;
+ 	data->markup[j].start = i;
+ 	data->markup[j].end = -1;
+       } else {
+ 	int found = 0;
+ 	/* Closing tag. For markup color, this means we
+ 	 * close the last opened tag. */
+ 	for (j = (data->nmarkups) - 1; j >= 0; j--) {
+ 	  if (data->markup[j].type == MARKUP_COLOR_OLD) {
+ 	    data->markup[j].type = MARKUP_COLOR;
+ 	    data->markup[j].end = i;
+ 	    found = 1;
+ 	  }
+ 	}
+ 	/* Is it an "opening" ansi_normal tag? */
+ 	if (!found) {
+ 	  j = data->nmarkups++;
+ 	  data->markup[j].start_code = NULL;
+ 	  data->markup[j].stop_code = NULL;
+ 	  data->markup[j].type = MARKUP_COLOR_OLD;
+ 	  data->markup[j].priority = priority++;
+ 	  data->markup[j].ansi.bits = 0;
+ 	  data->markup[j].ansi.offbits = 0;
+ 	  data->markup[j].ansi.fore = 'n';
+ 	  data->markup[j].ansi.back = 0;
+ 	  data->markup[j].start = i;
+ 	  data->markup[j].end = -1;
+ 	}
+       }
+       ptr = tmp;
+       if (*tmp)
+ 	ptr++;
+       break;
+     default:
+       txt[i++] = *(ptr++);
+     }
+   }
+ 
+   txt[i] = '\0';
+   data->len = i;
+ 
+   /* For everything left on the stack:
+    * If it's an ANSI code, close it with COL_NORMAL and i.
+    * If it's an HTML code, assume it's a standalone, and leave
+    *   its stop point where it is. */
+   for (j = 0; j < data->nmarkups; j++) {
+     info = &(data->markup[j]);
+     switch (info->type) {
+     case MARKUP_COLOR_OLD:
+       info->type = MARKUP_COLOR;
+     case MARKUP_COLOR:
+       /* If it's ANSI, we assume it affects the whole string */
+       /* Sucks, but ... */
+       if (info->end < 0)
+ 	info->end = i;
+       if (info->end == info->start) {
+ 	info->end = info->start = -1;
+       }
+       break;
+     case MARKUP_HTML:
+       /* If it's HTML, we treat it as standalone (<IMG>, <BR>, etc)
+        * This is ugly - it's not a "start" but a "stop" */
+       if (info->end < 0) {
+ 	mush_free(info->stop_code, "markup_code");
+ 	info->stop_code = info->start_code;
+ 	info->start_code = NULL;
+ 	info->end = info->start;
+ 	info->start = -1;
+       }
+       break;
+     }
+   }
+   return data;
+ broken_string:
+   /* This stinks. We treat this as if it's not ansi safe */
+   if (data == NULL)
+     return NULL;
+   strncpy(data->text, source, BUFFER_LEN);
+   data->len = strlen(data->text);
+   for (i = data->nmarkups - 1; i >= 0; i--) {
+     free_markup_info(&(data->markup[i]));
+   }
+   data->nmarkups = 0;
+   return data;
+ }
+ 
+ /** Reverse an ansi string, preserving its ansification.
+  * This function destructively modifies the ansi_string passed.
+  * \param as pointer to an ansi string.
+  */
+ void
+ flip_ansi_string(ansi_string *as)
+ {
+   int i, j;
+   markup_information *info;
+   char tmp;
+   int mid;
+   int len = as->len;
+ 
+   /* Reverse the text */
+   mid = len / 2;		/* Midpoint */
+   for (i = len - 1, j = 0; i >= mid; j++, i--) {
+     tmp = as->text[i];
+     as->text[i] = as->text[j];
+     as->text[j] = tmp;
+   }
+ 
+   /* Now reverse the markup. */
+   for (i = as->nmarkups - 1; i >= 0; i--) {
+     int start, end;
+     info = &(as->markup[i]);
+     if (info->start == -1) {
+       /* Standalones */
+       info->end = len - info->end;
+     } else {
+       end = len - info->start;
+       start = len - info->end;
+       info->start = start;
+       info->end = end;
+     }
+   }
+ }
+ 
+ /** Free an ansi_string.
+  * \param as pointer to ansi_string to free.
+  */
+ void
+ free_ansi_string(ansi_string *as)
+ {
+   int i;
+ 
+   if (!as)
+     return;
+ 
+   for (i = as->nmarkups - 1; i >= 0; i--) {
+     free_markup_info(&(as->markup[i]));
+   }
+   mush_free(as, "ansi_string");
+ }
+ 
+ /* Compress the markup information in an ansi_string.
+  *
+  * This combines adjacent identical markup.
+  */
+ 
+ static int
+ compare_starts(const void *a, const void *b)
+ {
+   markup_information *ai, *bi;
+ 
+   ai = (markup_information *) a;
+   bi = (markup_information *) b;
+ 
+   // if (ai->start == bi->start) return ai->priority - bi->priority;
+   return ai->start - bi->start;
+ }
+ 
+ void
+ optimize_ansi_string(ansi_string *as)
+ {
+   int i, j;
+ 
+   /* Nothing to optimize if we've only got 1 or none. */
+   if (as->nmarkups > 1) {
+     /* Sort the markup codes by their start position */
+     qsort(as->markup, as->nmarkups, sizeof(markup_information), compare_starts);
+ 
+     for (i = 0; i < as->nmarkups; i++) {
+       /* If start and end are negative, it's a standalone (img) */
+       if (as->markup[i].start == -1 && as->markup[i].end == -1)
+ 	continue;
+       for (j = i + 1; j < as->nmarkups; j++) {
+ 	/* Already removed? */
+ 	if (as->markup[j].start == -1 && as->markup[j].end == -1)
+ 	  continue;
+ 	if (as->markup[j].start > as->markup[i].end)
+ 	  break;		/* Far apart */
+ 	if (as->markup[i].type == MARKUP_COLOR &&
+ 	    as->markup[j].type == MARKUP_COLOR &&
+ 	    (as->markup[i].ansi.bits == as->markup[j].ansi.bits &&
+ 	     as->markup[i].ansi.offbits == as->markup[j].ansi.offbits &&
+ 	     as->markup[i].ansi.fore == as->markup[j].ansi.fore &&
+ 	     as->markup[i].ansi.back == as->markup[j].ansi.back)
+ 	  ) {
+ 	  if (as->markup[j].end > as->markup[i].end)
+ 	    as->markup[i].end = as->markup[j].end;
+ 	  if (as->markup[j].start < as->markup[i].start)
+ 	    as->markup[i].start = as->markup[j].start;
+ 	  if (as->markup[j].end > as->markup[i].end)
+ 	    as->markup[j].start = -1;
+ 	  as->markup[j].end = -1;
+ 	} else if ((as->markup[i].start_code && as->markup[j].start_code) &&
+ 		   strcmp(as->markup[j].start_code,
+ 			  as->markup[i].start_code) == 0) {
+ 	  /* i and j are adjacent and identical */
+ 	  if (as->markup[j].end > as->markup[i].end)
+ 	    as->markup[i].end = as->markup[j].end;
+ 	  if (as->markup[j].start < as->markup[i].start)
+ 	    as->markup[i].start = as->markup[j].start;
+ 	  as->markup[j].start = -1;
+ 	  as->markup[j].end = -1;
+ 	}
+       }
+     }
+   }
+ 
+   /* Get rid of all removed markups */
+   for (i = 0, j = 0; i < as->nmarkups; i++) {
+     if ((as->markup[i].end >= 0) && (as->markup[i].start != as->markup[i].end)) {
+       if (i != j) {
+ 	memmove(&(as->markup[j]), &(as->markup[i]), sizeof(markup_information));
+       }
+       j++;
+     } else {
+       free_markup_info(&(as->markup[i]));
+     }
+   }
+ 
+   as->nmarkups = j;
+ }
+ 
+ /* Copy the start code for a particular markup_info
+  * For HTML/Pueblo, this inserts TAG_START and TAG_END
+  * Otherwise it's just a plain copy */
+ static int
+ copy_start_code(markup_information * info, char *buff, char **bp)
+ {
+   int retval = 0;
+   char *save;
+   save = *bp;
+   if (info->start_code) {
+     retval += safe_chr(TAG_START, buff, bp);
+     retval += safe_chr(info->type, buff, bp);
+     retval += safe_str(info->start_code, buff, bp);
+     retval += safe_chr(TAG_END, buff, bp);
+   } else if (info->type == MARKUP_COLOR) {
+     retval += write_ansi_data(&(info->ansi), buff, bp);
+   }
+   if (retval)
+     *bp = save;
+   return retval;
+ }
+ 
+ /* Copy the stop code for a particular markup_info
+  * For HTML/Pueblo, this inserts TAG_START and TAG_END
+  * Otherwise it's just a plain copy */
+ static int
+ copy_stop_code(markup_information * info, char *buff, char **bp)
+ {
+   int retval = 0;
+   char *save;
+   save = *bp;
+   if (info->type == MARKUP_HTML && (info->stop_code != NULL)) {
+     retval += safe_chr(TAG_START, buff, bp);
+     retval += safe_chr(MARKUP_HTML, buff, bp);
+     retval += safe_str(info->stop_code, buff, bp);
+     retval += safe_chr(TAG_END, buff, bp);
+   } else if (info->type == MARKUP_COLOR) {
+     retval += safe_chr(TAG_START, buff, bp);
+     retval += safe_chr(MARKUP_COLOR, buff, bp);
+     retval += safe_chr('/', buff, bp);
+     retval += safe_chr(TAG_END, buff, bp);
+   }
+   if (retval)
+     *bp = save;
+   return retval;
+ }
+ 
+ #ifdef ANSI_DEBUG
+ void
+ inspect_ansi_string(ansi_string *as, dbref who)
+ {
+   markup_information *info;
+   int count = 0;
+   int j;
+   notify_format(who, "Inspecting ansi string");
+   notify_format(who, "  Text: %s", as->text);
+   notify_format(who, "  Nmarkups: %d", as->nmarkups);
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->type == MARKUP_HTML) {
+       notify_format(who,
+ 		    "    %d (%s): (start: %d end: %d) start_code: %s stop_code: %s",
+ 		    count++, (info->type == MARKUP_HTML ? "html" : "ansi"),
+ 		    info->start, info->end, info->start_code, info->stop_code);
+     } else {
+       notify_format(who,
+ 		    "    %d (%s): (start: %d end: %d) bits: %d fore: %c back: %c",
+ 		    count++, (info->type == MARKUP_HTML ? "html" : "ansi"),
+ 		    info->start, info->end, info->ansi.bits,
+ 		    (info->ansi.fore) ? info->ansi.fore : '-',
+ 		    (info->ansi.back) ? info->ansi.back : '-');
+     }
+   }
+   notify_format(who, "Inspecting ansi string complete");
+ }
+ #endif
+ 
+ /** Delete a portion of an ansi string.
+  * \param as ansi_string to delete from
+  * \param start start point to remove
+  * \param size length of string to remove
+  * \retval 0 success
+  * \reval 1 failure.
+  */
+ 
+ int
+ ansi_string_delete(ansi_string *as, int start, int count)
+ {
+   int i;
+   int end;
+   markup_information *dm;
+ 
+   if (start >= as->len)
+     return 0;
+   if (count <= 0)
+     return 0;
+   if ((start + count) > as->len)
+     count = as->len - start;
+ 
+   end = start + count;
+ 
+   as->optimized = 0;
+ 
+   dm = as->markup;
+ 
+   /* Remove or shrink the markup on dst */
+   for (i = 0; i < as->nmarkups; i++) {
+     if (dm[i].start >= start && dm[i].end <= end) {
+       dm[i].start = -1;
+       dm[i].end = -1;
+     }
+     if (dm[i].start >= start) {
+       dm[i].start -= count;
+       if (dm[i].start < start)
+ 	dm[i].start = start;
+     }
+     if (dm[i].end > start) {
+       dm[i].end -= count;
+       if (dm[i].end < start)
+ 	dm[i].end = start;
+     }
+   }
+ 
+   /* Shift text over */
+   memmove(as->text + start, as->text + end, as->len - end);
+   as->len -= count;
+   as->text[as->len] = '\0';
+   return 0;
+ }
+ 
+ #define copyto(x,y) \
+ do { \
+   x.type = y.type; \
+   x.priority = y.priority; \
+   x.start_code = NULL; \
+   x.stop_code = NULL; \
+   if (y.start_code) x.start_code = mush_strdup(y.start_code,"markup_code"); \
+   else (x.start_code = NULL); \
+   if (y.stop_code) x.stop_code = mush_strdup(y.stop_code,"markup_code"); \
+   else (x.stop_code = NULL); \
+   x.ansi.bits    = y.ansi.bits; \
+   x.ansi.offbits = y.ansi.offbits; \
+   x.ansi.fore    = y.ansi.fore; \
+   x.ansi.back    = y.ansi.back; \
+ } while (0)
+ 
+ /** Insert an ansi string into another ansi_string
+  * with markups kept as straight as possible.
+  * \param dst ansi_string to insert into.
+  * \param loc Location to  insert into, 0-indexed
+  * \param src ansi_string to insert
+  * \param start start point in src
+  * \param size length of string from src
+  * \retval 0 success
+  * \reval 1 failure.
+  */
+ 
+ int
+ ansi_string_insert(ansi_string *dst, int loc,
+ 		   ansi_string *src, int start, int count)
+ {
+   int i, j;
+   int len;
+   int end, m_end;
+   int rval = 0;
+   markup_information *dm, *sm;
+ 
+   if (loc >= dst->len)
+     loc = dst->len;
+   if (start >= src->len)
+     return 0;
+   if (count <= 0)
+     return 0;
+   if ((start + count) > src->len)
+     count = src->len - start;
+ 
+   dst->optimized = 0;
+ 
+   dm = dst->markup;
+   sm = src->markup;
+ 
+   /* End in src */
+   end = start + count;
+ 
+   /* Starting location */
+   if (loc <= 0)
+     loc = 0;
+   if (loc >= dst->len)
+     loc = dst->len;
+ 
+   /* End of src's insert location in dst */
+   m_end = loc + count;
+ 
+   /* shift or widen the markup on dst */
+   for (i = 0; i < dst->nmarkups; i++) {
+     if (dm[i].start >= loc)
+       dm[i].start += count;
+     if (dm[i].end > loc)
+       dm[i].end += count;
+   }
+   /* Copy markup */
+   for (j = 0; j < src->nmarkups; j++) {
+     /* It's possible, but not at all easy, to get this much ansi markup */
+     if (i >= BUFFER_LEN)
+       break;
+     if (((sm[j].start <= end && sm[j].end >= start) && sm[j].start >= 0) ||
+ 	(sm[j].end > start && sm[j].end <= end)) {
+       copyto(dm[i], sm[j]);
+       if (sm[j].start < 0) {
+ 	dm[i].start = -1;
+       } else {
+ 	dm[i].start = loc + sm[j].start - start;
+ 	if (dm[i].start < loc)
+ 	  dm[i].start = loc;
+       }
+       dm[i].end = loc + sm[j].end - start;
+       if (dm[i].end >= m_end)
+ 	dm[i].end = m_end;
+       i++;
+     }
+   }
+   dst->nmarkups = i;
+ 
+   dst->len += count;
+   if (dst->len >= BUFFER_LEN) {
+     rval = 1;
+     dst->len = BUFFER_LEN - 1;
+   }
+   len = dst->len - m_end;
+ 
+   /* Shift text over */
+   if (len > 0) {
+     if (m_end + len >= BUFFER_LEN) {
+       len = (BUFFER_LEN - m_end - loc - 1);
+       memmove(dst->text + m_end, dst->text + loc, len);
+     } else {
+       memmove(dst->text + m_end, dst->text + loc, len);
+     }
+   }
+ 
+   /* Copy text from src */
+   if (loc + count >= BUFFER_LEN)
+     count = BUFFER_LEN - 1 - loc;
+   memcpy(dst->text + loc, src->text + start, count);
+   dst->text[dst->len] = '\0';
+   return rval;
+ }
+ 
+ /** Replace a portion of an ansi string with
+  *  another ansi string, keeping markups as
+  *  straight as possible.
+  * \param dst ansi_string to insert into.
+  * \param loc Location to  insert into, 0-indexed
+  * \param len Length of string inside dst to replace
+  * \param src ansi_string to insert
+  * \param start start point in src
+  * \param size length of string from src
+  * \retval 0 success
+  * \reval 1 failure.
+  */
+ int
+ ansi_string_replace(ansi_string *dst, int loc, int len,
+ 		    ansi_string *src, int start, int count)
+ {
+   int i, j;
+   int end, m_end, s_end;
+   int diff;
+   int rval = 0;
+   markup_information *dm, *sm;
+ 
+   /* Is it really an insert? */
+   if (loc >= dst->len || len == 0) {
+     return ansi_string_insert(dst, loc, src, start, count);
+   }
+   /* Boundaries */
+   if (start <= 0)
+     start = 0;
+   if ((start + count) > src->len)
+     count = src->len - start;
+   if ((len + loc) > dst->len)
+     len = dst->len - loc;
+   /* Is it really a delete? */
+   if ((start >= src->len) || (count <= 0)) {
+     return ansi_string_delete(dst, loc, len);
+   }
+ 
+   /* Starting location */
+   if (loc <= 0)
+     loc = 0;
+   if (loc >= dst->len)
+     loc = dst->len;
+ 
+   end = loc + len;
+   diff = count - len;
+   m_end = loc + count;
+ 
+   dst->optimized = 0;
+ 
+   dm = dst->markup;
+   sm = src->markup;
+ 
+   /* Modify, remove, stretch, and mangle markup */
+   for (i = 0; i < dst->nmarkups; i++) {
+     /* If it doesn't cross into the replaced part, leave as is */
+     if (dm[i].end <= loc)
+       continue;
+     if (dm[i].start == loc && dm[i].end == end) {
+       /* Debatable: If it surrounds the replaced part exactly,
+        * keep it, stretching it to wrap around the replacement */
+       dm[i].end = m_end;
+     } else if (dm[i].start <= loc && dm[i].end >= end) {
+       dm[i].end += diff;
+       if (dm[i].end > BUFFER_LEN)
+ 	dm[i].end = BUFFER_LEN;
+     } else if (dm[i].start >= loc && dm[i].end <= end) {
+       /* If it's completely inside the removed area, remove it */
+       dm[i].start = -1;
+       dm[i].end = -1;
+     } else if (dm[i].start < loc && dm[i].end < end) {
+       /* If it ends inside, but begins to the left, push end left. */
+       if (dm[i].start >= 0)
+ 	dm[i].end = loc;
+       else			/* Standalone is inside */
+ 	dm[i].end = -1;
+     } else if (dm[i].end > end && dm[i].start < end) {
+       /* If it begins inside, but ends to the right, push start right. */
+       if (dm[i].start >= 0)
+ 	dm[i].start = m_end;
+       dm[i].end += diff;
+     } else if (dm[i].start > end) {
+       /* It's to the right */
+       dm[i].start += diff;
+       dm[i].end += diff;
+     } else {
+       /* Shift */
+       if (dm[i].start > loc)
+ 	dm[i].start += diff;
+       dm[i].end += diff;
+     }
+   }
+ 
+   s_end = start + count;
+   /* Copy markup */
+   for (j = 0; j < src->nmarkups; j++) {
+     /* It's possible, but not at all easy, to get this much ansi markup */
+     if (i >= BUFFER_LEN)
+       break;
+     if (((sm[j].start <= s_end && sm[j].end >= start) && sm[j].start >= 0) ||
+ 	(sm[j].end > start && sm[j].end <= s_end)) {
+       copyto(dm[i], sm[j]);
+       if (sm[j].start < 0) {
+ 	dm[i].start = -1;
+       } else {
+ 	dm[i].start = loc + sm[j].start - start;
+ 	if (dm[i].start < loc)
+ 	  dm[i].start = loc;
+       }
+       dm[i].end = loc + sm[j].end - start;
+       if (dm[i].end >= m_end)
+ 	dm[i].end = m_end;
+       i++;
+     }
+   }
+   if (i >= BUFFER_LEN)
+     i = BUFFER_LEN - 1;
+   dst->nmarkups = i;
+ 
+   /* length of original string after replace bits */
+   len = dst->len - end;
+ 
+   dst->len += diff;
+   if (dst->len >= BUFFER_LEN) {
+     rval = 1;
+     dst->len = BUFFER_LEN - 1;
+   }
+ 
+   /* Shift text over */
+   if (diff != 0) {
+     if (m_end + len >= BUFFER_LEN) {
+       len = BUFFER_LEN - (1 + m_end);
+       if (len > 0) {
+ 	memmove(dst->text + m_end, dst->text + end, len);
+       }
+     } else {
+       memmove(dst->text + m_end, dst->text + end, len);
+     }
+   }
+ 
+   /* Copy text from src */
+   if (loc + count >= BUFFER_LEN)
+     count = BUFFER_LEN - (1 + loc);
+   memcpy(dst->text + loc, src->text + start, count);
+   dst->text[dst->len] = '\0';
+   return rval;
+ }
+ 
+ #undef copyto
+ 
+ /** Safely append an ansi_string into a buffer as a real string,
+  * \param as pointer to ansi_string to append.
+  * \param start position in as to start copying from.
+  * \param len length in characters to copy from as.
+  * \param buff buffer to insert into.
+  * \param bp pointer to pointer to insertion point of buff.
+  * \retval 0 success.
+  * \retval 1 failure.
+  */
+ 
+ int
+ safe_ansi_string(ansi_string *as, int start, int len, char *buff, char **bp)
+ {
+   int i, j;
+   markup_information *info;
+   int nextstart, nextend, next;
+   int end = start + len;
+   int retval = 0;
+ 
+   if (as->optimized == 0) {
+     optimize_ansi_string(as);
+     as->optimized = 1;
+   }
+ 
+   if (len <= 0)
+     return 0;
+ 
+   i = start;
+   if (start >= as->len)
+     return 0;
+   if (end > as->len)
+     end = as->len;
+ 
+   /* Standalones (Stop codes with -1 for start) */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start == -1 && info->end == i) {
+       /* This is a standalone tag. YUCK! */
+       if (info->stop_code)
+ 	retval += safe_str(info->stop_code, buff, bp);
+     }
+   }
+ 
+   /* Now, start codes of everything that impacts us. */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start >= 0) {
+       if (info->start <= i && info->end > i) {
+ 	retval += copy_start_code(info, buff, bp);
+       }
+     }
+   }
+ 
+ 
+   /* Find  the next changes */
+   nextstart = BUFFER_LEN + 1;
+   nextend = BUFFER_LEN + 1;
+ 
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start > i && info->start < nextstart)
+       nextstart = info->start;
+     if (info->end > i && info->end < nextend)
+       nextend = info->end;
+   }
+ 
+   next = (nextend < nextstart) ? nextend : nextstart;
+   if (end < next)
+     next = end;
+ 
+   for (; i < next && i < as->len; i++) {
+     if (as->text[i])
+       safe_chr(as->text[i], buff, bp);
+   }
+   i = next;
+ 
+   while (i < end) {
+     if (i >= nextend) {
+       nextend = BUFFER_LEN + 2;
+       for (j = 0; j < as->nmarkups; j++) {
+ 	info = &(as->markup[j]);
+ 	if (info->end == i) {
+ 	  retval += copy_stop_code(info, buff, bp);
+ 	}
+ 	if (info->end > i && info->end < nextend)
+ 	  nextend = info->end;
+       }
+     }
+     if (i >= nextstart) {
+       nextstart = BUFFER_LEN + 2;
+       for (j = 0; j < as->nmarkups; j++) {
+ 	info = &(as->markup[j]);
+ 	if (info->start == i) {
+ 	  retval += copy_start_code(info, buff, bp);
+ 	} else if (info->start > i && info->start < nextstart) {
+ 	  nextstart = info->start;
+ 	}
+       }
+     }
+     next = (nextend < nextstart) ? nextend : nextstart;
+     if (end < next)
+       next = end;
+ 
+     for (; i < next && i < as->len; i++) {
+       if (as->text[i])
+ 	safe_chr(as->text[i], buff, bp);
+     }
+   }
+ 
+   /* Now, find all things that end for us */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start < i && info->end >= i)
+       retval += copy_stop_code(info, buff, bp);
+   }
+ 
+   return retval;
+ }
+ 
+ /* Following functions are used for
+  * decompose_str()
+  */
+ 
+ extern char escaped_chars[UCHAR_MAX + 1];
+ 
+ static int escape_strn(char *s, int start, int count, char *buff, char **bp);
+ 
+ static int
+ escape_str(char *in, char *buff, char **bp)
+ {
+   return escape_strn(in, 0, strlen(in), buff, bp);
+ }
+ 
+ static int
+ escape_strn(char *s, int start, int count, char *buff, char **bp)
+ {
+   unsigned char *in;
+   int retval = 0;
+   int dospace = 1;
+   int spaces = 0;
+   int i, j;
+ 
+   in = (unsigned char *) s;
+ 
+   if (*in) {
+     count += start;
+     for (i = start; in[i] && i < count; i++) {
+       if (in[i] == ' ') {
+ 	spaces++;
+       } else {
+ 	if (spaces) {
+ 	  if (spaces >= 5) {
+ 	    retval += safe_str("[space(", buff, bp);
+ 	    retval += safe_number(spaces, buff, bp);
+ 	    retval += safe_str(")]", buff, bp);
+ 	  } else {
+ 	    if (dospace) {
+ 	      spaces--;
+ 	      retval += safe_str("%b", buff, bp);
+ 	    }
+ 	    while (spaces) {
+ 	      retval += safe_chr(' ', buff, bp);
+ 	      if (--spaces) {
+ 		--spaces;
+ 		retval += safe_str("%b", buff, bp);
+ 	      }
+ 	    }
+ 	  }
+ 	}
+ 	spaces = 0;
+ 	dospace = 0;
+ 	if (in[i] == '\n') {
+ 	  retval += safe_str("%r", buff, bp);
+ 	} else if (in[i] == '\t') {
+ 	  retval += safe_str("%t", buff, bp);
+ 	} else if (in[i] == BEEP_CHAR) {
+ 	  for (j = i; in[i + 1] == BEEP_CHAR && (i - j) < 4; i++) ;
+ 	  retval += safe_format(buff, bp, "[beep(%d)]", (i - j) + 1);
+ 	} else if (escaped_chars[in[i]]) {
+ 	  retval += safe_chr('\\', buff, bp);
+ 	  retval += safe_chr(in[i], buff, bp);
+ 	} else {
+ 	  retval += safe_chr(in[i], buff, bp);
+ 	}
+       }
+     }
+     if (spaces) {
+       if (spaces >= 5) {
+ 	retval += safe_str("[space(", buff, bp);
+ 	retval += safe_number(spaces, buff, bp);
+ 	retval += safe_str(")]", buff, bp);
+       } else {
+ 	spaces--;		/* This is for the final %b space */
+ 	if (spaces && dospace) {
+ 	  spaces--;
+ 	  retval += safe_str("%b", buff, bp);
+ 	}
+ 	while (spaces) {
+ 	  safe_chr(' ', buff, bp);
+ 	  if (--spaces) {
+ 	    --spaces;
+ 	    retval += safe_str("%b", buff, bp);
+ 	  }
+ 	}
+ 	retval += safe_str("%b", buff, bp);
+       }
+     }
+   }
+   return retval;
+ }
+ 
+ 
+ static int
+ dump_start_code(markup_information * info, char *buff, char **bp)
+ {
+   int retval = 0;
+   char *save;
+   save = *bp;
+   if (info->type == MARKUP_HTML) {
+     if (info->stop_code != NULL) {
+       char *ptr;
+       retval += safe_str("[tagwrap(", buff, bp);
+       if ((ptr = strchr(info->start_code, ' ')) != NULL) {
+ 	*(ptr++) = '\0';
+ 	retval += escape_str(info->start_code, buff, bp);
+ 	if (*ptr) {
+ 	  retval += safe_chr(',', buff, bp);
+ 	  retval += escape_str(ptr, buff, bp);
+ 	}
+ 	ptr--;
+ 	*ptr = ' ';
+       } else {
+ 	retval += escape_str(info->start_code, buff, bp);
+       }
+       retval += safe_chr(',', buff, bp);
+     } else {
+       retval += safe_str("[tag(", buff, bp);
+       retval += escape_str(info->start_code, buff, bp);
+       retval += safe_str(")]", buff, bp);
+     }
+   } else {
+     /* Find the digits */
+     retval += safe_str("[ansi(", buff, bp);
+     if (info->ansi.fore == 'n') {
+       retval += safe_chr('n', buff, bp);
+     } else {
+ #define CBIT_SET(x,y) (x.bits & y)
+       if (CBIT_SET(info->ansi, CBIT_FLASH))
+ 	retval += safe_chr('f', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_HILITE))
+ 	retval += safe_chr('h', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_INVERT))
+ 	retval += safe_chr('i', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_UNDERSCORE))
+ 	retval += safe_chr('u', buff, bp);
+ #undef CBIT_SET
+ #define CBIT_SET(x,y) (x.offbits & y)
+       if (CBIT_SET(info->ansi, CBIT_FLASH))
+ 	retval += safe_chr('F', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_HILITE))
+ 	retval += safe_chr('H', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_INVERT))
+ 	retval += safe_chr('I', buff, bp);
+       if (CBIT_SET(info->ansi, CBIT_UNDERSCORE))
+ 	retval += safe_chr('U', buff, bp);
+ #undef CBIT_SET
+ 
+       if (info->ansi.fore)
+ 	retval += safe_chr(info->ansi.fore, buff, bp);
+       if (info->ansi.back)
+ 	retval += safe_chr(info->ansi.back, buff, bp);
+       retval += safe_chr(',', buff, bp);
+     }
+   }
+   if (retval)
+     *bp = save;
+   return retval;
+ }
+ 
+ static int
+ dump_stop_code(markup_information * info
+ 	       __attribute__ ((__unused__)), char *buff, char **bp)
+ {
+   char *save = *bp;
+   if (safe_str(")]", buff, bp)) {
+     *bp = save;
+   }
+   return 1;
+ }
+ 
+ int
+ dump_ansi_string(ansi_string *as, char *buff, char **bp)
+ {
+   int i, j;
+   markup_information *info;
+   int nextstart, nextend, next;
+   int end;
+   int start = 0;
+   int retval = 0;
+ 
+   end = as->len;
+ 
+   if (as->optimized == 0) {
+     optimize_ansi_string(as);
+     as->optimized = 1;
+   }
+ 
+   i = start;
+   if (start > as->len)
+     return 0;
+   if (end > as->len)
+     end = as->len;
+ 
+   /* Standalones (Stop codes with -1 for start) */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start == -1 && info->end == i) {
+       /* This is a standalone tag. YUCK! */
+       if (info->stop_code)
+ 	retval += safe_str(info->stop_code, buff, bp);
+     }
+   }
+ 
+   /* Now, start codes of everything that impacts us. */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start >= 0) {
+       if (info->start <= i && info->end > i) {
+ 	retval += dump_start_code(info, buff, bp);
+       }
+     }
+   }
+ 
+ 
+   /* Find  the next changes */
+   nextstart = BUFFER_LEN + 1;
+   nextend = BUFFER_LEN + 1;
+ 
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start > i && info->start < nextstart)
+       nextstart = info->start;
+     if (info->end > i && info->end < nextend)
+       nextend = info->end;
+   }
+ 
+   next = (nextend < nextstart) ? nextend : nextstart;
+   if (end < next)
+     next = end;
+ 
+   if (next > as->len)
+     next = as->len;
+   escape_strn(as->text, i, next - i, buff, bp);
+   i = next;
+ 
+   while (i < end) {
+     if (i >= nextend) {
+       nextend = BUFFER_LEN + 2;
+       for (j = 0; j < as->nmarkups; j++) {
+ 	info = &(as->markup[j]);
+ 	if (info->end == i) {
+ 	  retval += dump_stop_code(info, buff, bp);
+ 	}
+ 	if (info->end > i && info->end < nextend)
+ 	  nextend = info->end;
+       }
+     }
+     if (i >= nextstart) {
+       nextstart = BUFFER_LEN + 2;
+       for (j = 0; j < as->nmarkups; j++) {
+ 	info = &(as->markup[j]);
+ 	if (info->start == i) {
+ 	  retval += dump_start_code(info, buff, bp);
+ 	} else if (info->start > i && info->start < nextstart) {
+ 	  nextstart = info->start;
+ 	}
+       }
+     }
+     next = (nextend < nextstart) ? nextend : nextstart;
+     if (end < next)
+       next = end;
+ 
+     escape_strn(as->text, i, next - i, buff, bp);
+     i = next;
+   }
+ 
+   /* Now, find all things that end for us */
+   for (j = 0; j < as->nmarkups; j++) {
+     info = &(as->markup[j]);
+     if (info->start < i && info->end >= i)
+       retval += dump_stop_code(info, buff, bp);
+   }
+ 
+   return retval;
+ }
+ 
+ /** Our version of pcre_copy_substring, with ansi-safeness.
+  * \param as the ansi_string whose .text value was matched against.
+  * \param ovector the offset vectors
+  * \param stringcount the number of subpatterns
+  * \param stringnumber the number of the desired subpattern
+  * \param buff buffer to copy the subpattern to
+  * \param bp pointer to the end of buffer
+  * \return size of subpattern, or -1 if unknown pattern
+  */
+ int
+ ansi_pcre_copy_substring(ansi_string *as, int *ovector, int stringcount,
+ 			 int stringnumber, int nonempty, char *buff, char **bp)
+ {
+   int yield;
+   if (stringnumber < 0 || stringnumber >= stringcount)
+     return -1;
+   stringnumber *= 2;
+   yield = ovector[stringnumber + 1] - ovector[stringnumber];
+   if (!nonempty || yield) {
+     safe_ansi_string(as, ovector[stringnumber], yield, buff, bp);
+     **bp = '\0';
+   }
+   return yield;
+ }
+ 
+ 
+ /** Our version of pcre_copy_named_substring, with ansi-safeness.
+  * \param code the pcre compiled code
+  * \param as the ansi_string whose .text value was matched against.
+  * \param ovector the offset vectors
+  * \param stringcount the number of subpatterns
+  * \param stringname the name of the desired subpattern
+  * \param buff buffer to copy the subpattern to
+  * \param bp pointer to the end of buffer
+  * \return size of subpattern, or -1 if unknown pattern
+  */
+ int
+ ansi_pcre_copy_named_substring(const pcre * code, ansi_string *as, int *ovector,
+ 			       int stringcount, const char *stringname, int ne,
+ 			       char *buff, char **bp)
+ {
+   int n = pcre_get_stringnumber(code, stringname);
+   if (n <= 0)
+     return -1;
+   return ansi_pcre_copy_substring(as, ovector, stringcount, n, ne, buff, bp);
+ }
+ 
+ /** Safely add a tag into a buffer.
+  * If we support pueblo, this function adds the tag start token,
+  * the tag, and the tag end token. If not, it does nothing.
+  * If we can't fit the tag in, we don't put any of it in.
+  * \param a_tag the html tag to add.
+  * \param buf the buffer to append to.
+  * \param bp pointer to address in buf to insert.
+  * \retval 0, successfully added.
+  * \retval 1, tag wouldn't fit in buffer.
+  */
+ static int
+ safe_markup(char const *a_tag, char *buf, char **bp, char type)
+ {
+   int result = 0;
+   char *save = buf;
+ 
+   result = safe_chr(TAG_START, buf, bp);
+   result = safe_chr(type, buf, bp);
+   result = safe_str(a_tag, buf, bp);
+   result = safe_chr(TAG_END, buf, bp);
+   /* If it didn't all fit, rewind. */
+   if (result)
+     *bp = save;
+ 
+   return result;
+ }
+ 
+ int
+ safe_tag(char const *a_tag, char *buff, char **bp)
+ {
+   if (SUPPORT_PUEBLO)
+     return safe_markup(a_tag, buff, bp, MARKUP_HTML);
+   return 0;
+ }
+ 
+ /** Safely add a closing tag into a buffer.
+  * If we support pueblo, this function adds the tag start token,
+  * a slash, the tag, and the tag end token. If not, it does nothing.
+  * If we can't fit the tag in, we don't put any of it in.
+  * \param a_tag the html tag to add.
+  * \param buf the buffer to append to.
+  * \param bp pointer to address in buf to insert.
+  * \retval 0, successfully added.
+  * \retval 1, tag wouldn't fit in buffer.
+  */
+ static int
+ safe_markup_cancel(char const *a_tag, char *buf, char **bp, char type)
+ {
+   int result = 0;
+   char *save = buf;
+ 
+   result = safe_chr(TAG_START, buf, bp);
+   result = safe_chr(type, buf, bp);
+   result = safe_chr('/', buf, bp);
+   result = safe_str(a_tag, buf, bp);
+   result = safe_chr(TAG_END, buf, bp);
+   /* If it didn't all fit, rewind. */
+   if (result)
+     *bp = save;
+ 
+   return result;
+ }
+ 
+ int
+ safe_tag_cancel(char const *a_tag, char *buf, char **bp)
+ {
+   if (SUPPORT_PUEBLO)
+     return safe_markup_cancel(a_tag, buf, bp, MARKUP_HTML);
+   return 0;
+ }
+ 
+ /** Safely add a tag, some text, and a matching closing tag into a buffer.
+  * If we can't fit the stuff, we don't put any of it in.
+  * \param a_tag the html tag to add.
+  * \param params tag parameters.
+  * \param data the text to wrap the tag around.
+  * \param buf the buffer to append to.
+  * \param bp pointer to address in buf to insert.
+  * \param player the player involved in all this, or NOTHING if internal.
+  * \retval 0, successfully added.
+  * \retval 1, tagged text wouldn't fit in buffer.
+  */
+ int
+ safe_tag_wrap(char const *a_tag, char const *params, char const *data,
+ 	      char *buf, char **bp, dbref player)
+ {
+   int result = 0;
+   char *save = buf;
+ 
+   if (SUPPORT_PUEBLO) {
+     result = safe_chr(TAG_START, buf, bp);
+     result = safe_chr(MARKUP_HTML, buf, bp);
+     result = safe_str(a_tag, buf, bp);
+     if (params && *params && ok_tag_attribute(player, params)) {
+       result = safe_chr(' ', buf, bp);
+       result = safe_str(params, buf, bp);
+     }
+     result = safe_chr(TAG_END, buf, bp);
+   }
+   result = safe_str(data, buf, bp);
+   if (SUPPORT_PUEBLO) {
+     result = safe_tag_cancel(a_tag, buf, bp);
+   }
+   /* If it didn't all fit, rewind. */
+   if (result)
+     *bp = save;
+   return result;
+ }
+ 
+ /** Initialize the html tag hash table with all the safe tags from HTML 4.0 */
+ void
+ init_tag_hashtab(void)
+ {
+   static char dummy = 1;
+   int i = 0;
+   static const char *safetags[] = { "A", "B", "I", "U", "STRONG", "EM",
+     "ADDRESS", "BLOCKQUOTE", "CENTER", "DEL", "DIV",
+     "H1", "H2", "H3", "H4", "H5", "H6", "HR", "INS",
+     "P", "PRE", "DIR", "DL", "DT", "DD", "LI", "MENU", "OL", "UL",
+     "TABLE", "CAPTION", "COLGROUP", "COL", "THEAD", "TFOOT",
+     "TBODY", "TR", "TD", "TH",
+     "BR", "FONT", "IMG", "SPAN", "SUB", "SUP",
+     "ABBR", "ACRONYM", "CITE", "CODE", "DFN", "KBD", "SAMP", "VAR",
+     "BIG", "S", "SMALL", "STRIKE", "TT",
+     NULL
+   };
+   hashinit(&htab_tag, 64, 1);
+   while (safetags[i]) {
+     hashadd(safetags[i], (void *) &dummy, &htab_tag);
+     i++;
+   }
+ }
