[PENNMUSH-ANNOUNCE] 1.7.7-patch16

dunemush at tala.mede.uic.edu dunemush at tala.mede.uic.edu
Tue Jun 24 16:11:21 CDT 2003


This is patch16 to PennMUSH 1.7.7. After applying this patch, you will
have version 1.7.7p16

To apply this patch, save it to a file in your top-level MUSH directory,
and do the following:
	patch -p1 < 1.7.7-patch16
	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.
    - Alan/Javelin

In this patch:

Commands:
  * New switch /room for 'with' to run a $command available in a remote
    location. Suggested by Mike Griffiths. [SW]
Functions:
  * Added the terminfo() function, which returns information about a
    client's name and capabilities for a particular connection. [SW]
  * New lports() function. Like lwho() but returns port descriptors.
    For mux2 compatibility. [SW]
  * Functions that return information about a connected player now treat
    integer arguments as a port descriptor to look up, rather than using
    the least-idle connection of a player. To force a player name to
    be treated as such even when it's a number, prefix it with a * in
    softcode. For mux2 compatibility. [SW]
  * Players can use ports() on themselves and use the descriptors
    they're connected to as arguments to the information functions.
    For mux2 compatibility. [SW]
Minor Changes:
  * Compute various chunk stats (total used, total free space, etc.)
    on demand instead of keeping running totals. [TAP]
  * @chan/what displays information about a channel's recall buffer, if any.
    [SW]
  * @chan/recall'ed lines are more clearly marked as such. Suggested by 
    Oriens at Alexandria. [SW]
  * Consolidation of a common idiom used to format times throughout the source
    into a simple function call. [SW]
  * The time a @mail was sent was stored, unusually, as a string.
    No longer. Now it's handled the same way as all other times. [SW]
Fixes:
  * Bug in resizing @chan/recall buffers fixed. Reported by Oriens at Alexandria.
    [SW]
  * Objects with user-defined locks had problems with finding built-in locks
    on the object. Reported by Walker at M*U*S*H. [SW]
  * Unregistered() macro was checking wrong flag name. Report by
    Matt Kirby.
  * Help fix by Adu at M*U*S*H.
  * Potential problem with ambigious names in the information functions fixed.


Prereq: 1.7.7p15
*** 1_7_7.465/Patchlevel Mon, 02 Jun 2003 13:18:13 -0500 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.17 600)
--- 1_7_7.487(w)/Patchlevel Mon, 23 Jun 2003 11:09:15 -0500 dunemush (pennmush/5_Patchlevel 1.17.1.11.1.18 600)
***************
*** 1,2 ****
  Do not edit this file. It is maintained by the official PennMUSH patches.
! This is PennMUSH 1.7.7p15
--- 1,2 ----
  Do not edit this file. It is maintained by the official PennMUSH patches.
! This is PennMUSH 1.7.7p16
*** 1_7_7.465/CHANGES.176 Mon, 02 Jun 2003 13:40:12 -0500 dunemush (pennmush/g/17_CHANGES 1.10.1.6.1.2.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.9.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2 600)
--- 1_7_7.487(w)/CHANGES.176 Mon, 23 Jun 2003 21:46:26 -0500 dunemush (pennmush/g/17_CHANGES 1.10.1.6.1.2.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.9.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.2 600)
***************
*** 13,18 ****
--- 13,34 ----
  
  ==========================================================================
  
+ Version 1.7.6 patchlevel 12                     June 23, 2003
+ 
+ Minor changes:
+    * Users no longer see last connection information when they 
+      connect to Guests. Suggested by Jules at M*U*S*H.
+ Fixes:
+    * Potential problem with ambigious names in the information functions 
+      fixed. [SW]
+    * The 'chat' config group is no longer displayed when CHAT_SYSTEM
+      isn't defined. Report by Mike Griffiths. [SW]
+    * cygwin install instructions changed to remove obsolete exim
+      version information. Suggested by Cheetah at M*U*S*H.
+    * Objects with user-defined locks had problems with finding built-in locks
+      on the object. Reported by Walker at M*U*S*H. [SW]
+ 
+ 
  Version 1.7.6 patchlevel 11                     June 1, 2003
  
  Minor changes:
*** 1_7_7.465/CHANGES.177 Mon, 02 Jun 2003 13:18:13 -0500 dunemush (pennmush/g/23_CHANGES 1.48.1.94 600)
--- 1_7_7.487(w)/CHANGES.177 Mon, 23 Jun 2003 11:09:34 -0500 dunemush (pennmush/g/23_CHANGES 1.48.1.105 600)
***************
*** 18,23 ****
--- 18,63 ----
  
  ==========================================================================
  
+ Version 1.7.7 patchlevel 16                     June 23, 2003
+ 
+ Commands:
+   * New switch /room for 'with' to run a $command available in a remote
+     location. Suggested by Mike Griffiths. [SW]
+ Functions:
+   * Added the terminfo() function, which returns information about a
+     client's name and capabilities for a particular connection. [SW]
+   * New lports() function. Like lwho() but returns port descriptors.
+     For mux2 compatibility. [SW]
+   * Functions that return information about a connected player now treat
+     integer arguments as a port descriptor to look up, rather than using
+     the least-idle connection of a player. To force a player name to
+     be treated as such even when it's a number, prefix it with a * in
+     softcode. For mux2 compatibility. [SW]
+   * Players can use ports() on themselves and use the descriptors
+     they're connected to as arguments to the information functions.
+     For mux2 compatibility. [SW]
+ Minor Changes:
+   * Compute various chunk stats (total used, total free space, etc.)
+     on demand instead of keeping running totals. [TAP]
+   * @chan/what displays information about a channel's recall buffer, if any.
+     [SW]
+   * @chan/recall'ed lines are more clearly marked as such. Suggested by 
+     Oriens at Alexandria. [SW]
+   * Consolidation of a common idiom used to format times throughout the source
+     into a simple function call. [SW]
+   * The time a @mail was sent was stored, unusually, as a string.
+     No longer. Now it's handled the same way as all other times. [SW]
+ Fixes:
+   * Bug in resizing @chan/recall buffers fixed. Reported by Oriens at Alexandria.
+     [SW]
+   * Objects with user-defined locks had problems with finding built-in locks
+     on the object. Reported by Walker at M*U*S*H. [SW]
+   * Unregistered() macro was checking wrong flag name. Report by
+     Matt Kirby.
+   * Help fix by Adu at M*U*S*H.
+   * Potential problem with ambigious names in the information functions fixed.
+ 
+ 
  Version 1.7.7 patchlevel 15                     June 1, 2003
  
  Fixes:
*** 1_7_7.465/game/txt/hlp/pennfunc.hlp Sat, 31 May 2003 16:32:59 -0500 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.2 600)
--- 1_7_7.487(w)/game/txt/hlp/pennfunc.hlp Mon, 16 Jun 2003 23:42:11 -0500 dunemush (pennmush/16_pennfunc.h 1.2.1.50.1.1.1.1.1.2.1.7.1.8.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.9.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.4 600)
***************
*** 44,49 ****
--- 44,50 ----
    Boolean functions:  produce 0 or 1 (false or true) answers  (OR, AND)
    Channel functions: Get information about channels (CTITLE, CWHO)
    Communication functions: Send messages to objects (PEMIT, OEMIT)
+   Connection functions: Get information about a player's connection (CONN)
    Dbref functions: return dbref info related to objects (LOC, LEXITS)
    Global functions: local MUSH-specific functions defined with @function
    Html functions: output html tags for Pueblo-compatible clients
***************
*** 104,109 ****
--- 105,117 ----
  
    cemit()       emit()        lemit()       nspemit()     oemit()
    pemit()       remit()       zemit() 
+ & Connection functions
+   Connection functions return information about the connections open 
+   on a game, or about specific connections.
+ 
+   conn()        doing()       height()      hidden()      idle()
+   lports()      lwho()        mwho()        ports()       pueblo()
+   terminfo()    width() 
  & Dbref functions
    Dbref functions return a dbref or list of dbrefs related to some value
    on an object.
***************
*** 119,133 ****
  & Information functions
    Information functions return values related to objects.
   
!   andflags()    andlflags()   config()      conn()        controls()    
!   ctime()       doing()       elock()       findable()    flags()       
!   fullname()    hasattr()     hasattrp()    hasflag()     haspower()    
!   hastype()     height()      hidden()      idle()        iname()       
!   lflags()      lock()        lstats()      lwho()        money()       
!   mtime()       mwho()        name()        nattr()       nearby()      
!   objmem()      orflags()     orlflags()    playermem()   poll()        
!   ports()       powers()      quota()       type()        visible()     
!   width()
  
  & Mail functions
    Mail functions work with @mail.
--- 127,139 ----
  & Information functions
    Information functions return values related to objects.
   
!   andflags()    andlflags()   config()      controls()    ctime()
!   elock()       findable()    flags()       fullname()    hasattr()
!   hasattrp()    hasflag()     haspower()    hastype()     iname()       
!   lflags()      lock()        lstats()      money()       mtime()
!   name()        nattr()       nearby()      objmem()      orflags()
!   orlflags()    playermem()   poll()        powers()      quota()
!   type()        visible()     
  
  & Mail functions
    Mail functions work with @mail.
***************
*** 656,662 ****
    Ex: config(money_singular) => "Penny"
  
  & CONN()
!   conn(<player name>)
   
    This function returns the number of seconds a player has been connected.
    <player name> must be the full name of a player, or a player's dbref.
--- 662,668 ----
    Ex: config(money_singular) => "Penny"
  
  & CONN()
!   conn(<player|descriptor>)
   
    This function returns the number of seconds a player has been connected.
    <player name> must be the full name of a player, or a player's dbref.
***************
*** 891,897 ****
  
    See also: MODULO
  & DOING()
!   doing(<player>)
  
    Given the name of a connected player, returns that player's @doing
    string if they can be seen on the WHO list.
--- 897,903 ----
  
    See also: MODULO
  & 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.
***************
*** 1428,1434 ****
    Valid types are: ROOM, EXIT, PLAYER, THING.
    If an invalid type is given, #-1 is returned.
  & HIDDEN()
!   hidden(<player>)
  
    Returns 1 if the player is hidden, otherwise 0.
    Can only be called by someone privileged to see hidden players.
--- 1434,1440 ----
    Valid types are: ROOM, EXIT, PLAYER, THING.
    If an invalid type is given, #-1 is returned.
  & HIDDEN()
!   hidden(<player|descriptor>)
  
    Returns 1 if the player is hidden, otherwise 0.
    Can only be called by someone privileged to see hidden players.
***************
*** 1440,1447 ****
    the drop-to of a room, or source of an exit.
  & IDLE()
  & IDLESECS()
!   idle(<player name>)
!   idlesecs(<player name>)
   
    This function returns the number of seconds a player has been idle,
    much as WHO does. <player name> must be the full name of a player, or
--- 1446,1453 ----
    the drop-to of a room, or source of an exit.
  & IDLE()
  & IDLESECS()
!   idle(<player|descriptor>)
!   idlesecs(<player|descriptor>)
   
    This function returns the number of seconds a player has been idle,
    much as WHO does. <player name> must be the full name of a player, or
***************
*** 2558,2571 ****
    This function returns the current @poll. 
  
    See also: @poll, doing(), @doing 
  & PORTS()
    ports(<player name>)
   
!   This function returns the list of descriptors ("ports") that a player,
!   specified by full player name, or by dbref, is connected to. Only players
!   who are See_All or privileged may use this function; in other cases,
!   an empty list is returned. Otherwise, a list of ports is returned in 
!   order of most recent connection to least recent connection.
  & POS()
    pos(<string1>,<string2>)
  
--- 2564,2588 ----
    This function returns the current @poll. 
  
    See also: @poll, doing(), @doing 
+ & LPORTS()
  & PORTS()
+   lports()
    ports(<player name>)
   
!   These function returns the list of descriptors ("ports") that are used by
!   connected players. lports() returns all ports, in the same order as
!   lwho() returns dbrefs, and ports() returns those a specific player
!   is connected to, from most recent to least recent. Only players who
!   are See_All or privileged may use these functions; in other cases,
!   lports() returns #-1, and ports() an empty list. As an exception,
!   players can use ports() on themselves.
! 
!   These port numbers also appear in the wizard WHO, and can be used
!   with @boot/port, page/port, and the functions that return information
!   about a connection to make them use a specific connection rather than the
!   least-idle one when a player has multiple connections open. Players can
!   get information about their own connections. See_all is needed to use
!   them to get information about other people's ports.
  & POS()
    pos(<string1>,<string2>)
  
***************
*** 3356,3361 ****
--- 3373,3395 ----
    if true, makes the function act like @tel/silent.
  
    See also: @tel
+ & TERMINFO()
+   terminfo(<player|descriptor>)
+ 
+   Returns a list with at least one element - the type of client used
+   by the player, or "unknown" if the client being used doesn't support
+   being asked to identify itself using RFC 1091. 
+   
+   Other elements in the list describe client capabilities, and
+   currently include:
+   pueblo           present if the client is in Pueblo mode.
+   telnet           present if the client understands the telnet protocol.
+ 
+   Other fields may be added in the future, if, for example, MXP support
+   is ever added.
+ 
+   Players can use terminfo() on their own connections. Using it on
+   other players is restricted to see_all objects.  
  & TEXTFILE()
  & dynhelp()
    textfile(<type>,<entry>)
***************
*** 3763,3770 ****
  & HEIGHT()
  & SCREENWIDTH
  & SCREENHEIGHT
!   width(<player>)
!   height(<player>)
  
    These two functions return the screen width and height for a connected
    player. If the player's client is capable of doing so, it will let the
