The U Programming Language
Introduction.
The U programming language (hereafter referred to as
simply U) is a simple language reminiscent of C or awk. This
document is intended as a 'quick start' guide, rather than a
formal language definition, since it is expected that the
language will be constantly changing and evolving.
1. Hello World
The primary means of sending text output to a user's
terminal in U is via the echo command and its relative
echoto. Echo echoes its arguments back to the caller, per-
forming type conversion on numeric values, etc. C-like
escape sequences are recognized:
echo("hello world!\n");
will print "hello world!" followed by a newline. Unlike C,
there is no need for a main() function to call. Commands can
be executed inside or outside of a function.
echo("two plus two = ",2 + 2);
will print "two plus two = 4", with echo converting the
numeric value generated by the arithmetic expression into a
text string automatically. echo places no reasonable limit
on the number of parameters that can be passed to it in one
call; everything is printed in order, left to right.
2. Variables
2.1. Variable Types and Data Types
Variables in U can be of several types. The interpreter
hides the details of the types from the user to the greatest
possible extent. Currently, the existing types are as fol-
lows:
o+ Numbers - the basic numeric type in U is the integer. In
fact, there is no provision for floating point numbers at
all. Typical numerical operations such as division, multi-
plication, addition &c can be performed on numbers.
o+ Strings - the string type in U is a simple chunk of text,
June 22, 1990
U Programming Language
with the interpreter handling storage allocation (unlike C)
for the programmer. Numeric operations are NOT permitted on
strings, with the exception of the equality operator.
o+ NULL - the data value NULL in U is a special data type of
its own. Unlike C, it does not mean zero - it means nonex-
istence. NULL is provided for many reasons, primarily being
error checking, and to provide a fixed 'magic' value that
programmers can use. Many of U's internal operators return
NULL in the event of an error, or invalid argument. In real-
ity there is an error value that can be attached to NULL,
and accessed by the user, allowing a user to determine
exactly why their operation resulted in a NULL. Examples of
operations that can result in NULL are: calling a non-
function as if it were a function, attempting to access a
non-existent variable, and attempting to use a function
parameter that was not passed to a function.
o+ Objects - an object in U is a numbered entity to which
smaller variables (called elements) can be bound. It is the
basic "star stuff" of a Ubermud universe. Objects are always
referred to by number, preceded with a sharp, or '#' symbol.
There is a special object - the system object, which can be
abbreviated with the '@' symbol. This is always object #0,
but using the '@' syntax is preferred, since it is clearer.
o+ Lists - a list is an array of object numbers. There are
functions that manipulate, search, add to, and delete from
lists. The list is used heavily in implementing a universe,
since lists contain contents of rooms, player inventories,
and so forth. There is no heterogenous array type in U (due
to the complexity of implementing it), so lists may only
consist of object numbers. This is less of a drawback than
it may seem, since arrays of strings can easily be gotten at
indirectly through the object number.
o+ Functions - a function or procedure is actually a primary
data type in U. This permits assigning of functions to vari-
ables, and calling the function through that variable. This
can lead to confusing or very powerful code, depending on
how wisely it is used. There are some restrictions on the
use of functions and their declaration, that are discussed
later. Functions can be passed as parameters to other func-
tions, assigned to temporary variables, or assigned to
object elements, which are described in greater detail
later.
2.2. Temporary Variables
In U, a temporary variable can be used to store vola-
tile information. The temporary disappears after the func-
tion that it is in is terminated. The advantages of tem-
poraries is that they are quick and allocation and dealloca-
tion of them is handled by the system. Due to their
June 22, 1990
Copyright, Marcus J. Ranum, 1990
ephemeral nature, however, they cannot be used outside of
functions, without disappearing immediately. Temporaries CAN
be made to last outside of functions by enclosing the
desired block of code in curly braces:
{
$tmp = strtime();
echo("$tmp is:",$tmp,"\n");
}
Here $tmp is set to whatever the function strtime returns (a
string form of the system clock), and is later handed to
echo for printing.
2.3. Function Parameters
In U, a function can be passed an arbitrary number of
parameters. Unlike C, where the parameters are named, the
parameters in a U function are accessed with $-number syn-
tax, similar to the Bourne shell's. A second function param-
eter variable $# contains the parameter count (zero being
none). This allows a function to access its parameters
without having to name or type variables:
func @.foo
{
if($# > 0)
echo("the first parameter is \"",$1,"\"\n");
}
@.foo("bar!");
Here a function is called with one parameter. A check
against the parameter count is made, and if there is a
parameter, the first one is printed. The example above pro-
duces the following output:
the first parameter is "bar!"
note the escaped quotes preceded by the '\' character in the
parameters to echo.
It is forbidden to use the parameter variables outside
a function; attempting to do so will abort compilation of
the program at that point.
2.4. Elements
The element is the most important type of variable in
U, and will be gone into in greater depth later. Elements
are formed of an object number, and the name of a variable
stored in that object. An object can have any number of
elements stored in it, provided they are all uniquely named.
Some examples of elements are:
June 22, 1990
U Programming Language
#44.foo;
@.fuzzy;
#55.this.that;
The @.fuzzy syntax is an example of using '@' to specify the
system object. It could be written as #0.fuzzy as well. In
the case of #55.this.that, there is a level of indirection
added. When the value is presented to the interpreter, the
interpreter will first check the contents of #55.this. If
#55.this is itself an object number, it will then search the
contents of that object for variable name that. This can be
done to any number of levels, though there will not likely
be need for more than one or two levels of indirection. If a
variable in an indirection is not an object number, or does
not exist, then a "badly formed object specifier" error is
generated. For example:
#55.foo = 666;
#44.bar = #55;
echo("indirecting:",#44.bar.foo,"\n");
Will print:
indirecting:666
Since the object number #55 is stored in #44.bar, which is
looked up first, reducing #44.bar.foo to #55.foo, which is
in turn looked up, giving the number 666.
An element can be a value of the type of any of the
data types permitted in U. Elements are treated essentially
as variables in expressions:
#44.foo = 12;
$tmp = 2;
#55.bar = #44.foo * $tmp;
But they can just as easily be treated as functions:
#33.bar("this is a test\n");
$tmp = #33.bar;
$tmp("this is a test\n");
In both of the examples above, the function stored in
#33.bar is called with the message: "this is a test\n".
2.5. Accessing Elements Via Strings
Elements can be accessed by coercing a string into the
name of an object's element. This is done by providing an
arbitrary expression which returns a string in the object
element specifier:
June 22, 1990
Copyright, Marcus J. Ranum, 1990
#33.("foo") = 55;
The program above would convert the string value "foo" into
an element name and search #33 for it. Parenthesized expres-
sions can be mixed at will with other element names:
#33.foo.("bar") = 55;
$array = 1;
echo(#33.(str("foo",$array)));
In the examples above, #33.foo.bar would be set to 55, and
in the second #33.foo1 would be echoed. Note that there are
some restrictions on string to variable coercion. The max-
imum length of an element name is less than the maximum
length of a string, and attempts to coerce a name that is
too long will automatically set the value to NULL. If the
expression fails to return a string, the element name forma-
tion fails, and the value is set to NULL.
While string-to-variable coercion is a powerful func-
tion, it is also an indicator of a failing in the design of
the language that encompasses it. In the case of U,
string-to-variable coercion is incorporated to get around
the lack of support for heterogenous arrays. Programmers are
urged to use string-to-variable coercion sparingly, if at
all, and primarly to use it as a means of examining things
from the command line.
2.6. Making Elements Go Away
Elements attached to an object can be de-assigned
(annihilated) by assigning them to NULL. This frees their
storage in the database, and removes them from the index.
Objects themselves cannot be destroyed in this simple
manner.
3. Functions
3.1. Basics of Functions
A U function is similar to a C function in that it can
take a list of parameters of varying types, and return a
single value. Unlike C, U does not have a notion of pointers
as parameters, so it is difficult for a U function to modify
something passed as a parameter. Furthermore, unlike C, U
functions cannot modify the values of their numbered parame-
ters. Assignments to $1-N or $# are syntax errors, and abort
compilation. Functions that do not explicitly return a value
return NULL(function returned no value) to the caller, if
the value of the function is taken. Functions can return any
legitimate U data type, including other functions.
June 22, 1990
U Programming Language
3.2. Declaring a Function
A function is declared in the form of:
func #object-number . name
{
statements;
}
The object-number may be '@', if the function is to be
attached to the system object, but otherwise it must be the
number of an existing object that the programmer has permis-
sion to write to. The curly braces are optional, and may be
omitted if the function is a single statement. Take this
simple function:
func #33.days
{
$tmp = $1 * 24 * 60 * 60;
echo("in ",$1," days, it will be ",strtime($tmp),"\n");
}
which prints what it will be in a number of days passed to
the function as a parameter. Obviously, the function should
check for the existence of a parameter, since $1 will be
NULL if it was called with no parameters.
3.3. Returning From a Function
In the example of #33.days above, the function "drops
off the end" of the code. This causes the function to return
to the caller, and leaves the value NULL for the calling
function to access, should it so desire. A specific value
can be returned to the caller using the return operator, or
NULL can be specifically returned to indicate an error. To
simplify this instance, the return operator can be used
without a value, implicitly returning NULL.
func #33.bad
{
if($# != 3) {
echo("three arguments expected!\n");
return;
}
return($1 + $2 + $3);
}
echo(#33.bad(4,5),"\n");
echo(#33.bad(4,5,6),"\n");
This overly simplistic example checks its argument count,
and returns its parameters added together. In the first case
where it is called above with two parameters, the error
June 22, 1990
Copyright, Marcus J. Ranum, 1990
message will be printed, and NULL will be returned. echo
quietly refuses to print NULL (since NULL does not exist),
so there would be be a newline printed, but no other output
than the error message. In the second call, the function
returns 15, which is passed to echo, and is printed, with
the newline.
4. Conditionals
U's conditionals are the if and else syntax so familiar
to C programmers. Conditionals take the form of:
if ( expression )
statements;
if ( expression )
statements1;
else
statements2;
The expression can be any legal U expression. If the expres-
sion evaluates to a non-empty string, or a non-zero numeric
value, the statement following is executed. In the if-else
syntax, the second set of statements are executed in the
case that the condition of the first failed. U has the C-
like boolean operators '&&' and '||', and and or, respec-
tively. As in C, if statements can be cascaded and nested.
One crucial difference exists between C and U if state-
ments: unlike in C, all the clauses in the conditional are
evaluated (C stops when the condition is manifestly true or
false). Thus:
if(foo() && bar()) {
...
}
will evaluate foo() and bar() both, while in C, bar() will
never be called if foo() returns 0.
5. For Statements
5.1. Problems With For Statements, While Loops, and Recur-
sion
U does not have C-like for and while statments, since
they permit a programmer to write code that loops eternally.
This is intolerable in a multi-user game, and, in fact, they
turn out to be unnecessary. Another problem is recursive
functions, or functions that call each other endlessly back
and forth. The U interpreter handles these simply by permit-
ting recursion until the reasonably large internal stack is
overflowed, and then returning NULL when any further func-
tion calls are attempted. This effectively halts the
June 22, 1990
U Programming Language
recursion, causing it to "unwind" itself normally. Obvi-
ously, a function that recursively generates a huge amount
of text output will still be a tremendous annoyance, but
controlling such things cannot be done within the software.
5.2. Syntax of For Statements
Since a MUD deals primarily with lists of things, U has
syntax for iterating across lists, and across the parameters
to a function. The syntax of for loops takes two forms:
for $tmp in ( expression-returning a list)
statements;
foreacharg $tmp
statements;
In the first case, the temporary variable $tmp is succes-
sively set to each element number for every element on the
list. If the expression fails to return a list, or returns
an empty list, the statements are not executed at all. In
the foreacharg loop, the value of $tmp is successively set
to each parameter that the function was called with.
foreacharg cannot be used outside of a function, as this is
a syntax error. In the following example:
#11.str = "string 1";
#22.str = "string 2";
#33.str = "string 3";
#33.list = listnew(#11,#22,#33);
foreach $tmp in (#33.list) {
echo($tmp,".str is:",$tmp.str);
}
This creates a list containing the object numbers #11, #22,
and #33, and iterates across the list, printing (succes-
sively) the values of #11.str, #22.str, and #33.str. In the
next example:
func #33.bar
{
$cnt = 0;
foreacharg $tmp {
$cnt = $cnt + 1;
echo("parameter ",$cnt,"=",$tmp,"\n");
}
echo("total parameters:",$cnt,"\n");
}
#33.bar(4,"foo",strtime());
The following is printed:
June 22, 1990
Copyright, Marcus J. Ranum, 1990
parameter 1=4
parameter 2=foo
parameter 3=Fri May 11 16:24:21 EDT 1990
total parameters:3
6. References
6.1. Generating References
U stores all objects and object elements except for
those in the system table (#0) in a database with an index.
A reference is used in U to make two or more index entries
that "point" at the same basic object. This allows objects
that wish to share the same element values to do it easily,
and provides performance advantages as well as saving
storage space. Additionally, references are used in some
internal built-in functions to manipulate index entries for
permissions and ownership purposes. A reference to an object
element is generated by placing and ampersand '&' character
before the name of the element. EG:
!.foo;
&$tmp.foo;
,.bar.baz;
&@.froz;
All of these are examples of legal references. It is not
legal syntax to take a reference of anything but an object
element. IE:
!
&$tmp;
&@;
are all illegal references, producing syntax errors at
compile-time.
6.2. Assigning to References
When an assignment is made to a reference, instead of
saving a copy of the data, a pointer to the copy of the data
is saved. It must be emphasized that this is NOT a perfor-
mance expensive operation, rather the contrary. For all
intents and purposes, a referenced object is exactly the
same as any other stored object. When an assignment is made
to a referenced element, the contents of the reference are
NOT overwritten, the reference is broken, and new storage is
allocated for the assigned element. For example:
#33.foo = "foo!";
#44.foop = !.foo;
echo(#33.foo,#44.foop,"\n");
June 22, 1990
U Programming Language
#44.foop = "bar!";
echo(#33.foo,#44.foop,"\n");
The first time echo prints: "foo!foo!" followed by a new-
line. The second call to echo prints "foo!bar!" followed by
a newline. In certain cases it may be desirable to change to
contents of a reference. This can be done by prefixing the
assignment operator with an asterisk. This causes the con-
tents of the reference to be set, changing the value for
every element that "points" to it.
#33.foo = "foo!";
#44.foop = !.foo;
echo(#33.foo,#44.foop,"\n");
*#44.foop = "bar!";
echo(#33.foo,#44.foop,"\n");
As in the previous example, the first call to echo prints
"foo!foo!" and the newline. The second prints "bar!bar!" and
the newline. This can be invaluable for implementing objects
that change their appearance globally, but a programmer does
not want to have to maintain individually, such as a desti-
nation list for a teleporter system.
6.3. Restrictions on References
It is illegal to make a reference to object elements in
#0, the system object. This is because the entire system
object is stored only in main memory, not on disk (it is the
repository of that which must be fast). Thus, a reference to
something in #0 would become "stale" as soon as the server
was shut down. Assigning the contents of a reference to NULL
is a special case, and causes only that one link to the
referenced object to be broken. This is due to the fact that
there is no idea how many object elements actually "point"
to that referenced element.
7. Run-Time User Contexts
7.1. User-Id and Effective User-Id
When a U program is executing, there are several spe-
cial values attached to the running program. Primarily these
consist of the user-id and the effective user-id. These
values are used as the basis for all permissions in U. At
any time when a player is connected to the U-server, their
login has a user-id associated with it. Typically this will
be the object number of their 'self' in the game (though it
need not be). The effective user-id is almost always the
same as the user-id, but it can be changed under certain
circumstances (see Setuid Function below) or if the person's
connection is flagged with a "Wizard Bit" in the password
file. In the case of a connection with the "Wizard Bit" set,
June 22, 1990
Copyright, Marcus J. Ranum, 1990
the players user-id will be their object number, but their
effective user-id will be that of #0, the Creator, who has
absolute power over all things in the universe. At runtime,
built-in functions may also alter a connections effective
user-id, though it is impossible to alter the user-id, and
for good reason, since the user-id determines where any tex-
tual output intended for a player should go. Should two
objects somehow share the same user-id, they would both get
each other's output.
7.2. Selves and Actors
At run-time, certain special object number variables
are defined, and can be used in functions to reference the
player who triggered the current function calls, or the
object number of the current function's root object. These
values are #actor, #caller and #self. When a function uses
#actor as an object number, it is replaced with the object
number of the player whose input is currently being pro-
cessed. This is very, very useful:
func @.take
{
...
foreach $who in (#actor.location.players)
echoto($who,#actor.name," picks up ",$thing.name,"\n");
...
}
Suppose the object number of the person who typed the 'take'
command in were #44. This function would iterate across the
list of players in #44.location.players, echoing the
player's name, and so forth to them. This permits functions
to be written in a manner that is fairly independent of who
or what calls them. The #self special object identifier is
similar, but identifies the object number of the current
function's root object. This allows a function to modify its
proper root object. Suppose we have a radio that we wish to
have alter its description depending on whether or not it is
turned on. We might write:
func #44.turnon
{
...
if(#self.on == NULL) {
#self.on = 1;
#self.description = "a blaring ghetto-blaster...";
}
...
}
Granted, #44 could be substituted for #self in this example,
but using #self makes it easier to make programs that are
object number independent. Another crucial facet of #self
June 22, 1990
U Programming Language
arises when a function that is called is acually a function
called with a reference. In such a case, using #44 would set
the wrong value in the wrong radio depending on which
radio's function were called. Using #self guarantees that
the correct root object number is applied. Using the func-
tion above:
#55.turnon = ,.turnon;
#55.turnon();
#44.turnon();
The first call, to #55.turnon() would modify #55.on, and
#55.description since #self would be object number #55. In
the second call, the value of #self would be correctly set
to #44.
The #caller special object variable is replaced at
run-time with the object number of the object that has
invoked the current function. If the current function has
been called directly from a player, or the monitor, #caller
is identical to #actor.
func #33.bar
{
echo("self is ",#self,"\tcaller is ",#caller,"\n");
}
func #44.bar
{
echo("self is ",#self,"\tcaller is ",#caller,"\n");
#33.bar();
}
#44.bar();
In the example above, there are two functions, one of which
calls the other, and is in turn called from the monitor
directly. If this code were run with the user-id #55, the
results would look like:
self is #44 caller is #55
self is #33 caller is #44
Since the first call to #44.bar was executed directly by
object #55, the #caller value is exactly the same as #actor.
8. Permissions
In U, all objects have a variety of permissions associ-
ated with them. Each object has an owner, and a masked set
of permissions flags, somewhat reminiscent of the permis-
sions bits on a UNIX file. Permissions can be granted for
read or write. The concept of execute permission is meaning-
less, since functions are a primary data type, and being
June 22, 1990
Copyright, Marcus J. Ranum, 1990
able to read them implies being able to execute them. There
are three classes of permissions: owner permissions,
indirect permissions, and world permissions. Owner permis-
sions are checked when the owner of a on object or object
element attempts to modify or access it. Indirect permis-
sions are checked when a function that is running with the
effective user-id of the owner, but a different real user-id
tries to modify or access an object, and world permissions
are checked in all other cases. The purpose of the indirect
mode is to prevent objects from seriously damaging a player
or his internal data, as a result of the player's action.
There is no notion of 'groups' per se in U, since there are
really no classes of objects to group things by.
8.1. Permissions on a Root Object
When the system built-in function objectnew is called,
it allocates a new empty object, and returns its object
number. This empty object can have elements (data values)
added to it as part of the usual assignment process
described earlier. Whenever an assignment is made to an
object element that does not already exist, the permissions
on the root object are checked, and must be in a mode to
permit writing before the element can be created. If an
attempt is made to destroy the root object, a check is made
to see if it is writeable. This makes it easy for an object
to be created that can have new elements added to it by
other than the player, by simply making the root object
world-writeable. This also makes it feasible to create
objects that can be destroyed by people other than the
owner, which is sometimes desirable. Read permission on a
root object is (currently) meaningless.
8.2. Permissions on an Object Element
The same permissions bits exist for an object element
as for a root object, but some of the effects of permissions
bits are interpreted differently. If an assignment is made
to an element, the existing element must be in a mode that
permits writing by the caller, based on the effective user-
id. This applies even if the assignment is to NULL, so it is
possible to create an object element in someone else's root
object that they cannot destroy. This is a feature rather
than a bug, as it permits the universe rules designers to
make some values outside the user's control. If, however, a
root object is destroyed, all elements are destroyed,
regardless of ownership. There is an additional feature to
aid in designing universes: only the universe Creator can
create an element which has a name beginning with an under-
score '_' character. This is to reserve a set of names for
universe-implementations, without forcing the Creator to
always set values and permissions to "hold" the variable
names.
June 22, 1990
U Programming Language
8.3. Permissions on a Function - the Setuid Bit
A special permissions mode can be applied to functions,
which causes the function that is being executed to run with
its effective user-id to be that of the owner of the func-
tion. This provides a controlled means for giving access to
objects without having to make the object world-writeable.
Setuid functions owned by the Creator are especially useful
for implementing universe rules, since the Creator may want
to reserve some privileges for itseslf (such as moving
objects in rooms). Setuid is a very powerful capability, but
can also cause serious problems if not used with care. Any
functions that are called by a setuid function inherit their
effective user-id from the setuid function. Thus, the writer
of a setuid function must be careful to control sub-
functions that are called, since a joker might take advan-
tage of permissions to destroy objects, or change their per-
missions and ownerships.
The setuid model of permissions will be immediately
familiar to anyone who has more than a nodding acquaintance
with UNIX. Such a user will also know that 95% of the secu-
rity loopholes in UNIX are as a result of an incorrectly
applied setuid program. An Ubermud Creator will want to
exercise some caution, since a flaw in the universe rules
will allow players to assume the powers of a wizard. On the
other hand, Ubermud being a game, this can be fun - or at
least less serious than having a multi-user computer broken
into. The possibility of destructive Uber-virusses is not to
be ignored, however.
8.4. Changing Permissions
Permissions on object elements and root objects can be
altered with the built-in chmod() function. Chmod can be
given either the number of an object (to affect the root
object's permissions), or a reference to an object element.
Chmod takes a second parameter, which is a string specifying
the permissions modes for the three types: Owner, Indirect,
World. The types are signified with upper-case letters, fol-
lowed by a colon and a list of lower-case letters indicating
the desired mode for that type of permission.
chmod(#33,"O:rw");
chmod(!.bar,"O:rw,I:r,W:r");
chmod(!.somefunc,"O:rw,I:r,W:rs");
The first example sets the permissions for the root object
of #33 to be owner read and owner write. Since the permis-
sions for indirect and world are not specified, no opera-
tions are permitted to anyone but the owner. Since the
object is a root object, the read permission does not mean
much, but the lack of world or indirect write permission
means that nobody but the owner can create new elements in
June 22, 1990
Copyright, Marcus J. Ranum, 1990
that object. The second example shows a reference to #33.bar
being passed to chmod, with a request to set that element
only as owner readable and writeable, indirect and world
readable. This is, in fact, the default permissions mode,
which is automatically set when an object or element is ini-
tially created or assigned to. The third example shows a
function being marked as owner read and writeable, indirect
and world readable, and setuid. To flag an element as
setuid, the 's' must be marked within the world group of
permissions, as a reminder to the programmer that world
access is being given to the owner's permissions.
8.5. Changing Ownership
Objects can be given away in U, as can individual
object elements. Only the owner of an object or element (or
the Creator) can give an object away. Giving the root object
away does not automatically change the ownership of all the
object elements in that object, nor does giving away a sin-
gle object element give any other permissions within that
object. The only change other than ownership that occurrs
when an object is given away is that the setuid bit is
cleared if it is set in the gift. This is to prevent the
obvious security loophole. Calls to the built-in function
that manages changing ownerships are done similarly to set-
ting permissions. An object number, or a reference to an
object element is given, and the object number of the reci-
pient is provided as a second parameter:
chown(#33,#44);
chown(,.name,#44);
The first example above shows a root object #33 being given
to #44. Note that #44 does not have to be a player. In fact,
the recipient does not even have to exist. Object elements
within the system object (#0) can be given away by the Crea-
tor if so desired, but the system object itself cannot be
given away. Typically ownership changing is useful in
creating universe rules, where a creator may wish to create
a base object that will be a player, and then give it to
itself. In some universes, or in some cases, the Creator may
not want a player to own their base object. Such issues are
outside of the scope of this document.
9. Built-In Functions
U has a fairly powerful set of built-in functions,
which can be called exactly like user-defined functions, and
return (or do not) values as appropriate. For the purposes
of documenting them, they are listed below, in a manner
intended to be somewhat reminiscent of the manner in which C
functions are usually declared, with the return value, and
parameter types. Some U built-ins take arbitrary lists of
parameters, some take specific parameters of given types. In
June 22, 1990
U Programming Language
the case of the latter, NULL is returned when the parameter
count is incorrect, or the wrong data type is provided as a
parameter.
9.1. General Functions
NUM atoi(STR) - returns a numerical representation of the
string, or zero. If a NUM or OBJ is passed to the function
by accident or on purpose, a type conversion will be per-
formed, and the corresponding NUM value will be returned.
NULL is returned if more than one parameter is given, or the
parameter is a function, list, or other type.
OBJ atoobj(STR) - converts a string to an object number,
with the same caveats and type conversions at atoi.
NUM catfile(object#,STR) - sends the contents of the file
named in the string parameter to the output buffer of the
player object listed as the first parameter. Files are
searched for in a subdirectory of the U-server's current
directory named "files" for security reasons. If the file
cannot be opened, the player is informed as to the reason.
Files with a leading '/' character or a '..' in them are
forbidden, and will not be printed. In the event of a
failure, the function returns NULL; a successful call is
indicated with a numeric zero return value.
INT disconnect(object#1,object#2,object#N) - disconnects the
specified object numbers from the server if they are con-
nected. This function must be run with user-id or
effective-user-id of Creator. Note that since this is a
built-in command, the caller must generate their own call to
@._quit(), or whatever is necessary to cleanly disconnect
the player.
NULL echo(arg1,arg2,argN) - echos its parameters to #actor
in sequence, performing type conversion as necessary. NUM
values are converted to a text format, strings are printed
as such, and object numbers are printed preceeded with a
sharp '#' character. NULL values are not printed, and
attempts to print functions and lists prints either "<func-
tion>" or "<list>" respectively.
NULL echoto(object#,arg1,arg2,argN) - echoes its parameters
just like echo, except that the output it sent to the player
matching object# if that player is connected. If the desti-
nation object number is not connected, the output is thrown
away.
NUM errno(NULL) - returns a numeric representation of an
error attached to a particular NULL value. Error values are:
0 - No error.
1 - Error. This is produced when a user program
returns NULL.
June 22, 1990
Copyright, Marcus J. Ranum, 1990
2 - Out of memory. This indicates a memory alloca-
tion condition within the server.
3 - Numeric operation on non-number. This indi-
cates an attempt was made to perform a numerical
operation on a non-numeric value. The only numer-
ical operator that is permitted on non-numbers is
the equality ("==") operator, which functions
between NULLs and strings.
4 - Division by zero. This indicates a numerical
division by zero was attempted.
5 - Badly formed element specifier. This indicates
an attempt was made to create an element out of
something invalid. For example, trying to make an
element out of a string value and an element name.
(EG: $foo.bar where $foo = "foo").
6 - Bad parameter type. A built-in function was
called with an incorrect parameter type, or a
missing or extra parameter.
7 - Nonexistent object. An attempt was made to
access an object or object element that does not
exist. In the event that an object is not readable
by a person, this value is returned, not a permis-
sion denied value. This is to make non-readable
values completely and utterly "invisible".
8 - Cannot reference object. An attempt was made
to take a reference of an object that cannot be
referenced. This usually results from an attempt
to take a reference of a badly formed element
specifier. (EG: &$foo where $foo = "bar").
9 - Function returned no value. An attempt to use
a return code from a function that returned no
value results in the return code being NULL, with
this value. There are many cases where this is
perfectly acceptable.
10 - No such parameter. An attempt to use a func-
tion parameter that was not passed returns this
NULL. (EG: $4 in a function that was only called
with two parameters).
11 - I/O error (this is bad!). Something failed in
the disk-based database routines. This is very,
very, very bad. Run about in a panic.
12 - Permission denied. An attempt was made to
operate on an object which has permissions set
against the operation.
13 - Not owner. An attempt was made to change the
ownership of an object or object element that the
caller does not own.
14 - Stack over/underflow. Some form of recursive
or looping call was made, resulting in an eventual
failed function call.
STR error(NULL) - returns a string representation of an
error message attached to a particular NULL value. This is
useful for debugging or explaining why an operation failed.
June 22, 1990
U Programming Language
$ret = #33.bar = "this is a test";
if($ret == NULL)
echo("operation failed: ",error($ret),"\n");
The above example is a typical means of performing error-
checking withing a U function.
NUM islist(arg) - returns a numerical value of one if the
parameter is a list. Numerical zero is returned otherwise.
NUM isnum(arg) - returns a numerical value of one if the
parameter is a number. Numerical zero is returned otherwise.
NUM isobj(arg) - returns a numerical value of one if the
parameter is an object number. Numerical zero is returned
otherwise.
NUM isstr(arg) - returns a numerical value of one if the
parameter is a string. Numerical zero is returned otherwise.
NUM rand(optional arg) - returns a random number from zero
to the maximum numeric value allowed on the system. If a
single numeric parameter is given, the range of the number
returned will be between zero and the parameter. If a param-
eter that is non-numeric is provided, numeric zero is always
returned.
echo("1d6=",rand(6) + 1,"\n");
The above would produce a random number as if rolled on a
six-sided die.
NUM regcmp(string1,string2) - returns a one or a zero,
depending on whether or not string1 contains text matched by
the regular expression in string2. If the regular expression
in string2 is malformed, or a parameter is mis-matched, zero
is returned.
STR regexp(string1,string2) - returns a string consisting of
the first completely matching substring in string1, using
the regular expression in string2. If there is no match, a
parameter is missing or mismatched, or the regular expres-
sion is malformed, the function returns NULL. Regular
expressions are similar to those used in egrep, and include
the '|' operator.
echo(regexp("this is a test","this[ ][a-z]"));
In the example above, the string "this is" would be returned
by regexp, and echoed.
INT shutdown() - shuts the server down gracefully. This
function must be run with user-id or effective-user-id of
Creator.
June 22, 1990
Copyright, Marcus J. Ranum, 1990
STR str(arg1,arg2,argN) - returns a string assembled from
the parameters given. Conversion on the parameters by type
is performed in the same manner as in echo. This function is
very useful for producing concatenated values for assign-
ments:
#33.time = str("the time is:",strtime());
The above appends the returned value of strtime to the end
of the string, and assigns it to #33.time.
NUM strlen(STR) - returns the length of the string passed as
a parameter. If the value is not given, or is not a string,
numeric zero is returned.
STR strtime(optional arg1) - returns a text string represen-
tation of the system's current notion of the time. If a
numeric parameter is provided, the time string returned is
the notion of the time as if it were at the given number of
seconds GMT since January 1, 1970.
NUM time() - returns the system's notion of how many seconds
it has been GMT since January 1, 1970.
9.2. List Manipulation Functions
OBJLIST listadd(OBJLIST,object#1,object#2,object#N)
OBJLIST listprepend(OBJLIST,object#1,object#2,object#N) -
prepends the given object numbers to the list provided as a
first parameter, and returns the new list. In the event of
an parameter type mismatch in the first parameter, NULL is
returned. If any of the object number parameters are
incorrect, the new value is silently ignored. If a new value
already exists in the old list, it is not prepended again,
and retains its old position in the list. These two func-
tions are actually the same, under different names, listadd
being included for brevity's sake.
OBJLIST listappend(OBJLIST,object#1,object#2,object#N) -
appends the given object numbers, in the same manner as lis-
tappend.
NUM listcount(OBJLIST) - returns a numerical count of the
number of element numbers in the list provided. In the event
that parameter one is missing, or is not a list, NULL is
returned.
OBJLIST listdrop(OBJLIST,object#1,object#2,object#N) -
returns a new list, consisting of the object numbers from
the list in the first parameter, with any occurrences of the
object numbers given as parameters omitted. In the case of a
call to listdrop completely emptying a list, an empty list
is returned.
June 22, 1990
U Programming Language
OBJNUM listelem(OBJLIST,NUM) - returns the object number of
the N-th element in the list. Unlike C, U lists are indexed
with the first element being element number 1. If the index
number given is higher than the total number of object
numbers in the list, or the first parameter is not a list,
NULL is returned.
OBJLIST listmerge(OBJLIST,OBJLIST) - returns a new object
list, consisting of the union of the lists provided as
parameters. Duplicates are suppressed. As a special case,
either of the two lists provided as parameters can be NULL,
which will effectively return the other list.
OBJLIST listnew(object#1,object#2,object#N) - returns a new
object list, consisting of the object numbers given. If no
parameters are given, or all the provided parameters are
invalid, an empty list is returned.
NUM listsearch(OBJLIST,object#) - returns the index offset
of the given object number in the list, or NULL if the
object number is not in the list. NULL is returned if a
parameter is missing, or is of the wrong type. Since list
element offsets start at one, locating an object number in
the first 'slot' of the list returns numeric '1'.
OBJLIST listsetelem(OBJLIST,object#,NUM) - returns a list,
with the object number at the numeric index set to the pro-
vided object number. If there are not that many object
numbers in the list, the original list is returned
unchanged. NULL is returned in the case of a missing parame-
ter, or incorrect parameter type.
9.3. Object Manipulation Functions
NUM objectdestroy(object#) - destroys the numbered object,
if the caller has permission, destroying any object elements
bound to that object as well. NULL is returned in the event
that permission is denied. In the event of a successful
annihilation, numeric zero is returned. In some implementa-
tions, this permission requires that the caller's effective
user-id or real user-id be Creator. This is to avoid prob-
lems with players destroying objects that people are hold-
ing, etc, and is the default case.
NULL objectelements(object #) - lists the elements defined
in an object to the caller's terminal. The caller must have
read permission on the base object, in order to invoke this
function.
OBJNUM objectnew() - creates a new object and returns its
number. The new object is the property of the caller, and is
created empty, with default permissions.
OBJNUM objectowner(object#, or reference to object element)
June 22, 1990
Copyright, Marcus J. Ranum, 1990
- returns the object number of the object owning the root
object or object element provided. In the event of the
object or element's non-existence, NULL is returned.
9.4. Permissions and Environment Manipulation Functions
NUM chmod(object#,or reference to object element,STR) - sets
the permissions of the root object or object element, if the
caller is the owner of the root object or object element.
The string can contain a coding of permissions values con-
sisting of any of: 'O', signifying owner, 'I', signifying
indirect, and 'W' signifying world, followed by a colon, and
any of 'w' for write, or 'r' for read. Functions may have a
setuid specifier applied to the world permissions set, by
using 's'. For examples, see above. In the event of failure,
NULL or returned; numeric zero is returned if the operation
was a success.
NUM chown(object # or reference to object element,OBJNUM) -
sets the ownership of the specified root object or object
element to the object number provided. The call will fail if
the caller is not the owner of the object being given away.
If a setuid bit is marked in the object's permissions it is
cleared as part of this process. In the event of failure,
NULL is returned; numeric zero is returned if the operation
is successful.
OBJNUM geteuid() - returns the object number of the current
caller's effective user-id.
OBJNUM getuid() - returns the object number of the current
caller's real user-id.
NUM setruid(OBJNUM) - sets the real user-id of the current
calling function to the object number specified as a parame-
ter. This should only be used with extreme caution in rare
circumstances, as it affects the value returned by #actor
(unlike setuid). Its effect ends when the current call is
completed. If called directly from the monitor, the real
user-id is not permanently changed. This function can only
be called if the real or effective user-id of the caller is
the Creator. Successful calls return numeric zero; failed
calls return numeric one.
NUM setuid(OBJNUM) - sets the effective user-id of the
current calling function to the object number specified as a
parameter. This call will only succeed if the real user-id
of the caller is the number being set to, or the caller is
Creator. In the event of failure, NULL is returned; numeric
zero is returned if the operation succeeds.
9.5. Matching Functions
OBJNUM match(STR,OBJNUM or OBJLIST,STR,[OBJNUM or
June 22, 1990
U Programming Language
OBJLIST,STR]..) - match scans the object list, accessing
each object number in the list for an element name matching
the provided string. An arbitrary number of object
list/string pairs can be provided. Once each element is
accessed, it is compared against the first string given. The
object number of the best match found is returned. In the
event of multiple matches, the returned object is selected
based on the order in which the lists were passed to the
function (priority given the the first list). If a complete
match is made at any point, no further searching is done.
This is an especially useful function for matching a user's
input with objects in the surroundings:
...
$nam = match("foo",#actor.carr,"name",#actor.loc,"name");
if($nam != NULL) {
...
In the example above, a search is being made for things that
match the word "foo". The first list searched is the
#actor.carr list - each object in that list is checked for a
string element named "name". After #actor.carr is searched,
#actor.loc is searched, also for string elements named
"name". In the example above, if #actor.carr had the object
numbers #44 and #55 in the list, match would check to see if
there was a string stored in element #44.name, then
#55.name, and so on.
Individual root object numbers can be passed to match,
as well, so that:
...
$nam = match("foo",#99,"name");
if($nam != NULL) {
...
will return #99 if there is an element named "name" in #99
that is "foo", or begins with "foo". Individual root object
numbers can be freely combined with lists in calls to match.
An additional aspect of matching is the magic match
character, which can be used in strings to effect an "alias"
of sorts. The match character is inserted in a string with
an escaped semicolon: '\;'. This causes match to re-start
matching a string whenever it encounters the semicolon. This
is useful in many ways.
...
#99.name = "Rusty\;doofus";
$nam = match("doof",#99,"name");
if($nam != NULL) {
...
In the above example, match will retrieve #99.name and
June 22, 1990
Copyright, Marcus J. Ranum, 1990
compare "doof" with "Rusty", generating a failure. The match
character is then detected, which causes match to re-match
against "doofus", which succeeds and returns #99.
10. Programming Hints, Traps, and Pitfalls.
The mistake most commonly made by the author is to
store a variable within an object, which has the same name
as a function within that object. The net effect of this is
to destroy the function when it is called. This is surpris-
ingly easy to do:
/* a function to turn #44 on, whatever #44 is */
func #44.on
{
...
/* if the thing is turned off, turn it on.. */
if(#44.on == NULL) {
#44.on = 1;
}
...
}
The function runs perfectly, the first time. Subsequent
calls try to call #44.on (which is now the numeric value
one) and return NULL (no such function). Realizing that one
has done this in a program is infuriating in the extreme.
11. Annotated Programming Examples
11.1. A Room With Lights
The following is an example of a room with a light
switch that works. Assuming here that room #1 has already
been created, and so on, the programmer binds a function to
it as a light switch:
func #1.lights {
if(#self.on == NULL) {
$tmp = #self.on = 1;
if($tmp == NULL) {
echo("the switch is broken0);
return;
}
@.emote("turns on the lights");
#self.desc = "You see a dusty room, [...]";
return;
}
#self.desc = "It is pitch dark in here";
#self.on = NULL;
@.emote("turns off the lights");
}
chmod(.lights,"W:rs");
June 22, 1990
U Programming Language
The function checks to see if object element #self.on is set
- using that as a boolean value for the state of the lights.
Note that here, it is set and unset to NULL rather than a
value. When no value is desired, using NULL and non-NULL is
faster and saves database space besides. If the rooms
lights are off (#self.on == NULL), an attempt is made to set
the lights on. The value of the assign is checked to make
sure that the operation was successful ($tmp == NULL implies
a broken switch). If that operation was successful, a call
is made to one of the universe's functions 'emote' (@.emote)
which presumably sends a string to anyone else who happens
to be in the room. The room then alters its description to
adapt for the lights being on, and returns. If the lights
were already on, the first case fails, and the lights are
turned off by resetting the description of the room and set-
ting the value of #self.on to NULL.
11.2. More to come.
More good examples to be added, when people create
small nifty objects.
June 22, 1990