--- 3797,3804 ----
  & HEIGHT()
  & SCREENWIDTH
  & SCREENHEIGHT
!   width(<player|descriptor>)
!   height(<player|descriptor>)
  
    These two functions return the screen width and height for a connected
    player. If the player's client is capable of doing so, it will let the
*** 1_7_7.465/game/txt/hlp/penncmd.hlp Tue, 13 May 2003 15:08:27 -0500 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.1.1.3 600)
--- 1_7_7.487(w)/game/txt/hlp/penncmd.hlp Tue, 17 Jun 2003 16:36:11 -0500 dunemush (pennmush/18_penncmd.hl 1.2.1.1.1.47.1.1.1.1.1.3.1.4.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.10.1.1.1.1.1.1.1.1.1.5 600)
***************
*** 2486,2492 ****
  
  See also: @inprefix, AUDIBLE, @listen
  & @ps
!   @ps[/<switch>] [*<player> | all]
    
    @ps is a useful command for MUSHers.  It lists all commands currently on
    your 'to be executed' queue, thus allowing you to identify infinite (or
--- 2486,2492 ----
  
  See also: @inprefix, AUDIBLE, @listen
  & @ps
!   @ps[/<switch>] [*<player>]
    
    @ps is a useful command for MUSHers.  It lists all commands currently on
    your 'to be executed' queue, thus allowing you to identify infinite (or
***************
*** 3788,3795 ****
  
  See also: @doing, @poll, DOING
  & with
!   with <obj>=<command>
  
    Attempts to run a user-defined command on a specific object.
  
    See 'help USER-DEFINED COMMANDS'.
--- 3788,3797 ----
  
  See also: @doing, @poll, DOING
  & with
!   with[/room] <obj>=<command>
  
    Attempts to run a user-defined command on a specific object.
+   If the /room switch is given, <obj> must be a room, and its contents
+   are checked for commands as if it was a master room.
  
    See 'help USER-DEFINED COMMANDS'.
*** 1_7_7.465/utils/mkcmds.sh.SH Thu, 22 May 2003 11:39:55 -0500 dunemush (pennmush/g/16_mkcmds.sh. 1.5 750)
--- 1_7_7.487(w)/utils/mkcmds.sh.SH Tue, 24 Jun 2003 15:05:49 -0500 dunemush (pennmush/g/16_mkcmds.sh. 1.6 750)
***************
*** 101,107 ****
    $echo $n "."
    snum=`expr $snum + 1`
  done
! $echo "{NULL, 0}" >> ../src/temp.c
  $echo "};" >> ../src/temp.c
  $echo ""
  
--- 101,107 ----
    $echo $n "."
    snum=`expr $snum + 1`
  done
! $echo "  {NULL, 0}" >> ../src/temp.c
  $echo "};" >> ../src/temp.c
  $echo ""
  
*** 1_7_7.465/src/switchinc.c Mon, 02 Jun 2003 13:18:13 -0500 dunemush (pennmush/b/32_switchinc. 1.3.1.2.1.6.1.18.1.2.1.2.2.5.1.4.2.4.1.1.1.2.1.5.1.2.1.5.2.1.1.31.3.4.1.5.1.4.1.1.1.1.1.1.1.6 660)
--- 1_7_7.487(w)/src/switchinc.c Tue, 24 Jun 2003 15:09:56 -0500 dunemush (pennmush/b/32_switchinc. 1.3.1.2.1.6.1.18.1.2.1.2.2.5.1.4.2.4.1.1.1.2.1.5.1.2.1.5.2.1.1.31.3.4.1.5.1.4.1.1.1.1.1.1.1.7 660)
***************
*** 137,141 ****
    {"WIZARD", SWITCH_WIZARD},
    {"YES", SWITCH_YES},
    {"ZONE", SWITCH_ZONE},
! {NULL, 0}
  };
--- 137,141 ----
    {"WIZARD", SWITCH_WIZARD},
    {"YES", SWITCH_YES},
    {"ZONE", SWITCH_ZONE},
!   {NULL, 0}
  };
*** 1_7_7.465/src/strutil.c Mon, 28 Apr 2003 22:03:14 -0500 dunemush (pennmush/b/33_strutil.c 1.28.1.3.1.3.1.7.2.1.1.2.1.1.1.1.1.1.1.21.1.2.1.2.1.4 660)
--- 1_7_7.487(w)/src/strutil.c Tue, 24 Jun 2003 15:09:46 -0500 dunemush (pennmush/b/33_strutil.c 1.28.1.3.1.3.1.7.2.1.1.2.1.1.1.1.1.1.1.21.1.2.1.2.1.6 660)
***************
*** 1726,1728 ****
--- 1726,1773 ----
    safe_str(space, buff, bp);
  
  }
+ 
+ /** Return a stringified time in a static buffer
+  * Just like ctime() except without the trailing newlines.
+  * \param t the time to format.
+  * \param utc true if the time should be displayed in UTC, 0 for local time zone.
+  * \return a pointer to a static buffer with the stringified time.
+  */
+ char *
+ show_time(time_t t, int utc)
+ {
+   struct tm *when;
+ 
+   if (utc)
+     when = gmtime(&t);
+   else
+     when = localtime(&t);
+ 
+   return show_tm(when);
+ }
+ 
+ /** Return a stringified time in a static buffer
+  * Just like asctime() except without the trailing newlines.
+  * \param t the time to format.
+  * \return a pointer to a static buffer with the stringified time.
+  */
+ char *
+ show_tm(struct tm *when)
+ {
+   static char buffer[BUFFER_LEN];
+   int p;
+ 
+   if (!when)
+     return NULL;
+ 
+   strcpy(buffer, asctime(when));
+ 
+   p = strlen(buffer) - 1;
+   if (buffer[p] == '\n')
+     buffer[p] = '\0';
+ 
+   if (buffer[8] == ' ')
+     buffer[8] = '0';
+ 
+   return buffer;
+ }
*** 1_7_7.465/src/set.c Sat, 31 May 2003 16:17:58 -0500 dunemush (pennmush/b/38_set.c 1.26.1.5.1.1.2.1.1.1.1.1.1.11.1.1.1.1.1.1.1.1.1.1.1.1.1.2 660)
--- 1_7_7.487(w)/src/set.c Tue, 24 Jun 2003 15:09:46 -0500 dunemush (pennmush/b/38_set.c 1.26.1.5.1.1.2.1.1.1.1.1.1.11.1.1.1.1.1.1.1.1.1.1.1.1.1.3 660)
***************
*** 1059,1072 ****
    else
      notify(player, T("Attributes wiped."));
  }
- 
- COMMAND (cmd_with) {
-   dbref what;
- 
-   what = noisy_match_result(player, arg_left, NOTYPE, MAT_NEARBY);
-   if (!GoodObject(what))
-     return;
- 
-   if (!atr_comm_match(what, player, '$', ':', arg_right, 0, NULL, NULL))
-     notify(player, T("No matching command."));
- }
--- 1059,1061 ----
*** 1_7_7.465/src/player.c Mon, 28 Apr 2003 22:37:00 -0500 dunemush (pennmush/b/47_player.c 1.15.1.1.1.1.1.4.1.6.1.14 660)
--- 1_7_7.487(w)/src/player.c Tue, 24 Jun 2003 15:09:45 -0500 dunemush (pennmush/b/47_player.c 1.15.1.1.1.1.1.4.1.6.1.1.1.2 660)
***************
*** 371,387 ****
  {
  
    dbref player;
-   char *s;
  #ifdef QUOTA
    char temp[SBUF_LEN];
  #endif
    object_flag_type flags;
  
-   /* else he doesn't already exist, create him */
-   s = ctime(&mudtime);
-   s[strlen(s) - 1] = '\0';
-   if (s[8] == ' ')
-     s[8] = '0';
    player = new_object();
  
    /* initialize everything */
--- 371,381 ----
***************
*** 401,407 ****
    ModTime(player) = (time_t) 0;
    (void) atr_add(player, "XYXXY", mush_crypt(password), GOD, NOTHING);
    giveto(player, START_BONUS);	/* starting bonus */
!   (void) atr_add(player, "LAST", s, GOD, NOTHING);
    (void) atr_add(player, "LASTSITE", host, GOD, NOTHING);
    (void) atr_add(player, "LASTIP", ip, GOD, NOTHING);
    (void) atr_add(player, "LASTFAILED", " ", GOD, NOTHING);
--- 395,401 ----
    ModTime(player) = (time_t) 0;
    (void) atr_add(player, "XYXXY", mush_crypt(password), GOD, NOTHING);
    giveto(player, START_BONUS);	/* starting bonus */
!   (void) atr_add(player, "LAST", show_time(mudtime, 0), GOD, NOTHING);
    (void) atr_add(player, "LASTSITE", host, GOD, NOTHING);
    (void) atr_add(player, "LASTIP", ip, GOD, NOTHING);
    (void) atr_add(player, "LASTFAILED", " ", GOD, NOTHING);
***************
*** 478,505 ****
    char last_time[MAX_COMMAND_LEN / 8];
    char last_place[MAX_COMMAND_LEN];
  
-   s = ctime(&mudtime);
-   s[strlen(s) - 1] = '\0';
-   if (s[8] == ' ')
-     s[8] = '0';
    /* compare to last connect see if player gets salary */
    a = atr_get_noparent(player, "LAST");
    if (a && (strncmp(atr_value(a), s, 10) != 0))
      giveto(player, Paycheck(player));
    /* tell the player where he last connected from */
!   h = atr_get_noparent(player, "LASTSITE");
!   if (h && a) {
!     strcpy(last_place, atr_value(h));
!     strcpy(last_time, atr_value(a));
!     notify_format(player, T("Last connect was from %s on %s."),
! 		  last_place, last_time);
!   }
!   /* How about last failed connection */
!   h = atr_get_noparent(player, "LASTFAILED");
!   if (h && a) {
!     strcpy(last_place, atr_value(h));
!     if (strlen(last_place) > 2)
!       notify_format(player, T("Last FAILED connect was from %s."), last_place);
    }
    /* if there is no Lastsite, then the player is newly created.
     * the extra variables are a kludge to work around some weird
--- 472,499 ----
    char last_time[MAX_COMMAND_LEN / 8];
    char last_place[MAX_COMMAND_LEN];
  
    /* compare to last connect see if player gets salary */
+   s = show_time(mudtime, 0);
    a = atr_get_noparent(player, "LAST");
    if (a && (strncmp(atr_value(a), s, 10) != 0))
      giveto(player, Paycheck(player));
    /* tell the player where he last connected from */
!   if (!Guest(player)) {
!     h = atr_get_noparent(player, "LASTSITE");
!     if (h && a) {
!       strcpy(last_place, atr_value(h));
!       strcpy(last_time, atr_value(a));
!       notify_format(player, T("Last connect was from %s on %s."),
! 		    last_place, last_time);
!     }
!     /* How about last failed connection */
!     h = atr_get_noparent(player, "LASTFAILED");
!     if (h && a) {
!       strcpy(last_place, atr_value(h));
!       if (strlen(last_place) > 2)
! 	notify_format(player, T("Last FAILED connect was from %s."),
! 		      last_place);
!     }
    }
    /* if there is no Lastsite, then the player is newly created.
     * the extra variables are a kludge to work around some weird
***************
*** 521,533 ****
  void
  check_lastfailed(dbref player, const char *host)
  {
-   char *s;
    char last_place[BUFFER_LEN], *bp;
  
-   s = ctime(&mudtime);
-   s[strlen(s) - 1] = '\0';
    bp = last_place;
!   safe_format(last_place, &bp, T("%s on %s"), host, s);
    *bp = '\0';
    (void) atr_add(player, "LASTFAILED", last_place, GOD, NOTHING);
  }
--- 515,524 ----
  void
  check_lastfailed(dbref player, const char *host)
  {
    char last_place[BUFFER_LEN], *bp;
  
    bp = last_place;
!   safe_format(last_place, &bp, T("%s on %s"), host, show_time(mudtime, 0));
    *bp = '\0';
    (void) atr_add(player, "LASTFAILED", last_place, GOD, NOTHING);
  }
*** 1_7_7.465/src/look.c Mon, 02 Jun 2003 11:58:48 -0500 dunemush (pennmush/c/4_look.c 1.21.1.2.1.9.1.1.1.1.1.8 660)
--- 1_7_7.487(w)/src/look.c Tue, 24 Jun 2003 15:09:45 -0500 dunemush (pennmush/c/4_look.c 1.21.1.2.1.9.1.1.1.1.1.10 660)
***************
*** 744,757 ****
  
      notify_format(player, T("Warnings checked: %s"), unparse_warnings(thing));
  
!     tp = (char *) ctime(&CreTime(thing));
!     tp[strlen(tp) - 1] = '\0';
!     notify_format(player, T("Created: %s"), tp);
!     if (!IsPlayer(thing)) {
!       tp = (char *) ctime(&ModTime(thing));
!       tp[strlen(tp) - 1] = '\0';
!       notify_format(player, T("Last Modification: %s"), tp);
!     }
    }
    if ((brief != 1) && (EX_PUBLIC_ATTRIBS || ok)) {
      look_atrs(player, thing, NULL, all);
--- 744,753 ----
  
      notify_format(player, T("Warnings checked: %s"), unparse_warnings(thing));
  
!     notify_format(player, T("Created: %s"), show_time(CreTime(thing), 0));
!     if (!IsPlayer(thing))
!       notify_format(player, T("Last Modification: %s"),
! 		    show_time(ModTime(thing), 0));
    }
    if ((brief != 1) && (EX_PUBLIC_ATTRIBS || ok)) {
      look_atrs(player, thing, NULL, all);
*** 1_7_7.465/src/lock.c Thu, 15 May 2003 23:37:08 -0500 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.13 660)
--- 1_7_7.487(w)/src/lock.c Tue, 24 Jun 2003 15:09:45 -0500 dunemush (pennmush/c/6_lock.c 1.17.1.13.1.1.1.1.1.2 660)
***************
*** 414,420 ****
  	return 0;
        }
        t = &Locks(thing);
!       while (*t && strcmp(L_TYPE(*t), L_TYPE(ll)) < 0)
  	t = &L_NEXT(*t);
        L_NEXT(ll) = *t;
        *t = ll;
--- 414,420 ----
  	return 0;
        }
        t = &Locks(thing);
!       while (*t && strcasecmp(L_TYPE(*t), L_TYPE(ll)) < 0)
  	t = &L_NEXT(*t);
        L_NEXT(ll) = *t;
        *t = ll;
***************
*** 468,474 ****
        ll->flags = flags;
      }
      t = &Locks(thing);
!     while (*t && strcmp(L_TYPE(*t), L_TYPE(ll)) < 0)
        t = &L_NEXT(*t);
      L_NEXT(ll) = *t;
      *t = ll;
--- 468,474 ----
        ll->flags = flags;
      }
      t = &Locks(thing);
!     while (*t && strcasecmp(L_TYPE(*t), L_TYPE(ll)) < 0)
        t = &L_NEXT(*t);
      L_NEXT(ll) = *t;
      *t = ll;
*** 1_7_7.465/src/game.c Tue, 13 May 2003 12:53:40 -0500 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.1.1.2 660)
--- 1_7_7.487(w)/src/game.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/10_game.c 1.50.1.8.1.1.1.1.2.1.1.1.2.1.1.4.1.1.1.1.1.1.1.1.1.1.2.1.1.2.1.1.1.1.1.1.1.2.1.1.1.2.1.1.1.1.1.1.1.1.1.5.1.3.1.2.1.2.1.1.1.1.1.1.1.5 660)
***************
*** 198,210 ****
  		  Name(player), player);
        }
        do_rawlog(LT_CHECK, T("\tcheckpoint interval %d, at %s"),
! 		paranoid_checkpt, ctime(&mudtime));
      } else {
        /* normal dump */
        paranoid_dump = 0;	/* just to be safe */
        notify(player, "Dumping...");
        do_rawlog(LT_CHECK, "** DUMP ** done by %s(#%d) at %s",
! 		Name(player), player, ctime(&mudtime));
      }
      fork_and_dump(1);
      paranoid_dump = 0;
--- 198,210 ----
  		  Name(player), player);
        }
        do_rawlog(LT_CHECK, T("\tcheckpoint interval %d, at %s"),
! 		paranoid_checkpt, show_time(mudtime, 0));
      } else {
        /* normal dump */
        paranoid_dump = 0;	/* just to be safe */
        notify(player, "Dumping...");
        do_rawlog(LT_CHECK, "** DUMP ** done by %s(#%d) at %s",
! 		Name(player), player, show_time(mudtime, 0));
      }
      fork_and_dump(1);
      paranoid_dump = 0;
***************
*** 652,658 ****
      first_start_time = start_time;
    do_rawlog(LT_ERR, "%s", VERSION);
    do_rawlog(LT_ERR, T("MUSH restarted, PID %d, at %s"),
! 	    (int) getpid(), ctime(&start_time));
  
    /* initialize all the hash and prefix tables */
    init_flag_table();
--- 652,658 ----
      first_start_time = start_time;
    do_rawlog(LT_ERR, "%s", VERSION);
    do_rawlog(LT_ERR, T("MUSH restarted, PID %d, at %s"),
! 	    (int) getpid(), show_time(start_time, 0));
  
    /* initialize all the hash and prefix tables */
    init_flag_table();
***************
*** 870,876 ****
  /** Check each attribute on each object in x for a $command matching cptr */
  #define list_match(x)        list_check(x, player, '$', ':', cptr, 0)
  /** Check each attribute on x for a $command matching cptr */
! #define cmd_match(x)         atr_comm_match(x, player, '$', ':', cptr, 0, NULL, NULL);
  
  /** Attempt to match and execute a command.
   * This function performs some sanity checks and then attempts to
--- 870,876 ----
  /** Check each attribute on each object in x for a $command matching cptr */
  #define list_match(x)        list_check(x, player, '$', ':', cptr, 0)
  /** Check each attribute on x for a $command matching cptr */
! #define cmd_match(x)         atr_comm_match(x, player, '$', ':', cptr, 0, NULL, NULL)
  
  /** Attempt to match and execute a command.
   * This function performs some sanity checks and then attempts to
***************
*** 1071,1076 ****
--- 1071,1101 ----
  }
  
  
+ COMMAND (cmd_with) {
+   dbref what;
+   char *cptr = arg_right;
+ 
+   what = noisy_match_result(player, arg_left, NOTYPE, MAT_NEARBY);
+   if (!GoodObject(what))
+     return;
+ 
+   if (!SW_ISSET(sw, SWITCH_ROOM)) {
+     /* Run commands on a single object */
+     if (!cmd_match(what))
+       notify(player, T("No matching command."));
+   } else {
+     /* Run commands on objects in a masterish room */
+ 
+     if (!IsRoom(what)) {
+       notify(player, T("Make room! Make room!"));
+       return;
+     }
+ 
+     if (!list_match(Contents(what)))
+       notify(player, T("No matching command."));
+   }
+ }
+ 
  /* now undef everything that needs to be */
  #undef list_match
  #undef cmd_match
*** 1_7_7.465/src/funtime.c Wed, 19 Mar 2003 13:43:16 -0600 dunemush (pennmush/c/12_funtime.c 1.11.1.14.1.2 660)
--- 1_7_7.487(w)/src/funtime.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/12_funtime.c 1.11.1.14.1.3 660)
***************
*** 23,29 ****
  #include "log.h"
  #include "confmagic.h"
  
! int do_convtime(char *str, struct tm *ttm);
  void do_timestring(char *buff, char **bp, const char *format,
  		   unsigned long secs);
  
--- 23,29 ----
  #include "log.h"
  #include "confmagic.h"
  
! int do_convtime(const char *str, struct tm *ttm);
  void do_timestring(char *buff, char **bp, const char *format,
  		   unsigned long secs);
  
***************
*** 86,110 ****
  /* ARGSUSED */
  FUNCTION(fun_time)
  {
!   char *s;
!   struct tm *t;
  
    if (nargs == 1) {
      if (!strcasecmp("UTC", args[0]))
!       t = gmtime(&mudtime);
      else {
        safe_str(T("#-1 INVALID TIME ZONE"), buff, bp);
        return;
      }
    } else if (!strcmp("UTCTIME", called_as))
!     t = gmtime(&mudtime);
!   else
!     t = localtime(&mudtime);
!   s = (char *) asctime(t);
!   s[strlen(s) - 1] = '\0';
!   if (s[8] == ' ')
!     s[8] = '0';
!   safe_str(s, buff, bp);
  }
  
  /* ARGSUSED */
--- 86,104 ----
  /* ARGSUSED */
  FUNCTION(fun_time)
  {
!   int utc = 0;
  
    if (nargs == 1) {
      if (!strcasecmp("UTC", args[0]))
!       utc = 1;
      else {
        safe_str(T("#-1 INVALID TIME ZONE"), buff, bp);
        return;
      }
    } else if (!strcmp("UTCTIME", called_as))
!     utc = 1;
! 
!   safe_str(show_time(mudtime, utc), buff, bp);
  }
  
  /* ARGSUSED */
***************
*** 120,126 ****
  
    time_t tt;
    struct tm *ttm;
-   char *s;
    int utc = 0;
  
    if (strcmp(called_as, "CONVUTCSECS") == 0 ||
--- 114,119 ----
***************
*** 146,156 ****
    else
      ttm = localtime(&tt);
  
!   s = (char *) asctime(ttm);
!   s[strlen(s) - 1] = '\0';
!   if (s[8] == ' ')
!     s[8] = '0';
!   safe_str(s, buff, bp);
  }
  
  /* ARGSUSED */
--- 139,145 ----
    else
      ttm = localtime(&tt);
  
!   safe_str(show_tm(ttm), buff, bp);
  }
  
  /* ARGSUSED */
***************
*** 215,221 ****
  }
  
  #ifdef HAS_GETDATE
! int do_convtime_gd(char *str, struct tm *ttm);
  /** Convert a time string to a struct tm using getdate().
   * Formats for the time string are taken from the file referenced in
   * the DATEMSK environment variable.
--- 204,210 ----
  }
  
  #ifdef HAS_GETDATE
! int do_convtime_gd(const char *str, struct tm *ttm);
  /** Convert a time string to a struct tm using getdate().
   * Formats for the time string are taken from the file referenced in
   * the DATEMSK environment variable.
***************
*** 225,231 ****
   * \retval 0 failure.
   */
  int
! do_convtime_gd(char *str, struct tm *ttm)
  {
    /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
     * Formats of the time string are taken from the file listed in the
--- 214,220 ----
   * \retval 0 failure.
   */
  int
! do_convtime_gd(const char *str, struct tm *ttm)
  {
    /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
     * Formats of the time string are taken from the file listed in the
***************
*** 273,279 ****
   * \retval 0 failure.
   */
  int
! do_convtime(char *mystr, struct tm *ttm)
  {
    /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
     * Time string format is always 24 characters long, in format
--- 262,268 ----
   * \retval 0 failure.
   */
  int
! do_convtime(const char *mystr, struct tm *ttm)
  {
    /* converts time string to a struct tm. Returns 1 on success, 0 on fail.
     * Time string format is always 24 characters long, in format
*** 1_7_7.465/src/funmisc.c Thu, 27 Feb 2003 22:05:20 -0600 dunemush (pennmush/c/14_funmisc.c 1.30.1.1.1.18 660)
--- 1_7_7.487(w)/src/funmisc.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/14_funmisc.c 1.30.1.1.1.19 660)
***************
*** 335,357 ****
  /* ARGSUSED */
  FUNCTION(fun_starttime)
  {
!   char *s;
!   s = (char *) ctime(&first_start_time);
!   s[strlen(s) - 1] = '\0';
!   if (s[8] == ' ')
!     s[8] = '0';
!   safe_str(s, buff, bp);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_restarttime)
  {
!   char *s;
!   s = (char *) ctime(&start_time);
!   s[strlen(s) - 1] = '\0';
!   if (s[8] == ' ')
!     s[8] = '0';
!   safe_str(s, buff, bp);
  }
  
  /* ARGSUSED */
--- 335,347 ----
  /* ARGSUSED */
  FUNCTION(fun_starttime)
  {
!   safe_str(show_time(first_start_time, 0), buff, bp);
  }
  
  /* ARGSUSED */
  FUNCTION(fun_restarttime)
  {
!   safe_str(show_time(start_time, 0), buff, bp);
  }
  
  /* ARGSUSED */
*** 1_7_7.465/src/fundb.c Mon, 28 Apr 2003 22:37:00 -0500 dunemush (pennmush/c/17_fundb.c 1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.7.1.3.1.3.1.3.1.2.1.2.1.3.2.1.2.1.2.1.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.3.1.1.2.2.2.1.1.1.1.1.1.13 660)
--- 1_7_7.487(w)/src/fundb.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/17_fundb.c 1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.7.1.3.1.3.1.3.1.2.1.2.1.3.2.1.2.1.2.1.1.1.1.4.1.1.1.1.1.1.1.1.1.1.1.3.1.1.2.2.2.1.1.1.1.1.1.15 660)
***************
*** 1148,1164 ****
  FUNCTION(fun_ctime)
  {
    dbref it = match_thing(executor, args[0]);
-   char *s;
  
!   if (GoodObject(it)) {
!     s = (char *) ctime(&CreTime(it));
!     s[strlen(s) - 1] = '\0';
!     if (s[8] == ' ')
!       s[8] = '0';
!     safe_str(s, buff, bp);
!     return;
!   }
!   safe_str(T(e_notvis), buff, bp);
  }
  
  /* ARGSUSED */
--- 1148,1158 ----
  FUNCTION(fun_ctime)
  {
    dbref it = match_thing(executor, args[0]);
  
!   if (GoodObject(it))
!     safe_str(show_time(CreTime(it), 0), buff, bp);
!   else
!     safe_str(T(e_notvis), buff, bp);
  }
  
  /* ARGSUSED */
***************
*** 1169,1181 ****
      safe_str(T(e_notvis), buff, bp);
    else if (!Can_Examine(executor, it) || IsPlayer(it))
      safe_str(T(e_perm), buff, bp);
!   else {
!     char *s = (char *) ctime(&ModTime(it));
!     s[strlen(s) - 1] = '\0';
!     if (s[8] == ' ')
!       s[8] = '0';
!     safe_str(s, buff, bp);
!   }
  }
  
  /* ARGSUSED */
--- 1163,1170 ----
      safe_str(T(e_notvis), buff, bp);
    else if (!Can_Examine(executor, it) || IsPlayer(it))
      safe_str(T(e_perm), buff, bp);
!   else
!     safe_str(show_time(ModTime(it), 0), buff, bp);
  }
  
  /* ARGSUSED */
*** 1_7_7.465/src/function.c Mon, 28 Apr 2003 22:37:00 -0500 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.16 660)
--- 1_7_7.487(w)/src/function.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/18_function.c 1.29.1.14.1.3.1.6.1.1.1.1.1.14.1.2.1.1.1.7.1.18 660)
***************
*** 334,339 ****
--- 334,340 ----
    {"LOCK", fun_lock, 1, 2, FN_REG},
    {"LPARENT", fun_lparent, 1, 1, FN_REG},
    {"LPLAYERS", fun_lplayers, 1, 1, FN_REG},
+   {"LPORTS", fun_lports, 0, 0, FN_REG},
    {"LPOS", fun_lpos, 2, 2, FN_REG},
    {"LSEARCH", fun_lsearch, 1, 5, FN_REG},
    {"LSEARCHR", fun_lsearch, 1, 5, FN_REG},
***************
*** 477,482 ****
--- 478,484 ----
    {"T", fun_t, 1, 1, FN_REG},
    {"TABLE", fun_table, 1, 5, FN_REG},
    {"TEL", fun_tel, 2, 3, FN_REG},
+   {"TERMINFO", fun_terminfo, 1, 1, FN_REG},
    {"TEXTFILE", fun_textfile, 2, 2, FN_REG},
    {"TIME", fun_time, 0, 1, FN_REG},
    {"TIMEFMT", fun_timefmt, 1, 2, FN_REG},
*** 1_7_7.465/src/extmail.c Mon, 12 May 2003 16:36:25 -0500 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.1.1.3 660)
--- 1_7_7.487(w)/src/extmail.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/22_extmail.c 1.44.1.7.1.5.1.9.1.1.1.1.1.5 660)
***************
*** 97,103 ****
  /** Evaluate a function, and jump on error */
  #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0)
  
! extern int do_convtime(char *str, struct tm *ttm);	/* funtime.c */
  
  static void do_mail_flags
    (dbref player, const char *msglist, mail_flag flag, int negate);
--- 97,103 ----
  /** Evaluate a function, and jump on error */
  #define OUTPUT(fun) do { if ((fun) < 0) longjmp(db_err, 1); } while (0)
  
! extern int do_convtime(const char *str, struct tm *ttm);	/* funtime.c */
  
  static void do_mail_flags
    (dbref player, const char *msglist, mail_flag flag, int negate);
***************
*** 521,527 ****
  			      && Connected(mp->from)
  			      && (!hidden(mp->from)
  				  || Priv_Who(player))) ? " (Conn)" : "      ",
! 		      uncompress(mp->time), folderheader, Folder(mp),
  		      i[Folder(mp)], status_string(mp));
  	notify_format(player, T("Subject: %s"), get_subject(mp));
  	notify(player, DASH_LINE);
--- 521,527 ----
  			      && Connected(mp->from)
  			      && (!hidden(mp->from)
  				  || Priv_Who(player))) ? " (Conn)" : "      ",
! 		      show_time(mp->time, 0), folderheader, Folder(mp),
  		      i[Folder(mp)], status_string(mp));
  	notify_format(player, T("Subject: %s"), get_subject(mp));
  	notify(player, DASH_LINE);
***************
*** 594,600 ****
  					    (!hidden(mp->from)
  					     || Priv_Who(player)))
  		       ? '*' : ' '), sender, 30, subj,
! 		      mail_list_time(uncompress(mp->time), 1));
  	if (SUPPORT_PUEBLO)
  	  notify_noenter(player, tprintf("%c/A%c", TAG_START, TAG_END));
        }
--- 594,600 ----
  					    (!hidden(mp->from)
  					     || Priv_Who(player)))
  		       ? '*' : ' '), 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));
        }
***************
*** 671,677 ****
        if (mp->subject != mp->message)
  	free(mp->subject);
        free(mp->message);
-       free(mp->time);
        mush_free((Malloc_t) mp, "mail");
      } else {
        nextp = mp->next;
--- 671,676 ----
***************
*** 935,941 ****
    /* send a mail message */
  
    MAIL *newp, *mp;
-   char tbuf1[30];
    int rc, uc, cc;
    char *newmsg, *nm, *buff, *bp;
    char const *ms;
--- 934,939 ----
***************
*** 1012,1020 ****
      mush_free((Malloc_t) newmsg, "string");
    }
  
!   strcpy(tbuf1, ctime(&mudtime));
!   tbuf1[strlen(tbuf1) - 1] = '\0';	/* whack the newline */
!   newp->time = compress(tbuf1);
    newp->read = flags & M_FMASK;	/* Send to folder 0 */
  
    /* Where do we insert it? After mp, wherever that is.
--- 1010,1016 ----
      mush_free((Malloc_t) newmsg, "string");
    }
  
!   newp->time = mudtime;
    newp->read = flags & M_FMASK;	/* Send to folder 0 */
  
    /* Where do we insert it? After mp, wherever that is.
***************
*** 1089,1095 ****
      if (mp->subject != mp->message)
        free(mp->subject);
      free(mp->message);
-     free(mp->time);
      mush_free((Malloc_t) mp, "mail");
    }
  
--- 1085,1090 ----
***************
*** 1170,1176 ****
  	if (mp->subject != mp->message)
  	  free(mp->subject);
  	free(mp->message);
- 	free(mp->time);
  	mush_free((Malloc_t) mp, "mail");
        } else if (!GoodObject(mp->from)) {
  	/* Oops, it's from a player whose dbref is out of range!
--- 1165,1170 ----
***************
*** 1319,1325 ****
      }
      if (mp->to == target) {
        if (!tr && !tu)
! 	strcpy(last, (char *) uncompress(mp->time));
        if (Cleared(mp))
  	tc++;
        else if (Read(mp))
--- 1313,1319 ----
      }
      if (mp->to == target) {
        if (!tr && !tu)
! 	strcpy(last, show_time(mp->time, 0));
        if (Cleared(mp))
  	tc++;
        else if (Read(mp))
***************
*** 1723,1729 ****
      }
      if (mp->to == target) {
        if (!tr && !tu)
! 	strcpy(last, (char *) uncompress(mp->time));
        if (Cleared(mp))
  	tc++;
        else if (Read(mp))
--- 1717,1723 ----
      }
      if (mp->to == target) {
        if (!tr && !tu)
! 	strcpy(last, show_time(mp->time, 0));
        if (Cleared(mp))
  	tc++;
        else if (Read(mp))
***************
*** 1762,1769 ****
    if (!mp)
      safe_str("#-1", buff, bp);
    else
!     safe_str(uncompress(mp->time), buff, bp);
!   return;
  }
  
  /* ARGSUSED */
--- 1756,1762 ----
    if (!mp)
      safe_str("#-1", buff, bp);
    else
!     safe_str(show_time(mp->time, 0), buff, bp);
  }
  
  /* ARGSUSED */
***************
*** 1824,1830 ****
      putref(fp, mp->to);
      putref(fp, mp->from);
      putref(fp, mp->from_ctime);
!     putstring(fp, uncompress(mp->time));
      if (mp->subject)
        putstring(fp, uncompress(mp->subject));
      else
--- 1817,1823 ----
      putref(fp, mp->to);
      putref(fp, mp->from);
      putref(fp, mp->from_ctime);
!     putstring(fp, show_time(mp->time, 0));
      if (mp->subject)
        putstring(fp, uncompress(mp->subject));
      else
***************
*** 1938,1943 ****
--- 1931,1937 ----
    MAIL *mp, *tmpmp;
    int done = 0;
    char sbuf[BUFFER_LEN];
+   struct tm ttm;
  
    mail_init();
  
***************
*** 1977,1983 ****
      mp->from_ctime = getref(fp);
    else
      mp->from_ctime = 0;		/* No one will have this creation time */
!   mp->time = compress(getstring_noalloc(fp));
    if (mail_flags & MDBF_SUBJECT) {
      tbuf = compress(getstring_noalloc(fp));
    }
--- 1971,1982 ----
      mp->from_ctime = getref(fp);
    else
      mp->from_ctime = 0;		/* No one will have this creation time */
! 
!   if (do_convtime(getstring_noalloc(fp), &ttm))
!     mp->time = mktime(&ttm);
!   else				/* do_convtime failed. Odd. */
!     mp->time = mudtime;
! 
    if (mail_flags & MDBF_SUBJECT) {
      tbuf = compress(getstring_noalloc(fp));
    }
***************
*** 2004,2010 ****
        mp->from_ctime = getref(fp);
      else
        mp->from_ctime = 0;	/* No one will have this creation time */
!     mp->time = compress(getstring_noalloc(fp));
      if (mail_flags & MDBF_SUBJECT)
        tbuf = compress(getstring_noalloc(fp));
      else
--- 2003,2012 ----
        mp->from_ctime = getref(fp);
      else
        mp->from_ctime = 0;	/* No one will have this creation time */
!     if (do_convtime(getstring_noalloc(fp), &ttm))
!       mp->time = mktime(&ttm);
!     else			/* do_convtime failed. Odd. */
!       mp->time = mudtime;
      if (mail_flags & MDBF_SUBJECT)
        tbuf = compress(getstring_noalloc(fp));
      else
***************
*** 2243,2251 ****
  static int
  mail_match(dbref player, MAIL *mp, struct mail_selector ms, int num)
  {
!   time_t msgtime, diffdays;
!   char msgtimestr[BUFFER_LEN];
!   struct tm *msgtm;
    mail_flag mpflag;
  
    /* Does a piece of mail match the mail_selector? */
--- 2245,2251 ----
  static int
  mail_match(dbref player, MAIL *mp, struct mail_selector ms, int num)
  {
!   int diffdays;
    mail_flag mpflag;
  
    /* Does a piece of mail match the mail_selector? */
***************
*** 2263,2289 ****
    if (ms.days != -1) {
      /* Get the time now, subtract mp->time, and compare the results with
       * ms.days (in manner of ms.day_comp) */
!     msgtm = (struct tm *) malloc(sizeof(struct tm));
!     if (!msgtm) {
!       /* Ugly malloc failure */
!       do_log(LT_ERR, 0, 0, "MAIL: Couldn't malloc struct tm!");
!       return 0;
!     }
!     strcpy(msgtimestr, (char *) uncompress(mp->time));
!     if (do_convtime(msgtimestr, msgtm)) {
! #ifdef HAS_TIMELOCAL
!       msgtime = timelocal(msgtm);
! #else
!       msgtime = mktime(msgtm);
! #endif				/* HAS_TIMELOCAL */
!       free(msgtm);
!       diffdays = (mudtime - msgtime) / 86400;
!       if (sign(diffdays - ms.days) != ms.day_comp)
! 	return 0;
!     } else {
!       free(msgtm);
        return 0;
!     }
    }
    return 1;
  }
--- 2263,2273 ----
    if (ms.days != -1) {
      /* Get the time now, subtract mp->time, and compare the results with
       * ms.days (in manner of ms.day_comp) */
!     diffdays = difftime(mudtime, mp->time) / 86400;
!     if (sign(diffdays - ms.days) != ms.day_comp)
        return 0;
!     else
!       return 1;
    }
    return 1;
  }
*** 1_7_7.465/src/extchat.c Mon, 28 Apr 2003 22:37:00 -0500 dunemush (pennmush/c/23_extchat.c 1.1.1.1.1.1.1.1.1.2.1.1.1.3.1.1.1.5.1.1.1.1.1.5.1.2.1.3.1.3.1.1.1.4.1.2.1.6.1.2.1.1.2.4.2.9.1.2.1.2.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.2.1.1.2.1.1.2.1.1.1.1.1.30 660)
--- 1_7_7.487(w)/src/extchat.c Tue, 24 Jun 2003 15:09:44 -0500 dunemush (pennmush/c/23_extchat.c 1.1.1.1.1.1.1.1.1.2.1.1.1.3.1.1.1.5.1.1.1.1.1.5.1.2.1.3.1.3.1.1.1.4.1.2.1.6.1.2.1.1.2.4.2.9.1.2.1.2.1.3.1.2.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.2.1.1.2.1.1.2.1.1.1.1.1.34 660)
***************
*** 2178,2205 ****
    char cleanp[CHAN_NAME_LEN];
  
    strcpy(cleanname, remove_markup(partname, NULL));
!   if (ShowAnsi(player)) {
!     for (c = channels; c; c = c->next) {
!       strcpy(cleanp, remove_markup(ChanName(c), NULL));
!       if (Chan_Can_See(c, player) && string_prefix(cleanp, cleanname)) {
! 	notify_format(player, "%s<%s>%s",
! 		      ANSI_HILITE, ChanName(c), ANSI_NORMAL);
! 	notify_format(player, "%s: %s", T("Creator"), Name(ChanCreator(c)));
! 	notify(player, privs_to_string(priv_table, ChanType(c)));
! 	notify(player, ChanTitle(c));
! 	found++;
!       }
!     }
!   } else {
!     for (c = channels; c; c = c->next) {
!       strcpy(cleanp, remove_markup(ChanName(c), NULL));
!       if (Chan_Can_See(c, player) && string_prefix(cleanp, cleanname)) {
! 	notify_format(player, "<%s>", ChanName(c));
! 	notify_format(player, "%s: %s", T("Creator"), Name(ChanCreator(c)));
! 	notify(player, privs_to_string(priv_table, ChanType(c)));
! 	notify(player, ChanTitle(c));
! 	found++;
!       }
      }
    }
    if (!found)
--- 2178,2196 ----
    char cleanp[CHAN_NAME_LEN];
  
    strcpy(cleanname, remove_markup(partname, NULL));
!   for (c = channels; c; c = c->next) {
!     strcpy(cleanp, remove_markup(ChanName(c), NULL));
!     if (Chan_Can_See(c, player) && string_prefix(cleanp, cleanname)) {
!       notify_format(player, "%s<%s>%s", ANSI_HILITE, ChanName(c), ANSI_NORMAL);
!       notify_format(player, T("Description: %s"), ChanTitle(c));
!       notify_format(player, T("Creator: %s"), Name(ChanCreator(c)));
!       notify_format(player, T("Flags: %s"),
! 		    privs_to_string(priv_table, ChanType(c)));
!       if (ChanBuffer(c))
! 	notify_format(player,
! 		      T("Recall buffer: %dk, with %d lines stored."),
! 		      channel_buffer_lines(c) * 8, ChanNumBuffered(c));
!       found++;
      }
    }
    if (!found)
***************
*** 2776,2781 ****
--- 2767,2774 ----
    int room = len + sizeof(len) + sizeof(time_t) + 1;
    if (!ChanBuffer(c))
      return;
+   if (room > ChanBufferSize(c))
+     return;
    if ((ChanBufferEnd(c) > ChanBuffer(c)) &&
        ((ChanBufferSize(c) - (ChanBufferEnd(c) - ChanBuffer(c))) < room))
      channel_shift_buffer(c, room);
***************
*** 2805,2811 ****
      skipped++;
    }
  
!   if (space_needed > 0) {
      /* Not good. We couldn't get the space we needed even after we
       * emptied the buffer. This should never happen, but if it does,
       * we'll just log a fault and do nothing.
--- 2798,2804 ----
      skipped++;
    }
  
!   if ((p != ChanBufferEnd(c)) && (space_needed > 0)) {
      /* Not good. We couldn't get the space we needed even after we
       * emptied the buffer. This should never happen, but if it does,
       * we'll just log a fault and do nothing.
***************
*** 2844,2850 ****
  channel_allocate_buffer(CHAN *c, int lines)
  {
    int bytes = lines * (BUFFER_LEN + sizeof(int) + sizeof(time_t));
!   ChanBuffer(c) = (char *) malloc(bytes);
    *ChanBuffer(c) = '\0';
    ChanBufferEnd(c) = ChanBuffer(c);
    ChanNumBuffered(c) = 0;
--- 2837,2843 ----
  channel_allocate_buffer(CHAN *c, int lines)
  {
    int bytes = lines * (BUFFER_LEN + sizeof(int) + sizeof(time_t));
!   ChanBuffer(c) = mush_malloc(bytes, "channel.buffer");
    *ChanBuffer(c) = '\0';
    ChanBufferEnd(c) = ChanBuffer(c);
    ChanNumBuffered(c) = 0;
***************
*** 2854,2859 ****
--- 2847,2854 ----
  static void
  channel_reallocate_buffer(CHAN *c, int lines)
  {
+   char *newbuff;
+   ptrdiff_t bufflen;
    int bytes = lines * (BUFFER_LEN + sizeof(int) + sizeof(time_t));
    /* If we were accidentally called without a buffer, deal */
    if (!ChanBuffer(c)) {
***************
*** 2868,2882 ****
      if ((ChanBufferEnd(c) - ChanBuffer(c)) >= bytes)
        channel_shift_buffer(c, ChanBufferEnd(c) - ChanBuffer(c) - bytes);
    }
!   ChanBuffer(c) = (char *) realloc(ChanBuffer(c), bytes);
!   ChanBufferSize(c) = bytes;
  }
  
  static void
  channel_free_buffer(CHAN *c)
  {
    if (ChanBuffer(c))
!     free(ChanBuffer(c));
    ChanBuffer(c) = ChanBufferEnd(c) = NULL;
    ChanBufferSize(c) = 0;
    ChanNumBuffered(c) = 0;
--- 2863,2882 ----
      if ((ChanBufferEnd(c) - ChanBuffer(c)) >= bytes)
        channel_shift_buffer(c, ChanBufferEnd(c) - ChanBuffer(c) - bytes);
    }
!   bufflen = ChanBufferEnd(c) - ChanBuffer(c);
!   newbuff = realloc(ChanBuffer(c), bytes);
!   if (newbuff) {
!     ChanBuffer(c) = newbuff;
!     ChanBufferEnd(c) = ChanBuffer(c) + bufflen;
!     ChanBufferSize(c) = bytes;
!   }
  }
  
  static void
  channel_free_buffer(CHAN *c)
  {
    if (ChanBuffer(c))
!     mush_free(ChanBuffer(c), "channel.buffer");
    ChanBuffer(c) = ChanBufferEnd(c) = NULL;
    ChanBufferSize(c) = 0;
    ChanNumBuffered(c) = 0;
***************
*** 2956,2972 ****
      }
    }
    /* Now show everything from this point on */
    while (p < ChanBufferEnd(chan)) {
!     memcpy(&size, p, sizeof(size));
!     p += sizeof(size);
!     memcpy(&timestamp, p, sizeof(time_t));
!     stamp = (char *) ctime(&timestamp);
!     stamp[strlen(stamp) - 1] = '\0';
!     p += sizeof(time_t);
      memcpy(tbuf1, p, size + 1);
!     notify_format(player, "%s %s", stamp, tbuf1);
      p += size + 1;
    }
  }
  
  /** Set the size of a channel's buffer in maximum lines.
--- 2956,2973 ----
      }
    }
    /* Now show everything from this point on */
+   notify_format(player, T("CHAT: Recall from channel <%s>"), ChanName(chan));
    while (p < ChanBufferEnd(chan)) {
!     memcpy(&size, p, sizeof size);
!     p += sizeof size;
!     memcpy(&timestamp, p, sizeof timestamp);
!     stamp = show_time(timestamp, 0);
!     p += sizeof timestamp;
      memcpy(tbuf1, p, size + 1);
!     notify_format(player, "[%s] %s", stamp, tbuf1);
      p += size + 1;
    }
+   notify(player, T("CHAT: End recall"));
  }
  
  /** Set the size of a channel's buffer in maximum lines.
*** 1_7_7.465/src/conf.c Tue, 13 May 2003 12:26:58 -0500 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.12 660)
--- 1_7_7.487(w)/src/conf.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/c/31_conf.c 1.41.2.3.1.3.1.2.1.15.1.1.1.1.1.1.1.2 660)
***************
*** 492,498 ****
--- 492,500 ----
  /** The table of all configuration groups. */
  CONFGROUP confgroups[] = {
    {"attribs", "Options affecting attributes", 0},
+ #ifdef CHAT_SYSTEM
    {"chat", "Chat system options", 0},
+ #endif
    {"cmds", "Options affecting command behavior", 0},
    {"compile", "Compile-time options", 0},
    {"cosmetic", "Cosmetic options", 0},
*** 1_7_7.465/src/command.c Wed, 28 May 2003 10:06:29 -0500 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.8 660)
--- 1_7_7.487(w)/src/command.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/c/36_command.c 1.56.1.1.1.1.1.1.1.2.1.1.1.1.1.5.1.2.1.1.1.1.1.2.1.3.1.10.1.1.3.9 660)
***************
*** 314,320 ****
  
    {"WHISPER", "LIST NOISY SILENT NOEVAL", cmd_whisper,
     CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0},
!   {"WITH", "NOEVAL", cmd_with, CMD_T_PLAYER | CMD_T_THING | CMD_T_EQSPLIT,
     0, 0},
  
  /* ATTRIB_SET is an undocumented command - it's sugar to make it possible
--- 314,320 ----
  
    {"WHISPER", "LIST NOISY SILENT NOEVAL", cmd_whisper,
     CMD_T_ANY | CMD_T_EQSPLIT | CMD_T_NOGAGGED, 0, 0},
!   {"WITH", "NOEVAL ROOM", cmd_with, CMD_T_PLAYER | CMD_T_THING | CMD_T_EQSPLIT,
     0, 0},
  
  /* ATTRIB_SET is an undocumented command - it's sugar to make it possible
*** 1_7_7.465/src/cmds.c Wed, 28 May 2003 10:06:29 -0500 dunemush (pennmush/c/37_cmds.c 1.33.1.1.1.2.1.2.2.3.1.1.1.2.1.1.1.3.1.8.1.1.2.2.2.17 660)
--- 1_7_7.487(w)/src/cmds.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/c/37_cmds.c 1.33.1.1.1.2.1.2.2.3.1.1.1.2.1.1.1.3.1.8.1.1.2.2.2.18 660)
***************
*** 772,780 ****
  COMMAND (cmd_stats) {
    if (SW_ISSET(sw, SWITCH_TABLES))
      do_list_memstats(player);
!   else if (SW_ISSET(sw, SWITCH_CHUNKS))
!     chunk_stats(player, 0);
!   else if (SW_ISSET(sw, SWITCH_REGIONS))
      chunk_stats(player, 1);
    else if (SW_ISSET(sw, SWITCH_PAGING))
      chunk_stats(player, 2);
--- 772,783 ----
  COMMAND (cmd_stats) {
    if (SW_ISSET(sw, SWITCH_TABLES))
      do_list_memstats(player);
!   else if (SW_ISSET(sw, SWITCH_CHUNKS)) {
!     if (SW_ISSET(sw, SWITCH_REGIONS))
!       chunk_stats(player, 4);
!     else
!       chunk_stats(player, 0);
!   } else if (SW_ISSET(sw, SWITCH_REGIONS))
      chunk_stats(player, 1);
    else if (SW_ISSET(sw, SWITCH_PAGING))
      chunk_stats(player, 2);
*** 1_7_7.465/src/bsd.c Mon, 02 Jun 2003 11:58:48 -0500 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.3 660)
--- 1_7_7.487(w)/src/bsd.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/c/38_bsd.c 1.58.1.11.1.2.1.5.1.7.1.14.1.13.1.9.1.4.1.2.1.12.1.1.1.1.1.2.1.1.1.13.1.1.1.1.1.1.1.1.1.1.1.3.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3.1.8.2.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.3 660)
***************
*** 224,229 ****
--- 224,230 ----
  #define TN_SGA 3		/**< Suppress go-ahead */
  #define TN_LINEMODE 34		/**< Line mode */
  #define TN_NAWS 31		/**< Negotiate About Window Size */
+ #define TN_TTYPE 24		/**< Ask for termial type information */
  static void test_telnet(DESC *d);
  static void setup_telnet(DESC *d);
  static int handle_telnet(DESC *d, unsigned char **q, unsigned char *qend);
***************
*** 1640,1645 ****
--- 1641,1647 ----
  #endif
    {
      freeqs(d);
+     mush_free(d->ttype, "terminal description");
      mush_free((Malloc_t) d, "descriptor");
    }
  
***************
*** 1683,1688 ****
--- 1685,1693 ----
    d->conn_flags = CONN_DEFAULT;
    d->input_chars = 0;
    d->output_chars = 0;
+   d->width = 78;
+   d->height = 24;
+   d->ttype = mush_strdup("unknown", "terminal description");
  #ifdef HAS_OPENSSL
    d->bio = NULL;
  #endif
***************
*** 1713,1720 ****
    d->bConnectionShutdown = FALSE;	/* not shutdown yet */
    d->bConnectionDropped = FALSE;	/* not dropped yet */
  #else
-   d->width = 78;
-   d->height = 24;
    test_telnet(d);
    welcome_user(d);
  #endif
--- 1718,1723 ----
***************
*** 2186,2197 ****
       option for local echo, just remote echo. */
    d->conn_flags |= CONN_TELNET;
    if (d->conn_flags & CONN_TELNET_QUERY) {
!     /* IAC DO NAWS */
!     unsigned char extra_options[3] = "\xFF\xFD\x1F";
      d->conn_flags &= ~CONN_TELNET_QUERY;
      do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Switching to Telnet mode."),
  	   d->descriptor, d->addr, d->ip);
!     queue_newwrite(d, extra_options, 3);
      process_output(d);
    }
  }
--- 2189,2200 ----
       option for local echo, just remote echo. */
    d->conn_flags |= CONN_TELNET;
    if (d->conn_flags & CONN_TELNET_QUERY) {
!     /* IAC DO NAWS IAC DO TERMINAL-TYPE */
!     unsigned char extra_options[6] = "\xFF\xFD\x1F" "\xFF\xFD\x18";
      d->conn_flags &= ~CONN_TELNET_QUERY;
      do_log(LT_CONN, 0, 0, T("[%d/%s/%s] Switching to Telnet mode."),
  	   d->descriptor, d->addr, d->ip);
!     queue_newwrite(d, extra_options, 6);
      process_output(d);
    }
  }
***************
*** 2273,2278 ****
--- 2276,2313 ----
        if (*q + 1 >= qend)
  	return -1;
        (*q)++;
+     } else if (**q == TN_TTYPE) {
+       /* Read the terminal type: TERMINAL-TYPE IS blah IAC SE */
+       char tbuf[BUFFER_LEN], *bp = tbuf;
+       if (*q >= qend)
+ 	return -1;
+       (*q)++;
+       /* Skip IS */
+       if (*q >= qend)
+ 	return -1;
+       (*q)++;
+ 
+       /* Read up to IAC SE */
+       while (1) {
+ 	if (*q >= qend)
+ 	  return -1;
+ 	if (**q == IAC) {
+ 	  if (*q + 1 >= qend)
+ 	    return -1;
+ 	  if (*(*q + 1) == IAC) {
+ 	    safe_chr(IAC, tbuf, &bp);
+ 	    (*q)++;
+ 	  } else
+ 	    break;
+ 	} else
+ 	  safe_chr(**q, tbuf, &bp);
+ 	(*q)++;
+       }
+       while (*q < qend && **q != SE)
+ 	(*q)++;
+       *bp = '\0';
+       mush_free(d->ttype, "terminal description");
+       d->ttype = mush_strdup(tbuf, "terminal description");
      } else {
        while (*q < qend && **q != SE)
  	(*q)++;
***************
*** 2292,2297 ****
--- 2327,2336 ----
  #ifdef DEBUG_TELNET
        fprintf(stderr, "Setting linemode options.\n");
  #endif
+     } else if (**q == TN_TTYPE) {
+       /* Ask for terminal type id: IAC SB TERMINAL-TYPE SEND IAC SEC */
+       unsigned char reply[6] = "\xFF\xFA\x18\x01\xFF\xF0";
+       queue_newwrite(d, reply, 6);
      } else if (**q == TN_SGA || **q == TN_NAWS) {
        /* This is good to be at. */
      } else {			/* Refuse options we don't handle */
***************
*** 3156,3162 ****
      }
    }
    queue_string_eol(call_by, tprintf("Name: %s", options.mud_name));
!   queue_string(call_by, tprintf("Uptime: %s", ctime(&first_start_time)));
    queue_string_eol(call_by, tprintf("Connected: %d", count));
    queue_string_eol(call_by, tprintf("Size: %d", db_top));
    queue_string_eol(call_by, tprintf("Version: %s", SHORTVN));
--- 3195,3201 ----
      }
    }
    queue_string_eol(call_by, tprintf("Name: %s", options.mud_name));
!   queue_string(call_by, tprintf("Uptime: %s", show_time(first_start_time, 0)));
    queue_string_eol(call_by, tprintf("Connected: %d", count));
    queue_string_eol(call_by, tprintf("Size: %d", db_top));
    queue_string_eol(call_by, tprintf("Version: %s", SHORTVN));
***************
*** 3518,3531 ****
    DESC *d;
    char tbuf1[BUFFER_LEN];
    dbref zone, obj;
-   char *p;
    int j;
  
-   p = ctime(&mudtime);
-   p[strlen(p) - 1] = '\0';
-   if (p[8] == ' ')
-     p[8] = '0';
- 
    loc = Location(player);
    if (!GoodObject(loc))
      return;
--- 3557,3564 ----
***************
*** 3577,3583 ****
        (void) queue_attribute(obj, "ADISCONNECT", player);
      }
      clear_flag_internal(player, "CONNECTED");
!     (void) atr_add(player, "LASTLOGOUT", p, GOD, NOTHING);
    } else {
      /* note: when you partially disconnect, ADISCONNECTS are not executed */
      sprintf(tbuf1, T("%s has partially disconnected."), Name(player));
--- 3610,3616 ----
        (void) queue_attribute(obj, "ADISCONNECT", player);
      }
      clear_flag_internal(player, "CONNECTED");
!     (void) atr_add(player, "LASTLOGOUT", show_time(mudtime, 0), GOD, NOTHING);
    } else {
      /* note: when you partially disconnect, ADISCONNECTS are not executed */
      sprintf(tbuf1, T("%s has partially disconnected."), Name(player));
***************
*** 3810,3838 ****
  }
  
  
  static DESC *
  lookup_desc(dbref executor, const char *name)
  {
!   DESC *d, *match;
!   dbref target;
  
!   target = lookup_player(name);
!   if (target == NOTHING) {
!     target = match_result(executor, name, TYPE_PLAYER,
! 			  MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
!   }
!   if ((target == NOTHING) || !Connected(target)) {
      return NULL;
    }
-   /* else walk the descriptor list looking for a match */
-   match = NULL;
-   DESC_ITER_CONN(d) {
-     if ((d->player == target) &&
- 	(!Hidden(d) || Priv_Who(executor)) &&
- 	(!match || (d->last_time > match->last_time)))
-       match = d;
-   }
-   return match;
  }
  
  /* ARGSUSED */
--- 3843,3890 ----
  }
  
  
+ /** Look up a DESC by character name or file descriptor.
+  * \param executor the dbref of the object calling the function calling this.
+  * \param name the name or descriptor to look up.
+  * \retval a pointer to the proper DESC, or NULL
+  */
  static DESC *
  lookup_desc(dbref executor, const char *name)
  {
!   DESC *d;
  
!   /* Given a file descriptor. See-all only. */
!   if (is_strict_integer(name)) {
!     int fd = parse_integer(name);
!     DESC_ITER_CONN(d) {
!       if (d->descriptor == fd) {
! 	if (Priv_Who(executor) || d->player == executor)
! 	  return d;
! 	else
! 	  return NULL;
!       }
!     }
      return NULL;
+   } else {			/* Look up player name */
+     DESC *match = NULL;
+     dbref target = lookup_player(name);
+     if (target == NOTHING) {
+       target = match_result(executor, name, TYPE_PLAYER,
+ 			    MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
+     }
+     if (!GoodObject(target) || !Connected(target))
+       return NULL;
+     else {
+       /* walk the descriptor list looking for a match of a dbref */
+       DESC_ITER_CONN(d) {
+ 	if ((d->player == target) &&
+ 	    (!Hidden(d) || Priv_Who(executor)) &&
+ 	    (!match || (d->last_time > match->last_time)))
+ 	  match = d;
+       }
+       return match;
+     }
    }
  }
  
  /* ARGSUSED */
***************
*** 3889,3894 ****
--- 3941,3960 ----
      safe_str("24", buff, bp);
  }
  
+ FUNCTION(fun_terminfo)
+ {
+   DESC *match;
+   if (!*args[0])
+     safe_str(T("#-1 FUNCTION REQUIRES ONE ARGUMENT"), buff, bp);
+   else if ((match = lookup_desc(executor, args[0]))) {
+     safe_str(match->ttype, buff, bp);
+     if (match->conn_flags & CONN_HTML)
+       safe_str(" pueblo", buff, bp);
+     if (match->conn_flags & CONN_TELNET)
+       safe_str(" telnet", buff, bp);
+   } else
+     safe_str(T(e_perm), buff, bp);
+ }
  
  /* ARGSUSED */
  FUNCTION(fun_idlesecs)
***************
*** 3919,3924 ****
--- 3985,4010 ----
  }
  
  /* ARGSUSED */
+ FUNCTION(fun_lports)
+ {
+   DESC *d;
+   int first = 1;
+ 
+   if (!Priv_Who(executor)) {
+     safe_str(T(e_perm), buff, bp);
+     return;
+   }
+ 
+   DESC_ITER_CONN(d) {
+     if (first)
+       first = 0;
+     else
+       safe_chr(' ', buff, bp);
+     safe_integer(d->descriptor, buff, bp);
+   }
+ }
+ 
+ /* ARGSUSED */
  FUNCTION(fun_ports)
  {
    /* returns a list of the network descriptors that a player is
***************
*** 3929,3947 ****
    DESC *d;
    int first;
  
-   if (!Priv_Who(executor)) {
-     notify(executor, T("Permission denied."));
-     return;
-   }
    target = lookup_player(args[0]);
    if (target == NOTHING) {
      target = match_result(executor, args[0], TYPE_PLAYER,
! 			  MAT_ABSOLUTE | MAT_PLAYER);
    }
!   if ((target == NOTHING) || !Connected(target)) {
      return;
    }
!   /* Walk descriptor chain. Don't worry about buffer length limits. */
    first = 1;
    DESC_ITER_CONN(d) {
      if (d->player == target) {
--- 4015,4034 ----
    DESC *d;
    int first;
  
    target = lookup_player(args[0]);
    if (target == NOTHING) {
      target = match_result(executor, args[0], TYPE_PLAYER,
! 			  MAT_ABSOLUTE | MAT_PLAYER | MAT_ME);
!   }
!   if (target != executor && !Priv_Who(executor)) {
!     /* This should probably be a safe_str */
!     notify(executor, T("Permission denied."));
!     return;
    }
!   if (!GoodObject(target) || !Connected(target)) {
      return;
    }
!   /* Walk descriptor chain. */
    first = 1;
    DESC_ITER_CONN(d) {
      if (d->player == target) {
***************
*** 4263,4269 ****
  {
    FILE *f;
    DESC *d;
!   long flags = RDBF_SCREENSIZE;
    if (setjmp(db_err)) {
      flag_broadcast(0, 0, T("GAME: Error writing reboot database!"));
      exit(0);
--- 4350,4356 ----
  {
    FILE *f;
    DESC *d;
!   long flags = RDBF_SCREENSIZE | RDBF_TTYPE;
    if (setjmp(db_err)) {
      flag_broadcast(0, 0, T("GAME: Error writing reboot database!"));
      exit(0);
***************
*** 4311,4316 ****
--- 4398,4404 ----
        putref(f, d->conn_flags);
        putref(f, d->width);
        putref(f, d->height);
+       putstring(f, d->ttype);
      }				/* for loop */
  
      putref(f, 0);
***************
*** 4377,4383 ****
--- 4465,4478 ----
      if (flags & RDBF_SCREENSIZE) {
        d->width = getref(f);
        d->height = getref(f);
+     } else {
+       d->width = 78;
+       d->height = 24;
      }
+     if (flags & RDBF_TTYPE)
+       d->ttype = mush_strdup(getstring_noalloc(f), "terminal description");
+     else
+       d->ttype = mush_strdup("unknown", "terminal description");
      d->input_chars = 0;
      d->output_chars = 0;
      d->output_size = 0;
*** 1_7_7.465/src/access.c Mon, 28 Apr 2003 22:14:16 -0500 dunemush (pennmush/c/43_access.c 1.11.1.2.1.4.1.5 660)
--- 1_7_7.487(w)/src/access.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/c/43_access.c 1.11.1.2.1.4.1.6 660)
***************
*** 484,490 ****
  {
    struct access *end;
    struct access *tmp;
-   char *date;
  
    tmp = (struct access *) mush_malloc(sizeof(struct access), "struct_access");
    if (!tmp)
--- 484,489 ----
***************
*** 494,502 ****
    tmp->can = can;
    tmp->cant = cant;
    strcpy(tmp->host, host);
!   date = (char *) ctime(&mudtime);
!   date[strlen(date) - 1] = '\0';
!   sprintf(tmp->comment, "By %s(#%d) on %s", Name(player), player, date);
    tmp->next = NULL;
  
    if (!access_top) {
--- 493,500 ----
    tmp->can = can;
    tmp->cant = cant;
    strcpy(tmp->host, host);
!   sprintf(tmp->comment, "By %s(#%d) on %s", Name(player), player,
! 	  show_time(mudtime, 0));
    tmp->next = NULL;
  
    if (!access_top) {
*** 1_7_7.465/hdrs/version.h Mon, 02 Jun 2003 13:18:13 -0500 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.19 660)
--- 1_7_7.487(w)/hdrs/version.h Tue, 24 Jun 2003 15:09:51 -0500 dunemush (pennmush/c/47_version.h 1.32.1.2.1.7.1.9.1.1.1.17.1.20 660)
***************
*** 1,3 ****
! #define VERSION "PennMUSH version 1.7.7 patchlevel 15 [06/01/2003]"
! #define SHORTVN "PennMUSH 1.7.7p15"
! #define NUMVERSION 001007007015
--- 1,3 ----
! #define VERSION "PennMUSH version 1.7.7 patchlevel 16 [06/23/2003]"
! #define SHORTVN "PennMUSH 1.7.7p16"
! #define NUMVERSION 001007007016
*** 1_7_7.465/hdrs/mushdb.h Tue, 04 Feb 2003 22:25:42 -0600 dunemush (pennmush/d/2_mushdb.h 1.1.1.9.1.1.1.10 660)
--- 1_7_7.487(w)/hdrs/mushdb.h Tue, 24 Jun 2003 15:09:50 -0500 dunemush (pennmush/d/2_mushdb.h 1.1.1.9.1.1.1.11 660)
***************
*** 150,154 ****
--- 150,155 ----
   * They are successive binary numbers
   */
  #define RDBF_SCREENSIZE         0x01
+ #define RDBF_TTYPE              0x02
  
  #endif				/* __DB_H */
*** 1_7_7.465/hdrs/externs.h Tue, 06 May 2003 17:45:57 -0500 dunemush (pennmush/d/16_externs.h 1.1.1.53.1.2.1.8.2.1.1.2.1.1.1.1.1.2.1.6.1.3.1.4.3.1.1.1.1.9 660)
--- 1_7_7.487(w)/hdrs/externs.h Tue, 24 Jun 2003 15:09:46 -0500 dunemush (pennmush/d/16_externs.h 1.1.1.53.1.2.1.8.2.1.1.2.1.1.1.1.1.2.1.6.1.3.1.4.3.1.1.1.1.11 660)
***************
*** 391,396 ****
--- 391,399 ----
      extern void safe_itemizer(int cur_num, int done, const char *delim,
  			      const char *conjoin, const char *space,
  			      char *buff, char **bp);
+     extern char *show_time(time_t t, int utc);
+     extern char *show_tm(struct tm *t);
+ 
  
  /** This structure associates html entities and base ascii representations */
      typedef struct {
*** 1_7_7.465/hdrs/dbdefs.h Mon, 28 Apr 2003 22:14:16 -0500 dunemush (pennmush/d/18_dbdefs.h 1.1.1.1.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.2.1.1.15 660)
--- 1_7_7.487(w)/hdrs/dbdefs.h Tue, 24 Jun 2003 15:09:46 -0500 dunemush (pennmush/d/18_dbdefs.h 1.1.1.1.1.1.1.1.1.1.1.2.2.2.1.1.1.1.1.1.2.1.1.19 660)
***************
*** 78,84 ****
  #define Suspect(x)      (IS(x, TYPE_PLAYER, "SUSPECT"))	/* 0x40 */
  #define Connected(x)    (IS(x, TYPE_PLAYER, "CONNECTED"))	/* 0x200 */
  #define ZMaster(x)      (IS(x, TYPE_PLAYER, "ZONE"))	/* 0x800 */
! #define Unregistered(x) (IS(x, TYPE_PLAYER, "UNREGISTERD"))
  #define Fixed(x)        (IS(Owner(x), TYPE_PLAYER, "FIXED"))
  
  #ifdef VACATION_FLAG
--- 78,84 ----
  #define Suspect(x)      (IS(x, TYPE_PLAYER, "SUSPECT"))	/* 0x40 */
  #define Connected(x)    (IS(x, TYPE_PLAYER, "CONNECTED"))	/* 0x200 */
  #define ZMaster(x)      (IS(x, TYPE_PLAYER, "ZONE"))	/* 0x800 */
! #define Unregistered(x) (IS(x, TYPE_PLAYER, "UNREGISTERED"))
  #define Fixed(x)        (IS(Owner(x), TYPE_PLAYER, "FIXED"))
  
  #ifdef VACATION_FLAG
***************
*** 276,282 ****
    dbref from;			/**< Sender's dbref */
    time_t from_ctime;		/**< Sender's creation time */
    unsigned char *message;	/**< Message text, compressed */
!   unsigned char *time;		/**< Message date/time, compressed */
    unsigned char *subject;	/**< Message subject, compressed */
    int read;			/**< Bitflags of message status */
  };
--- 276,282 ----
    dbref from;			/**< Sender's dbref */
    time_t from_ctime;		/**< Sender's creation time */
    unsigned char *message;	/**< Message text, compressed */
!   time_t time;			/**< Message date/time */
    unsigned char *subject;	/**< Message subject, compressed */
    int read;			/**< Bitflags of message status */
  };
*** 1_7_7.465/hdrs/attrib.h Mon, 02 Jun 2003 11:58:48 -0500 dunemush (pennmush/d/25_attrib.h 1.4.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.2.1.1.2.3.1.6 660)
--- 1_7_7.487(w)/hdrs/attrib.h Tue, 24 Jun 2003 15:09:46 -0500 dunemush (pennmush/d/25_attrib.h 1.4.1.1.1.1.1.2.1.1.1.1.1.2.1.2.1.1.1.2.1.1.2.3.1.8 660)
***************
*** 57,65 ****
  extern void init_atr_name_tree(void);
  extern unsigned const char *atr_get_compressed_data(ATTR *atr);
  extern char *atr_value(ATTR *atr);
! extern char *safe_atr_value(ATTR *atr);
! extern void atr_migrate(int amount);
! 
  
  /* possible attribute flags */
  #define AF_ODARK        0x1	/* OBSOLETE! Leave here but don't use */
--- 57,66 ----
  extern void init_atr_name_tree(void);
  extern unsigned const char *atr_get_compressed_data(ATTR *atr);
  extern char *atr_value(ATTR *atr);
! extern char *
! safe_atr_value(ATTR *atr)
!   __attribute_malloc__;
!     extern void atr_migrate(int amount);
  
  /* possible attribute flags */
  #define AF_ODARK        0x1	/* OBSOLETE! Leave here but don't use */
***************
*** 84,90 ****
  #define AF_VEILED       0x400000	/* On ex, show presence, not value */
  
  /* external predefined attributes. */
! extern ATTR attr[];
  
  #define AL_ATTR(alist)          (alist)
  #define AL_NAME(alist)          ((alist)->name)
--- 85,91 ----
  #define AF_VEILED       0x400000	/* On ex, show presence, not value */
  
  /* external predefined attributes. */
!     extern ATTR attr[];
  
  #define AL_ATTR(alist)          (alist)
  #define AL_NAME(alist)          ((alist)->name)
*** 1_7_7.465/MANIFEST Mon, 28 Apr 2003 22:09:49 -0500 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.4 600)
--- 1_7_7.487(w)/MANIFEST Tue, 03 Jun 2003 12:13:37 -0500 dunemush (pennmush/d/34_MANIFEST 1.21.1.2.1.7.1.3.1.5 600)
***************
*** 123,129 ****
  src/warnings.c
  src/wild.c
  src/wiz.c
- src/SWITCHES
  src/cmdlocal.dst
  src/funlocal.dst
  src/local.dst
--- 123,128 ----
***************
*** 228,230 ****
--- 227,230 ----
  win32/pennmush.dsw
  win32/pennmush.vcproj
  win32/pennmush.sln
+ src/SWITCHES
*** 1_7_7.465/win32/funs.h Sat, 31 May 2003 16:07:52 -0500 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3 660)
--- 1_7_7.487(w)/win32/funs.h Tue, 17 Jun 2003 14:32:30 -0500 dunemush (pennmush/f/12_funs.h 1.11.1.9.2.8.2.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.8.1.3.1.7.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2 660)
***************
*** 152,157 ****
--- 152,158 ----
  FUNCTION_PROTO(fun_log);
  FUNCTION_PROTO(fun_lparent);
  FUNCTION_PROTO(fun_lplayers);
+ FUNCTION_PROTO(fun_lports);
  FUNCTION_PROTO(fun_lpos);
  FUNCTION_PROTO(fun_lsearch);
  FUNCTION_PROTO(fun_lstats);
***************
*** 285,290 ****
--- 286,292 ----
  FUNCTION_PROTO(fun_tagwrap);
  FUNCTION_PROTO(fun_tan);
  FUNCTION_PROTO(fun_tel);
+ FUNCTION_PROTO(fun_terminfo);
  FUNCTION_PROTO(fun_textfile);
  FUNCTION_PROTO(fun_time);
  FUNCTION_PROTO(fun_timefmt);
*** 1_7_7.465/hdrs/mushtype.h Tue, 06 May 2003 17:27:48 -0500 dunemush (pennmush/f/20_mushtype.h 1.2.1.1.1.13 660)
--- 1_7_7.487(w)/hdrs/mushtype.h Tue, 24 Jun 2003 15:09:50 -0500 dunemush (pennmush/f/20_mushtype.h 1.2.1.1.1.15 660)
***************
*** 120,125 ****
--- 120,126 ----
    unsigned long output_chars;	/**< Characters sent */
    int width;			/**< Screen width */
    int height;			/**< Screen height */
+   char *ttype;			/**< Terminal type */
  #ifdef HAS_OPENSSL
    BIO *bio;
  #endif
*** 1_7_7.465/INSTALL Sun, 22 Dec 2002 01:18:33 -0600 dunemush (pennmush/g/20_INSTALL 1.3 600)
--- 1_7_7.487(w)/INSTALL Wed, 18 Jun 2003 12:04:49 -0500 dunemush (pennmush/g/20_INSTALL 1.4 600)
***************
*** 100,106 ****
        win32/README.txt and then skip down to step #6 below
     b. Compile with the Cygwin unix emulation tools (http://www.cygwin.com)
        In addition to the base cygwin stuff, you'll want the following packages:
!          binutils, gcc, make, patch, perl, exim-4.10-1 source code
        (gcc 3.2 is recommended.)
        These are also recommended:
           gettext, gettext-devel, indent, vim or emacs
--- 100,107 ----
        win32/README.txt and then skip down to step #6 below
     b. Compile with the Cygwin unix emulation tools (http://www.cygwin.com)
        In addition to the base cygwin stuff, you'll want the following packages:
!          binutils, gcc, make, patch, perl, exim (the latest *source* code
!          package, not the binary)
        (gcc 3.2 is recommended.)
        These are also recommended:
           gettext, gettext-devel, indent, vim or emacs
*** 1_7_7.465/game/txt/hlp/pennvOLD.hlp Sat, 31 May 2003 16:17:58 -0500 dunemush (pennmush/g/30_pennvOLD.h 1.1.1.1.1.1.1.2 660)
--- 1_7_7.487(w)/game/txt/hlp/pennvOLD.hlp Tue, 24 Jun 2003 15:09:52 -0500 dunemush (pennmush/g/30_pennvOLD.h 1.1.1.1.1.1.1.1.1.2 660)
***************
*** 4417,4424 ****
  For information on a specific patchlevel of one of the versions listed,
  type 'help <version>p<patchlevel>'. For example, 'help 1.7.2p3'
  
! 1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
! 1.7.6: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
  1.7.5: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
  1.7.4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
         19, 20
--- 4417,4424 ----
  For information on a specific patchlevel of one of the versions listed,
  type 'help <version>p<patchlevel>'. For example, 'help 1.7.2p3'
  
! 1.7.7: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
! 1.7.6: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
  1.7.5: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
  1.7.4: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
         19, 20
*** 1_7_7.465/game/txt/hlp/pennv176.hlp Mon, 02 Jun 2003 13:40:12 -0500 dunemush (pennmush/g/33_pennv176.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.3 660)
--- 1_7_7.487(w)/game/txt/hlp/pennv176.hlp Tue, 24 Jun 2003 15:09:52 -0500 dunemush (pennmush/g/33_pennv176.h 1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.1.2.1.1.1.1.1.2 660)
***************
*** 1,3 ****
--- 1,20 ----
+ & 1.7.6p12
+ Version 1.7.6 patchlevel 12                     June 23, 2003
+ 
+ Minor changes:
+    * Users no longer see last connection information when they 
+      connect to Guests. Suggested by Jules at M*U*S*H.
+ Fixes:
+    * Potential problem with ambigious names in the information functions 
+      fixed. [SW]
+    * The 'chat' config group is no longer displayed when CHAT_SYSTEM
+      isn't defined. Report by Mike Griffiths. [SW]
+    * cygwin install instructions changed to remove obsolete exim
+      version information. Suggested by Cheetah at M*U*S*H.
+    * Objects with user-defined locks had problems with finding built-in locks
+      on the object. Reported by Walker at M*U*S*H. [SW]
+ 
+ 
  & 1.7.6p11
  Version 1.7.6 patchlevel 11                     June 1, 2003
  
*** 1_7_7.465/game/txt/hlp/pennv177.hlp Mon, 02 Jun 2003 13:18:13 -0500 dunemush (pennmush/g/34_pennv177.h 1.64 660)
--- 1_7_7.487(w)/game/txt/hlp/pennv177.hlp Tue, 24 Jun 2003 15:09:52 -0500 dunemush (pennmush/g/34_pennv177.h 1.75 660)
***************
*** 1,4 ****
! & 1.7.7p15
  & changes
  This is a list of changes in this patchlevel which are probably of
  interest to players. More information about new commands and functions
--- 1,4 ----
! & 1.7.7p16
  & changes
  This is a list of changes in this patchlevel which are probably of
  interest to players. More information about new commands and functions
***************
*** 11,16 ****
--- 11,57 ----
  A list of the patchlevels associated with each release can
  be read in 'help patchlevels'.
  
+ Version 1.7.7 patchlevel 16                     June 23, 2003
+ 
+ Commands:
+   * New switch /room for 'with' to run a $command available in a remote
+     location. Suggested by Mike Griffiths. [SW]
+ Functions:
+   * Added the terminfo() function, which returns information about a
+     client's name and capabilities for a particular connection. [SW]
+   * New lports() function. Like lwho() but returns port descriptors.
+     For mux2 compatibility. [SW]
+   * Functions that return information about a connected player now treat
+     integer arguments as a port descriptor to look up, rather than using
+     the least-idle connection of a player. To force a player name to
+     be treated as such even when it's a number, prefix it with a * in
+     softcode. For mux2 compatibility. [SW]
+   * Players can use ports() on themselves and use the descriptors
+     they're connected to as arguments to the information functions.
+     For mux2 compatibility. [SW]
+ Minor Changes:
+   * Compute various chunk stats (total used, total free space, etc.)
+     on demand instead of keeping running totals. [TAP]
+   * @chan/what displays information about a channel's recall buffer, if any.
+     [SW]
+   * @chan/recall'ed lines are more clearly marked as such. Suggested by 
+     Oriens at Alexandria. [SW]
+   * Consolidation of a common idiom used to format times throughout the source
+     into a simple function call. [SW]
+   * The time a @mail was sent was stored, unusually, as a string.
+     No longer. Now it's handled the same way as all other times. [SW]
+ Fixes:
+   * Bug in resizing @chan/recall buffers fixed. Reported by Oriens at Alexandria.
+     [SW]
+   * Objects with user-defined locks had problems with finding built-in locks
+     on the object. Reported by Walker at M*U*S*H. [SW]
+   * Unregistered() macro was checking wrong flag name. Report by
+     Matt Kirby.
+   * Help fix by Adu at M*U*S*H.
+   * Potential problem with ambigious names in the information functions fixed.
+ 
+ 
+ & 1.7.7p15
  Version 1.7.7 patchlevel 15                     June 1, 2003
  
  Fixes:
*** 1_7_7.465/src/chunk.c Mon, 02 Jun 2003 11:58:48 -0500 dunemush (pennmush/g/38_chunk.c 1.17 660)
--- 1_7_7.487(w)/src/chunk.c Tue, 24 Jun 2003 15:09:43 -0500 dunemush (pennmush/g/38_chunk.c 1.22 660)
***************
*** 277,285 ****
   * spilling into next page */
  #define REGION_SIZE 65500
  
  /** Maximum chunk length.
   * This is fairly arbitrary, but must be less than
!  * REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION (it must fit in a region).
   */
  #define MAX_CHUNK_LEN (16384-1)
  
--- 277,290 ----
   * spilling into next page */
  #define REGION_SIZE 65500
  
+ /** Region capacity.
+  * This is the size minus the fixed region overhead.
+  */
+ #define REGION_CAPACITY (REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION)
+ 
  /** Maximum chunk length.
   * This is fairly arbitrary, but must be less than
!  * REGION_CAPACITY (it must fit in a region).
   */
  #define MAX_CHUNK_LEN (16384-1)
  
***************
*** 298,307 ****
  /** Constants affecting the way things are migrated around.
   * Details in the migration discussion. */
  #define UNHAPPINESS_HARD 0
! #define UNHAPPINESS_SOFT 16
  #define UNHAPPINESS_MAYBE 32
  #define UNHAPPINESS_TWO 0
! #define UNHAPPINESS_ONE 16
  #define UNHAPPINESS_ZERO 24
  #define UNHAPPINESS_SPACE_DIVISOR 1024
  #define UNHAPPINESS_DEREFS_DIVISOR 1
--- 303,312 ----
  /** Constants affecting the way things are migrated around.
   * Details in the migration discussion. */
  #define UNHAPPINESS_HARD 0
! #define UNHAPPINESS_SOFT 4
  #define UNHAPPINESS_MAYBE 32
  #define UNHAPPINESS_TWO 0
! #define UNHAPPINESS_ONE 2
  #define UNHAPPINESS_ZERO 24
  #define UNHAPPINESS_SPACE_DIVISOR 1024
  #define UNHAPPINESS_DEREFS_DIVISOR 1
***************
*** 423,429 ****
  #define MAX_MEDIUM_CHUNK_LEN \
          ((CHUNK_MEDIUM_LEN_MSB_MASK << 8) | CHUNK_MEDIUM_LEN_LSB_MASK)
  #define MAX_LONG_CHUNK_LEN \
!         (REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION - CHUNK_LONG_DATA_OFFSET)
  
  #define LenToFullLen(len) \
    ((len) + \
--- 428,434 ----
  #define MAX_MEDIUM_CHUNK_LEN \
          ((CHUNK_MEDIUM_LEN_MSB_MASK << 8) | CHUNK_MEDIUM_LEN_LSB_MASK)
  #define MAX_LONG_CHUNK_LEN \
!         (REGION_CAPACITY - CHUNK_LONG_DATA_OFFSET)
  
  #define LenToFullLen(len) \
    ((len) + \
***************
*** 555,572 ****
  /*
   * statistics
   */
- static int stat_used_count;		/**< How many chunks are used? */
- static int stat_used_bytes;		/**< How much chunk space is used,
-                                              including the headers? */
  static int stat_used_short_count;	/**< How many short chunks? */
  static int stat_used_short_bytes;	/**< How much space in short chunks? */
  static int stat_used_medium_count;	/**< How many medium chunks? */
  static int stat_used_medium_bytes;	/**< How much space in medium chunks? */
  static int stat_used_long_count;	/**< How many long chunks? */
  static int stat_used_long_bytes;	/**< How much space in long chunks? */
- static int stat_free_count;		/**< How many chunks are free? */
- static int stat_free_bytes;		/**< How many bytes in free chunks? */
- static int stat_free_fragmented;	/**< How much not in largest hole? */
  static int stat_deref_count;		/**< Dereferences this period */
  static int stat_deref_maxxed;		/**< Number of chunks with max derefs */
  /** histogram for average derefs of regions being paged in/out */
--- 560,571 ----
***************
*** 957,967 ****
      next = ChunkNextFree(region, next);
      write_free_chunk(region, offset, full_len, next);
      rp->free_count--;
-     stat_free_count--;
-     stat_free_fragmented -= rp->free_bytes - rp->largest_free_chunk;
      if (rp->largest_free_chunk < full_len)
        rp->largest_free_chunk = full_len;
-     stat_free_fragmented += rp->free_bytes - rp->largest_free_chunk;
    }
  }
  
--- 956,963 ----
***************
*** 979,992 ****
    rp->total_derefs -= ChunkDerefs(region, offset);
    rp->used_count--;
    rp->free_count++;
-   stat_free_fragmented -= rp->free_bytes - rp->largest_free_chunk;
    rp->free_bytes += full_len;
    if (rp->largest_free_chunk < full_len)
      rp->largest_free_chunk = full_len;
-   stat_free_fragmented += rp->free_bytes - rp->largest_free_chunk;
  
-   stat_used_count--;
-   stat_used_bytes -= full_len;
    if (ChunkIsShort(region, offset)) {
      /* chunk is short */
      stat_used_short_count--;
--- 975,984 ----
***************
*** 1000,1007 ****
      stat_used_long_count--;
      stat_used_long_bytes -= full_len;
    }
-   stat_free_count++;
-   stat_free_bytes += full_len;
  
    left = rp->in_memory->first_free;
    if (!left) {
--- 992,997 ----
***************
*** 1073,1087 ****
      stat_used_long_count++;
      stat_used_long_bytes += full_len;
    }
-   stat_used_count++;
-   stat_used_bytes += full_len;
  
    if (hole_len == full_len) {
      rp->free_count--;
-     stat_free_fragmented -= rp->free_bytes - rp->largest_free_chunk;
      rp->free_bytes -= full_len;
-     stat_free_count--;
-     stat_free_bytes -= full_len;
      if (rp->in_memory->first_free == offset)
        rp->in_memory->first_free = ChunkNextFree(region, offset);
      else {
--- 1063,1072 ----
***************
*** 1095,1125 ****
      }
      if (rp->largest_free_chunk == hole_len)
        rp->largest_free_chunk = largest_hole(region);
-     stat_free_fragmented += rp->free_bytes - rp->largest_free_chunk;
      return offset;
    }
  
    ASSERT(hole_len >= full_len + MIN_REMNANT_LEN);
    if (rp->in_memory->first_free == offset) {
-     stat_free_fragmented -= rp->free_bytes - rp->largest_free_chunk;
      rp->free_bytes -= full_len;
-     stat_free_bytes -= full_len;
      rp->in_memory->first_free += full_len;
      write_free_chunk(region, offset + full_len,
  		     hole_len - full_len, ChunkNextFree(region, offset));
      if (rp->largest_free_chunk == hole_len)
        rp->largest_free_chunk = largest_hole(region);
-     stat_free_fragmented += rp->free_bytes - rp->largest_free_chunk;
      return offset;
    } else {
-     stat_free_fragmented -= rp->free_bytes - rp->largest_free_chunk;
      rp->free_bytes -= full_len;
-     stat_free_bytes -= full_len;
      write_free_chunk(region, offset,
  		     hole_len - full_len, ChunkNextFree(region, offset));
      if (rp->largest_free_chunk == hole_len)
        rp->largest_free_chunk = largest_hole(region);
-     stat_free_fragmented += rp->free_bytes - rp->largest_free_chunk;
      return offset + hole_len - full_len;
    }
  }
--- 1080,1103 ----
***************
*** 1310,1316 ****
  
    regions[region].used_count = 0;
    regions[region].free_count = 1;
!   regions[region].free_bytes = REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION;
    regions[region].largest_free_chunk = regions[region].free_bytes;
    regions[region].total_derefs = 0;
    regions[region].period_last_touched = curr_period;
--- 1288,1294 ----
  
    regions[region].used_count = 0;
    regions[region].free_count = 1;
!   regions[region].free_bytes = REGION_CAPACITY;
    regions[region].largest_free_chunk = regions[region].free_bytes;
    regions[region].total_derefs = 0;
    regions[region].period_last_touched = curr_period;
***************
*** 1321,1329 ****
    write_free_chunk(region, FIRST_CHUNK_OFFSET_IN_REGION,
  		   regions[region].free_bytes, 0);
  
-   stat_free_count++;
-   stat_free_bytes += REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION;
- 
    touch_cache_region(regions[region].in_memory);
    return region;
  }
--- 1299,1304 ----
***************
*** 2036,2041 ****
--- 2011,2030 ----
  {
    const char *s;
    int overhead;
+   int free_count = 0;
+   int free_bytes = 0;
+   int free_large = 0;
+   int used_count = 0;
+   int used_bytes = 0;
+   u_int_16 rid;
+ 
+   for (rid = 0; rid < region_count; rid++) {
+     free_count += regions[rid].free_count;
+     free_bytes += regions[rid].free_bytes;
+     free_large += regions[rid].largest_free_chunk;
+     used_count += regions[rid].used_count;
+   }
+   used_bytes = (REGION_CAPACITY * region_count) - free_bytes;
  
    if (!GoodObject(player)) {
      do_rawlog(LT_TRACE, "---- Chunk statistics");
***************
*** 2045,2052 ****
      stat_used_long_count * CHUNK_LONG_DATA_OFFSET;
    STAT_OUT(tprintf
  	   ("Chunks:    %10d allocated (%10d bytes, %10d (%2d%%) overhead)",
! 	    stat_used_count, stat_used_bytes, overhead,
! 	    stat_used_bytes ? overhead * 100 / stat_used_bytes : 0));
    overhead = stat_used_short_count * CHUNK_SHORT_DATA_OFFSET;
    STAT_OUT(tprintf
  	   ("             %10d short     (%10d bytes, %10d (%2d%%) overhead)",
--- 2034,2041 ----
      stat_used_long_count * CHUNK_LONG_DATA_OFFSET;
    STAT_OUT(tprintf
  	   ("Chunks:    %10d allocated (%10d bytes, %10d (%2d%%) overhead)",
! 	    used_count, used_bytes, overhead,
! 	    used_bytes ? overhead * 100 / used_bytes : 0));
    overhead = stat_used_short_count * CHUNK_SHORT_DATA_OFFSET;
    STAT_OUT(tprintf
  	   ("             %10d short     (%10d bytes, %10d (%2d%%) overhead)",
***************
*** 2066,2079 ****
  	    stat_used_long_bytes ? overhead * 100 / stat_used_long_bytes : 0));
    STAT_OUT(tprintf
  	   ("           %10d free      (%10d bytes, %10d (%2d%%) fragmented)",
! 	    stat_free_count, stat_free_bytes, stat_free_fragmented,
! 	    stat_free_bytes ? stat_free_fragmented * 100 /
! 	    stat_free_bytes : 0));
    STAT_OUT(tprintf("Regions:   %10d total, %8d cached",
  		   region_count, cached_region_count));
    overhead = region_count * REGION_SIZE + region_array_len * sizeof(Region);
    STAT_OUT(tprintf("Storage:   %10d total (%2d%% saturation)",
! 		   overhead, stat_used_bytes * 100 / overhead));
    STAT_OUT(tprintf("Paging:    %10d out, %10d in",
  		   stat_page_out, stat_page_in));
    STAT_OUT(" ");
--- 2055,2067 ----
  	    stat_used_long_bytes ? overhead * 100 / stat_used_long_bytes : 0));
    STAT_OUT(tprintf
  	   ("           %10d free      (%10d bytes, %10d (%2d%%) fragmented)",
! 	    free_count, free_bytes, free_bytes - free_large,
! 	    free_bytes ? (free_bytes - free_large) * 100 / free_bytes : 0));
    STAT_OUT(tprintf("Regions:   %10d total, %8d cached",
  		   region_count, cached_region_count));
    overhead = region_count * REGION_SIZE + region_array_len * sizeof(Region);
    STAT_OUT(tprintf("Storage:   %10d total (%2d%% saturation)",
! 		   overhead, used_bytes * 100 / overhead));
    STAT_OUT(tprintf("Paging:    %10d out, %10d in",
  		   stat_page_out, stat_page_in));
    STAT_OUT(" ");
***************
*** 2089,2094 ****
--- 2077,2104 ----
  		   stat_migrate_fill_inexact));
  }
  
+ /** Display the per-region stats.
+  * \param player the player to display it to, or NOTHING to log it.
+  */
+ static void
+ chunk_region_statistics(dbref player)
+ {
+   u_int_16 rid;
+   const char *s;
+ 
+   if (!GoodObject(player)) {
+     do_rawlog(LT_TRACE, "---- Region statistics");
+   }
+   for (rid = 0; rid < region_count; rid++) {
+     STAT_OUT(tprintf
+ 	     ("region:%4d  #used:%5d  #free:%5d  "
+ 	      "fbytes:%04x  largest:%04x  deref:%3d",
+ 	      rid, regions[rid].used_count, regions[rid].free_count,
+ 	      regions[rid].free_bytes, regions[rid].largest_free_chunk,
+ 	      RegionDerefs(rid)));
+   }
+ }
+ 
  /** Display a histogram.
   * \param player the player to display it to, or NOTHING to log it.
   * \param histogram the histogram data to display.
***************
*** 2442,2449 ****
    /* Make sure that the away movements can't loop due to the chunks
     * being unhappy all by themselves... */
    ASSERT(UNHAPPINESS_AWAY_THRESHOLD >
! 	 (REGION_SIZE - FIRST_CHUNK_OFFSET_IN_REGION - MIN_REMNANT_LEN) /
! 	 UNHAPPINESS_SPACE_DIVISOR);
  
    swap_fd = open(CHUNK_SWAP_FILE, O_RDWR | O_TRUNC | O_CREAT, 0600);
    if (swap_fd < 0)
--- 2452,2458 ----
    /* Make sure that the away movements can't loop due to the chunks
     * being unhappy all by themselves... */
    ASSERT(UNHAPPINESS_AWAY_THRESHOLD >
! 	 (REGION_CAPACITY - MIN_REMNANT_LEN) / UNHAPPINESS_SPACE_DIVISOR);
  
    swap_fd = open(CHUNK_SWAP_FILE, O_RDWR | O_TRUNC | O_CREAT, 0600);
    if (swap_fd < 0)
***************
*** 2495,2500 ****
--- 2504,2512 ----
      chunk_histogram(player, chunk_freehist(),
  		    "Chart region free space (y) vs. references (x)");
      break;
+   case 4:
+     chunk_region_statistics(player);
+     break;
    }
  }
  


More information about the Pennmush-announce mailing list