aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AddingNewMOOTypes.txt38
-rw-r--r--ChangeLog.txt2297
-rw-r--r--MOOCodeSequences.txt347
-rw-r--r--Makefile.in439
-rw-r--r--Minimal.db76
-rw-r--r--README211
-rw-r--r--README.Minimal75
-rw-r--r--aclocal.m432
-rw-r--r--ast.c466
-rw-r--r--ast.h258
-rw-r--r--bf_register.h55
-rw-r--r--client_bsd.c119
-rw-r--r--client_sysv.c238
-rw-r--r--code_gen.c1205
-rw-r--r--code_gen.h37
-rw-r--r--config.h.in319
-rwxr-xr-xconfigure2894
-rw-r--r--configure.in574
-rw-r--r--db.h547
-rw-r--r--db_file.c757
-rw-r--r--db_io.c438
-rw-r--r--db_io.h115
-rw-r--r--db_objects.c569
-rw-r--r--db_private.h159
-rw-r--r--db_properties.c700
-rw-r--r--db_verbs.c573
-rw-r--r--decompile.c1015
-rw-r--r--decompile.h36
-rw-r--r--disassemble.c496
-rw-r--r--disassemble.h38
-rw-r--r--eval_env.c131
-rw-r--r--eval_env.h59
-rw-r--r--eval_vm.c172
-rw-r--r--eval_vm.h60
-rw-r--r--exceptions.c87
-rw-r--r--exceptions.h178
-rw-r--r--execute.c2850
-rw-r--r--execute.h185
-rw-r--r--extensions.c167
-rw-r--r--functions.c510
-rw-r--r--functions.h132
-rw-r--r--getpagesize.h59
-rw-r--r--gnu-malloc.c923
-rw-r--r--keywords.c189
-rw-r--r--keywords.gperf91
-rw-r--r--keywords.h49
-rw-r--r--list.c1198
-rw-r--r--list.h55
-rw-r--r--log.c201
-rw-r--r--log.h55
-rw-r--r--malloc.c90
-rw-r--r--match.c180
-rw-r--r--match.h47
-rw-r--r--md5.c325
-rw-r--r--md5.h75
-rw-r--r--my-ctype.h43
-rw-r--r--my-fcntl.h61
-rw-r--r--my-in.h75
-rw-r--r--my-inet.h45
-rw-r--r--my-ioctl.h51
-rw-r--r--my-math.h43
-rw-r--r--my-poll.h48
-rw-r--r--my-signal.h80
-rw-r--r--my-socket.h83
-rw-r--r--my-stat.h60
-rw-r--r--my-stdarg.h79
-rw-r--r--my-stdio.h100
-rw-r--r--my-stdlib.h114
-rw-r--r--my-string.h124
-rw-r--r--my-stropts.h45
-rw-r--r--my-sys-time.h70
-rw-r--r--my-time.h80
-rw-r--r--my-tiuser.h63
-rw-r--r--my-types.h80
-rw-r--r--my-unistd.h88
-rw-r--r--my-wait.h42
-rw-r--r--name_lookup.c404
-rw-r--r--name_lookup.h62
-rw-r--r--net_bsd_lcl.c219
-rw-r--r--net_bsd_tcp.c364
-rw-r--r--net_mp_fake.c188
-rw-r--r--net_mp_poll.c123
-rw-r--r--net_mp_selct.c106
-rw-r--r--net_mplex.c46
-rw-r--r--net_mplex.h88
-rw-r--r--net_multi.c828
-rw-r--r--net_multi.h62
-rw-r--r--net_proto.c52
-rw-r--r--net_proto.h196
-rw-r--r--net_single.c287
-rw-r--r--net_sysv_lcl.c424
-rw-r--r--net_sysv_tcp.c435
-rw-r--r--network.c48
-rw-r--r--network.h287
-rw-r--r--numbers.c782
-rw-r--r--numbers.h41
-rw-r--r--objects.c683
-rw-r--r--opcode.h144
-rw-r--r--options.h358
-rw-r--r--parse_cmd.c305
-rw-r--r--parse_cmd.h75
-rw-r--r--parser.h64
-rw-r--r--parser.y1325
-rw-r--r--pattern.c194
-rw-r--r--pattern.h49
-rw-r--r--pgperf/Makefile77
-rw-r--r--pgperf/README10
-rw-r--r--pgperf/boolarray.c90
-rw-r--r--pgperf/boolarray.h48
-rw-r--r--pgperf/getopt.c413
-rw-r--r--pgperf/gperf-to-do22
-rw-r--r--pgperf/hashtable.c132
-rw-r--r--pgperf/hashtable.h37
-rw-r--r--pgperf/iterator.c106
-rw-r--r--pgperf/iterator.h47
-rw-r--r--pgperf/keylist.c1085
-rw-r--r--pgperf/keylist.h54
-rw-r--r--pgperf/listnode.c116
-rw-r--r--pgperf/listnode.h43
-rw-r--r--pgperf/main.c96
-rw-r--r--pgperf/mon.outbin0 -> 85682 bytes
-rw-r--r--pgperf/options.c451
-rw-r--r--pgperf/options.h154
-rw-r--r--pgperf/perfect.c353
-rw-r--r--pgperf/perfect.h45
-rw-r--r--pgperf/prototype.h15
-rw-r--r--pgperf/readline.c87
-rw-r--r--pgperf/readline.h31
-rw-r--r--pgperf/stderr.c90
-rw-r--r--pgperf/stderr.h29
-rw-r--r--pgperf/version.c22
-rw-r--r--pgperf/xmalloc.c78
-rw-r--r--program.c151
-rw-r--r--program.h84
-rw-r--r--property.c385
-rw-r--r--quota.c99
-rw-r--r--quota.h38
-rw-r--r--random.h68
-rw-r--r--ref_count.c237
-rw-r--r--ref_count.h33
-rw-r--r--regexpr.c1727
-rw-r--r--regexpr.h128
-rwxr-xr-xrestart66
-rwxr-xr-xrestart.sh59
-rw-r--r--server.c1877
-rw-r--r--server.h189
-rw-r--r--storage.c247
-rw-r--r--storage.h83
-rw-r--r--streams.c263
-rw-r--r--streams.h58
-rw-r--r--structures.h131
-rw-r--r--sym_table.c200
-rw-r--r--sym_table.h86
-rw-r--r--tasks.c2074
-rw-r--r--tasks.h154
-rw-r--r--timers.c347
-rw-r--r--timers.h55
-rw-r--r--tokens.h36
-rw-r--r--unparse.c786
-rw-r--r--unparse.h75
-rw-r--r--utils.c530
-rw-r--r--utils.h108
-rw-r--r--verbs.c666
-rw-r--r--verbs.h36
-rw-r--r--version.c51
-rw-r--r--version.h87
166 files changed, 47849 insertions, 0 deletions
diff --git a/AddingNewMOOTypes.txt b/AddingNewMOOTypes.txt
new file mode 100644
index 0000000..0d0a32d
--- /dev/null
+++ b/AddingNewMOOTypes.txt
@@ -0,0 +1,38 @@
+There are a number of places in the server that have to be changed when adding
+a new type of MOO value; this file attempts to list all of them for easy
+reference.
+
+First, though, it is important to realize that there are several types of MOO
+values that are invisible to MOO programmers; they are used for a variety of
+purposes internally in the virtual machine or database, and they can appear in
+the DB file on disk, but they are never the value of a MOO expression. Adding
+one of these `invisible' types involves only a few changes to the server.
+Adding one of the other kind, though, the `visible' types of values that *can*
+be the value of a MOO expression, involves a number of additional changes, as
+seen below.
+
+For all new types, both visible and invisible, you must:
+-- add an item *at the end* of the `var_type' enumeration in structures.h
+-- perhaps add a clause to the union in `struct Var' in structures.h
+-- add a case to each of dbio_read_var() and dbio_write_var(), in db_io.c
+-- add a case to each of free_var(), var_ref(), and var_dup(), in utils.c
+
+In addition, for visible types, you must:
+-- consider adding a new built-in MOO variable like OBJ, STR, and LIST:
+ -- add a new SLOT_XXX constant *at the end* of the list in sym_table.h
+ -- add a new DB version number *just before the end* of the
+ `DB_Version' enumeration in version.h
+ -- add a version-contingent clause to fill_in_rt_consts(),
+ in eval_env.c
+ -- add a version-contingent clause to each of new_builtin_names() and
+ first_user_slot(), in sym_table.c
+-- consider adding a new kind of MOO literal or expression that produces values
+ of the new type.
+-- add a case to each of list2str() and print_to_stream(), in list.c; the
+ former is used by the MOO function tostr() and the latter by toliteral().
+-- add a case to to each of become_integer() and become_float(), in numbers.c;
+ the former is used by toint() and toobj() and the latter by tofloat().
+-- consider adding a clause to is_true(), equality(), and value_bytes(),
+ in utils.c; the first implements MOO's notion of truth values, the second is
+ used in the `==' and `!=' expressions and the equals() function, and the
+ third is used in the MOO function of the same name.
diff --git a/ChangeLog.txt b/ChangeLog.txt
new file mode 100644
index 0000000..b36fa8a
--- /dev/null
+++ b/ChangeLog.txt
@@ -0,0 +1,2297 @@
+Version 1.0.0, 5 February 1991
+-- First public release.
+
+Version 1.1.0, 8 February 1991
+-- recycle(x) now calls x:recycle() before destroying the object.
+-- the little-used and functionally-dubious update() function has been removed.
+ **** NOTE: Technically, this change should make this a major release, since
+ **** it can break old code that depended on update() existing. In
+ **** practice, however, only $prog:@update used it (in LambdaCore.db,
+ **** anyway). Thus, to install this server, you should first @rmverb
+ **** $prog:@update, since its code would otherwise be syntactically
+ **** illegal.
+-- added renumber() and reset_max_object(), to facilitate automatically
+ generating new versions of LambdaCore.db from the current LambdaMOO db.
+-- made it so that only wizards can set the .name property of players; code in
+ the database now checks that players keep unique names.
+-- added set_player_flag() and boot_player() to enable registration-style
+ player creation and @toad-ing.
+
+Version 1.1,1, 12 February 1991
+-- Much rearrangement of #include's to come as close as possible to conformance
+ with ANSI C and POSIX.
+-- Many changes to improve portability, especially to sites with only 'nearly'
+ ANSI-compliant compilers.
+-- Added backtrace-printing for tasks aborted for running too long.
+
+Version 1.1.2, 13 February 1991
+-- Fixed nasty infinite recursion problem on server panic.
+-- Fixed the implementation of renumber() to really do the job. It's now much
+ more expensive (linear in the size of the database rather than constant
+ time), but at least it's correct. It's only intended for use on a tiny
+ DB anyway.
+
+Version 1.1.3, 11 March 1991
+-- Fixed benign unparsing bug that always parenthesized unary-minus
+ expressions.
+-- Changed move() to insert the newly-moved object as the last item in the
+ contents list, rather than the first.
+-- Fixed a less-than-benign bug in the unparsing of conditional expressions,
+ where 'x ? y | (z ? w | v)' was being unparsed without the parentheses.
+-- Fixed a bug whereby an extra checkpoint timer was created every time the
+ '.dump' command was used.
+-- Added printing the value of 'this' to error backtraces whenever it is
+ different from the verb location.
+-- Fixed bug in propagation of property ownership on object creation.
+-- WARNING: I believe that there is a storage leak somewhere in the server. If
+ so, it is not very fast. With any luck, I'll find it soon. For
+ safety's sake, check on the size of your server's address space
+ and reboot the server every few days if it's getting large.
+
+Version 1.2.0, 11 April 1991
+-- Fixed bug in parser that made 'after' a reserved word; this was part of an
+ old (and never completely implemented) alternative syntax for the 'fork'
+ statement.
+-- Added memory_usage() function, to help in tracking down any memory-leak
+ bugs, among other things.
+-- Removed ancient restrictions on players recycling themselves.
+-- Changed recycle() so that children of recycled objects are not themselves
+ recycled. Instead, they are adopted by their former grandparent.
+-- Fixed bugs in which errors are returned from move() in certain cases.
+-- Removed useless error value E_BADMOVE.
+-- Fixed bug in move() that allowed the enterfunc to be called even if the
+ object being moved was no longer valid.
+-- Changed several uses of E_INVIND to E_INVARG; E_INVIND is now only used when
+ an attempt is made to fetch or store the value of a property or verb from an
+ invalid object.
+-- Changed several functions to improve consistency in the order in which
+ certain kinds of errors are checked for.
+-- Made the allocator keep track of statistics concerning memory usage by type
+ of object being allocated.
+-- Changed queued_tasks() to return the value of 'this' in each task as well.
+ This new value is returned at the end of the sublist for each task, so old
+ code should continue to work unless it depended upon the exact length of the
+ sublists.
+-- Changed set_task_perms() not to return E_PERM if the argument is the same as
+ the current permissions of the verb (i.e., if the change in permissions
+ would be a no-op).
+-- Made tonum() and toobj() more robust in the face of extra spaces before or
+ after tokens.
+
+Version 1.3.0, 7 June 1991
+-- Removed the now-useless `.help' and `.mem' commands, and all code concerning
+ the also-removed CHECKMALLOC option.
+-- Fixed allocation bug in inheritance of 'dobjstr' and its ilk, when the
+ current value of the variable has the wrong type (e.g., 'dobjstr' is a list
+ when another verb-call happens).
+-- Fixed the timers implementation not to try to free storage inside an
+ interrupt procedure.
+-- Changed move() so that :accept is called even if it's a wizard doing the
+ moving; of course, the wizards gets to do the move even if :accept returns
+ false.
+-- Added 'off/off of' as a new preposition.
+-- Fixed the built-in parser to return #-1 ($nothing) for the empty string,
+ rather than #-2 ($ambiguous_match).
+-- Fixed a few bugs in how the allocator keeps track of types.
+-- Fixed two memory leaks:
+ 1) If you returned or aborted from inside an iteration over a list,
+ the list was never freed.
+ 2) If you killed a task, its associated environment was never freed.
+-- Added ticks_left() and seconds_left(), for determining how much longer the
+ current task will be allowed to run.
+-- Reduced the maximum number of seconds for each task from 60 to 15.
+-- Removed restriction that the first argument to notify() and boot_player()
+ must be a valid player object.
+-- Removed the .shutdown and .dump commands.
+-- Added shutdown() and dump_database() functions to replace their respective
+ built-in commands. The shutdown() function takes a single string argument
+ that is printed to all players as their connections are closed.
+-- Added output_delimiters() as a way to discover the current PREFIX and SUFFIX
+ strings on a given connection.
+-- Completely rewrote and modularized the network interface (now in
+ bsd_network.c), the main loop, connection-management, and checkpointing code
+ (now in server.c), and the task dispatcher (now in tasks.c).
+-- Eliminated the notion of `clocks' associated with tasks. All tasks,
+ including forked tasks, begin execution with a full complement of ticks and
+ seconds. To make this reasonable, the task dispatcher maintains a separate
+ queue per player, with command tasks and ready forked tasks intermingled.
+ Thus, a malicious player who forks many tasks can only clog his own queue.
+-- Eliminated the `command burst' heuristics from the dispatcher; this always
+ seemed like just a patch for having a fair dispatcher anyway.
+-- Added a timeout for closing idle un-logged-in connections.
+-- Removed the QUIT command; the boot_player() function can now be used for
+ that purpose. boot_player() now does a normal disconnect, including running
+ the disfuncs. At the same time, I made it allow players to boot themselves.
+-- Because clocks have been eliminated, the lists returned by queued_tasks()
+ have changed somewhat, though in a mostly-compatible way. The clock ID and
+ clock ticks fields are both constant now, with the ticks equal to 20,000.
+-- ****************** All processing of commands typed by un-logged-in players
+ *** VITAL NOTE *** has been moved into the database. Every un-logged-in
+ ****************** connection is assigned a unique negative player number.
+ The functions notify() and boot_player() can be used by wizards to send
+ text to and to terminate such a connection, respectively, using that player
+ number as the connection identifier. Each line of input on such a
+ connection is first parsed into words in the usual way and then these words
+ are passed as the arguments to #0:do_login_command(). For example, the line
+ connect Munchkin frebblebit
+ would result in the following call being made:
+ #0:do_login_command("connect", "Munchkin", "frebblebit")
+ If #0:do_login_command returns a valid player object, then the connection is
+ considered to have logged into that player. When the connection is first
+ established, the null command is automatically entered, resulting in a call
+ to #0:do_login_command with no arguments. This signal can be used by the
+ verb to print out a welcome message, for example; this service is no longer
+ provided by the server.
+ Because of this change, it will be **NECESSARY** to add an implementation
+ of #0:do_login_command to your database *before* beginning to use version
+ 1.3.0 of the server. The Minimal.db database and the version of the
+ LambdaCore database released concurrently with 1.3.0 already provide such an
+ implementation. The following code can be used as a simple implementation
+ that preserves most of the functionality of version 1.2.0:
+
+ if (callers())
+ "This code should only be run as a server task.";
+ return;
+ endif
+ if (args == {})
+ args = {"help"};
+ endif
+ command = args[1];
+ args = args[2..length(args)];
+ nargs = length(args);
+ if (command == "connect")
+ if (nargs < 1)
+ notify(player,
+ "Usage: connect <existing-player-name> <password>");
+ else
+ name = args[1];
+ password = nargs >= 2 ? args[2] | "";
+ for p in (players())
+ if (p.name == name
+ && (typeof(p.password) != STR
+ || (length(p.password) >= 2
+ && crypt(password, p.password[1..2]) == p.password)))
+ p.last_connect_time = time();
+ return p;
+ endif
+ endfor
+ notify(player, "Unknown player or wrong password.");
+ endif
+ elseif (command == "create")
+ if (nargs != 2)
+ notify(player, "Usage: create <new-player-name> <new-password>");
+ else
+ name = args[1];
+ password = args[2];
+ for p in (players())
+ if (p.name == name)
+ notify(player, "That player name is already in use.");
+ return 0;
+ endif
+ endfor
+ new = create($player_class, $nothing);
+ set_player_flag(new, 1);
+ new.name = name;
+ new.aliases = {name};
+ new.programmer = $player_class.programmer;
+ new.password = crypt(password);
+ new.last_connect_time = time();
+ move(new, $player_start);
+ return new;
+ endif
+ elseif (strcmp(command, "QUIT") == 0)
+ boot_player(player);
+ else
+ msg = $welcome_message;
+ if (typeof(msg) != LIST)
+ msg = {msg};
+ endif
+ for line in (msg)
+ if (typeof(line) == STR)
+ notify(player, line);
+ endif
+ endfor
+ endif
+ return 0;
+
+ Of course, the whole point of bringing this code into the database is to
+ enable easy experimentation with new facilities for un-logged-in users, such
+ as multiple Guest players, friendlier coping with similarly-named players,
+ provision of a `who' command, etc.
+-- The network interface now copes more nicely with running out of buffer
+ space for output to a player. Before, excess output was simply dropped on
+ the floor; now, it instead attempts to push it out the network socket and
+ only flushes output when that doesn't work. I was able to have a single
+ command send me over 60,000 characters of output without flushing any.
+-- The PREFIX and SUFFIX strings are no longer printed either for the .program
+ command or for any of the lines of program input.
+-- Recycled players with active connections are more promptly noticed and their
+ connections closed.
+-- The numeric ID associated with forked tasks is now guaranteed to be the same
+ as the value of task_id() in that task when it executes.
+-- The create() function now invokes the :initialize verb on the newly-created
+ object before returning it. It is not an error if there is no :initialize
+ verb defined on the object.
+-- Changed listinsert() and listappend() never to generate E_RANGE errors.
+ Instead, the following expressions are now always equivalent:
+ listinsert(list, element, index)
+ listappend(list, element, index - 1)
+ {@list[1..index - 1], element, @list[index..length(list)]}
+-- Changed the matching algorithm for verb names so that the old behavior of
+ `*' matching anything is generalized to `foo*' matching anything beginning
+ with `foo'.
+-- Changed eval() and set_verb_code() to require programmer permissions.
+-- Added a timezone abbreviation onto the end of ctime()'s result.
+-- Added min(), max(), and abs() functions.
+-- The messages printed for running out of ticks and for running out of seconds
+ are now disinguishable.
+
+Version 1.4.0, 22 August 1991
+-- Fixed a bug whereby running out of seconds might be misreported as running
+ out of ticks.
+-- Fixed a bug whereby using `@' on a non-list in a verb's argument list could
+ end up passing E_TYPE as the value of `args'.
+-- Fixed parsing bug that would ignore an extraneous comma at the front of an
+ argument list.
+-- Changed random() to raise E_INVARG on non-positive numerical arguments,
+ rather than E_TYPE.
+-- Fixed bug in handling of the case where a verb disappears between a
+ `.program' and the corresponding `.'.
+-- Made it a type error to use <, <=, >, or >= either on operands of different
+ types or on lists.
+-- Completely rewrote the MOO-code parser, unparser, and interpreter using a
+ new program representation based on vectors of bytecodes instead of abstract
+ syntax trees. This saves space and time and also allows the interpreter to
+ be iterative instead of recursive. This, in turn, allows us to implement
+ various operations that involve saving the entire state of a running task
+ for later resumption.
+-- Added read() and suspend() functions, using the new capabilities of the
+ interpreter.
+-- The new interpreter counts a few more ticks than the old one; in particular,
+ every IF, ELSEIF, and FORK now counts a tick. In view of this, and in view
+ of the improved performance of the interpreter, the ticks limit for tasks
+ has been increased from 20,000 to 30,000.
+-- Added an indexed assignment statement for updating lists stored in variables
+ and properties.
+-- Fixed bug in numeric comparisons where `-2147483647 > 1' was false but
+ `-2147483647 > 2' was true.
+-- Fixed database dumping to detect and recover from filesystem errors. For
+ normal periodic checkpoints, the dump attempt is simply abandoned. For
+ panic and shutdown dumps, the server retries the dump from the beginning
+ after waiting 60 seconds (during which time, presumably, the archwizard will
+ fix whatever the problem was). In all cases, explanatory messages are
+ printed in the log. This should help mitigate the problems that arise when
+ there's no more room left on the filesystem on which the server is dumping.
+-- Enhanced the callers() function to return the verb-location and initial
+ value of `player' for each caller.
+
+Version 1.4.5, 20 September 1991
+-- Made unparser failures panic the server rather than try to press on.
+-- Changed the task scheduler in two important ways:
+ 1) Effectively gave each player's input tasks a higher priority than
+ their forked or suspended tasks. This should both improve
+ interactive response and make it possible for users to recover from
+ accidentally forking off many tasks; they can now run commands to
+ kill those tasks and avoid having those commands wait behind all of
+ the forked tasks.
+ 2) The scheduler no longer uses a strictly round-robin algorithm for
+ choosing which player's task to run next. Instead, it keeps track
+ of how many seconds have been consumed by each player's tasks and
+ runs the next task from the queue with the least total `usage'.
+ Fresh queues and those queues that are empty when it becomes their
+ turn to run again are kept apart from non-empty queues. When an
+ empty queue next gets a task to run, its usage is set equal to the
+ least usage of already-non-empty queues. Thus, such queues go to
+ the head of the list but have no advantage over the queues that were
+ never empty. This more complex scheduling scheme is intended to
+ ameliorate the problems with some players having long-running tasks
+ constantly runnable in the background; such players will now get
+ only their fair share of the server.
+-- Fixed queued_tasks() to list the player being read from as the owner of a
+ reading task. This makes it consistent with kill_task(), which only allows
+ that player to kill such a task.
+-- Fixed a bug with my use of signal() on some POSIX-compliant machines. One
+ possible consequence of the bug (actually manifest on the IRIS) is that all
+ checkpointing processes after the first one will never be waited for,
+ leaving `defunct' processes lying around until the server itself terminates.
+-- Fixed a small bug in my use of ANSI C: the `typedef enum Opcode Opcode;'
+ line in opcode.h must come *after* the definition of `enum Opcode'; unlike
+ struct types, enum types may not be forwardly referenced. Thanks to yduJ
+ and the Lucid C compiler for finding this bug.
+-- Fixed a bug in the line-number-finding code; this bug was identical to one
+ we fixed long ago in the unparser. Since the LNF code is *derived* from the
+ unparsing code, this is unsurprising. What's embarassing is that we forgot
+ to fix the LNF code at the same time...
+
+Version 1.5.0, 3 November 1991
+-- Added connection_name(player) to allow wizards some level of reasonable
+ control over whom they allow to connect to their servers.
+-- Changed read() to accept an optional argument, player, specifying the player
+ from whose connection to read. If this argument is given, the check that
+ the current task is an input task is not done. The only good use for this
+ that I'm aware of is in conjunction with the next change...
+-- The new compilation option OUTBOUND_NETWORK enables a new built-in function
+ called open_network_connection(). The arguments are specific to the network
+ module in use, but the intent is that they somehow specify a remote place to
+ which a network connection can be made. For the default, BSD networking
+ module, there are two arguments, a host-name string and a port number. The
+ function creates a connection to that place, if possible, and tricks the
+ rest of the server into believing that a normal connection just came from
+ that place. Open_network_connection() returns the negative object number
+ that is the pseudo-player object for that connection. One can then use
+ read() and notify() to send and receive lines of ASCII text on that
+ connection. Of course, if the server should ever receive a line of input
+ when there's no task read()ing from the connection, that input will be
+ treated as a command in the usual way. Also, unless the connection `logs
+ in' at some point, the usual five-minute timeout-if-no-input-received
+ connection shutdown will take place. The usual practice is to have the same
+ task that opens the connection go into an infinite loop read()ing from it.
+ As usual, boot_player() can be used to shut down the connection. This
+ function can only be called by wizards, of course. If the OUTBOUND_NETWORK
+ option is not used, then open_network_connection() still exists, but always
+ raises E_PERM.
+-- Changed the bsd_network module to map tab to space on input, rather than
+ simply dropping tabs on the floor.
+
+Version 1.5.1, 12 November 1991
+-- Fixed bug in checkpointing whereby the server's command log was flushed even
+ if the checkpoint failed.
+-- Various minor tweaks to make the server work on an IBM RS/6000.
+
+Version 1.5.9, 6 December 1991
+-- Gave different ticks/seconds limits to non-input tasks. They get only 15,000
+ ticks and 5 seconds.
+-- The task seconds limit is now measured in server CPU seconds (as opposed to
+ wall-clock seconds) on systems that can support it.
+-- Adding some simple object-hierarchy checking and (in very simple cases)
+ repair code to the DB reading process. If unfixable errors are found, the
+ server refuses to run.
+-- Fixed minor inconsistency in the log format.
+-- Fixed a few minor memory leaks.
+-- Fixed a bug whereby the bottom frame of a suspended task that was read from
+ the database file could be given the wrong size runtime value stack.
+-- Made the DB-reading code a bit pickier about the correctness of its input.
+-- Tweaked create() to make it more properly initialize the new object's
+ location and contents.
+-- Fixed bug in move() that could be tickled if an `accept' verb recycled
+ either the movee or the destination.
+-- Fixed several bugs in recycle() that could (and did!) corrupt the contents
+ hierarchy in the database. The implementation is now much simpler, too!
+
+Version 1.5.14, 3 March 1992
+-- Various minor tweaks to make the server work on a Macintosh running A/UX.
+-- Tweak to work around bug in isgraph() on systems that default to signed
+ characters.
+-- Fixed bug in boot_player() that didn't allow players to boot themselves.
+ [I know it says under release 1.3.0 above that boot_players() already worked
+ this way, but as far as I can see, that was a lie!]
+-- Fixed bsd_network startup sequence so that the server does not `listen' on
+ its port until after the DB has been read. Connections during the
+ DB-reading phase will most likely time out. I think this is better than the
+ old situation, in which those connections succeeded and then hung without
+ printing anything until the server finished reading in the DB.
+-- Changed behavior of server when there aren't enough file descriptors to
+ accept a new connection. The server now arranges to stop just short of this
+ point and to immediately close any new connections after printing an
+ explanatory message.
+-- Fixed properties(), verbs(), x.contents, and queued_tasks() to run in time
+ linear in the length of their results, rather than quadratic.
+
+Version 1.5.20, 24 July 1992
+-- Added check for an empty string as the second argument to strsub(); this now
+ raises E_INVARG.
+-- Conditionalized the keeping of the command log; it is now controlled by the
+ LOG_COMMANDS symbol.
+-- Removed a number of causes for GCC 2.1 to issue warnings about server code.
+ (Version 1.5.15)
+-- Fixed an ordering bug whereby the task queue for a newly-connected player
+ was not fully initialized when the :confunc task(s) were run. In
+ particular, the new, connected queue was not marked as belonging to that
+ player, and certain functions like output_delimiters() would always fail
+ (not that that's a very interesting function to call in a :confunc, since
+ it's guaranteed to return {"", ""} then...). (Version 1.5.16)
+-- Finally fixed the bug whereby it was possible to use chparent() to create a
+ situation in which an object defines a property with the same name as one
+ defined by one of its ancestors. Such an attempt now results in chparent()
+ raising E_INVARG. (Version 1.5.17)
+-- Finally fixed the bug whereby add_property() allowed the definition of a
+ property in a parent that was already defined in a child. With this change,
+ it should no longer be possible to create a situation in which a parent and
+ child define properties with the same name. (Version 1.5.18)
+-- Eliminated some minor warnings from the Dell SVR4 and HP/UX 9000 compilers.
+ (Version 1.5.19)
+-- Added logging of all instances of x.wizard = 1.
+-- The very first task run on booting the server is now a server task calling
+ #0:server_started() with player == #-1. This task runs even before the
+ server gets the value of $dump_interval in order to schedule the first
+ checkpoint. (Version 1.5.20)
+
+Version 1.6.0, 13 August 1992
+-- Changed MOO-code parser to generate the old version 1.3 program-
+ representation based on abstract syntax trees. These syntax trees are then
+ translated to vectors of bytecodes. This is much cleaner and easier to
+ understand than the old method; perhaps surprisingly, it also appears to be
+ significantly faster, too. (Version 1.5.21)
+-- Added support for compilation on MIPS SVR3 machines.
+-- Added support for a remote `checkpoint-request' signal (SIGUSR2), analogous
+ to the existing `shutdown-request' signal (SIGUSR1).
+-- Changed unparser to first convert bytecodes to trees and then use output
+ routines from version 1.3 of the server. Same for line numbers.
+-- Error tracebacks now include line numbers for all frames on stack.
+ (Version 1.5.24)
+-- Cleaned up interpreter code.
+-- Added sqrt() and server_log() built in functions (Version 1.5.25)
+-- All built-in function registration procedures are now located in one list,
+ so it is easier to add new ones.
+-- Added match(), rmatch(), and substitute() built-in functions to handle
+ regular expression searches. (Version 1.6.0)
+
+Version 1.6.1, 14 August 1992
+-- Fixed off-by-one bug in GNU regexp searching code. (*sigh*)
+
+Version 1.7.0, 23 October 1992
+-- Fixed behavior of index(), rindex(), match(), and rmatch() on empty pattern
+ and/or subject strings. (Version 1.6.3)
+-- Added the prefix `> ' to all log messages generated by the server_log()
+ built-in function, so that they can be distinguished from server-generated
+ messages.
+-- Added a compile-time option (OUT_OF_BAND_PREFIX) enabling a method of
+ entering commands that bypass both normal command parsing and any pending
+ read()ing task. If OUT_OF_BAND_PREFIX is #define'd (in config.h) as a
+ non-empty string, then any lines of player input that begin with that prefix
+ will be parsed into a list of words and those words passed as arguments in a
+ server task invoking #0:do_out_of_band_command. This is intended for use by
+ fancy MOO clients that need to send reliably-understood messages to the
+ server, such as window-event notifications.
+-- Added output to the `.program' built-in command, stating that it is obsolete
+ and will be replaced `soon' (i.e., in the next release).
+-- Rearranged the per-system configuration procedure for compiling the server;
+ now it should be easier for people to understand just what needs to be done
+ for their particular system. The user changes in the Makefile are now
+ confined to specfying details of the compiler; all other kinds of options
+ are handled by editing the `config.h' file, which has been radically
+ reorganized.
+-- Renamed a few files (notably parse_command.[ch]) to fit within the
+ 14-character limit imposed on certain systems. (Version 1.6.4)
+-- Implemented reference counting on string and list values, replacing the old,
+ CPU-intensive deep copying method of storage management.
+-- Added subrange assignment for strings and lists. Added indexed assignment
+ for strings. (Version 1.6.5)
+-- Fixed bug whereby a read()ing task with no more input to consume was never
+ resumed if the connection being read was subsequently closed, from either
+ side. Now the read() call raises E_INVARG, just as it would if the read()
+ were begun when the connection was already closed and no more input was left
+ to consume.
+-- Added TYPE_CLEAR value to properties, which cause property lookup on the
+ parent. Added built in functions `clear_property()' and
+ `is_clear_property()' to assist in TYPE_CLEAR property manipulation.
+-- Added hash-lookup for properties and changed property definition
+ representation to be arrays instead of linked-lists, both to speed up
+ property lookup. (1.6.6)
+-- Added support for multiple complete networking implementations. The first
+ use of this flexibility is a new SINGLE_USER option, which creates a version
+ of the server that accepts only one connection at a time and uses the
+ server's own standard input and output streams for it.
+-- Added a new built-in property on objects, the `f' (for `fertile') bit; it
+ replaces the use of the `r' bit to allow children to be made of the object
+ by either create() or chparent(). That is, now those operations check that
+ the `f' bit is set and disregard the setting of the `r' bit. (Version 1.6.7)
+ ******** Before upgrading an existing MOO to use this version of the server,
+ * NOTE * you should make sure that no object in your database already has a
+ ******** property named `f'; the following MOO program, run by a wizard,
+ will perform this check:
+ for i in [0..tonum(max_object())]
+ o = toobj(i);
+ if (ticks_left() < 1000 || seconds_left() < 2)
+ notify(player, tostr("Checking ", o, " ..."));
+ suspend(0);
+ endif
+ if (valid(o) && "f" in properties(o))
+ notify(player, tostr("*** ", o.name, " (", o,
+ ") has an `f' property!"));
+ endif
+ endfor
+ After eliminating all such properties from the database and restarting with
+ this version of the server, you should, as a wizard, run the following MOO
+ code to initialize the `f' bits of all of the objects:
+ for i in [0..tonum(max_object())]
+ o = toobj(i);
+ if (ticks_left() < 1000 || seconds_left() < 2)
+ notify(player, tostr("Fixing ", o, ".f ..."));
+ suspend(0);
+ endif
+ if (valid(o))
+ o.f = o.r;
+ endif
+ endfor
+-- Reorganized the files so that all database modifying procedures were in one
+ of four modules. (Version 1.6.8)
+-- Reorganized existing network protocol and multiplexing wait implementations
+ into a pluggable modular form. (Version 1.6.9)
+-- Fixed bug in the interpreter that could pass a garbage program counter to
+ the line-number-finding code, used in printing error tracebacks. The l-n-f
+ code responded semi-robustly by printing a message in the server log and
+ returning a line number of zero. (Version 1.6.10)
+-- Fixed bug whereby passing negative numbers to random() failed to evoke an
+ E_INVARG error.
+-- Fixed a bug in MOO-code compilation that led to real nastiness if there were
+ more than 255 literals in a single verb.
+-- Incorporated several new networking implementations, allowing for use by a
+ single-user (using the standard input and output streams of the server
+ itself), and by multiple users on either System V or BSD-style UNIX systems,
+ either with or without TCP/IP networking. (Version 1.6.11)
+-- Fixed a bug in built-in function management that caused max() and min() to
+ always raise E_TYPE on some systems.
+-- Added a new automatic configuration system to the server distribution, so
+ that people do not, in general, need to know much of anything about their
+ local system in order to compile the server. (Version 1.6.12)
+-- Fixed a misfeature of substitute whereby it was not possible to include a
+ percent-sign in the string that was to survive into the output. Now, `%%'
+ is replaced by `%' in the output. (Version 1.7.0)
+
+Version 1.7.2, 3 August 1993
+-- Made tabs input as themselves, rather than being mapped to spaces. This is
+ necessary in order to communicate with Gopher servers from within the MOO.
+-- Reduced the default `seconds-left' limit for task; forground tasks went from
+ 15 to 5 seconds and background tasks from 5 to 3.
+-- Fixed regular-expression matching to respect task seconds limits, aborting
+ the match.
+-- Fixed bugs whereby both kill_task() and queued_tasks() would miss reading
+ tasks that had no ready input (i.e., whose associated task queues were
+ empty).
+-- Fixed memory leak when a read() call fails with E_INVARG; the copied
+ interpreter stack was never being freed.
+-- Fixed possible free of the null pointer during object recycling.
+-- Fixed a number of places that generated warnings in newer versions of GCC.
+-- Fixed inconsistency of the behavior of strcmp() between different server
+ machines; now it always returns one of 1, 0, or -1.
+-- Added a five-second timeout to outbound connection attempts, aborting them
+ if the timer expires.
+-- Made the error message to users about network buffers overflowing and lines
+ of output being flushed much more understandable.
+-- Added log entry for each time the server refuses a connection due to being
+ full.
+-- Fixed premature-free bug in preposition matching.
+-- Bowing to popular pressure, removed warning from `.program' command about
+ its (no longer) imminent demise.
+-- Added more error checks to options.h to make it easier for installers to
+ diagnose problems.
+-- Fixed a bunch of auto-configuration problems encountered by installers.
+-- Fixed a bug in substitute() where certain successful match() results were
+ rejected as invalid.
+-- Removed restrictions that only player objects could be used in place of `x'
+ in the following:
+ object.owner = x
+ add_property(object, pname, value, {x, perms})
+ set_property_info(object, pname, {x, perms})
+ add_verb(object, {x, perms, names}, args)
+ set_verb_info(object, vname, {x, perms, names})
+ set_task_perms(x)
+ That is, non-players may now own objects, verbs, and properties, and running
+ tasks may take on the authority of a non-player.
+-- Fixed bug where it was possible for a task to run out of seconds during a
+ call to set_verb_code() and have a truncated version of the program
+ installed as the new verb code. Now, either all of it gets installed or
+ none of it.
+-- The log messages produced by successful open_network_connection() calls are
+ more useful now, containing the hostname and port of the connection.
+-- Outbound network connections are no longer susceptible to connection
+ timeouts due to a lack of input.
+-- Fixed a bug whereby verbs with numeric names screwed up DB loading, with the
+ wrong code getting put into some verbs and others getting no code at all.
+
+Version 1.7.3, 4 August 1993
+-- Outbound connections no longer get a spurious blank line of input as soon as
+ they're created.
+-- Fixed stupid bug introduced in fixing bug in substitute().
+-- Fixed some bugs in the Makefile and the configuration script.
+
+Version 1.7.4, 10 August 1993
+-- Fixed more configuration problems and warnings from GCC.
+
+Version 1.7.5, 12 August 1993
+-- Added more log entries during loading, to make it clearer where the time is
+ being spent.
+-- Fixed a long-standing denial-of-service attack vulnerability, in which a
+ connection could keep the server from ever running any tasks at all by
+ *always* providing ready input.
+
+Version 1.7.6, 11 October 1993
+-- The built-in command parser now matches the (in)direct object string against
+ the names of objects as well as their aliases.
+-- The server no longer calls :confunc or :disfunc directly when users connect
+ or disconnect; instead, it calls the following verbs on #0 in the
+ appropriate circumstances:
+ :user_created(USER)
+ When #0:do_login_command() returns USER, a valid player object
+ whose number is greater than the value max_object() returned
+ before the server called #0:do_login_command(). That is, when
+ a new user has been created.
+ :user_connected(USER)
+ When #0:do_login_command() returns USER, a previously-existing
+ valid player object for which no active connection already
+ existed.
+ :user_reconnected(USER)
+ When #0:do_login_command() returns USER, a previously-existing
+ valid player object for which there was already an active
+ connection.
+ :user_disconnected(USER)
+ After the end of a task that called either boot_player(USER) or
+ set_player_flag(USER, 0).
+ :user_client_disconnected(USER)
+ When USER's client unilaterally closes its connection to the
+ server.
+ ******** Before upgrading an existing MOO to use this version of the server,
+ * NOTE * you should install the following verbs on #0 in order to remain
+ ******** compatible with existing code; NOTE WELL that they should be
+ installed with the `d' permissions bit UNSET:
+ #0:"user_created user_connected"
+ user = args[1];
+ fork (0)
+ user:confunc();
+ endfork
+ user.location:confunc(user);
+ #0:"user_disconnected user_client_disconnected"
+ user = args[1];
+ fork (0)
+ user.location:disfunc(user);
+ endfork
+ user:disfunc();
+-- Non-wizard users can now use the read() built-in function when they give a
+ connected object they own as an argument. Thus, it is possible, for
+ example, for a non-wizard to prompt itself for input.
+-- The match() and rmatch() built-in functions now cache some number of the
+ most recently used patterns, somewhat speeding up the matching process.
+-- The server now calls #0:checkpoint_started() whenever it starts to dump a
+ checkpoint of the database and #0:checkpoint_finished(SUCCESS) whenever the
+ dump finishes, where SUCCESS is true iff and only if the checkpoint was
+ successful.
+-- Fixed a bug (!) in the built-in command parser whereby in the command
+ ="This is in quotes" and this is not
+ the variable `verb' would correctly be set to "=This is in quotes" but the
+ variable `argstr' would be "is in quotes\" and this is not" instead of the
+ correct "and this is not". (Reported by Shirgall.)
+-- Fixed some compilation problems with the SYSV/TCP configuration on Solaris
+ machines.
+-- The value of `argstr' when the server calls either #0:do_login_command() or
+ #0:do_out_of_band_command() is now the raw command line as received from the
+ connection, as opposed to the empty string.
+
+Version 1.7.7, 20 December 1993
+-- Added some extra configuration tests and other changes to work around bugs
+ in HP/UX.
+-- Fixed an output-timing bug in calling the #0:checkpoint_started() hook.
+-- Fixed bug in the SYSV/LOCAL client whereby some output from the server could
+ be lost, never being printed.
+-- Added the server FIFO file name to an error message in the SYSV/LOCAL
+ configuration.
+-- Added the first rudimentary support for in-DB command parsing. Each user
+ command is broken up into words, a list of which is passed as the arguments
+ in a call to #0:do_command(), if it exists, with `argstr' initialized to the
+ raw command line typed by the user. If #0:do_command() does not exist, or
+ if that verb-call completes normally (i.e., without suspending or aborting)
+ and returns a false value, then the built-in command parser is invoked as
+ usual to handle the command. Otherwise, it is assumed that the DB code
+ handled the command completely and no further action is taken by the server
+ for that command.
+
+Version 1.7.8, 30 September 1994
+-- Many serious bugs in the MOO-code compiler (some server-crashing) were fixed
+ by rewriting the code-generator portion from scratch. This should fix the
+ problems some folks have had with panics due to `Unparse failed!'.
+-- As a side-effect of the support I wrote for debugging the new code
+ generator, there is a new built-in function, disassemble(OBJ, VERB), which
+ returns a (longish) list of strings giving the `assembly-language' listing
+ for the compiled code of the verb VERB on the object OBJ. The permissions
+ checks are the same as for verb_code(OBJ, VERB). I don't make any promises
+ about how that output might change in later releases and I probably won't be
+ writing any detailed documentation of the opcode set, etc. Think of it as a
+ little puzzle gift, from me to you... :-)
+-- Added a number of new configuration tests, especially to aid compilation on
+ the DEC Alpha running OSF/1 and any machine running NeXT Mach 3.1.
+-- The checkpoint process now changes how its command line shows up in the
+ output of the `ps' command (on systems where that's possible), to make it
+ clear what process it is.
+-- Added the first stages of a general new facility for customizing the
+ operation of the server dynamically, from inside the database. At a number
+ of times, the server checks for whether the property $server_options exists
+ and has an object number as its value. If so, then the server looks for a
+ variety of other properties on that $server_options object and, if they
+ exist, uses their values to control how the server operates. There are only
+ a couple of uses of this facility in 1.7.8; many more are expected in the
+ future.
+-- As the first use of the above facility, the server uses the value of
+ $server_options.fg_ticks and $server_options.fg_seconds (if they exist and
+ are numbers) as the numbers of ticks and seconds allotted to command tasks
+ and server tasks; the values of DEFAULT_FG_TICKS and DEFAULT_FG_SECONDS in
+ options.h are used if the corresponding properties either don't exist or are
+ not numbers. Similarly, the values of $server_options.bg_ticks and
+ $server_options.bg_seconds are used for forked tasks and suspended tasks,
+ falling back on DEFAULT_BG_TICKS and DEFAULT_BG_SECONDS. These properties
+ are looked up anew every time a task begins or resumes execution.
+-- As the second use of the $server_options facility, on every call to a
+ built-in function `foo()', if the property $server_options.protect_foo
+ exists and is true, and the programmer is not a wizard, then E_PERM is
+ raised. Thus, for example, to make the `chparent()' function wiz-only,
+ simply set $server_options.protect_chparent to 1.
+-- Fixed a bug in the MOO-code unparser where any use of a string naming a MOO
+ keyword as a property or verb name would be unparsed in such a way as to
+ make the resulting code syntactically incorrect. Thus, for example, the
+ expression
+ x.("while")
+ now unparses into the same code, instead of into
+ x.while
+ which won't parse properly.
+-- The server now uses a pair of additional (smallish) UNIX processes to
+ perform all network host-name lookups. Under the new system, it can
+ reliably abort stalled lookups without causing problems; in particular, this
+ provides the real fix for the long-standing `VM exhausted' problem on NeXT
+ machines.
+-- A few small storage leaks were fixed.
+-- A number of new progress-report log messages were added to the DB loading
+ process.
+
+Version 1.7.8p1, 3 October 1994
+-- Fixed a file-descriptor leak in the new name-lookup code; if the server has
+ enough name-lookup timeouts, killing the name-lookup worker process over and
+ over, then the name-lookup intermediary will eventually run out of file
+ descriptors and fail, shutting down name-lookup services until the server is
+ rebooted.
+-- Fixed a minor portability problem in the new code generator; believe it or
+ not, there are still C compiler's out there (on Ultrix, for example) that
+ don't implement initializers for locally-declared structures...
+-- Fixed a bug in disassemble() whereby it couldn't find !x verbs.
+
+Version 1.7.8p2, 6 October 1994
+-- Fixed another little bug in disassemble(), whereby it could crash the server
+ if you try to disassemble an unprogrammed verb.
+-- Added a hard minimum of 100 ticks and 1 second for all tasks, to prevent
+ disaster when somewiz somewhere blows the values of $server_options.fg_ticks
+ and company.
+-- Fixed a bug whereby the new name-lookup code would fail to recognize even
+ dotted-decimal numeric addresses once the intermediary was presumed dead.
+-- Fixed a null-pointer free in the disassembler that was crashing servers that
+ were using the GNU malloc() implementation.
+-- Added Craig Horman's patch to the regex.c code for the Alpha, which avoids
+ occasional random failures of the matching code. I don't know why it took
+ me so long to apply this...
+
+Version 1.7.8p3, 19 October 1994
+-- Fixed a typo in the "hard minimum" code added in 1.7.8p2; the minimum
+ seconds allocation is now really 1, not 2...
+-- Fixed a bug in the disassembler wherein it could produce garbage or even
+ possibly crash the server if the C compiler decided to evaluate certain sets
+ of function arguments in other than left-to-right order.
+-- Fixed a bug in is_clear_property() that could attempt to access an
+ uninitialized variable. (Thanks to Quowong P Liu for pointing out this and
+ others of the bugs fixed in the various 1.7.8 patch releases.)
+-- Fixed a bug whereby the server was, on some systems, always susceptible to
+ being shut down prematurely by the `hangup' signal. Now, the `restart'
+ script uses `nohup' and the server continues to ignore SIGHUP if it was
+ started ignoring it (i.e., by `nohup').
+-- Fixed MOO-level tracebacks to include lines for built-in functions that call
+ verbs, such as eval(), move(), etc.
+-- Added MOO-level traceback printing for all server panics that happen during
+ the execution of a MOO task.
+
+Version 1.7.8p4, 2 November 1994
+-- Fixed minor memory leak in disassemble().
+-- Fixed bug whereby verb_info() and friends allowed an argument of "" to
+ always match the first verb on an object.
+-- Fixed bug whereby MOO keywords (e.g., `if', `E_PERM', `return', etc.) were
+ case-sensitive.
+-- Fixed (I hope...) the 32-bit assumptions that break on the (64-bit) Alpha.
+
+Version 1.7.9alpha1, 25 September 1995
+-- Fixed a few minor configuration and portability problems.
+-- Fixed a potentially server-crashing bug in subrange assignments.
+-- Renamed the internal server function `log()', to avoid name conflicts with
+ the logarithm function in the math library.
+-- Changed the name-lookup subsystem to be more optimistic about the chances of
+ eventually recovering from an earlier failure to restart the lookup process.
+ Also bullet-proofed it against problems with its read() calls getting
+ interrupted by the checkpoint timer.
+-- Added a paragraph to `README' explaining how to boost the limit on the
+ number of connections a server can support.
+-- Added two new built-in functions enabling faster case-sensitive tests:
+ equal(X, Y) returns true iff the values X and Y are completely equal,
+ including the case of any strings they might contain; this is just
+ a case-sensitive version of the `==' expression.
+ is_member(X, L) is a similarly case-sensitive version of `X in L'.
+-- Made the `.program' built-in command wiz-only if
+ $server_options.protect_set_verb_code exists and is true.
+-- Added the built-in function `set_connection_option(CONN, OPTION, VALUE)',
+ for controlling various optional behaviors on the connection CONN. The only
+ allowed values for OPTION in this release are as follows:
+ "hold-input" -- if VALUE is true, then input received on CONN will
+ never be treated as a command; instead, it will remain in the queue
+ until retrieved by a call to read().
+ "client-echo" -- (NP_TCP configurations only) sends the Telnet Protocol
+ WON'T ECHO or WILL ECHO commands (depending on whether VALUE is
+ true or false, respectively). For clients that support the Telnet
+ Protocol, this should toggle whether or not the client echoes
+ locally the characters typed by the user. Note that the server
+ never echoes input characters under any circumstances.
+-- Fixed stupid bug that let people lose by setting $server_options.fg_ticks
+ and company to negative values.
+-- Added an optional second argument to the `read()' built-in function. If it
+ is provided and true, then this call to `read()' will not suspend the
+ calling task under any circumstances. If there is input currently
+ available, it will be returned immediately; otherwise, `read()' returns 0.
+ (As before, if no input is available and no more is coming, `read()' raises
+ E_INVARG as an end-of-input indicator.)
+-- Added flow-control to the server's input-handling: if more than a reasonable
+ amount of unprocessed input accumulates for any connection, the server will
+ temporarily stop trying to read from that connection at all, until the
+ backlog drops down substantially.
+-- Fixed a longstanding bug in match() that could make it return garbage in
+ certain circumstances. More bugs in match() almost certainly still exist.
+ (Thanks to Judy Anderson for finding this.)
+-- Fixed a minor memory leak in the case where #0:do_command() exists and
+ returns a list or string value. (Thanks to Ian Macintosh for finding this.)
+-- Fixed a possible race condition in the TCP networking code, where a timer
+ could go off before we've installed the exception handler. (Thanks to Alex
+ Stewart for finding this.)
+-- Officially deprecated the USE_GNU_MALLOC option in options.h, since it's not
+ aging very well.
+-- Completely replaced the regular-expression matching implementation that
+ underlies match() and rmatch(); it used to be the GNU `regex' package and is
+ now the GNU `RX' package shipped with GNU `sed'. This may not eliminate all
+ bugs in match(), but it almost certainly has moved them around a bit.
+ ********** The old GNU regex package had a bug in its handling of certain
+ ** NOTE ** patterns with parentheses in them, and it is reasonably likely
+ ********** that many MOO programmers have, perhaps unconsciously, come to
+ depend upon this buggy behavior. Unfortunately for such programmers, RX
+ does not have this bug, so you will want to fix your regular expressions
+ before upgrading to this release; the fixed patterns will work correctly on
+ both releases.
+ The old bug concerns patterns of the form `%( ... %)*', that is, a starred
+ parenthesized sub-pattern; for example, consider the MOO expression
+ match("foo", "%(o%)*")
+ Using the old regex package, this returns
+ {2, 3, {{2, 3}, {0, -1}, ...}, "foo"}
+ which is *wrong*; the last successful match of the parenthesized sub-pattern
+ covered just the third character, not the second and third ones. Using the
+ new RX package, this expression returns the proper value:
+ {2, 3, {{3, 3}, {0, -1}, ...}, "foo"}
+ To get the effect of the old bug, you need another set of parentheses around
+ the whole starred sub-pattern:
+ match("foo", "%(%(o%)*%)")
+ Under both GNU regex and RX, if M is the result of this expression, we have
+ M[3][1] == {2, 3}
+ You should look carefully at your uses of match() and rmatch() before
+ upgrading to this release, fixing those places where your code depends on
+ the old, buggy behavior.
+-- Added an optional third argument to the built-in function notify(); if it is
+ provided and true, and if there isn't enough room left in the given user's
+ output buffer to hold the given line, then notify() will return false and
+ the line will not have been queued for output. In all other circumstances,
+ notify() now returns true. If the new optional argument is false or not
+ provided, then the old behavior is invoked, in which some of the
+ already-queued output is discarded to make room for the new line.
+-- Made it possible to change the maximum verb-call depth from inside the DB.
+ The MAX_VERB_DEPTH constant in options.h was replaced by
+ DEFAULT_MAX_STACK_DEPTH, which can be overridden by
+ $server_options.max_stack_depth. The maximum stack depth for any task is
+ set at the time that task is created and cannot be changed thereafter. This
+ implies that suspended tasks, even after being saved in and restored from
+ the DB, are not affected by later changes to
+ $server_options.max_stack_depth.
+-- The task scheduler is now guaranteed never to assign a task_id() of zero.
+-- The built-in functions notify(), connected_players(), connected_seconds(),
+ idle_seconds(), connection_name(), and set_connection_option() now treat
+ connections on which boot_player() has been called as if they did not exist.
+-- A number of the messages printed to a connection by the server under various
+ circumstances can now be customized or eliminated from within the DB. In
+ each case, a property on $server_options is checked at the time the message
+ would be printed. If the property does not exist, the standard message is
+ printed. If the property exists and its value is not a string, then no
+ message is printed at all. Otherwise, the string is printed in place of the
+ standard message. The following list covers all of the newly customizable
+ messages, showing for each the name of the relevant property on
+ $server_options, the default/standard message, and the circumstances under
+ which the message is printed:
+ timeout_msg "*** Timed-out waiting for login. ***"
+ This in-bound network connection was idle and un-logged-in for
+ at least CONNECT_TIMEOUT seconds (as defined in options.h).
+ recycle_msg "*** Recycled ***"
+ The logged-in user of this connection has been recycled.
+ boot_msg "*** Disconnected ***"
+ The function boot_player() was called on this connection.
+ redirect_from_msg "*** Redirecting connection to new port ***"
+ The logged-in user of this connection has just logged in on
+ some other connection.
+ redirect_to_msg "*** Redirecting old connection to this port ***"
+ The user who just logged in on this connection was already
+ logged in on some other connection.
+ create_msg "*** Created ***"
+ The user object that just logged in on this connection did not
+ exist before #0:do_login_command() was called.
+ connect_msg "*** Connected ***"
+ The user object that just logged in on this connection existed
+ before #0:do_login_command() was called.
+-- The `for VAR in [EXPR..EXPR]' looping construct can now be used with either
+ numbers or object numbers. That is, the construct `for o in [#0..#100]' is
+ now legal and does the obvious thing. NOTE that in the example `o' will
+ take on each of 101 object numbers in the specified range, regardless of
+ whether or not those object numbers are valid.
+-- By popular request, added the built-in function `value_bytes(VALUE)', which
+ returns the number of bytes of memory required to store the given value. [I
+ was also asked to provide an `object_bytes(OBJ)' function, to give the total
+ memory required to store the given valid object, but I wanted to think
+ longer about possible interactions with 1.8.0's new modularity wall between
+ the DB implementation and the rest of the server.]
+-- At long last, there is a DB-settable limit on the number of queued tasks any
+ single user can have at once. If $server_utils.user_task_limit exists and
+ is a non-negative number, then that is the `task limit' for normal users;
+ otherwise, the task limit is infinite. For wizards, the task limit is
+ controlled similarly by $server_utils.wizard_task_limit. Whenever a `fork'
+ statement or `suspend()' call are executed, the server checks whether or not
+ the current verb's owner (really, the current task perms) is already at or
+ above their task limit; if so, E_QUOTA is raised instead of either forking
+ or suspending. Reading tasks are not affected by the task limit.
+-- The result of `tostr(E_QUOTA)' has been changed to the string
+ "Resource limit exceeded".
+-- Applied Alex Stewart's pAS4 patch, which modifies the result of the
+ built-in function `connection_name()' on TCP networking configurations to
+ contain the remote port of the connection as well as the host name, in the
+ following format:
+ "99 from FOO.BAR.COM, port 9999"
+ As before, the first number in the result is pretty useless to MOO
+ programmers (it's the server's file descriptor for the connection) but can,
+ believe it or not, occasionally be useful to the maintainer.
+ ******** Before upgrading an existing MOO to use this version of the server,
+ * NOTE * you should modify the verb $string_utils:connection_hostname_bsd as
+ ******** follows:
+ @chmod $string_utils:connection_hostname_bsd -d
+ @program $string_utils:connection_hostname_bsd
+ s = args[1];
+ return strsub($string_utils:explode(s)[3], ",", "") || "";
+ .
+ This code should work compatibly with either version of the server.
+-- Applied the key part of Alex Stewart's pAS7 patch, which fixes a problem
+ with the server occasionally hanging under Sun's Solaris 2.X system. [I
+ just removed the (useless) call to `shutdown()'; the SO_LINGER setting
+ didn't seem necessary or very useful.]
+-- Added a new item to options.h, UNFORKED_CHECKPOINTS (off by default), that
+ prevents the server from forking a separate process to make checkpoints;
+ instead, the main server process performs the checkpoints itself, halting
+ all user interaction and MOO task execution for the duration.
+-- Made the server's log output during the initial database load *slightly*
+ easier to understand.
+-- Removed perhaps the last hard limit in the server; you can now have input
+ lines with more than 500 words on them. (Thanks to Bill Drury for sending
+ the message to MOO-Cows that finally got me to fix this longstanding bug.)
+-- Disallowed empty verb names and those made up only of spaces.
+-- Added a new built-in function `toliteral(VALUE)' such that
+ eval("return " + toliteral(VALUE) + ";") == {1, VALUE}
+ for all MOO values.
+-- The `create()' built-in function will now create children of #-1.
+-- Added an `emergency wizard mode' to the server's start-up sequence; if you
+ give an initial `-e' option on the command line, then after loading in the
+ database but *before* running #0:server_started(), the server will use its
+ standard input and output streams to allow execution of wizardly `eval'
+ commands and re-programming of verbs. For more details, type `help' from
+ inside the mode.
+-- The verbs #0:user_disconnected() and #0:user_client_disconnected() are now
+ called for un-logged-in and outbound connections, too, just as they are for
+ logged-in ones.
+ ******** Before upgrading an existing MOO to use this version of the server,
+ * NOTE * you should check your versions of these verbs to ensure that they
+ ******** will work appropriately when passed negative (and therefore
+ invalid) object numbers. In most cases, it is probably sufficient to add
+ the following lines to the top of these verbs:
+ if (args[1] < #0)
+ return;
+ endif
+ This code should work compatibly with either version of the server, since
+ these verbs weren't being called with such object numbers before.
+-- The server now saves, in the DB file, a list of all users with active
+ connections at the time of the checkpoint, shutdown, or panic that made the
+ file. Upon server start-up, if such a list is present in the DB file, a
+ call is made to #0:user_disconnected() for each formerly active connection.
+ These calls are made *before* the call to #0:server_started(). In this way,
+ there are no longer any discontinuities across a server reboot; from the
+ point of view of code in the DB, the only evidence of a reboot is that,
+ first, a relatively long time has passed since the last task execution,
+ second, all connections to the server (in-bound or out, logged-in or not)
+ have simultaneously been closed, and third, #0:server_started() has been
+ called by the server.
+-- Fixed RX to do reverse-searching properly, so now rmatch() works again.
+ Also added a abort-check in the searching inner loop for the MOO interpreter
+ having run out of seconds.
+
+Version 1.7.9, 18 October 1995
+-- Fixed stupid bug in registration of toliteral(); it now shouln't raise
+ E_TYPE on all calls...
+-- Added new built-in function `queue_info([USER])'. If USER is omitted,
+ returns a list of object numbers naming all users that currently have task
+ queues inside the server; if USER is provided, returns the number of tasks
+ currently queued for that user. It is guaranteed that queue_info(X) will
+ return zero for any X not in the result of queue_info(). In essence,
+ queue_info(X) is a very efficient version of
+ set_task_perms(X);
+ return length(queued_tasks());
+ In particular, it (a) doesn't have to allocate a large list structure, and
+ (b) tells you when passed no arguments the complete set of users for whom
+ there might actually be any queued tasks.
+-- Fixed initialization bug in code to track number of queued tasks.
+-- Liberalized the rules for when a call to read() without arguments will
+ succeed. Wizards won't get E_PERM if the current task is the one that was
+ last spawned by a command from the connection in question. You can assure
+ this in a number of ways:
+ 1) Never suspend, but rather only call read(). This is the one way
+ that used to work.
+ 2) Get lucky, go ahead and call suspend(), and have it just happen that
+ no commands have been read since the last time you called read(),
+ perhaps because the user was waiting for a prompt and no typing
+ ahead. This technique is *not* recommended.
+ 3) Before suspending, call
+ set_connection_option(player, "hold-input", 1)
+ thereby ensuring that no commands will be taken from this player's
+ queue until you call
+ set_connection_option(player, "hold-input", 0)
+ This new third technique, suggested by Alex Stewart, was the impetus for
+ making this change.
+-- Fixed a memory leak in the server's use of the new matcher.
+-- Fixed a bug whereby the third element of a successful result of match() or
+ rmatch() contained 29 elements instead of the usual 9.
+-- Changed the method for determining a given user's queued-task limit, which
+ is checked on every `fork' or `suspend()' from code running with that user's
+ permissions. If the current task perms are valid, and that object has a
+ `queued_task_limit' property, and the value of that property is a
+ non-negative number, then that number is the limit. Otherwise, if
+ $server_options.queued_task_limit exists and its value is a non-negative
+ number, then that's the limit. Otherwise, there is no limit. (Thanks to
+ Gustavo Glusman for his suggestion on how to do this.)
+-- The following release note, given for the 1.7.9alpha1 release, contained an
+ error; the following version corrects it.
+ ********** The old GNU regex package had a bug in its handling of certain
+ ** NOTE ** patterns with parentheses in them, and it is reasonably likely
+ ********** that many MOO programmers have, perhaps unconsciously, come to
+ depend upon this buggy behavior. Unfortunately for such programmers, RX
+ does not have this bug, so you will want to fix your regular expressions
+ before upgrading to this release; the fixed patterns will work correctly on
+ both releases.
+ The old bug concerns patterns of the form `%( ... %)*', that is, a starred
+ or plussed parenthesized sub-pattern; for example, consider the MOO
+ expression
+ match("foo", "%(o%)+")
+ Using the old regex package, this returns
+ {2, 3, {{2, 3}, {0, -1}, ...}, "foo"}
+ which is *wrong*; the last successful match of the parenthesized sub-pattern
+ covered just the third character, not the second and third ones. Using the
+ new RX package, this expression returns the proper value:
+ {2, 3, {{3, 3}, {0, -1}, ...}, "foo"}
+ To get the effect of the old bug, you need another set of parentheses around
+ the whole starred sub-pattern:
+ match("foo", "%(%(o%)+%)")
+ Under both GNU regex and RX, if M is the result of this expression, we have
+ M[3][1] == {2, 3}
+ You should look carefully at your uses of match() and rmatch() before
+ upgrading to this release, fixing those places where your code depends on
+ the old, buggy behavior.
+-- ********** There is a severe performance bug in the new matcher, causing it
+ ** NOTE ** to run exponentially slowly in certain cases. Fortunately, these
+ ********** cases are usually easy to work around. If you get an `out of
+ seconds' traceback inside a call to `match()' or `rmatch()' (which will be
+ accompanied by an error message in the log giving the pattern in use), you
+ should probably check first for an instance of this problem.
+ The problem concerns starred or plussed sub-patterns inside a starred or
+ plussed parenthesized pattern. For example, here is a perfectly reasonable
+ pattern matching MOO strings:
+ "\"%([^\"\\]+%|\\.%)*\""
+ (It matches double quotes around a sequence of either (a) a cluster of
+ characters that don't require escaping, or (b) a single escaped character.)
+ Note, however, that it contains a plussed sub-pattern inside of starred
+ parentheses, precisely the bad case for the new matcher. Fortunately, this
+ pattern can be altered slightly, removing the `+', without changing the
+ meaning:
+ "\"%([^\"\\]%|\\.%)*\""
+ This pattern does not cause problems for the new matcher. I don't know of
+ any problematical patterns in LambdaCore, but there is at least one in
+ JHCore, in $code_utils:safe_eval, where it uses this pattern:
+ "^%([^\"()=]+%|\"%([^\\\"]*%|\\.%)*\"%)*$"
+ ^ ^ ^
+ There are three instances of the problem here, indicated by the up-arrows;
+ the first two of them can be removed without changing the meaning:
+ "^%([^\"()=]%|\"%([^\\\"]%|\\.%)*\"%)*$"
+ The third instance, in my testing, does not appear to cause any problems in
+ practice.
+ I am continuing my search for a better regexp implementation, but this one
+ has worked well enough for us in practice that I didn't feel it was worth
+ holding up the release for it.
+
+Version 1.7.9p1, 18 October 1995
+-- Fixed another place in the MOO's interface to the regexp code where it was
+ assuming 29 registers instead of 9. I think that's all of them, now... :-(
+
+Version 1.7.9p2, 28 October 1995
+-- Fixed a bug that could crash the server if #0:do_command suspended or
+ aborted.
+-- Fixed a minor storage leak in the emergency wizard mode.
+-- Switched over to yet another implementation of regular expressions, this one
+ extracted from the Python-1.3 release. It's much simpler than RX, and has
+ seen a lot more use, so it's much more likely to be stable. It does share
+ some of the same inherent limitations as the old regex package used in the
+ 1.7.8p4 release of the server (which it resembles pretty closely in many
+ ways), but I think I've tweaked the parameters in such a way as to render
+ those limitations invisible for the vast majority of MOO uses. This matcher
+ also doesn't have the exponential performance bug that existed in the one
+ used in 1.7.9p1.
+-- The match() and rmatch() functions now raise E_QUOTA if they run out of
+ memory during matching. Previously, they simply returned {}, just as if the
+ pattern had not matched.
+
+Version 1.8.0alpha1, 29 November 1995
+-- Much internal cleanup, especially surrounding the interface between the DB
+ module and the rest of the server.
+-- Added an optional argument to the built-in function callers(); if provided
+ and true, each element of the returned value will have a sixth element, the
+ currently executing line number of the corresponding verb.
+-- Added new facilities for raising and handling MOO errors, as described in
+ the next four points.
+-- Added built-in function `raise(CODE [, MSG [, VALUE]])' where CODE can be
+ any MOO value (*not* just one of type ERR), MSG defaults to `tostr(CODE)',
+ and VALUE defaults to 0. This raises CODE as an error, just like dividing
+ by zero raises E_DIV. If the error is not caught (by one of the other new
+ constructs described below), then MSG will appear on the first line of the
+ resulting traceback printed to the user. VALUE is accessible to an error
+ handler established by the new TRY-EXCEPT-ENDTRY construct; see below.
+-- Added new error-catching expression:
+ `EXPR ! CODES => EXPR_H'
+ NOTE: The open- and close-quotation marks in the previous line are really
+ part of the syntax!
+ In this new expression, EXPR and EXPR_H are arbitrary expressions. CODES
+ is either the new keyword "ANY" or a non-empty "argument list" of
+ expressions; just like normal argument lists, CODES can contain "@"-marked
+ expressions that evaluate to lists to be spliced into the resulting list of
+ error codes. The "=> EXPR_H" part is optional.
+ First, CODES is evaluated yielding a list of error codes that should be
+ caught if raised; if CODES is "ANY", then it is equivalent to the list of
+ all possible MOO values.
+ Next, EXPR is evaluated. If it evaluated normally, without raising an
+ error, then its value becomes the value of the entire error-catching
+ expression. If evaluating EXPR results in an error being raised, then call
+ it E. If E is in the list resulting from CODES, then E is considered
+ "caught" by this error-catching expression. In such a case, if EXPR_H was
+ given, it is evaluated to get the outcome of the entire error-catching
+ expression; if EXPR_H was omitted, then E is the value of the entire
+ expression. If E is *not* in the list resulting from CODES, then this
+ expression does not catch the error and it continues to be raised, possibly
+ to be caught by some piece of code either surrounding this expression or
+ higher up on the verb-call stack.
+ Here are some examples:
+ `x + 1 ! E_TYPE => 0'
+ Returns x + 1 if x is a number, returns 0 if x is not a number,
+ and raises E_VARNF if x doesn't have a value.
+ `x.y ! E_PROPNF, E_PERM => 17'
+ Returns x.y if that doesn't cause an error, 17 if x doesn't
+ have a "y" property or that property isn't readable, and raises
+ some other kind of error (like E_INVIND) if x.y does.
+ `1 / 0 ! ANY'
+ Returns E_DIV.
+-- Added new error-catching statement:
+ TRY
+ statements_0
+ EXCEPT id_1 (codes_1)
+ statements_1
+ EXCEPT id_2 (codes_2)
+ statements_2
+ ENDTRY
+ The IDs are optional, CODES has the same syntax as above, and there can be
+ anywhere from 1 to 255 EXCEPT clauses.
+ First, each CODES is evaluated yielding a list of error codes that should
+ be caught if raised; if any CODES is "ANY", then it is equivalent to the
+ list of all possible MOO values.
+ Next, STATEMENTS_0 is executed; if it doesn't raise an error, then that's
+ all that happens for the entire TRY statement. Otherwise, let E be the
+ error it raises. From top to bottom, E is searched for in the lists
+ resulting from the various CODES_i; if it isn't found in any of them, then
+ it continues to be raised, possibly to be caught by some piece of code
+ either surrounding this TRY statement or higher up on the verb-call stack.
+ If E is found first in CODES_i, then ID_i (if provided) assigned a value
+ containing information about the error being raised and STATEMENTS_i is
+ executed. The value assigned to ID_i list a list of four elements:
+ {CODE, MSG, VALUE, TRACEBACK}
+ where CODE is E, the error being raised, MSG and VALUE are as provided by
+ the code that raised the error (built-in errors, such as division by zero,
+ currently act as if the MSG and VALUE arguments to raise() were omitted),
+ and TRACEBACK is a list like that returned by callers(), including line
+ numbers. The TRACEBACK list contains entries for every verb from the one
+ that raised the error through the one containing this TRY statement.
+ Here is an example:
+ try
+ result = object:(command)(@arguments);
+ player:tell("=> ", toliteral(result));
+ except v (ANY)
+ tb = v[4];
+ if (length(tb) == 1)
+ player:tell("** Illegal command: ", v[2]);
+ else
+ top = tb[1];
+ tb[1..1] = {};
+ player:tell(top[1], ":", top[2], ", line ", top[6], ":",
+ v[2]);
+ for fr in (tb)
+ player:tell("... called from ", fr[1], ":", fr[2],
+ ", line ", fr[6]);
+ endfor
+ player:tell("(End of traceback)");
+ endif
+ endtry
+-- Added new error-cleanup statement:
+ TRY
+ statements_0
+ FINALLY
+ statements_1
+ ENDTRY
+ STATEMENTS_0 is executed; if it completes without raising an error or
+ returning from this verb, then STATEMENTS_1 is executed and that's
+ all that happens for the entire TRY statement. Otherwise, the process of
+ raising the error past this point or returning from this verb (as
+ appropriate) is interrupted and STATEMENTS_1 is executed. If STATEMENTS_1
+ itself completes without raising an error or returning from this verb, then
+ the interrupted raising or returning process is resumed. If STATEMENTS_1
+ does return or raise an error, then the interrupted raising or returning
+ process is simply forgotten in favor of the new one.
+ In short, this statement ensures that STATEMENTS_1 is executed after
+ control leaves STATEMENTS_0 for whatever reason; it can thus be used to make
+ sure that some piece of cleanup code is run even if STATEMENTS_0 doesn't
+ simply run normally to completion.
+ Here's an example:
+ try
+ start = time();
+ object:(command)(@arguments);
+ finally
+ end = time();
+ this:charge_user_for_seconds(player, end - start);
+ endtry
+-- Completely rewrote the MOO-code decompiler, restructuring it to be able to
+ cope with the new exception-handling constructs.
+-- Fixed bug in handling of EOF in emergency wizard mode; it is now treated as
+ equivalent to the `quit' command.
+-- Added internal interfaces allowing built-in function implementations (a) to
+ be notified when given file descriptors are readable/writable (see file
+ net_multi.h), (b) to resume tasks that they earlier caused to suspend (see
+ resume_task() in file tasks.h), and (c) to make it possible for their
+ suspended tasks to be listed by queued_tasks() and killed by kill_task().
+-- Added a new, essentially empty module `extensions.c', intended to be easily
+ replaced by users with a file of their own MOO extensions. The file also
+ contains some examples of extensions using all of the new internal
+ interfaces.
+-- Clarification to change made in 1.7.9alpha1: $server_options.max_stack_depth
+ can only be used to override DEFAULT_MAX_STACK_DEPTH if it *increases* the
+ value. This is good because (a) there probably aren't any good reasons to
+ want to lower the limit, and (b) you could get good and screwed if you set
+ it too low.
+-- Fixed match() and rmatch() to treat unrecognized escape sequences in
+ patterns as if the `%' were not there. Thus, `%X' in a pattern is now
+ equivalent to simply `X', for all X not explicitly mentioned in the
+ programmer's manual.
+
+Version 1.8.0alpha2, 11 December 1995
+-- Added an optional argument to `connected_players()'; if provided and true,
+ this now includes *all* active connections in the returned list, including
+ outbound or un-logged-in ones.
+-- Fixed checkpoint timing to test for overflow when adding $dump_interval to
+ the current time; when this happens, it pretends that $dump_interval = 3600.
+ This should fix the problem someone found where setting $dump_interval to
+ $maxint caused continuous checkpointing.
+-- Fixed `make clean' to remove a few more derived files that aren't part of
+ the distribution, including `Makefile' itself.
+-- Added two new DB-settable options:
+ $server_options.name_lookup_timeout
+ is the maximum number of seconds the server will wait for a response
+ to a hostname <--> IP address lookup before giving up.
+ $server_options.outbound_connect_timeout
+ is the maximum number of seconds that a server will hang waiting for
+ an outbound network connection attempt to succeed; after that, it
+ gives up.
+-- Added new built-in function `resume(TASK_ID [, VALUE])', where TASK_ID is
+ the task ID of an existing suspended task and VALUE defaults to zero. If
+ the programmer is a wizard or owns the specified task, the task is made
+ immediately runnable; when it runs, its call to `suspend()' will return
+ VALUE. Raises E_PERM if the programmer does not own the specified task and
+ E_INVARG if TASK_ID does not specify an existing suspended task.
+-- Made the argument to `suspend()' be optional; if omitted, the suspension is
+ effectively forever. Such eternally suspended tasks can only be made
+ runnable again with the new `resume()' function.
+-- Verbs that have not yet been programmed are now completely equivalent to
+ those with the empty program. In particular, they are no longer invisible
+ to normal verb-calls.
+-- Added new built-in function `task_stack(TASK_ID [, LINE_NUMBERS_TOO])',
+ where TASK_ID is the task ID of an existing suspended task and
+ LINE_NUMBERS_TOO defaults to false. Returns a value like that returned by
+ `callers()', but covering all of the stack frames of the specified task.
+-- It is now possible to make specific built-in properties readable only by
+ wizards. For a built-in property `foo', if $server_options.protect_foo
+ exists and has a true value, then any attempt by a non-wizard to read the
+ value of the `foo' property of any object will raise E_PERM. For example,
+ if $server_options.protect_location existed with the value 1, then the
+ `location' property of all objects would only be readable by wizards.
+-- Added OUTPUTPREFIX and OUTPUTSUFFIX as synonyms for the built-in PREFIX and
+ SUFFIX commands. I'm told that this would make the MOO compatible with some
+ other servers, allowing the same somewhat fancy clients to be used with
+ both.
+-- Added new built-in function `object_bytes(OBJECT)', where OBJECT is a valid
+ object and the result is the number of bytes of memory required to represent
+ that object and all of its verbs and properties. Raises E_PERM if called by
+ a non-wizard. [There's a surprising amount of difference between this very
+ accurate count and the one returned by the rough-guess :object_bytes() verb
+ I wrote long ago, which is still used as the basis of most byte-based quota
+ implementations. In particular, the vast majority of objects are, in fact,
+ smaller than the rough guess, sometimes by substantial amounts. For
+ example, the $player object in LambdaCore-1Oct94.db is farthest off, being
+ 43,260 bytes smaller than :object_bytes() guessed. The majority though,
+ including Mr. Spell, are within about 5,000 bytes of the guess, which isn't
+ too bad, eh? It would appear that the guesses were most wrong for verb
+ code, which isn't very surprising.]
+-- Added the syntax `$vname(...)' as an abbreviation for `#0:vname(...)', by
+ analogy to the `$' notation for properties on #0.
+-- Fixed a server-crashing bug in which the resumption value of suspended tasks
+ was not properly saved and restored after reboot. (Thanks to Jin-su Ahn for
+ pointing this out.)
+-- Fixed a bug in which forked tasks were written out incorrectly in checkpoint
+ files. (Thanks to Jin-su Ahn for noticing the problem.)
+-- Fixed a server-crashing bug in the new decompiler. (Thanks to Jin-su Ahn
+ for discovering the bug.)
+-- Made it possible to rename properties. If the third argument to
+ set_property_info(OBJECT, NAME, INFO) has a third element that is a string,
+ NEW, and the property NAME is defined on OBJECT (not merely inherited), and
+ NEW names neither a built-in property nor an existing property on OBJECT or
+ any of its ancestors or descendants, then "OBJECT.NAME" is renamed to
+ "OBJECT.NEW". This more-or-less parallels the way verb renaming works.
+-- Traceback lists, returned by callers() and task_stack() and also made
+ available in the try-except-endtry construct, now include entries for
+ built-in functions that have made MOO verb calls. Such entries have the
+ same form as normal entries, but the THIS, PROGRAMMER, and VERB-LOC fields
+ (elements 1, 3, and 4) are all set to #-1, the VERB-NAME field (element 2)
+ is set to the name of the built-in function, and the LINE-NUMBER field
+ (element 6), if any, is set to an index used internally to determine the
+ current state of the built-in function. The simplest safe test for a
+ built-in function entry is
+ (VERB-LOC == #-1 && PROGRAMMER == #-1 && VERB-NAME != "")
+ This combination was not previously possible. I leave it to the interested
+ reader to discover the previously possible circumstances under which any
+ weaker test might fail...
+
+Version 1.8.0alpha3, 27 December 1995
+-- Added new built-in function `call_function(FNAME, @ARGS)' where FNAME is a
+ string naming a built-in function to call and ARGS are the arguments to pass
+ to it. This allows you to compute the name of the function to call and, in
+ particular, allows you to write a call to a built-in function that may or
+ may not exist in the particular version of the server you're using.
+-- Changed MOO-code parser to just issue a warning during DB loading if a call
+ to an unknown built-in function is encountered; in such a case, the call is
+ replaced by an equivalent one to `call_function()'.
+-- Changed parser error and warning messages during DB loading to include
+ information about where the program in question is located.
+-- Fixed registration of `suspend()' to allow for it being called with no
+ arguments. (Thanks to Jin-su Ahn for discovering the bug.)
+-- Fixed a nasty memory leak in the new match() implementation. (Thanks to
+ Bill Garrett and Judy Anderson for tracking down a test case.)
+-- Fixed a bug in the line-number-finding code in the new decompiler. (Thanks
+ to Jin-su Ahn for noticing the problem.)
+-- Fixed a new bug preventing the loading of suspended tasks from the DB file.
+ (Thanks once again to Jin-su Ahn for pointing this out.)
+-- Fixed a new memory leak in the handling of errors raised from built-in
+ functions called from !d verbs.
+-- Fixed a new memory leak in command parsing.
+-- Fixed a new memory leak in built-in property reference.
+-- Fixed a serious memory leak in the new decompiler, triggered every time the
+ server needed to determine a line number in a verb program.
+-- Fixed a minor memory leak in the loading of forked tasks from the DB file.
+-- Fixed bug that would cause prepositions not to be properly matched in the
+ very first user command processed after booting. Beware C functions that
+ return pointers to static private data... :-/
+-- Added new built-in function `db_disk_size()', which returns the total size,
+ in bytes, of the most recent full representation of the database as one or
+ more disk files. It raises E_QUOTA if, for some reason, no such on-disk
+ representation is currently available.
+-- Changed network input handler to accept any of CR, LF, or CRLF as an end-of-
+ line indicator. This should improve robustness in the face of broken telnet
+ clients that assume local EOL conventions will work across the net.
+-- Added progress messages to the dumping/checkpointing process just like those
+ printed during the loading process.
+-- Added some extra configuration help for NeXT machines.
+-- Fixed server-crashing bug in case where a raised error is not handled by the
+ innermost handler. (Thanks to Nate Massey for finding the problem.)
+-- Added support for unambiguous reference to specific verbs in the built-in
+ functions `delete_verb()', `verb_info()', `set_verb_info()', `verb_args()',
+ `set_verb_args()', `verb_code()', `set_verb_code()', and `disassemble(). In
+ all cases, if the second argument (the verb name) is a positive number
+ instead of a string, it is treated as a 1-based index into the list of all
+ verbs defined on the given object.
+ ********** The old behavior, in which numeric strings in these argument
+ ** NOTE ** positions (and in the built-in `.program' command) were sometimes
+ ********** treated as 0-based indices into the list of defined verbs, has
+ now been disabled by default. To re-enable it, define the property
+ `$server_options.support_numeric_verbname_strings' and give it a true value.
+ Most existing databases will require this re-enabling, at least for a period
+ of transition; a certain amount of important old code critically depends on
+ this old behavior.
+ I chose to do things this way in order to emphasize the strong incentive
+ for sites to update the affected old code to the new, more reliable
+ mechanism; with the old mechanism enabled, it is possible to create
+ situations where some verbs (with numeric names) simply cannot be referred
+ to by name.
+-- Fixed bug where errors raised by built-in functions called from !d verbs are
+ not properly squelched if the built-in has itself previously called a MOO
+ verb. (Thanks to Brian Buchanan for noticing this bug.)
+
+Version 1.8.0alpha4, 30 December 1995
+-- Fixed foolish C-syntax bug in the delete_verb() built-in. (Thanks to Brian
+ Buchanan and Nate Massey for finding this.)
+-- Fixed configuration problem on FreeBSD and perhaps other platforms. (Thanks
+ again to Brian Buchanan for pointing this out.)
+-- Fixed compilation warning in net_bsd_tcp.c when OUTBOUND_NETWORK was
+ defined. (Thanks to Martian and others for reporting this.)
+-- Fixed nasty occasional memory smash in delete_property(). (Thanks to Kai
+ Storbeck for reporting it.)
+-- All DB-configurable messages (e.g., $server_options.timeout_msg, etc.) can
+ now be given as either a string or a list of strings; in the latter case,
+ each string will be printed on a separate line.
+-- New DB-configurable message $server_options.server_full_msg is printed to
+ any connection that arrives when the server really can't accept any more
+ connections; after printing the message, the temporarily-accepted connection
+ is immediately closed. The default for this message is as was formerly
+ hardwired into the server:
+ *** Sorry, but the server cannot accept any more connections right now.
+ *** Please try again later.
+-- The DB-configurable messages are no longer printed on outbound connections.
+-- Added a version of the MPL (multiple-port listening) extension first
+ implemented by Ivan Judson. This involves the addition of three new
+ built-in functions:
+ listen(OBJ, DESC [, PRINT_MESSAGES]) => CANON_DESC
+ Create a new point at which the server will listen for network
+ connections, just as it does normally. OBJ is the object whose
+ verbs :do_login_command, :do_command, :do_out_of_band_command,
+ :user_connected, :user_created, :user_reconnected,
+ :user_disconnected, and :user_client_disconnected will be called at
+ appropriate points, just like these verbs are called on #0 for
+ normal connections. (If a user reconnects and the user's old and
+ new connections are on two different listening points being handled
+ by different objects, then :user_client_disconnected is called for
+ the old connection and :user_connected for the new one.) DESC is a
+ network-configuration- specific parameter describing the listening
+ point. For the TCP configurations, DESC is a port number on which
+ to listen. If PRINT_MESSAGES is provided and true, then the
+ various DB- configurable messages will be printed on connections
+ received at the new listening point. CANON_DESC is a
+ `canonicalized' version of DESC, with any configuration-specific
+ defaulting or aliasing accounted for. For the TCP configurations,
+ CANON_DESC is equal to DESC unless DESC is zero, in which case
+ CANON_DESC is a port number assigned by the operating system. This
+ raises E_PERM if the programmer is not a wizard, E_INVARG if OBJ is
+ invalid or there is already a listening point described by DESC,
+ and E_QUOTA if some network-configuration-specific error occurred.
+ unlisten(DESC)
+ Stop listening for connections on the point described by DESC,
+ which should be a value returned by some successful call to
+ listen(). Raises E_PERM if the programmer is not a wizard and
+ E_INVARG if there does not exist a listener with that description.
+ listeners() => {{OBJ, DESC, PRINT_MESSAGES}, ...}
+ Returns a list of all existing listeners, including the default one
+ set up by the server at boot time.
+ I believe that this implementation is upward-compatible with the existing
+ uses of Ivan's package. As far as I know, the only differences are that his
+ package did not have the PRINT_MESSAGES argument to listen(), did not work
+ for the non-BSD/TCP networking configurations, did not call :do_command or
+ :do_out_of_band_command, did not distinguish the case of a user reconnecting
+ on a different listening point, and did not have the listeners() function.
+ Please note that there is now nothing special about the initial listening
+ point created by the server at boot time; you can use unlisten() on it just
+ as if it had been created by listen(). This can be useful; for example, you
+ might start up your server on some obscure port, say 12345, connect to it by
+ yourself for a while, and then open it up to normal users with the command
+ ;;unlisten(12345); listen(#0, 7777, 1)
+-- Changed the result of connection_name() to expose information about the
+ listening point on which the connection was accepted. For the TCP
+ networking configurations, the format is now:
+ "port 7777 from foo.bar.com, port 3456"
+ where 7777 is the server port on which the connection arrived and the rest
+ is as before.
+ ********** Before upgrading an existing LambdaCore-based MOO to use this
+ ** NOTE ** version of the server, you should modify the verb
+ ********** $string_utils:connection_hostname_bsd as follows:
+ @program $string_utils:connection_hostname_bsd
+ s = args[1];
+ m = match(s, "^.* %(from%|to%) %([^, ]+%)");
+ return m ? substitute("%2", m) | "";
+ .
+ This code should work compatibly with any version of the server since 1.6.0.
+-- Added a new kind of expression, allowed only within the indexing/subranging
+ brackets `[...]'; in this context, the expression `$' means the length of
+ the value being indexed or subranged. This allows, for example, expressions
+ like `x[2..$]' to get the `rest' of a list after the first element or
+ `x[random($)]' to get a random element of a list. You can also use this on
+ the left-hand side of an assignment; for example `x[$] = 7' sets the last
+ element of a list, and `x[$+1..$] = {y}' adds y onto the end of the list x.
+ My favorite abuse of the new syntax is `x[l = $]' as a substitute for the
+ verbose and now old-fashioned `l = length(x)'.
+-- Fixed longstanding bug in `client_sysv', the client for the SYSV/LOCAL
+ networking configuration; it never worked to specify a server connection
+ file on the command line!
+
+Version 1.8.0alpha5, 11 January 1996
+-- Removed test that caused commands from disconnected users to be discarded.
+ (Thanks to Nate Massey for asking the question that led to this.)
+-- If $do_command() returns false without suspending (signalling that the
+ server should carry on with its normal command parsing and dispatch), the
+ resulting input task will have the same task_id() value as the $do_command()
+ task did. (Thanks to Nate Massey for the suggestion.)
+-- Added configuration code to cope with the C function crypt() not existing;
+ in such a case, the crypt() built-in function simply returns the first
+ argument, unchanged. (Thanks again to Nate Massey for the suggestion.)
+-- Fixed bogus-memory freeing bug in add_verb(). (Thanks to Mark Blanchard for
+ reporting this and to Purify for finding it.)
+-- Fixed missing declaration of strlen() in parser.y. (Thanks again to Mark
+ Blanchard for the report.)
+-- Fixed bug where the wrong permissions are used by built-in functions who ask
+ after they've already called some MOO function. (Thanks to Brian Buchanan
+ for finding this.)
+-- Added new built-in function `buffered_output_length([CONN])', where CONN is
+ an active connection and the result is the number of bytes currently
+ buffered up for output. If CONN is not provided, this returns the maximum
+ number of bytes that will be buffered up for output on any connection.
+ (Thanks to Martian for the impetus for this and to Gustavo Glusman and Jaime
+ Prilusky for inspiration on the interface.)
+-- Made it easier for server code to get the result of a suspended task, by
+ adding an `result' argument to resume_from_previous_vm(). (Thanks to Jin-su
+ Ahn for pointing out this irregularity in the code.)
+-- Fixed problem with non-flushing notify() calls when the string to be output
+ is larger than MAX_QUEUED_OUTPUT; this always failed, even when there wasn't
+ any other output in the buffer. Now, if the buffer is currently empty, any
+ line of output, no matter how large, will be accepted.
+-- Added facility for doing fully-general binary I/O on connections. If you
+ call `set_connection_option(CONN, "binary", 1)', then both input from and
+ output to that connection can contain arbitrary bytes. On input, any byte
+ that isn't an ASCII printing character, the space character, or the TAB
+ character will be represented as the four-character substring "~NNN", where
+ NNN is the octal representation of the byte; the input character `~' is
+ represented by the two-character substring "~~". Call this representation
+ of bytes "binary strings". Input from a connection in binary mode is *not*
+ broken into lines at all; it is delivered to either the read() function or
+ the built-in command parser in whatever size chunks come back from the
+ operating system. For output to a connection in binary mode, the second
+ argument to `notify()' must be a binary string; if it is malformed, E_INVARG
+ is raised. Of course, the call `set_connection_option(CONN, "binary", 0)'
+ sets the connection back into the normal MOO I/O mode.
+-- For convenience in manipulating binary strings, two new built-in functions
+ are provided:
+ decode_binary(BIN_STRING [, FULLY]) => LIST
+ Returns a list of strings and/or numbers representing the bytes in
+ BIN_STRING in order. If FULLY is false or omitted, the list
+ contains a number only for each non-printing, non-space, non-tab
+ byte; all other characters are grouped into the longest possible
+ contiguous substrings. If FULLY is provided and true, the list
+ contains only numbers, one for each byte represented in BIN_STRING.
+ Raises E_INVARG if BIN_STRING is malformed.
+ Examples:
+ decode_binary("foo") => {"foo"}
+ decode_binary("~~foo") => {"~foo"}
+ decode_binary("foo~015~012") => {"foo", 13, 10}
+ decode_binary("foo~012bar~012baz")
+ => {"foo", 10, "bar", 10, "baz}
+ decode_binary("foo~015~012", 1) => {102, 111, 111, 13, 10}
+ encode_binary(@ARGS) => BIN_STRING
+ ARGS must be a list of numbers between 0 and 255, strings, and
+ lists of this same form. Translates each number and string in turn
+ into its binary string equivalent, returning the concatenation of
+ all these substrings into a single binary string.
+ Examples:
+ encode_binary("~foo") => "~~foo"
+ encode_binary({"foo", 13, 10}, {"bar", 13, 10})
+ => "foo~015~012bar~015~012"
+ encode_binary("foo", 13, 10, "bar", 13, 10)
+ => "foo~015~012bar~015~012"
+-- Fixed two places missed in a change made in 1.7.9alpha1;
+ $user_disconnected() is now called for un-logged-in connections that either
+ time out or are the target of a call to boot_player(). (Thanks to Brian
+ Buchanan for pointing out the discrepancy.)
+-- Fixed memory-smash bug in the renumber() built-in function. (Thanks again
+ to Brian Buchanan for reporting this and to Purify for making it easy to
+ find the problem.)
+
+Version 1.8.0alpha6, 16 January 1996
+-- Replaced panic with raising E_TYPE in the case where `x[$]' is applied to a
+ non-string, non-list `x'. (Thanks to Kipp the Kid for reporting this.)
+-- Fixed (yet another) bug in the stack-unwinding code; this one stuck the
+ return information for each of a built-in's second and subsequent verb calls
+ on successively higher stack frames, causing all manner of havoc, the least
+ of which was somewhat surprising results from the (correct!) callers()
+ built-in. (Thanks once again to Brian Buchanan for reporting this bug; this
+ marks the *third* bug in stack-unwinding that Brian has uncovered!)
+-- Changed the rules for binary strings slightly so that (a) TAB is no longer
+ treated as a normal, printing character (i.e., it gets converted to `~011'
+ on input in binary mode), and (b) `~' is treated just like all of the
+ non-printing characters (i.e., it gets converted to `~176' on input in
+ binary mode and the sequence `~~' is illegal in binary strings). Change (a)
+ removes one more source of TAB characters from the MOO (which can only be a
+ good thing), and change (b) both simplifies the specification and makes it
+ easier to compute the true length of a binary string (i.e., it's the length
+ of the string itself minus three times the number of tildes). (Thanks to
+ James Deikun for these suggestions.)
+-- Fixed uninitialized-variable bug in the `program' command in emergency
+ wizard mode. (Thanks to Kipp the Kid for reporting it.)
+-- Fixed potential memory smash bug if #100 (or #200 or #400 or #800 or ...)
+ was a recycled object in the DB file being loaded. (Thanks to Dave Van
+ Buren for sending me a DB file that tickled the bug and to Purify for
+ pinpointing it.)
+-- Fixed bug that caused *all* settings of the .wizard property to be logged,
+ instead of just those that set it to a true value. (Thanks again to Dave
+ Van Buren for reporting this.)
+-- Fixed bug accuracy of the top line number in tracebacks resulting from the
+ setting of a wizard bit. (Thanks one last time to DVB for sending me a
+ traceback that contained such an error.)
+-- Added new built-in function `function_info([NAME])' which returns
+ descriptions of built-in functions available on the server. If NAME is
+ provided, only the description of the function with that name is returned;
+ if NAME is omitted, a list of descriptions is returned, one for each
+ function available on the server. Each description is a list of the
+ following form:
+ {NAME, MIN_ARGS, MAX_ARGS, TYPES}
+ NAME is the name of the built-in function, MIN_ARGS is the minimum number of
+ arguments that must be provided to the function, MAX_ARGS is the maximum
+ number of arguments that can be provided to the function or -1 if there is
+ no maximum, and TYPES is a list of MAX_ARGS numbers (or MIN_ARGS if MAX_ARGS
+ is -1), each of which represents the type of argument required in the
+ corresponding position. Each type number is as would be returned from the
+ typeof() built-in function except that -1 indicates that any type of value
+ is acceptable. For example, here are several entries from the list:
+ {"listdelete", 2, 2, {4, 0}}
+ {"suspend", 0, 1, {0}}
+ {"server_log", 1, 2, {2, -1}}
+ {"tostr", 0, -1, {}}
+ `listdelete()' takes exactly 2 arguments, of which the first must be a list
+ (LIST == 4) and the second must be a number (NUM == 0). `suspend()' has one
+ optional argument that, if provided, must be a number. `server_log()' has
+ one required argument that must be a string (STR == 2) and one optional
+ argument that, if provided, may be of any type. `tostr()' takes any number
+ of arguments and it can't be determined from this description which argument
+ types would be acceptable in which positions.
+ It should be noted that, in a feat of synchronicity, this new built-in
+ brings the total number supported in the official server release to exactly
+ 100! (Thanks to some-user-whose-message-I've-lost [feel free to mail me
+ again so that I can patch this thank-you note] for suggesting such a
+ built-in function.)
+-- Fixed the definitions of the built-in functions match(), rmatch(), index(),
+ rindex(), and strsub() to accept any type of value for their final,
+ `case-matters' flag argument. Also fixed `verb_code()' to allow any type of
+ value for either of its final two flag arguments.
+-- Added `scattering assignment' expression, allowing the elements of a list to
+ be spread among multiple variables simultaneously; this could be used, for
+ example, to get at the arguments to a verb in a more convenient form than
+ the list `args'.
+ A scattering assignment expression looks like this:
+ { TARGETS } = EXPR
+ where TARGETS is a comma-separated list of places to store elements of the
+ list that results from evaluating EXPR. A target has one of the following
+ forms:
+ VARIABLE
+ This is the simplest target, just a simple variable; the list
+ element in the corresponding position is assigned to the variable.
+ I call this a `required' target, since the assignment is required
+ to put one of the list elements into the variable.
+ ? VARIABLE
+ I call this an `optional' target, since it doesn't always get
+ assigned an element. If there are any list elements left over
+ after all of the required targets (and all of the other optionals
+ to the left of this one) have been accounted for, then this
+ variable is treated like a required one and the list element in the
+ corresponding position is assigned to the variable. If there
+ aren't enough elements to assign one to this target, then no
+ assignment is made to this variable, leaving it with whatever its
+ previous value was.
+ ? VARIABLE = EXPR
+ This is also an optional target, but if there aren't enough list
+ elements available to assign one to this target, the result of
+ evaluating EXPR is assigned to it instead. Thus, EXPR is a kind of
+ `default value' for the variable. The default value expressions
+ are evaluated and assigned working from left to right *after* all
+ of the other assignments have been performed.
+ @ VARIABLE
+ Analogously to MOO argument lists, this variable is assigned a list
+ of all of the `leftover' list elements in this part of the list
+ after all of the other targets have been filled in. It will be
+ assigned the empty list, if there aren't any elements left over. I
+ call this a `rest' target, since it gets the rest of the elements.
+ There may be at most one rest target in TARGETS.
+ If there aren't enough list elements to fill all of the required targets, or
+ if there are more than enough to fill all of the required and optional
+ targets but there isn't a rest target to take the leftover ones, then E_ARGS
+ is raised.
+ Here are some examples of how this works. Assume first that the verb
+ `me:foo()' contains the following code:
+ b = c = e = 17;
+ {a, ?b, ?c = 8, @d, ?e = 9, f} = args;
+ return {a, b, c, d, e, f};
+ Then the following calls return the given values:
+ `me:foo(1) ! ANY' => E_ARGS
+ me:foo(1, 2) => {1, 17, 8, {}, 9, 2}
+ me:foo(1, 2, 3) => {1, 2, 8, {}, 9, 3}
+ me:foo(1, 2, 3, 4) => {1, 2, 3, {}, 9, 4}
+ me:foo(1, 2, 3, 4, 5) => {1, 2, 3, {}, 4, 5}
+ me:foo(1, 2, 3, 4, 5, 6) => {1, 2, 3, {4}, 5, 6}
+ me:foo(1, 2, 3, 4, 5, 6, 7) => {1, 2, 3, {4, 5}, 6, 7}
+ me:foo(1, 2, 3, 4, 5, 6, 7, 8) => {1, 2, 3, {4, 5, 6}, 7, 8}
+ Finally MOO has a convenient mechanism for naming verb arguments, checking
+ for there being exactly the right number of arguments, handling optional and
+ `rest' arguments, etc. I intend to start every new MOO verb of mine with a
+ scattering assignment of `args', and I encourage other MOO programmers to do
+ the same.
+
+Version 1.8.0beta1, 7 February 1996
+-- Fixed bug where scattering assignment was not checking for the right-hand
+ side value being a list, and a severe bug where errors in a scattering
+ assignment in a !d verb caused the interpreter to execute many of the
+ operands of the EOP_SCATTER instruction as if they were opcodes. (Thanks to
+ Kipp the Kid for finding this problem.)
+-- Changed several routines to panic the server instead of simply logging an
+ error message; these were places where such an error indicated a memory
+ smash or some other very serious error had occurred; it made no sense to try
+ to press on with normal operations. This eliminates the "Impossible var
+ type" log messages sometimes seen from FREE_VAR, VAR_REF, and VAR_DUP.
+-- Added support for in-DB handling of all tracebacks, of which there are two
+ kinds: unhandled errors and tasks that have timed out.
+ If an error is raised and not caught, then the verb-call
+ #0:handle_uncaught_error(CODE, MSG, VALUE, TRACEBACK, FORMATTED)
+ is made, where CODE, MSG, VALUE, and TRACEBACK are the values that would
+ have been passed to a `try-except-endtry' handler for the error and
+ FORMATTED is a list of strings being the lines of traceback output that
+ will be printed to the player.
+ If a task runs out of ticks or seconds, then the verb-call
+ #0:handle_task_timeout(RESOURCE, TRACEBACK, FORMATTED)
+ is made, where RESOURCE is the appropriate one of the strings "ticks" or
+ "seconds", and TRACEBACK and FORMATTED are as above.
+ In both situations, the indicated verb call is made with the same
+ task_id() as the task that caused the traceback. If the handler verb call
+ either suspends or returns a true value, then that code is considered to
+ have handled the traceback and no further processing will be done by the
+ server. On the other hand, if the appropriate handler verb does not exist,
+ or returns a false value without suspending, or itself causes a traceback,
+ the original traceback (i.e., FORMATTED) will be printed to the player as in
+ earlier versions of the server.
+ Note that, if the handler verb-call itself causes a traceback, no
+ `nested' handler call is made; its traceback is simply printed to the player
+ without further processing. This prevents what might otherwise be quite a
+ nasty vicious cycle.
+ (Thanks to ThwartedEfforts for suggesting such a feature.)
+-- Added a way to flush all pending input on a given connection, mostly for use
+ by users who change their minds about having typed something and can react
+ before the server has processed it. Each connection may have a defined
+ `flush command'; if a raw line of input is equal to that connection's flush
+ command, then all pending input on the connection is flushed and a message
+ is printed back to the connection describing what happened. By default,
+ each connection's flush command is `.flush'; you can change this default by
+ setting $server_options.default_flush_command either to a non-empty string
+ (the new default) or something else (a default of `no defined flush
+ command'). On any given connection, you can redefine the flush command with
+ set_connection_option(CONN, "flush-command", VALUE)
+ Again, if VALUE is a non-empty string, it becomes the new flush command for
+ CONN; otherwise, CONN is set to have no defined flush command.
+ ********** This could confuse things for certain kinds of unusual server
+ ** NOTE ** connections, such as outbound ones or ones to non-MOO servers
+ ********** running in the database (e.g., HTTP servers). You may want to
+ set $server_options.default_flush_command to the empty string (to disable
+ flush commands by default) and use set_connection_option() to change this
+ just for appropriate connections (e.g., in #0:do_login_command).
+ (Thanks to Kent Pitman and others for help in designing this feature.)
+-- Added the `connection_options(CONN)' built-in function, which returns a list
+ of {NAME, VALUE} pairs describing the current settings of all of the allowed
+ options for the connection CONN. (Thanks to Brian Buchanan for suggesting
+ this.)
+-- Added `list' and `disassemble' commands to emergency wizard mode. (Thanks
+ to H. Peter Anvin for writing the first versions of these.)
+-- Added a new `named' form of the `while' loop:
+ WHILE id (expression)
+ statements
+ ENDWHILE
+ This behaves exactly like the statement
+ WHILE (id = (expression))
+ statements
+ ENDWHILE
+ This was added solely to provide a way to give a name to a `while' loop, for
+ use in the new `break' and `continue' statements, described below.
+-- Added new MOO statements `break' and `continue', similar to the ones in C or
+ Java. The syntax is
+ BREAK [id];
+ CONTINUE [id];
+ A `break' statement causes your program to exit an enclosing `for' or
+ `while' loop; a `continue' statement causes your program to skip ahead to
+ the begining of the next iteration of an enclosing loop. If provided, the
+ ID in the `break' or `continue' statement specifies which enclosing loop is
+ meant; ID should be the variable name appearing directly after the `for' or
+ `while' keyword in the desired loop. If no ID is provided, the innermost
+ enclosing loop is indicated. If a `break' or `continue' statement causes
+ control to leave the main body of a `try - finally - entry' statement, the
+ `finally' part will be executed first, just as with a `return' statement.
+ Here's an example:
+ x = 0;
+ for i in [1..5]
+ notify(player, "top");
+ try
+ if (!x)
+ x = 1;
+ notify(player, "continuing");
+ continue;
+ endif
+ x = x + 1;
+ finally
+ notify(player, "finally");
+ endtry
+ notify(player, "after");
+ if (x > 1)
+ break;
+ endif
+ endfor
+ notify(player, "done");
+ This verb produces the following output:
+ top
+ continuing
+ finally
+ top
+ finally
+ after
+ done
+ I don't claim that this is a useful verb, mind you, but it does illustrate
+ all of the possible interactions.
+-- By popular demand, I added the new built-in function
+ force_input(CONN, LINE [, AT_FRONT])
+ which inserts the string LINE as an input task in the queue for the
+ connection CONN, just as if it had arrived as input over the network. If
+ AT_FRONT is provided and true, then the new line of input is put at the
+ front of CONN's queue, so that it will be the very next line of input
+ processed even if there is already some other input in that queue.
+-- Fixed a bug whereby the very most common case of resuming a suspended task
+ with a new value failed to work. (Thanks to Brian Buchanan for reporting
+ this.)
+-- Changed the server to make a log entry whenever the value of a wizard bit
+ changes (as opposed to just when it goes from false to true). (Thanks to
+ Marc <marc@got.net> for this very sensible suggestion.)
+-- Fixed a bug where the server thought it sometimes advisable to parenthesize
+ the single-character `$' expression...
+-- Fixed stack-overflow memory-smash bug that could occur if, in a !d verb, the
+ `$' expression got a type error. (Thanks to Kipp the Kid for reporting
+ this.)
+-- Fixed a typo in the registration of the functions decode_binary() and
+ encode_binary(). (Thanks to Richard Connamacher and H. Peter Anvin for
+ finding this.)
+-- Added floating-point numbers as a new MOO value type; this involves a number
+ of changes to the behavior of existing MOO primitives, described in the
+ following several items. The representation of these values is in the local
+ C compiler's type `double', which is IEEE double precision on almost all
+ modern systems. IEEE infinite and NaN values are not allowed in MOO; the
+ new error code E_FLOAT is raised whenever one of these values would
+ otherwise be computed.
+ (Enormous thanks go to H. Peter Anvin, without whose great
+ efforts, understanding, and persistence these floating-point
+ facilities would not have made it into any release of the
+ server on my watch. I have not taken his patches without
+ modification, but nearly every change I made in adding these
+ features was patterned closely on what he had done. Of course,
+ any bugs that still remain in the server are solely my
+ responsibility and should not be taken to reflect badly on HPA
+ in any way.)
+-- MOO numeric literals now have the following syntax:
+ digit+ [. digit+] [{e | E} [+ | -] digit+]
+ The number is represented in floating-point if and only if either a decimal
+ point or a scientific-notation marker (`e' or `E') appears in the
+ literal.
+-- Added new built-in variables `INT' (with the same meaning as the old `NUM'
+ variable) and `FLOAT' (the result of typeof() applied to a floating-point
+ number).
+-- Floating-point numbers not equal to 0.0 are treated as `true' in MOO
+ conditionals.
+-- Both tostr() and toliteral() display floating-point numbers in the fullest
+ available precision, with 15 decimal digits on most machines.
+-- The new built-in function `floatstr(FLOAT, PRECISION [, USE_SCI_NOTATION])'
+ can be used to get more control over the conversion of floating-point
+ numbers to strings. In this function, FLOAT is a floating-point number and
+ PRECISION is the number of digits to appear to the right of the decimal
+ point (at most the maximum available precision, 15 digits on most machines).
+ If USE_SCI_NOTATION is false or not provided, the result is a string in the
+ form "MMMMMMM.DDDDDD", preceded by a minus sign if FLOAT is negative. If
+ USE_SCI_NOTATION is provided and true, the result is a string in the form
+ "M.DDDDDDe+EEE", again preceded by a minus sign if FLOAT is negative.
+-- The following operators now work in the obvious way (analogously to the
+ integer case) if X and Y are both floating-point numbers:
+ -X
+ X + Y X - Y X * Y X / Y X % Y
+ X == Y X != Y
+ X < Y X <= Y X > Y X >= Y
+ If one of X or Y is an integer and the other is a floating-point number,
+ then most of these operators raise E_TYPE; there are no automatic coercions
+ of integers to floating-point numbers. The expression (X == Y) is always
+ false and (X != Y) always true if X and Y do not have the same type.
+ (This is the most major place where I decided to depart from
+ HPA's patches; I was persuaded by the discussion on the
+ MOO-Cows list that the potential dangers posed by automatic
+ coercions in MOO's ubiquitously persistent world outweighed
+ their added convenience in some kinds of programs. It's my
+ guess that this decision will generate more dialog on the list,
+ and I welcome the input; it's always possible to extend the
+ server upward-compatibly later to allow such coercions.)
+-- The following operations all raise E_TYPE if either X or Y is a
+ floating-point number:
+ Z[X] Z[X] = E Z[X..Y] Z[X..Y] = E
+ List and string indices must be integers.
+-- Added built-in functions `toint()' (a synonym for `tonum()') and
+ `tofloat()'. The former can be used to convert a floating-point number to
+ an integer by truncation toward zero. The latter can be used to convert an
+ integer, a floating-point number, an object number, a string containing a
+ floating-point literal, or an error value into a floating-point number.
+-- The functions `min()', `max()', and `abs()' now work analogously on
+ floating-point numbers. If `min()' or `max()' are passed some integers and
+ some floating-point numbers in the same call, they raise E_TYPE.
+-- ************ The function `sqrt()' no longer accepts integer arguments; its
+ *** NOTE *** argument must now be a floating-point number and its result
+ ************ will always be such a number. The old and nearly useless
+ behavior of a call to `sqrt(X)' can be simulated with the new expression
+ `toint(sqrt(tofloat(x)))'. (I did this because it made no sense for the
+ various new math functions, like `sin()' and `exp()', to map integer
+ arguments to integer results and it seemed important to keep all of the math
+ functions consistent. It's my guess that there's very little existing code
+ that uses the old `sqrt()' function, so that this will not represent much of
+ an upgrading burden; I'm sure you'll let me know if I'm wrong...)
+-- Added a new expression type `X ^ Y', which returns X raised to the power of
+ Y. If X is an integer, then Y must be an integer as well. If X is a
+ floating-point number, then Y may be either integer or floating-point.
+ (Yes, maybe this is inconsistent with the complete lack of
+ coercions described above; feel free to try to argue me around
+ to a position you think is better.)
+-- Added the following new functions:
+ sin(X) sine of X
+ cos(X) cosine of X
+ tan(X) tangent of X
+ asin(X) arc-sine (inverse sine) of X in range [-pi/2, pi/2],
+ for X in range [-1, 1]
+ asin(X) arc-cosine (inverse cosine) of X in range [0, pi], for
+ X in range [-1, 1]
+ atan(X [, Y]) arc-tangent (inverse tangent) of X in range
+ [-pi/2, pi/2] if Y is not provided, or of Y/X in range
+ [-pi, pi] is Y is provided
+ sinh(X) hyperbolic sine of X
+ cosh(X) hyperbolic cosine of X
+ tanh(X) hyperbolic tangent of X
+ exp(X) exponential function e^X
+ log(X) natural logarithm ln(X), for X > 0
+ log10(X) base 10 logarithm of X, for X > 0
+ ceil(X) smallest integer not less than X, as a floating-point
+ number
+ floor(X) largest integer not greater than X, as a floating-point
+ number
+ All of these functions take only floating-point arguments and return
+ floating-point results. They raise E_INVARG if their argument is out of
+ range or E_FLOAT if the result overflows. On underflow, they return zero.
+ [Incredibly, some systems also print an error message on standard error if
+ the argument is out of range; there isn't anything I can do to stop it, so
+ just ignore such messages in the log.]
+ (This ends the floating-point changes.)
+-- Changed the function `random()' to allow calls with no arguments; this is
+ effectively the same as passing in the largest MOO integer.
+-- Fixed a long-neglected loophole in tick-counting; the following constructs
+ all newly take one tick now:
+ -- exception-handling expression: `expr ! codes'
+ -- exception-handling statement: try ... except (expr) ... endtry
+ -- cleanup statement: try ... finally ... endtry
+ -- scattering assignment: {A, B, @C} = X
+-- Fixed a bug in the decompiler that could panic the server if a WHILE loop
+ was the first thing inside the ELSE part of an IF statement. (Thanks to Ron
+ Stanions for reporting this.)
+-- Added documentation of what's required in order to add a new MOO value type
+ to the server; see the new file AddingNewMOOTypes.txt.
+
+Version 1.8.0beta2, 10 February 1996
+-- Made the source of standard messages printed on connections dependent on the
+ object acting as listener for connection. Connections received on a
+ listening point handled by an object OID print messages taken from
+ properties on OID.server_options or, if that does not exist, then from
+ $server_options. Thus, for example, after doing `listen(#987, LP, 1)', a
+ connection that arrives on the listening point LP and then is disconnected
+ will have the text of #987.server_options.boot_msg printed to it; if #987
+ does not have a property named "server_options", then
+ $server_options.boot_msg will be used instead, just as before. (Thanks to
+ Michael Brundage for persistently reminding me to add this feature.)
+-- Fixed bug in clear_property(OID, PNAME) where it could crash the server if
+ the object OID defines the property PNAME. (Thanks to Don Schwarz for
+ reporting this.)
+-- Fixed bug in floating-point number parsing where literals with exponents but
+ no decimal point (e.g., `1E6') were treated as integers. (Thanks to
+ H. Peter Anvin for pointing this out to me.)
+-- Slightly liberalized parsing of floating-point numbers not to require any
+ digits before the decimal point if there are digits after it. Thus, `.02'
+ is now parsed identically to `0.02'. (Thanks to Kipp the Kid for suggesting
+ this.)
+-- Fixed bug where `kill_task(task_id())' could panic the server. (Thanks to
+ Brian Buchanan for reporting this.)
+-- Fixed handling of floating-point exceptions on systems that support the
+ SVID3 `matherr()' handler function. (Thanks to H. Peter Anvin for reminding
+ me to do this.)
+-- Added built-in function `trunc(X)' which returns the floating-point number
+ obtained by truncating the floating-point number X at the decimal point.
+ For X < 0.0, this is equivalent to `ceil(X)'; otherwise, it is equivalent to
+ `floor(X)'. (Thanks to HPA again for suggesting this.)
+
+Version 1.8.0beta3, 18 February 1996
+-- Once more liberalized parsing of floating-point numbers, this time not to
+ require any digits *after* the decimal point if there are digits *before*
+ it. Thus, `32.' is now parsed identically to `32.0' and `32.e1' is parsed
+ identically to `32.0e1'. (Thanks to Brian Buchanan for pointing out this
+ remaining inconsistency with the behavior of tofloat().)
+-- Added built-in function `value_hash(X)', which returns a 32-character
+ hexadecimal string equal to `MD5(toliteral(X))', where MD5 is a well-known
+ cryptographically secure hash function. In essence, if
+ `equals(value_hash(X), value_hash(Y))' then you can be pretty damned sure
+ that `equals(X, Y)', even if the two hashes were computed on different
+ machines. Also added `string_hash(X)', which just computes the MD5 hash of
+ the contents of the string X, and `binary_hash(X)', which does the same for
+ the binary string X.
+-- Fixed `toint(X)', where X is a string denoting a floating-point number, to
+ return the same as `toint(tofloat(X))'. (Thanks to Kai Storbeck for
+ suggesting this.)
+-- Fixed bug in `floatstr(X, PREC)' where PREC was allowed to be negative.
+ (Thanks to `slayer' for reporting this bug.)
+-- Changed parser to replace calls of the form `call_function("foo", ...)'
+ with simply `foo(...)' if `foo' is a known function. (Thanks to Don Schwarz
+ for suggesting this.)
+
+Version 1.8.0, 9 March 1996
+-- Verbs defined on #0 are no longer subject to the wiz-only permissions check
+ on built-in functions generated by defining $server_options.protect_FOO with
+ a true value. Thus, you can now write a `wrapper' for a built-in function
+ without having to re-implement all of the server's built-in permissions
+ checks for that function. (Thanks to ThwartedEfforts for bringing this idea
+ back to my attention.)
+-- If a built-in function FOO has been made wiz-only (by defining
+ $server_options.protect_FOO with a true value) and a call is made to that
+ function from a non-wiz verb not defined on #0 (that is, if the server is
+ about to raise E_PERM), the server first checks to see if the verb #0:bf_FOO
+ exists. If so, it calls it instead of raising E_PERM and returns or raises
+ whatever it returns or raises. (Thanks to yduJ and ThwartedEfforts for
+ putting this idea together.)
+-- Added the source code for `pgperf' to a subdirectory of the release `tar'
+ file. You need `pgperf' in order to recreate the keyword-lookup table used
+ in the MOO-code parser should you ever change the set of keywords accepted
+ by the parser. Check out the `pgperf/README' file for (slightly) more
+ information.
+-- Backed out the change in 1.8.0beta3 where the parser was replacing calls of
+ the form `call_function("foo", ...)' with simply `foo(...)' if `foo' was a
+ known function. While this change made sense for porting whole databases
+ between server versions, it made it harder to port single verbs or objects
+ from place to place. (Thanks to Richard Connamacher for being persistent
+ enough to finally make me see this point.)
+-- Added new built-in function `connection_option(CONN, OPTION)', which returns
+ the current setting of the option OPTION on the connection CONN. It raises
+ E_INVARG if OPTION is not a valid option for that connection. (Thanks to
+ Don Schwarz for pointing out how inconvenient it could be to have to use
+ `connection_options(CONN)' for this.)
+-- Changed the format of binary strings to have non-printing characters
+ represented in hexadecimal instead of octal, for improved concision and
+ consistency with the output of the `*_hash()' functions. (Thanks to
+ ThwartedEfforts for suggesting this.)
+-- Made optional the argument to `shutdown()'. (Thanks to Richard Godard for
+ suggesting this.)
+-- Moved the DEFAULT_PORT and DEFAULT_CONNECT_FILE options into `options.h'.
+ (Thanks to Richard Godard for suggesting this, too.)
+-- Fixed line numbers reported in load-time warnings about renaming old uses of
+ new keywords. (Thanks to Ron Stanions and others for noticing this bug.)
+
+Version 1.8.0p1, 11 March 1996
+-- Fixed bug in the transition of binary strings from octal to hex. (Thanks to
+ Adrian Irving-Beer for reporting this.)
+-- Fixed longer-standing bug in decode_binary() where bytes over 127 would be
+ decoded as negative numbers on some systems.
+
+Version 1.8.0p2, 18 March 1996
+-- Fixed off-by-one bug in set_verb_code() that failed to type-check the last
+ element of the CODE list. (Thanks to Kipp the Kid for reporting this.)
+-- Reordered printing of start-up messages and the registration of built-in
+ functions to allow function registrars to more aesthetically print out their
+ own such messages. (Thanks to Richard Godard for suggesting this.)
+-- Minor configuration change to improve chances of finding an ANSI C compiler
+ on SGI IRIX 5.2 systems. (Thanks to John Wilson for the suggestion here.)
+-- Changed the behavior of calls to built-in functions made wiz-only via
+ $server_options; for a call to a protected function FOO from an object other
+ than #0, if $bf_FOO() exists, it is always called instead of the built-in,
+ *even for wizards*. (Thanks to Roger Crew for helping me beat this horse
+ thoroughly dead.)
+-- Fixed conversions of floating-point numbers into strings. The default
+ conversion, used in tostr() and toliteral(), uses the exact number of
+ decimal digits of precision supported in the underlying representation
+ (i.e., DBL_DIG in ANSI C). The writing of the database file uses four more
+ digits to be certain of extracting all of the available information. The
+ floatstr() function allows specification of any non-negative number of
+ digits up to four more than DBL_DIG. (Thanks to Brian Buchanan for
+ reporting the problem and to H. Peter Anvin for suggesting the appropriate
+ fix.)
+-- Fixed task_stack() to test for the given task ID being valid (i.e., naming a
+ suspended task) *before* testing that the caller's permissions are
+ sufficient. (Thanks to Judy Anderson for reporting this.)
+-- The idle un-logged-in connection timeout can now be set in the database.
+ For a connection accepted by a listening object L, the server checks for the
+ property `connect_timeout' being defined on either L.server_options or, if
+ L.server_options is not defined, on $server_options. If the
+ `connect_timeout' property exists and is a positive integer, that is the
+ number of seconds to use for the timeout. If `connect_timeout' exists but
+ is not a positive integer, then no timeout is used. Finally, if
+ `connect_timeout' does not exist, the old default of 300 seconds is used as
+ the timeout. (Thanks to Richard Godard for prodding me into this.)
+-- Setting the .programmer or .wizard bit on an object no longer raises
+ E_INVARG if the object is not a player. (Thanks to Roger Crew for
+ suggesting this.)
+-- Fixed `;' command in emergency wizard mode so that such tasks now have
+ legitimate task IDs.
+
+Version 1.8.0p3, 7 April 1996
+-- Fixed panic when a line of input existed from a logged-in but invalid object
+ (as is made possible by force_input()).
+-- Fixed `usage' message to include the new `-e' flag.
+-- Fixed bug where it didn't work to rename a property changing only the case
+ of the letters. (Thanks to Brack for reporting this.)
+-- Exported `get_server_option()' to `server.h', for possible use by server
+ extensions. (Thanks to Richard Godard for suggesting this.)
+-- Added `-l LOGFILE' command-line option, to allow specifying the destination
+ for logging messages separately from redirecting the standard-error stream.
+ This is most useful when intending to `continue' from emergency wizard mode.
+ (Thanks to Seth Rich for suggesting this.)
+-- Moved test for a built-in function being `protected' and the possible call
+ to $bf_FOO() before the test for the correct number and type of arguments.
+ This allows a $bf_FOO() overriding verb to take different arguments from the
+ built-in it's overriding. (Thanks to Brian Buchanan for suggesting this.)
+-- Added built-in function `flush_input(CONN [, SHOW_MESSAGES])' that does the
+ same thing as the user CONN typing their defined `flush' command. That is,
+ it removes all pending lines of input from CONN's queue and, if
+ SHOW_MESSAGES is provided and true, prints a message to CONN listing the
+ flushed lines, if any. This makes it possible to flush an input queue from
+ within verb code. (Thanks to Seth Rich for pointing out this issue.)
+-- An un-logged-in connection can now only `log in' (via $do_login_command()
+ returning a valid player object) if the network connection is actually still
+ open. In particular, the following cases, which used to cause panics, now
+ have no effect aside from the obvious calls to $do_login_command():
+ 1) Something opens a connection, sends input that would log it in, and
+ then closes the connection before the server has had time to run the
+ $do_login_command() task that would log it in.
+ 2) Someone executes `force_input(CONN, LINE)' where CONN is negative
+ and LINE would log in an un-logged-in connection.
+ (Thanks to Dave Kormann for reporting this bug.)
+-- Changed the definition of value_bytes() to make it more intuitive to users.
+ Now, value_bytes() always returns a non-zero value, since *every* MOO value
+ requires *some* memory in order to be represented. (Thanks to several
+ members of the MOO-Cows mailing list for the discussion that led to this.)
+-- Fixed disassemble() to properly print the mnemonic for the PUSH_LABEL
+ instruction. (Thanks to Brian Buchanan for reporting this bug.)
+
+Version 1.8.0p4, 18 April 1996
+-- Fixed potential memory-smash bug in the parsing of a misplaced `$'
+ expression. (Thanks to Brack for reporting this.)
+-- Fixed odd choice of errors raised by chparent(A, B); it used to raise E_PERM
+ when A was not valid or B was neither valid nor equal to #-1. It now raises
+ E_INVARG in these cases.
+-- Fixed *really* nasty bug in the way the $bf_FOO() overrides for protected
+ built-in functions were implemented. The first (and least nasty!) effect
+ was a potential memory smash and/or server panic if you killed a task that
+ was in the middle of a call to some $bf_FOO(). (Thanks to slayer@kaiwan.com
+ for reporting this effect of the bug and thereby ruining the rest of my day,
+ spent tracking down and trying to cope with the *rest* of the effects.) The
+ more subtle and terrifying effect is given below.
+ ********** There was a serious bug in versions 1.8.0 through 1.8.0p3 of the
+ ** NOTE ** server that could cause a database written by such a server to be
+ ********** read back in incorrectly and, in some cases, *undetectably*,
+ causing one or more bad effects outlined below. One of the potential
+ effects, perhaps the most serious one, *is* detectable and fixable; release
+ 1.8.0p4 of the server detects and fixes this problem during loading. If it
+ discovers/fixes an instance of the problem, it also prints a warning message
+ into the log.
+ ----------------------------------------------------------------------------
+ NOTE PLEASE: As described below, most databases will NOT be at risk from ANY
+ of the potential problems. I only describe them in this much detail to make
+ sure that all POSSIBLE cases are disclosed.
+ ----------------------------------------------------------------------------
+ In a nutshell, the problem is that the server can write out a database
+ file that, on reloading, makes it look as if an overridden built-in function
+ (i.e., one that is made wiz-only by a $server_options.protect_FOO property
+ and then overridden by a $bf_FOO() verb) *made a verb call* to $bf_FOO()
+ instead of being *replaced* by that verb. This would be in the saved state
+ of some task that was suspended at the time the database file was written.
+ This means that, after the task resumes and the call to $bf_FOO() returns,
+ the built-in function implementation could be re-entered in a very confused
+ state. This confusion could have the following effects:
+ 1) A function that never actually calls a verb (i.e., almost any of the
+ current built-in functions) could mistake $bf_FOO()'s returned value
+ for an argument list and smash memory all over the server.
+ (Fortunately, this is the detectable and fixable case mentioned
+ above; therefore, this effect cannot happen under 1.8.0p4.)
+ 2) The eval() function could end up wrapping an extra {1, ...} around
+ the correct returned value. That is, instead of returning {F, V} as
+ it should, it would return {1, {F, V}} in this case.
+ 3) The create() function could return a different object number from
+ the correct one; in particular, it could return an invalid or even
+ negative object number.
+ 4) The recycle() function could recycle the wrong object, without
+ checking permissions and without calling that object's :recycle
+ verb, and/or leave the correct object unrecycled.
+ 5) The move() function could move the wrong object to the wrong
+ destination and/or leave the correct object where it was.
+ If you reboot your server under 1.8.0p4, I believe that effect (1) above is
+ completely prevented and that, in effects (4) and (5), the function in
+ question is extremely unlikely to operate on the wrong object. If you are
+ actually at risk from any of these effects (see below), then by far the most
+ likely cases are as follows:
+ 1) Not possible.
+ 2) The eval() function will have the full effect given above.
+ 3) The create() function will have the full effect given above.
+ 4) The correct object will not actually be recycled, even though its
+ :recycle verb will have been called.
+ 5) The correct object will not actually be moved, even though the
+ destination's :accept verb will have been called.
+ >>> NOT ALL DATABASES ARE AT RISK FROM THESE EFFECTS <<<
+ For a database to be at risk, all of the following must be true:
+ a) One or more of the functions eval(), create(), recycle(), or move()
+ must have been made wiz-only via the $server_options.protect_FOO
+ property, for the appropriate FOO.
+ b) Such a function must have been overridden by a $bf_FOO() verb.
+ c) It must have been possible for the code of the $bf_FOO() verb (or
+ any code it calls) to call suspend().
+ d) The database file must have been written to disk (i.e., either via a
+ checkpoint or a shutdown or a panic) during the time that (a) and
+ (b) were true and while the call to $bf_FOO() was suspended.
+ e) You must be restarting your server from the database mentioned in
+ point (d).
+ Even in a database that is at risk, it is only at risk for the effects of
+ the specific function(s) for which all of points (a) through (e) are true.
+ For example, if you have never protected and overridden the move() function,
+ then your database is not at risk for effect (5).
+ These facts imply that, if your server might be at risk but is still
+ running under 1.8.0p3 or earlier, you may be able to remove the risk before
+ shutting down, by making one or more of points (a) through (e) false. For
+ example, you might be able to stop overriding one of the functions in
+ question and then wait until there are no longer any suspended tasks inside
+ calls to the appropriate $bf_FOO() verb.
+ >>> WARNING: Do NOT attempt to kill such a suspended task in order to
+ remove the risk from your system; this would trigger the bug
+ mentioned at the top of this release note, quite possibly
+ panicking your server.
+ Naturally, I am dismayed at both the existence of this bug and at the fact
+ that I cannot guarantee even those not yet bitten by it that they can avoid
+ eventually losing. This is the first time I can recall this sort of thing
+ happening in the entire time LambdaMOO has existed. Just my luck, just as
+ I'm about to retire... :-(
+
+Version 1.8.0p5, 12 May 1996
+-- Fixed two memory leaks, one triggered every time `match()' was called with a
+ malformed pattern and the other every time `add_verb()' was called at all.
+ (Thanks to Gustavo Glusman for putting me onto the trail of the first one.)
+-- Fixed mis-initialization of the value of the "client-echo" connection
+ option. (Thanks to Wisquatuk for reporting this.)
+-- Fixed backward-compatibility problem in 1.8.0 where the semantically
+ erroneous but syntactically correct expression
+ -"a string"
+ stopped even compiling, let alone running. (Thanks to Seth Rich for
+ pointing this out.)
+-- Fixed another backward-compatibility problem in which the (again)
+ semantically erroneous but syntactically correct expression
+ 36789.foo
+ (an understandable typo for #6789.foo) stopped compiling when found in a
+ database made by a pre-1.8.0 server.
diff --git a/MOOCodeSequences.txt b/MOOCodeSequences.txt
new file mode 100644
index 0000000..7f325b2
--- /dev/null
+++ b/MOOCodeSequences.txt
@@ -0,0 +1,347 @@
+This file documents, in very terse fashion, the opcode sequences emitted by the
+MOO compiler and accepted by the MOO decompiler. It is an invaluable reference
+while reading the code in either `code_gen.c' or `decompile.c'.
+
+NOTE: Since MOO database files contain suspended tasks, and since the file
+ representation of those tasks includes both source code and a current PC
+ for each frame on the stack, IT IS CRITICALLY IMPORTANT that these code
+ sequences not change between releases of the server. Otherwise, when the
+ code for a frame was recompiled at server start-up, its associated saved
+ PC value might no longer be valid, leading to almost certain catastrophe.
+ If the database file format were at some point changed to contain the
+ bytecodes (and not the source code) for suspended task frames, then this
+ restriction could (at least one release later) be relaxed.
+
+stmt:
+ {[ELSE]IF ( expr ) stmts}+ [ELSE stmts] ENDIF
+
+ <expr> ; Once for each arm
+ IF / EIF next ;
+ <stmts> ;
+ JUMP done ;
+ next: ;
+
+ <stmts> ; If there's an ELSE part
+
+ done:
+
+ | FOR id IN ( expr ) stmts ENDFOR
+
+ <expr>
+ NUM 1
+ top:
+ FOR_LIST id done
+ <stmts>
+ JUMP top
+ done:
+
+ | FOR id IN [ expr1 .. expr2 ] stmts ENDFOR
+
+ <expr1>
+ <expr2>
+ top:
+ FOR_RANGE id done
+ <stmts>
+ JUMP top
+ done:
+
+ | WHILE [id] ( expr ) stmts ENDWHILE
+
+ top:
+ <expr>
+
+ WHILE done ; if there is no ID
+ WHILE_ID id done ; if there is an ID
+
+ <stmts>
+ JUMP top
+ done:
+
+ | FORK [id] ( expr ) stmts ENDFORK
+
+ <expr>
+ FORK / FORK_WITH_ID vector [id]
+
+ {vector: <stmts>}
+
+ | expr ;
+
+ <expr>
+ POP
+
+ | RETURN ;
+
+ RETURN0
+
+ | RETURN expr ;
+
+ <expr>
+ RETURN
+
+ | TRY stmts_b {EXCEPT [id_i] ( codes_i ) stmts_i}+ ENDTRY
+
+ <codes_1>
+ PUSH_LABEL handler_1
+ ...
+ <codes_N>
+ PUSH_LABEL handler_N
+ TRY_EXCEPT N
+ <stmts_b>
+ END_EXCEPT done
+ ...
+ handler_i:
+ PUT id_i ; if <id_i> is supplied
+ POP
+ <stmts_i>
+ JUMP done ; all but last handler
+ ...
+ done:
+
+ | TRY stmts_b FINALLY stmts_h ENDTRY
+
+ TRY_FINALLY handler
+ <stmts_b>
+ END_FINALLY
+ handler:
+ <stmts_h>
+ CONTINUE
+
+ | BREAK [id];
+ | CONTINUE [id];
+
+ EXIT <stack-level> <label> ; if there is no ID
+ EXIT_ID <stack-level> <label> ; if there is an ID
+ ;
+
+expr:
+ NUMBER
+ | STRING
+ | # NUMBER
+ | # - NUMBER
+ | ERROR
+
+ NUM n ; if NUMBER and IN_OPTIM_NUM_RANGE(n)
+
+ IMM v ; otherwise
+
+ | id
+
+ PUSH id
+
+ | expr1 && expr2
+ | expr1 || expr2
+
+ <expr1>
+ AND / OR done
+ <expr2>
+ done:
+
+ | - expr
+ | ! expr
+
+ <expr>
+ UNARY_MINUS / NOT
+
+ | $ id
+ | expr1 . id
+ | expr1 . ( expr2 )
+
+ <expr1>
+ <expr2>
+ GET_PROP
+
+ | expr1 == expr2
+ | expr1 != expr2
+ | expr1 < expr2
+ | expr1 <= expr2
+ | expr1 > expr2
+ | expr1 >= expr2
+ | expr1 IN expr2
+ | expr1 + expr2
+ | expr1 - expr2
+ | expr1 * expr2
+ | expr1 / expr2
+ | expr1 % expr2
+ | expr1 [ expr2 ]
+
+ <expr1>
+ <expr2>
+ EQ / NE / LT / LE / GT / GE / IN / ADD / MINUS / MULT / DIV
+ / MOD / REF
+
+ | expr1 [ expr2 .. expr3 ]
+
+ <expr1>
+ <expr2>
+ <expr3>
+ RANGE_REF
+
+ | { arglist }
+
+ <arglist>
+
+ | id ( arglist )
+
+ <arglist>
+ BI_FUNC_CALL fn
+
+ | expr1 : id ( arglist )
+ | expr1 : ( expr2 ) ( arglist )
+
+ <expr1>
+ <expr2>
+ <arglist>
+ CALL_VERB
+
+ | expr1 ? expr2 | expr3
+
+ <expr1>
+ IF_QUES else
+ <expr2>
+ JUMP done
+ else:
+ <expr3>
+ done:
+
+ | {id | expr_l1 . ( expr_l2 )}
+ {[ expr_i ]}*
+ {[ expr_r1 .. expr_r2 ]}? = expr_r
+
+ PUSH id ; if id and indexed/subranged
+
+ <expr_l1> ; if expr.expr
+ <expr_l2> ;
+ PUSH_GET_PROP ; if expr.expr and indexed/subranged
+
+ ... ; if indexed
+ <expr_i> ;
+ PUSH_REF ; if indexed/subranged further
+ ... ;
+
+ <expr_r1> ; if subranged
+ <expr_r2> ;
+
+ <expr_r>
+
+ PUT_TEMP ; if indexed/subranged
+
+ RANGESET ; if subranged
+
+ ... ; if indexed
+ INDEXSET ;
+ ... ;
+
+ PUT ; if id
+
+ PUT_PROP ; if expr.expr
+
+ POP ; if indexed/subranged
+ PUSH_TEMP ;
+
+ | { scatter } = expr_r
+
+ /* Example:
+ * {a, ?b, ?c = expr_c, @d, ?e = expr_e, f} = expr_r
+ */
+
+ <expr_r>
+ SCATTER 6, 2, 4: a/0, b/1, c/default_c, d/0, e/default_e,
+ ^ ^ ^ f/0, done
+ | | | ^
+ | | | |
+ | | | +-- list of id/label pairs + done label
+ | | +----- 1-index of `@' argument (or # of args)
+ | +-------- number of required arguments
+ +----------- number of arguments
+ default_c:
+ <expr_c>
+ PUT c
+ POP
+ default_e:
+ <expr_e>
+ PUT e
+ POP
+ done:
+
+ | ` expr ! codes [=> expr_d] '
+
+ <codes>
+ PUSH_LABEL handler
+ CATCH
+ <expr>
+ END_CATCH done
+ handler:
+ NUM 1 ; if <expr_d> is omitted
+ REF ;
+
+ POP ; if <expr_d> is supplied
+ <expr_d> ;
+ done:
+
+ ;
+
+codes:
+ ANY
+
+ NUM 0
+
+ | ne_arglist
+
+ <ne_arglist>
+
+arglist:
+ /* NOTHING */
+
+ MAKE_EMPTY_LIST
+
+ | ne_arglist
+
+ <ne_arglist>
+
+ ;
+
+ne_arglist:
+ expr
+
+ <expr>
+ MAKE_SINGLETON_LIST
+
+ | @ expr
+
+ <expr>
+ CHECK_LIST_FOR_SPLICE
+
+ | ne_arglist , expr
+
+ <ne_arglist>
+ <expr>
+ LIST_ADD_TAIL
+
+ | ne_arglist , @ expr
+
+ <ne_arglist>
+ <expr>
+ LIST_APPEND
+
+ ;
+
+
+# $Log$
+# Revision 1.1 1997/03/03 03:45:06 nop
+# Initial revision
+#
+# Revision 2.3 1996/03/10 01:25:45 pavel
+# Fixed a grammar problem. Release 1.8.0.
+#
+# Revision 2.2 1996/02/08 05:43:09 pavel
+# Added named WHILE loops and the BREAK and CONTINUE statements.
+# Release 1.8.0beta1.
+#
+# Revision 2.1 1996/01/16 07:15:25 pavel
+# Add support for scattering assignment. Release 1.8.0alpha6.
+#
+# Revision 2.0 1995/11/30 05:41:16 pavel
+# New baseline version, corresponding to release 1.8.0alpha1.
+#
+# Revision 1.1 1995/11/30 05:36:21 pavel
+# Initial revision
diff --git a/Makefile.in b/Makefile.in
new file mode 100644
index 0000000..1d2bbcc
--- /dev/null
+++ b/Makefile.in
@@ -0,0 +1,439 @@
+# Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+# Portions of this code were written by Stephen White, aka ghond.
+# Use and copying of this software and preparation of derivative works based
+# upon this software are permitted. Any distribution of this software or
+# derivative works must comply with all applicable United States export
+# control laws. This software is made available AS IS, and Xerox Corporation
+# makes no warranty about the software, its performance or its conformity to
+# any specification. Any person obtaining a copy of this software is requested
+# to send their name and post office or electronic mail address to:
+# Pavel Curtis
+# Xerox PARC
+# 3333 Coyote Hill Rd.
+# Palo Alto, CA 94304
+# Pavel@Xerox.Com
+
+CC = @CC@
+LIBRARIES = @LIBS@
+YACC = @YACC@
+
+CFLAGS = -O
+YFLAGS = -d
+COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) -c
+
+CSRCS = ast.c code_gen.c db_file.c db_io.c db_objects.c db_properties.c \
+ db_verbs.c decompile.c disassemble.c eval_env.c eval_vm.c \
+ exceptions.c execute.c extensions.c functions.c keywords.c list.c \
+ log.c malloc.c match.c md5.c name_lookup.c network.c net_mplex.c \
+ net_proto.c numbers.c objects.c parse_cmd.c pattern.c program.c \
+ property.c quota.c ref_count.c regexpr.c server.c storage.c streams.c \
+ sym_table.c tasks.c timers.c unparse.c utils.c verbs.c version.c
+
+OPT_NET_SRCS = net_single.c net_multi.c \
+ net_mp_selct.c net_mp_poll.c net_mp_fake.c \
+ net_bsd_tcp.c net_bsd_lcl.c net_sysv_tcp.c net_sysv_lcl.c
+
+OPT_NET_OBJS = $(OPT_NET_SRCS:.c=.o)
+
+OPT_CSRCS = gnu-malloc.c $(OPT_NET_SRCS)
+
+YSRCS = parser.y
+
+HDRS = ast.h bf_register.h code_gen.h db.h db_io.h db_private.h decompile.h \
+ disassemble.h eval_env.h eval_vm.h exceptions.h execute.h functions.h \
+ getpagesize.h keywords.h list.h log.h match.h md5.h name_lookup.h \
+ network.h net_mplex.h net_multi.h net_proto.h numbers.h opcode.h \
+ options.h parse_cmd.h parser.h pattern.h program.h quota.h random.h \
+ ref_count.h regexpr.h server.h storage.h streams.h structures.h \
+ sym_table.h tasks.h timers.h tokens.h unparse.h utils.h verbs.h \
+ version.h
+
+SYSHDRS = my-ctype.h my-fcntl.h my-in.h my-inet.h my-ioctl.h my-math.h \
+ my-poll.h my-signal.h my-socket.h my-stat.h my-stdarg.h my-stdio.h \
+ my-stdlib.h my-string.h my-stropts.h my-sys-time.h my-time.h \
+ my-tiuser.h my-types.h my-unistd.h my-wait.h
+
+CLIENT_SRCS = client_bsd.c client_sysv.c
+
+ALL_CSRCS = $(CSRCS) $(OPT_CSRCS) $(CLIENT_SRCS)
+
+SRCS = $(ALL_CSRCS) keywords.gperf $(YSRCS) $(HDRS) $(SYSHDRS)
+
+DISTFILES = $(SRCS) Makefile.in restart restart.sh \
+ Minimal.db README.Minimal \
+ ChangeLog.txt README configure.in configure config.h.in \
+ MOOCodeSequences.txt AddingNewMOOTypes.txt pgperf aclocal.m4
+
+COBJS = $(CSRCS:.c=.o)
+
+YOBJS = $(YSRCS:.y=.o)
+
+OBJS = $(COBJS) $(YOBJS)
+
+moo: $(OBJS)
+ $(CC) $(CFLAGS) $(OBJS) $(LIBRARIES) -o $@
+
+pure_moo: moo
+ purify $(CC) $(CFLAGS) $(OBJS) $(LIBRARIES) -o $@
+
+client_bsd: client_bsd.o
+ $(CC) $(CFLAGS) client_bsd.o $(LIBRARIES) -o $@
+
+client_sysv: client_sysv.o
+ $(CC) $(CFLAGS) client_sysv.o $(LIBRARIES) -o $@
+
+# This rule gets around some "make"s' desire to `derive' it from `restart.sh'.
+restart:
+ touch restart
+
+configure: configure.in
+ autoconf
+
+config.status: configure
+ env CC= YACC= ./configure --no-create
+
+Makefile: Makefile.in config.status
+ @echo "[ $@ : $? ]"
+ ./config.status
+
+config.h: config.h.in config.status
+ @echo "[ $@ : $? ]"
+ ./config.status
+
+y.tab.h: parser.o
+ touch y.tab.h
+
+keywords.c: keywords.gperf
+ pgperf -aCIptT -k1,3,$$ keywords.gperf \
+ | sed -e 's/#include <ctype.h>/#include "my-ctype.h"/' \
+ > keywords.c
+
+dist.tar.Z: $(DISTFILES)
+ rm -f version_number
+ grep "server_version = " version.c \
+ | sed -e 's/^.* = "//' -e 's/";//' > version_number
+ rm -fr MOO-`cat version_number`
+ mkdir MOO-`cat version_number`
+ chmod 755 MOO-`cat version_number`
+ cp -pr $(DISTFILES) MOO-`cat version_number`
+ chmod u+w MOO-`cat version_number`/*
+ tar cvf - MOO-`cat version_number` | compress -c > dist.tar.Z.NEW
+ mv dist.tar.Z.NEW dist.tar.Z
+ rm -f LambdaMOO-`cat version_number`.tar.Z
+ ln dist.tar.Z LambdaMOO-`cat version_number`.tar.Z
+ rm -fr MOO-`cat version_number` version_number
+
+tags:
+ etags -t $(SRCS)
+
+clean:
+ rm -f $(OBJS) core y.tab.c y.tab.h y.output makedep eddep config.h \
+ Makefile config.status
+
+depend: ${ALL_CSRCS}
+ rm -f eddep makedep
+ gcc -MM ${CFLAGS} ${ALL_CSRCS} | sed -e '/:$$/d' > makedep
+ echo '/^# DO NOT DELETE THIS LINE/+1,$$d' >eddep
+ echo '$$r makedep' >>eddep
+ echo 'w' >>eddep
+ cp Makefile.in Makefile.in.bak
+ ex - Makefile.in < eddep
+ rm -f eddep makedep
+
+###############################################################################
+# $Log$
+# Revision 1.1 1997/03/03 03:45:05 nop
+# Initial revision
+#
+# Revision 2.10 1996/04/19 01:28:40 pavel
+# Ran a new `make depend'. Release 1.8.0p4.
+#
+# Revision 2.9 1996/03/10 01:31:24 pavel
+# Added pgperf directory and aclocal.m4 file to the distribution.
+# Release 1.8.0.
+#
+# Revision 2.8 1996/02/18 23:18:02 pavel
+# Added md5.[ch]. Release 1.8.0beta3.
+#
+# Revision 2.7 1996/02/08 07:26:19 pavel
+# Updated copyright notice for 1996. Release 1.8.0beta1 (again).
+#
+# Revision 2.6 1996/02/08 05:46:27 pavel
+# Added numbers.h, AddingNewMOOTypes.txt. Updated dependencies. Made pure_moo
+# depend on moo. Release 1.8.0beta1.
+#
+# Revision 2.5 1996/01/16 07:35:55 pavel
+# Fixed dependencies. Release 1.8.0alpha6.
+#
+# Revision 2.4 1996/01/11 07:58:54 pavel
+# Fixed dependencies for parser.o. Release 1.8.0alpha5.
+#
+# Revision 2.3 1995/12/31 03:29:29 pavel
+# Fixed dependencies for parser.y and the networking modules.
+# Release 1.8.0alpha4.
+#
+# Revision 2.2 1995/12/28 00:32:32 pavel
+# Added verbs.h. Release 1.8.0alpha3.
+#
+# Revision 2.1 1995/12/11 08:07:47 pavel
+# Added a few more derived files to `make clean'. Release 1.8.0alpha2.
+#
+# Revision 2.0 1995/11/30 05:45:03 pavel
+# New baseline version, corresponding to release 1.8.0alpha1.
+#
+# Revision 1.26 1992/10/23 23:10:29 pavel
+# Updated dependency information for release 1.7.0 and added copyright notice.
+#
+# Revision 1.25 1992/10/23 19:12:08 pavel
+# A few last portability changes for release 1.7.0.
+#
+# Revision 1.24 1992/10/21 03:02:35 pavel
+# Converted to use new automatic configuration system.
+#
+# Revision 1.23 1992/10/17 20:14:17 pavel
+# Numerous changes in response to beta-testing portability problems.
+#
+# Revision 1.22 1992/10/06 01:32:41 pavel
+# Dyked out useless XNS network support.
+# Added sources for clients and a few new my-*.h files.
+# Fixed nasty bug in `make depend' code.
+#
+# Revision 1.21 1992/09/23 17:23:08 pavel
+# Added support for the whole family of networking protocol and multiplexing
+# wait implementations; it should now be easy to add new `optional' sources,
+# only used if certain combinations of preprocessor flags are set.
+#
+# Revision 1.20 1992/09/14 18:42:47 pjames
+# Updated.
+#
+# Revision 1.19 1992/09/14 17:46:39 pjames
+# Updated.
+#
+# Revision 1.18 1992/09/08 22:49:59 pjames
+# Took out bf_move.c
+#
+# Revision 1.17 1992/09/08 22:09:23 pjames
+# Updated for new bf_ filenames
+#
+# Revision 1.16 1992/09/03 23:49:13 pavel
+# Added support for multiple complete network implementations.
+#
+# Revision 1.15 1992/09/03 16:32:40 pjames
+# Updated.
+#
+# Revision 1.14 1992/08/31 23:43:57 pavel
+# Added math library to the LIBRARIES line for A/UX.
+#
+# Revision 1.13 1992/08/28 16:32:18 pjames
+# Added ref_count modules.
+#
+# Revision 1.12 1992/08/21 01:09:56 pavel
+# Minor fix.
+#
+# Revision 1.11 1992/08/21 00:56:39 pavel
+# Shortened the names of several files to fit within the 14-character limit
+# imposed on certain systems.
+# Moved the specification of all configuration options other than those
+# directly related to the compiler command line into config.h. Also
+# reorganized the ones that are left to make it easier for folks to see what's
+# necessary.
+#
+# Revision 1.10 1992/08/14 00:35:21 pavel
+# Fixed rule for dist.tar.Z to be idempotent.
+#
+# Revision 1.9 1992/08/13 23:35:42 pavel
+# Cleaned up CFLAGS organization.
+#
+# Revision 1.8 1992/08/13 23:24:34 pavel
+# Did a more recent `make depend'...
+#
+# Revision 1.7 1992/08/13 22:48:20 pjames
+# Added my-math.h
+#
+# Revision 1.6 1992/08/13 21:29:58 pjames
+# Added -DMOO to COMPILE.c
+#
+# Revision 1.5 1992/08/11 17:06:45 pjames
+# Updated with new source files, removed old source files, fixed dependencies.
+#
+# Revision 1.3 1992/07/27 18:30:21 pjames
+# Update what vector.o and vector.po depend on.
+###############################################################################
+
+# Have to do this one manually, since 'make depend' can't hack yacc files.
+parser.o: my-ctype.h my-math.h my-stdlib.h my-string.h \
+ ast.h code_gen.h config.h functions.h \
+ keywords.h list.h log.h numbers.h opcode.h parser.h program.h \
+ storage.h streams.h structures.h sym_table.h utils.h version.h
+
+# Must do these specially, since they depend upon C preprocessor options.
+network.o: net_single.o net_multi.o
+net_proto.o: net_bsd_tcp.o net_bsd_lcl.o net_sysv_tcp.o net_sysv_lcl.o
+net_mplex.o: net_mp_selct.o net_mp_poll.o net_mp_fake.o
+
+$(OPT_NET_OBJS):
+ touch $@
+
+# DO NOT DELETE THIS LINE -- 'make depend' replaces everything below it.
+ast.o : ast.c my-string.h config.h ast.h parser.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h sym_table.h list.h log.h storage.h utils.h \
+ execute.h db.h opcode.h parse_cmd.h
+code_gen.o : code_gen.c ast.h config.h parser.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h sym_table.h exceptions.h opcode.h storage.h \
+ utils.h execute.h db.h parse_cmd.h
+db_file.o : db_file.c my-stat.h config.h my-stdio.h my-types.h my-stdarg.h my-stdlib.h \
+ db.h program.h structures.h version.h db_io.h db_private.h exceptions.h list.h \
+ log.h options.h server.h network.h storage.h streams.h tasks.h execute.h opcode.h \
+ parse_cmd.h timers.h my-time.h
+db_io.o : db_io.c my-ctype.h config.h my-stdarg.h my-stdio.h my-types.h my-stdlib.h \
+ db_io.h program.h structures.h version.h db_private.h exceptions.h list.h log.h \
+ numbers.h parser.h storage.h streams.h unparse.h
+db_objects.o : db_objects.c config.h db.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h db_private.h exceptions.h list.h storage.h \
+ utils.h execute.h opcode.h parse_cmd.h
+db_properties.o : db_properties.c config.h db.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h db_private.h exceptions.h list.h storage.h \
+ utils.h execute.h opcode.h parse_cmd.h
+db_verbs.o : db_verbs.c my-stdlib.h config.h my-string.h db.h program.h structures.h \
+ my-stdio.h my-types.h my-stdarg.h version.h db_private.h exceptions.h parse_cmd.h \
+ storage.h utils.h execute.h opcode.h
+decompile.o : decompile.c ast.h config.h parser.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h sym_table.h decompile.h exceptions.h opcode.h \
+ storage.h utils.h execute.h db.h parse_cmd.h
+disassemble.o : disassemble.c my-stdio.h config.h my-types.h my-stdarg.h bf_register.h \
+ db.h program.h structures.h version.h functions.h execute.h opcode.h parse_cmd.h \
+ list.h storage.h streams.h unparse.h utils.h verbs.h
+eval_env.o : eval_env.c config.h eval_env.h structures.h my-stdio.h my-types.h \
+ my-stdarg.h version.h storage.h sym_table.h utils.h execute.h db.h program.h \
+ opcode.h parse_cmd.h
+eval_vm.o : eval_vm.c config.h db_io.h program.h structures.h my-stdio.h my-types.h \
+ my-stdarg.h version.h decompile.h ast.h parser.h sym_table.h eval_vm.h execute.h \
+ db.h opcode.h parse_cmd.h log.h options.h storage.h tasks.h
+exceptions.o : exceptions.c exceptions.h config.h
+execute.o : execute.c my-string.h config.h db.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h db_io.h decompile.h ast.h parser.h sym_table.h \
+ eval_env.h eval_vm.h execute.h opcode.h parse_cmd.h exceptions.h functions.h \
+ list.h log.h numbers.h options.h server.h network.h storage.h streams.h tasks.h \
+ timers.h my-time.h utils.h
+extensions.o : extensions.c bf_register.h functions.h my-stdio.h config.h my-types.h \
+ my-stdarg.h execute.h db.h program.h structures.h version.h opcode.h parse_cmd.h
+functions.o : functions.c my-stdarg.h config.h bf_register.h db_io.h program.h \
+ structures.h my-stdio.h my-types.h version.h functions.h execute.h db.h opcode.h \
+ parse_cmd.h list.h log.h server.h network.h options.h storage.h streams.h unparse.h \
+ utils.h
+keywords.o : keywords.c my-ctype.h config.h my-string.h keywords.h structures.h \
+ my-stdio.h my-types.h my-stdarg.h version.h tokens.h ast.h parser.h program.h \
+ sym_table.h y.tab.h utils.h execute.h db.h opcode.h parse_cmd.h
+list.o : list.c my-ctype.h config.h my-string.h bf_register.h exceptions.h functions.h \
+ my-stdio.h my-types.h my-stdarg.h execute.h db.h program.h structures.h version.h \
+ opcode.h parse_cmd.h list.h log.h md5.h options.h pattern.h random.h ref_count.h \
+ streams.h storage.h unparse.h utils.h
+log.o : log.c my-stdarg.h config.h my-stdio.h my-types.h my-string.h my-time.h \
+ bf_register.h functions.h execute.h db.h program.h structures.h version.h opcode.h \
+ parse_cmd.h log.h options.h storage.h streams.h utils.h
+malloc.o : malloc.c options.h config.h
+match.o : match.c my-stdlib.h config.h my-string.h db.h program.h structures.h \
+ my-stdio.h my-types.h my-stdarg.h version.h exceptions.h match.h parse_cmd.h \
+ storage.h unparse.h utils.h execute.h opcode.h
+md5.o : md5.c my-string.h config.h md5.h
+name_lookup.o : name_lookup.c options.h config.h my-signal.h my-stdlib.h my-unistd.h \
+ my-inet.h my-in.h my-types.h my-socket.h my-wait.h my-string.h log.h my-stdio.h \
+ my-stdarg.h structures.h server.h network.h storage.h timers.h my-time.h
+network.o : network.c options.h config.h net_multi.c my-ctype.h my-fcntl.h my-ioctl.h \
+ my-signal.h my-stdio.h my-types.h my-stdarg.h my-stdlib.h my-string.h my-unistd.h \
+ exceptions.h list.h structures.h log.h net_mplex.h net_multi.h net_proto.h network.h \
+ server.h streams.h storage.h timers.h my-time.h utils.h execute.h db.h program.h \
+ version.h opcode.h parse_cmd.h
+net_mplex.o : net_mplex.c options.h config.h net_mp_selct.c my-string.h my-sys-time.h \
+ my-types.h log.h my-stdio.h my-stdarg.h structures.h net_mplex.h
+net_proto.o : net_proto.c options.h config.h net_bsd_tcp.c my-inet.h my-in.h \
+ my-types.h my-socket.h my-stdlib.h my-string.h my-unistd.h list.h structures.h \
+ my-stdio.h my-stdarg.h log.h name_lookup.h net_proto.h server.h network.h streams.h \
+ timers.h my-time.h utils.h execute.h db.h program.h version.h opcode.h parse_cmd.h
+numbers.o : numbers.c my-math.h my-stdlib.h config.h my-string.h my-time.h my-types.h \
+ functions.h my-stdio.h my-stdarg.h execute.h db.h program.h structures.h version.h \
+ opcode.h parse_cmd.h log.h random.h storage.h utils.h
+objects.o : objects.c db.h config.h program.h structures.h my-stdio.h my-types.h \
+ my-stdarg.h version.h db_io.h exceptions.h execute.h opcode.h parse_cmd.h functions.h \
+ list.h numbers.h quota.h server.h network.h options.h storage.h utils.h
+parse_cmd.o : parse_cmd.c my-ctype.h config.h my-stdio.h my-types.h my-stdarg.h \
+ my-stdlib.h my-string.h my-time.h db.h program.h structures.h version.h list.h \
+ match.h parse_cmd.h storage.h utils.h execute.h opcode.h
+pattern.o : pattern.c my-ctype.h config.h my-stdlib.h my-string.h pattern.h \
+ regexpr.h storage.h structures.h my-stdio.h my-types.h my-stdarg.h streams.h
+program.o : program.c ast.h config.h parser.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h sym_table.h exceptions.h list.h storage.h utils.h \
+ execute.h db.h opcode.h parse_cmd.h
+property.o : property.c db.h config.h program.h structures.h my-stdio.h my-types.h \
+ my-stdarg.h version.h functions.h execute.h opcode.h parse_cmd.h list.h storage.h \
+ utils.h
+quota.o : quota.c config.h db.h program.h structures.h my-stdio.h my-types.h \
+ my-stdarg.h version.h quota.h
+ref_count.o : ref_count.c config.h exceptions.h ref_count.h storage.h structures.h \
+ my-stdio.h my-types.h my-stdarg.h
+regexpr.o : regexpr.c my-stdio.h config.h my-types.h my-stdarg.h regexpr.h my-stdlib.h \
+ my-string.h
+server.o : server.c my-types.h config.h my-signal.h my-stdarg.h my-stdio.h my-stdlib.h \
+ my-string.h my-unistd.h my-wait.h db.h program.h structures.h version.h db_io.h \
+ disassemble.h execute.h opcode.h parse_cmd.h functions.h list.h log.h network.h \
+ options.h server.h parser.h random.h storage.h streams.h tasks.h timers.h my-time.h \
+ unparse.h utils.h
+storage.o : storage.c my-stdlib.h config.h exceptions.h list.h structures.h \
+ my-stdio.h my-types.h my-stdarg.h options.h ref_count.h storage.h
+streams.o : streams.c my-stdarg.h config.h my-string.h my-stdio.h my-types.h \
+ log.h structures.h storage.h streams.h
+sym_table.o : sym_table.c my-stdio.h config.h my-types.h my-stdarg.h ast.h parser.h \
+ program.h structures.h version.h sym_table.h exceptions.h log.h storage.h utils.h \
+ execute.h db.h opcode.h parse_cmd.h
+tasks.o : tasks.c my-string.h config.h my-time.h my-types.h db.h program.h structures.h \
+ my-stdio.h my-stdarg.h version.h db_io.h decompile.h ast.h parser.h sym_table.h \
+ eval_env.h eval_vm.h execute.h opcode.h parse_cmd.h exceptions.h functions.h \
+ list.h log.h match.h options.h random.h server.h network.h storage.h streams.h \
+ tasks.h utils.h verbs.h
+timers.o : timers.c my-signal.h config.h my-stdlib.h my-sys-time.h options.h \
+ my-types.h my-time.h my-unistd.h timers.h
+unparse.o : unparse.c my-ctype.h config.h my-stdio.h my-types.h my-stdarg.h \
+ ast.h parser.h program.h structures.h version.h sym_table.h decompile.h exceptions.h \
+ functions.h execute.h db.h opcode.h parse_cmd.h keywords.h list.h log.h unparse.h \
+ storage.h streams.h utils.h
+utils.o : utils.c my-ctype.h config.h my-stdio.h my-types.h my-stdarg.h my-string.h \
+ db.h program.h structures.h version.h db_io.h exceptions.h list.h log.h match.h \
+ numbers.h ref_count.h server.h network.h options.h storage.h streams.h utils.h \
+ execute.h opcode.h parse_cmd.h
+verbs.o : verbs.c my-string.h config.h db.h program.h structures.h my-stdio.h \
+ my-types.h my-stdarg.h version.h exceptions.h execute.h opcode.h parse_cmd.h \
+ functions.h list.h log.h match.h parser.h server.h network.h options.h storage.h \
+ unparse.h utils.h verbs.h
+version.o : version.c config.h version.h
+gnu-malloc.o : gnu-malloc.c getpagesize.h
+net_single.o : net_single.c my-ctype.h config.h my-fcntl.h my-stdio.h my-types.h \
+ my-stdarg.h my-unistd.h log.h structures.h network.h options.h server.h streams.h \
+ utils.h execute.h db.h program.h version.h opcode.h parse_cmd.h
+net_multi.o : net_multi.c my-ctype.h config.h my-fcntl.h my-ioctl.h my-signal.h \
+ my-stdio.h my-types.h my-stdarg.h my-stdlib.h my-string.h my-unistd.h exceptions.h \
+ list.h structures.h log.h net_mplex.h net_multi.h net_proto.h options.h network.h \
+ server.h streams.h storage.h timers.h my-time.h utils.h execute.h db.h program.h \
+ version.h opcode.h parse_cmd.h
+net_mp_selct.o : net_mp_selct.c my-string.h config.h my-sys-time.h options.h \
+ my-types.h log.h my-stdio.h my-stdarg.h structures.h net_mplex.h
+net_mp_poll.o : net_mp_poll.c my-poll.h config.h log.h my-stdio.h my-types.h \
+ my-stdarg.h structures.h net_mplex.h storage.h
+net_bsd_tcp.o : net_bsd_tcp.c my-inet.h config.h my-in.h my-types.h my-socket.h \
+ my-stdlib.h my-string.h my-unistd.h list.h structures.h my-stdio.h my-stdarg.h \
+ log.h name_lookup.h net_proto.h options.h server.h network.h streams.h timers.h \
+ my-time.h utils.h execute.h db.h program.h version.h opcode.h parse_cmd.h
+net_bsd_lcl.o : net_bsd_lcl.c my-socket.h config.h my-types.h my-stdio.h my-stdarg.h \
+ my-string.h my-unistd.h log.h structures.h net_proto.h options.h storage.h utils.h \
+ execute.h db.h program.h version.h opcode.h parse_cmd.h
+net_sysv_tcp.o : net_sysv_tcp.c my-inet.h config.h my-in.h my-types.h my-fcntl.h \
+ my-ioctl.h my-socket.h my-stdlib.h my-string.h my-stropts.h my-tiuser.h my-unistd.h \
+ log.h my-stdio.h my-stdarg.h structures.h name_lookup.h net_proto.h options.h \
+ server.h network.h streams.h timers.h my-time.h
+net_sysv_lcl.o : net_sysv_lcl.c my-fcntl.h config.h my-stat.h my-stdio.h my-types.h \
+ my-stdarg.h my-stdlib.h my-unistd.h exceptions.h list.h structures.h log.h net_multi.h \
+ net_proto.h options.h storage.h streams.h utils.h execute.h db.h program.h version.h \
+ opcode.h parse_cmd.h
+client_bsd.o : client_bsd.c my-socket.h config.h my-types.h my-stdio.h my-stdarg.h \
+ my-stdlib.h my-string.h my-sys-time.h options.h my-unistd.h
+client_sysv.o : client_sysv.c my-fcntl.h config.h my-signal.h my-stdio.h my-types.h \
+ my-stdarg.h my-stdlib.h my-string.h my-stat.h my-unistd.h options.h
diff --git a/Minimal.db b/Minimal.db
new file mode 100644
index 0000000..8ecbcee
--- /dev/null
+++ b/Minimal.db
@@ -0,0 +1,76 @@
+** LambdaMOO Database, Format Version 1 **
+4
+1
+0
+1
+3
+#0
+System Object
+
+16
+3
+-1
+-1
+-1
+1
+-1
+2
+1
+do_login_command
+3
+173
+-1
+0
+0
+#1
+Root Class
+
+16
+3
+-1
+-1
+-1
+-1
+0
+-1
+0
+0
+0
+#2
+The First Room
+
+0
+3
+-1
+3
+-1
+1
+-1
+3
+1
+eval
+3
+88
+-2
+0
+0
+#3
+Wizard
+
+7
+3
+2
+-1
+-1
+1
+-1
+-1
+0
+0
+0
+#0:0
+return #3;
+.
+0 clocks
+0 queued tasks
+0 suspended tasks
diff --git a/README b/README
new file mode 100644
index 0000000..41cf0ca
--- /dev/null
+++ b/README
@@ -0,0 +1,211 @@
+WARNING: If you are upgrading an existing LambdaMOO database from an earlier
+version of the server, you should read the relevant notes in ChangeLog.txt
+regarding DB changes you may need to make *before* doing the upgrade. Such
+notes are marked in ChangeLog.txt with the string `NOTE'.
+
+------------------------------------------------------------------------------
+
+ Getting Started With LambdaMOO
+ ------------------------------
+
+LambdaMOO has been successfully compiled and tested on several machines and
+operating systems; a table appears at the end of this file.
+
+People compiling on any of these machines should only need to do the following:
+ -- run the command `sh configure'; it will take a couple of minutes, trying
+ out various features of your operating system, figuring out which of a
+ long list of known quirks must be patched around for your system. It will
+ produce a fair amount of output as it runs, every line of it beginning
+ with the word `checking'. When it's finished poking at your system, it
+ will print out a little note concerning which networking options will work
+ on your particular machine. Make a note of these, since they'll constrain
+ your choice of edits in the next step.
+ -- edit the file `options.h', choosing the options appropriate to your needs
+ and your local configuration. In particular, this is where you specify
+ the kind of networking the server should support, choosing from the
+ options printed by `configure' in the first step.
+ -- type `make'; the code should compile with *almost* no errors or warnings.
+ The exception is warnings about code in files from your own system;
+ an amazing number of systems contain header files and other files that
+ don't compile without warnings. Obviously, I can't do anything about
+ these files, so just ignore such warnings.
+
+That should do it, you'll now have a working LambdaMOO server.
+ [EXCEPTION: If you've defined NETWORK_PROTOCOL to be NP_LOCAL in
+ `options.h', then you will also need a specialized client program
+ for connecting to the server. Type either `make client_bsd' or
+ `make client_sysv', depending on how you defined NETWORK_STYLE, to
+ create the appropriate client program.]
+
+If you're not on one of these configurations, you may still get lucky. You
+will need the following things:
+
+ -- You need a C compiler that is at least mostly compliant with the ANSI C
+ standard. Old-style, purely Kernighan & Ritchie compilers will fail
+ miserably. The more closely it adheres to the standard, the less trouble
+ you'll have.
+ -- You need support for signals, forking, and pausing, preferably according
+ to the POSIX standard; if you haven't got POSIX, then I might be ready for
+ your particular non-standard system anyway.
+ -- You need an implementation of the crypt() password-encryption function
+ that comes with all BSD UNIX operating systems.
+
+If you've got all of this, then try the above procedure (i.e., type
+`sh configure', edit `options.h', and type `make') and there's a good chance
+that it will just work. If so, please let me know so that I can add your
+machine and operating system to the table at the end of this file. If not,
+feel free to send me mail and I'll help you try to make it work.
+
+To start the server, use a command like this:
+
+ ./moo INITIAL-DB-FILE CHECKPOINT-DB-FILE
+
+where INITIAL-DB-FILE is the name of an existing LambdaMOO database file and
+CHECKPOINT-DB-FILE is the filename the server should use for its periodic
+checkpoints of the current DB, which is otherwise kept only in its memory. IT
+IS STRONGLY ADVISED that you not use the same file name for both
+INITIAL-DB-FILE and CHECKPOINT-DB-FILE; this could, in the event of a crash,
+leave you with no useful DB file at all.
+
+Included with the release is a little shell script called `restart' that
+handles the server start-up in a more convenient way. You type a command like
+
+ ./restart FOOBAR
+
+and the script does the following:
+
+ -- If FOOBAR.db.new exists, then
+ + Rename FOOBAR.db to FOOBAR.db.old, and start a background process to
+ compress that file.
+ + Rename FOOBAR.db.new to FOOBAR.db
+ -- Start the server in the background, reading the initial DB from FOOBAR.db
+ and writing the checkpoints to FOOBAR.db.new. The server's log of network
+ connections, checkpoints, and errors will be put into FOOBAR.log. If
+ there was already a file named FOOBAR.log, its old contents are appended
+ to FOOBAR.log.old and FOOBAR.log is removed first.
+
+The `restart' script is really the only good way to start up the server; it's
+all I ever use for LambdaMOO itself.
+
+If your server gets a lot of use and your users start getting the error message
+ *** Sorry, but the server cannot accept any more connections right now.
+ *** Please try again later.
+when they try to connect, it's because your server has run out of UNIX file
+descriptors. If you'd like to allow more folks to connect, you'll have to bump
+up the limit (though there's a hard limit in the UNIX kernel that you won't be
+able to exceed). To bump up the limit as far as it will go, remove the `#' at
+the beginning of the line
+ #unlimit descriptors
+in the `restart' script before starting up your server.
+
+For most of the networking options supported by the server, both the `moo' and
+`restart' commands take an optional argument for changing some network
+connection information; type just `./moo' to see what the argument is for the
+option you've chosen. The only one for which you're at all likely to want to
+override the default concerns the NP_TCP networking options; for them, the
+optional argument is the TCP port number on which the server should listen for
+new connections (the default is 7777).
+
+The `moo' command can take an optional first argument `-e'; if provided, the
+server does not start accepting connections immediately after loading the
+database file. Instead, using its standard input and output streams, it enters
+`emergency wizard mode', in which you can list and (re)set verb programs,
+execute MOO expressions and programs, etc. This mode is useful for recovering
+from having made terrible mistakes in the database, perhaps by reprogramming
+crucial verbs in such a way as to make it impossible to log in as usual. Type
+`help' in this mode to see a complete list of available emergency commands.
+
+The only database included with the release is Minimal.db. Getting from there
+to something usable is possible, but tedious; see README.Minimal for details.
+
+Also available for FTP from ftp.parc.xerox.com is a version of LambdaCore.db, a
+snapshot of the core pieces of the LambdaMOO database. New snapshots are made
+at irregular intervals as sufficient changes happen to the LambdaMOO database.
+
+The LambdaMOO Programmer's Manual is also available for FTP from the same
+place. It comes in plain-text, Texinfo, and Postscript formats.
+
+Once the database has been loaded, the server reacts to various standard UNIX
+process signals as follows:
+
+ Signal(s) Action
+ --------- ------
+ FPE Ignored
+ HUP (if it was already Ignored
+ being ignored)
+ HUP (otherwise) Panic the server
+ ILL, QUIT, SEGV, BUS Panic the server
+ INT, TERM, USR1 Shut down the server cleanly
+ USR2 Schedule a checkpoint ASAP
+
+For the most part, this just means that the following commands might be useful
+to you:
+ kill -INT <server-pid> Cleanly shut down the server
+ kill -USR2 <server-pid> Make the server write a checkpoint soon
+
+If you're using an NP_TCP networking configuration, then if you do a `ps'
+command you should see either three or four UNIX processes concerned with the
+running server. The one with the lowest process number (probably) is the main
+server itself, the program that's actually executing MOO commands, etc. The
+next two processes are associated with the way the MOO server does lookups of
+network host names; by using extra (smallish) processes for this, the server
+can robustly recover from nameserver flakiness. The fourth process is only
+present from time to time; it is a copy of the main server process that is
+`checkpointing,' writing out a copy of the database into a file. On many
+systems (wherever possible), the server changes the output of the `ps' command
+to show you explicitly which of these processes is which.
+
+Finally, if you're putting up a LambdaMOO server, you should probably be a
+member of the MOO-Cows mailing list. Send email to MOO-Cows-Request@Xerox.Com
+saying just `subscribe' to get yourself added to the list.
+
+ Pavel Curtis
+ aka Lambda
+ aka Haakon
+ Archwizard of LambdaMOO
+
+ Pavel@Xerox.Com
+
+------------------------------------------------------------------------------
+
+ How to Add New Built-In Functions to the MOO Language
+ -----------------------------------------------------
+
+Implement your functions by following the many examples in the distributed
+server code (e.g., in the file `numbers.c'). Then follow the directions in the
+comment at the top of functions.c.
+
+Alternatively, save a copy of the distributed file `extensions.c' and then edit
+the original to implement your new functions. The C function
+`register_extensions()' is already called at server startup to register
+whatever built-in functions are implemented in `extensions.c'. In future
+releases, you can simply replace the newly distributed `extensions.c' with your
+own.
+
+NOTE that, in this release, I'm not making any guarantees about what interfaces
+within the server will remain stable. Thus, any code you add to the server may
+well be horribly broken by a future release. Your best bet for avoiding this
+cruel fate is to send that code to me and convince me to add it to the
+distribution; that way, I'll fix up your code for each new release.
+
+------------------------------------------------------------------------------
+
+ Machines On Which Version 1.8.0 of LambdaMOO Was Tested
+ -------------------------------------------------------
+
+ Hardware Operating System(s) Networking Options Compiler
+ -------- ------------------- ------------------ --------
+ Sun 4/SPARC SunOS 4.1.3, 5.5 SU, BT, BL, VT, VL GCC
+ SGI Iris IRIX 5.3 SU, BT, BL, VT, VL Vendor's
+ DEC Alpha DEC OSF/1 V3.2 SU, BT, BL, VL Vendor's
+ Intel x86 Linux 1.3.30 SU, BT, BL, VL GCC
+
+Key to `Networking Options' codes:
+
+ Code NETWORK_PROTOCOL NETWORK_STYLE
+ ---- ---------------- -------------
+ SU NP_SINGLE -----
+ BT NP_TCP NS_BSD
+ BL NP_LOCAL NS_BSD
+ VT NP_TCP NS_SYSV
+ VL NP_LOCAL NS_SYSV
diff --git a/README.Minimal b/README.Minimal
new file mode 100644
index 0000000..c5213c5
--- /dev/null
+++ b/README.Minimal
@@ -0,0 +1,75 @@
+Instructions on using Minimal.db
+------------ -- ----- ----------
+
+The Minimal database is really quite minimal; there are no commands defined
+other than the built-in ones (`PREFIX', `SUFFIX', and `.program') and only
+enough verbs defined and/or programmed to make it possible to bootstrap. It's
+almost a certainty that you'd better off starting from the most recent release
+of the LambdaCore database and possibly trimming things from that. Some people
+can't resist a challenge, though, so this document exists to orient them to the
+meager contents of the Minimal database.
+
+There are four objects in the Minimal database, arranged in the parent/child
+hierarchy as follows:
+
+ Root Class (#1)
+ System Object (#0)
+ The First Room (#2)
+ Wizard (#3)
+
+The System Object has one defined verb, #0:do_login_command, which simply
+returns #3. This has the effect that all connections to the Minimal database
+are automatically logged-in to Wizard, the only player.
+
+The First Room has a verb named `eval' defined but not yet programmed. It has
+arguments `any any any' and permissions `rxd'.
+
+The Wizard is initially located in the First Room and the other three objects
+are initially located in #-1 (i.e., nowhere).
+
+There are no other properties or verbs defined in the database. There are no
+forked or suspended tasks either.
+
+The only way to get started in the Minimal database is either to change the
+program for #0:do_login_command to perform some useful task (probably a more
+tedious way to go, since you'd have to reconnect to get that verb to run), or
+to provide a useful definition for `#2:eval' (probably the right course, since
+you can invoke that more easily).
+
+Of course, there are no restrictions on what you make this `eval' verb do, but
+I recommend that you implement a simple, general MOO-code expression evaluator,
+perhaps using code like this:
+
+ .program here:eval
+ notify(player, toliteral(eval("return " + argstr + ";")[2]));
+ .
+
+Or, perhaps something a tad fancier, to help you diagnose syntax errors more
+easily:
+
+ .program here:eval
+ answer = eval("return " + argstr + ";");
+ if (answer[1])
+ notify(player, tostr("=> ", toliteral(answer[2])));
+ else
+ for line in (answer[2])
+ notify(player, line);
+ endfor
+ endif
+ .
+
+Of course, much fancier `eval' verbs are possible, but you'll discover them for
+yourself, I'm sure.
+
+From such a basis, you can do anything by using the various built-in functions
+directly, like add_verb(), add_property(), etc. With sufficient amounts of
+work, you can build up a set of easier-to-use command-line interfaces to these
+facilities to make programming and building much easier.
+
+If you should decide, after reading this, to build up an interesting database
+starting from the Minimal one, and you succeed in doing so, please drop me a
+line; I'd love to hear about it.
+
+ Pavel Curtis
+ aka Haakon, ArchWizard of LambdaMOO
+ Pavel@PARC.Xerox.Com
diff --git a/aclocal.m4 b/aclocal.m4
new file mode 100644
index 0000000..b95574d
--- /dev/null
+++ b/aclocal.m4
@@ -0,0 +1,32 @@
+dnl LambdaMOO-specific version of AC_FUNC_CHECK, uses <assert.h> instead of
+dnl <ctype.h>, since OSF/1's <ctype.h> includes <sys/types.h> which includes
+dnl <sys/select.h> which declares select() correctly in conflict with the
+dnl bogus `extern char foo();' declaration below. This change is adapted
+dnl from autoconf-2.4, which we ought to start using at some point.
+dnl
+undefine([AC_FUNC_CHECK])dnl
+define(AC_FUNC_CHECK,
+[ifelse([$3], , [AC_COMPILE_CHECK($1, [#include <assert.h>], [
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$1) || defined (__stub___$1)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $1(); $1();
+#endif
+],
+$2)], [AC_COMPILE_CHECK($1, [#include <assert.h>], [
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$1) || defined (__stub___$1)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $1(); $1();
+#endif
+],
+$2, $3)])dnl
+])dnl
diff --git a/ast.c b/ast.c
new file mode 100644
index 0000000..fa26496
--- /dev/null
+++ b/ast.c
@@ -0,0 +1,466 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "my-string.h"
+
+#include "ast.h"
+#include "config.h"
+#include "list.h"
+#include "log.h"
+#include "program.h"
+#include "structures.h"
+#include "sym_table.h"
+#include "storage.h"
+#include "utils.h"
+
+struct entry {
+ void *ptr;
+ Memory_Type type;
+};
+
+static int pool_size, next_pool_slot;
+static struct entry *pool;
+
+void
+begin_code_allocation()
+{
+ pool_size = 10;
+ next_pool_slot = 0;
+ pool = mymalloc(pool_size * sizeof(struct entry), M_AST_POOL);
+}
+
+void
+end_code_allocation(int aborted)
+{
+ if (aborted) {
+ int i;
+
+ for (i = 0; i < next_pool_slot; i++) {
+ if (pool[i].ptr != 0)
+ myfree(pool[i].ptr, pool[i].type);
+ }
+ }
+
+ myfree(pool, M_AST_POOL);
+}
+
+static void *
+allocate(int size, Memory_Type type)
+{
+ if (next_pool_slot >= pool_size) { /* enlarge the pool */
+ struct entry *new_pool;
+ int i;
+
+ pool_size *= 2;
+ new_pool = mymalloc(pool_size * sizeof(struct entry), M_AST_POOL);
+ for (i = 0; i < next_pool_slot; i++) {
+ new_pool[i] = pool[i];
+ }
+ myfree(pool, M_AST_POOL);
+ pool = new_pool;
+ }
+
+ pool[next_pool_slot].type = type;
+ return pool[next_pool_slot++].ptr = mymalloc(size, type);
+}
+
+static void
+deallocate(void *ptr)
+{
+ int i;
+
+ for (i = 0; i < next_pool_slot; i++) {
+ if (ptr == pool[i].ptr) {
+ myfree(ptr, pool[i].type);
+ pool[i].ptr = 0;
+ return;
+ }
+ }
+
+ errlog("DEALLOCATE: Unknown pointer deallocated\n");
+}
+
+char *
+alloc_string(const char *buffer)
+{
+ char *string = allocate(strlen(buffer) + 1, M_STRING);
+
+ strcpy(string, buffer);
+ return string;
+}
+
+void
+dealloc_string(char *str)
+{
+ deallocate(str);
+}
+
+double *
+alloc_float(double value)
+{
+ double *d = allocate(sizeof(double), M_FLOAT);
+
+ *d = value;
+ return d;
+}
+
+void
+dealloc_node(void *node)
+{
+ deallocate(node);
+}
+
+Stmt *
+alloc_stmt(enum Stmt_Kind kind)
+{
+ Stmt *result = allocate(sizeof(Stmt), M_AST);
+
+ result->kind = kind;
+ result->next = 0;
+ return result;
+}
+
+Cond_Arm *
+alloc_cond_arm(Expr *condition, Stmt *stmt)
+{
+ Cond_Arm *result = allocate(sizeof(Cond_Arm), M_AST);
+
+ result->condition = condition;
+ result->stmt = stmt;
+ result->next = 0;
+ return result;
+}
+
+Except_Arm *
+alloc_except(int id, Arg_List *codes, Stmt *stmt)
+{
+ Except_Arm *result = allocate(sizeof(Except_Arm), M_AST);
+
+ result->id = id;
+ result->codes = codes;
+ result->stmt = stmt;
+ result->label = 0;
+ result->next = 0;
+ return result;
+}
+
+Expr *
+alloc_expr(enum Expr_Kind kind)
+{
+ Expr *result = allocate(sizeof(Expr), M_AST);
+
+ result->kind = kind;
+ return result;
+}
+
+Expr *
+alloc_var(var_type type)
+{
+ Expr *result = alloc_expr(EXPR_VAR);
+
+ result->e.var.type = type;
+ return result;
+}
+
+Expr *
+alloc_binary(enum Expr_Kind kind, Expr *lhs, Expr *rhs)
+{
+ Expr *result = alloc_expr(kind);
+
+ result->e.bin.lhs = lhs;
+ result->e.bin.rhs = rhs;
+ return result;
+}
+
+Expr *
+alloc_verb(Expr *obj, Expr *verb, Arg_List *args)
+{
+ Expr *result = alloc_expr(EXPR_VERB);
+
+ result->e.verb.obj = obj;
+ result->e.verb.verb = verb;
+ result->e.verb.args = args;
+ return result;
+}
+
+Arg_List *
+alloc_arg_list(enum Arg_Kind kind, Expr *expr)
+{
+ Arg_List *result = allocate(sizeof(Arg_List), M_AST);
+
+ result->kind = kind;
+ result->expr = expr;
+ result->next = 0;
+ return result;
+}
+
+Scatter *
+alloc_scatter(enum Scatter_Kind kind, int id, Expr *expr)
+{
+ Scatter *sc = allocate(sizeof(Scatter), M_AST);
+
+ sc->kind = kind;
+ sc->id = id;
+ sc->expr = expr;
+ sc->next = 0;
+ sc->label = sc->next_label = 0;
+
+ return sc;
+}
+
+static void free_expr(Expr *);
+
+static void
+free_arg_list(Arg_List *args)
+{
+ Arg_List *arg, *next_arg;
+
+ for (arg = args; arg; arg = next_arg) {
+ next_arg = arg->next;
+ free_expr(arg->expr);
+ myfree(arg, M_AST);
+ }
+}
+
+static void
+free_scatter(Scatter *sc)
+{
+ Scatter *next_sc;
+
+ for (; sc; sc = next_sc) {
+ next_sc = sc->next;
+ if (sc->expr)
+ free_expr(sc->expr);
+ myfree(sc, M_AST);
+ }
+}
+
+static void
+free_expr(Expr *expr)
+{
+ switch (expr->kind) {
+
+ case EXPR_VAR:
+ free_var(expr->e.var);
+ break;
+
+ case EXPR_ID:
+ case EXPR_LENGTH:
+ /* Do nothing. */
+ break;
+
+ case EXPR_PROP: case EXPR_INDEX: case EXPR_PLUS:
+ case EXPR_MINUS: case EXPR_TIMES: case EXPR_DIVIDE:
+ case EXPR_MOD: case EXPR_AND: case EXPR_OR:
+ case EXPR_EQ: case EXPR_NE: case EXPR_LT:
+ case EXPR_LE: case EXPR_GT: case EXPR_GE:
+ case EXPR_IN: case EXPR_ASGN: case EXPR_EXP:
+ free_expr(expr->e.bin.lhs);
+ free_expr(expr->e.bin.rhs);
+ break;
+
+ case EXPR_COND:
+ free_expr(expr->e.cond.condition);
+ free_expr(expr->e.cond.consequent);
+ free_expr(expr->e.cond.alternate);
+ break;
+
+ case EXPR_VERB:
+ free_expr(expr->e.verb.obj);
+ free_expr(expr->e.verb.verb);
+ free_arg_list(expr->e.verb.args);
+ break;
+
+ case EXPR_RANGE:
+ free_expr(expr->e.range.base);
+ free_expr(expr->e.range.from);
+ free_expr(expr->e.range.to);
+ break;
+
+ case EXPR_CALL:
+ free_arg_list(expr->e.call.args);
+ break;
+
+ case EXPR_NEGATE:
+ case EXPR_NOT:
+ free_expr(expr->e.expr);
+ break;
+
+ case EXPR_LIST:
+ free_arg_list(expr->e.list);
+ break;
+
+ case EXPR_CATCH:
+ free_expr(expr->e.catch.try);
+ free_arg_list(expr->e.catch.codes);
+ if (expr->e.catch.except)
+ free_expr(expr->e.catch.except);
+ break;
+
+ case EXPR_SCATTER:
+ free_scatter(expr->e.scatter);
+ break;
+
+ default:
+ errlog("FREE_EXPR: Unknown Expr_Kind: %d\n", expr->kind);
+ break;
+ }
+
+ myfree(expr, M_AST);
+}
+
+void
+free_stmt(Stmt *stmt)
+{
+ Stmt *next_stmt;
+ Cond_Arm *arm, *next_arm;
+ Except_Arm *except, *next_e;
+
+ for (; stmt; stmt = next_stmt) {
+ next_stmt = stmt->next;
+
+ switch (stmt->kind) {
+
+ case STMT_COND:
+ for (arm = stmt->s.cond.arms; arm; arm = next_arm) {
+ next_arm = arm->next;
+ free_expr(arm->condition);
+ free_stmt(arm->stmt);
+ myfree(arm, M_AST);
+ }
+ if (stmt->s.cond.otherwise)
+ free_stmt(stmt->s.cond.otherwise);
+ break;
+
+ case STMT_LIST:
+ free_expr(stmt->s.list.expr);
+ free_stmt(stmt->s.list.body);
+ break;
+
+ case STMT_RANGE:
+ free_expr(stmt->s.range.from);
+ free_expr(stmt->s.range.to);
+ free_stmt(stmt->s.range.body);
+ break;
+
+ case STMT_WHILE:
+ free_expr(stmt->s.loop.condition);
+ free_stmt(stmt->s.loop.body);
+ break;
+
+ case STMT_FORK:
+ free_expr(stmt->s.fork.time);
+ free_stmt(stmt->s.fork.body);
+ break;
+
+ case STMT_EXPR:
+ case STMT_RETURN:
+ if (stmt->s.expr)
+ free_expr(stmt->s.expr);
+ break;
+
+ case STMT_TRY_EXCEPT:
+ free_stmt(stmt->s.catch.body);
+ for (except = stmt->s.catch.excepts; except; except = next_e) {
+ next_e = except->next;
+ free_arg_list(except->codes);
+ free_stmt(except->stmt);
+ myfree(except, M_AST);
+ }
+ break;
+
+ case STMT_TRY_FINALLY:
+ free_stmt(stmt->s.finally.body);
+ free_stmt(stmt->s.finally.handler);
+ break;
+
+ case STMT_BREAK:
+ case STMT_CONTINUE:
+ break; /* Nothing extra to free */
+
+ default:
+ errlog("FREE_STMT: unknown Stmt_Kind: %d\n", stmt->kind);
+ break;
+ }
+
+ myfree(stmt, M_AST);
+ }
+}
+
+char rcsid_ast[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.4 1996/02/08 07:11:54 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1 (again).
+ *
+ * Revision 2.3 1996/02/08 05:49:08 pavel
+ * Renamed err/logf() to errlog/oklog(). Added support for floating-point and
+ * the X^Y expression. Added named WHILE loops and the BREAK and CONTINUE
+ * statements. Release 1.8.0beta1.
+ *
+ * Revision 2.2 1996/01/16 07:11:46 pavel
+ * Added alloc/free for scatters. Release 1.8.0alpha6.
+ *
+ * Revision 2.1 1995/12/31 03:14:13 pavel
+ * Added EXPR_LENGTH case to free_expr(). Release 1.8.0alpha4.
+ *
+ * Revision 2.0 1995/11/30 04:16:25 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.12 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.11 1992/10/23 19:16:20 pavel
+ * Eliminated all uses of the useless macro NULL.
+ *
+ * Revision 1.10 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.9 1992/08/31 22:21:28 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.8 1992/08/28 23:12:13 pjames
+ * Added ASGN_RANGE arm of `free_expr()'.
+ *
+ * Revision 1.7 1992/08/28 16:11:25 pjames
+ * Changed myfree(*, M_STRING) to free_str(*).
+ * Removed free_ast_var. Use free_var instead.
+ * Removed ak_dealloc_string. Use free_str instead.
+ *
+ * Revision 1.6 1992/08/14 00:01:17 pavel
+ * Converted to a typedef of `var_type' = `enum var_type'.
+ *
+ * Revision 1.5 1992/08/10 16:53:07 pjames
+ * Updated #includes.
+ *
+ * Revision 1.4 1992/07/30 21:20:25 pjames
+ * Checks for NULL before freeing M_STRINGS.
+ *
+ * Revision 1.3 1992/07/27 17:57:06 pjames
+ * Changed myfree(*, M_AST) to (*, M_STRING) when * was a (char *)
+ * because that is how they are allocated.
+ *
+ * Revision 1.2 1992/07/20 23:48:48 pavel
+ * Added rcsid_<filename-root> declaration to hold the RCS identification
+ * string.
+ *
+ * Revision 1.1 1992/07/20 23:23:12 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/ast.h b/ast.h
new file mode 100644
index 0000000..cc6e5f4
--- /dev/null
+++ b/ast.h
@@ -0,0 +1,258 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#ifndef AST_h
+#define AST_h 1
+
+#include "config.h"
+#include "parser.h"
+#include "structures.h"
+#include "sym_table.h"
+
+typedef struct Expr Expr;
+typedef struct Arg_List Arg_List;
+typedef struct Stmt Stmt;
+typedef struct Cond_Arm Cond_Arm;
+typedef struct Except_Arm Except_Arm;
+typedef struct Scatter Scatter;
+
+struct Expr_Binary {
+ Expr *lhs, *rhs;
+};
+
+enum Arg_Kind {
+ ARG_NORMAL, ARG_SPLICE
+};
+
+struct Arg_List {
+ Arg_List *next;
+ enum Arg_Kind kind;
+ Expr *expr;
+};
+
+enum Scatter_Kind {
+ SCAT_REQUIRED, SCAT_OPTIONAL, SCAT_REST
+};
+
+struct Scatter {
+ Scatter *next;
+ enum Scatter_Kind kind;
+ int id;
+ Expr *expr;
+ /* These fields for convenience during code generation and decompiling */
+ int label, next_label;
+};
+
+struct Expr_Call {
+ unsigned func;
+ Arg_List *args;
+};
+
+struct Expr_Verb {
+ Expr *obj, *verb;
+ Arg_List *args;
+};
+
+struct Expr_Range {
+ Expr *base, *from, *to;
+};
+
+struct Expr_Cond {
+ Expr *condition, *consequent, *alternate;
+};
+
+struct Expr_Catch {
+ Expr *try;
+ Arg_List *codes;
+ Expr *except;
+};
+
+enum Expr_Kind {
+ EXPR_VAR, EXPR_ID,
+ EXPR_PROP, EXPR_VERB,
+ EXPR_INDEX, EXPR_RANGE,
+ EXPR_ASGN, EXPR_CALL,
+ EXPR_PLUS, EXPR_MINUS, EXPR_TIMES, EXPR_DIVIDE, EXPR_MOD, EXPR_EXP,
+ EXPR_NEGATE,
+ EXPR_AND, EXPR_OR, EXPR_NOT,
+ EXPR_EQ, EXPR_NE, EXPR_LT, EXPR_LE, EXPR_GT, EXPR_GE,
+ EXPR_IN, EXPR_LIST, EXPR_COND,
+ EXPR_CATCH, EXPR_LENGTH, EXPR_SCATTER,
+ SizeOf_Expr_Kind /* The last element is also the number of elements... */
+};
+
+union Expr_Data {
+ Var var;
+ int id;
+ struct Expr_Binary bin;
+ struct Expr_Call call;
+ struct Expr_Verb verb;
+ struct Expr_Range range;
+ struct Expr_Cond cond;
+ struct Expr_Catch catch;
+ Expr *expr;
+ Arg_List *list;
+ Scatter *scatter;
+};
+
+struct Expr {
+ enum Expr_Kind kind;
+ union Expr_Data e;
+};
+
+struct Cond_Arm {
+ Cond_Arm *next;
+ Expr *condition;
+ Stmt *stmt;
+};
+
+struct Except_Arm {
+ Except_Arm *next;
+ int id;
+ Arg_List *codes;
+ Stmt *stmt;
+ /* This field is for convenience during code generation and decompiling */
+ int label;
+};
+
+struct Stmt_Cond {
+ Cond_Arm *arms;
+ Stmt *otherwise;
+};
+
+struct Stmt_List {
+ int id;
+ Expr *expr;
+ Stmt *body;
+};
+
+struct Stmt_Range {
+ int id;
+ Expr *from, *to;
+ Stmt *body;
+};
+
+struct Stmt_Loop {
+ int id;
+ Expr *condition;
+ Stmt *body;
+};
+
+struct Stmt_Fork {
+ int id;
+ Expr *time;
+ Stmt *body;
+};
+
+struct Stmt_Catch {
+ Stmt *body;
+ Except_Arm *excepts;
+};
+
+struct Stmt_Finally {
+ Stmt *body;
+ Stmt *handler;
+};
+
+enum Stmt_Kind {
+ STMT_COND, STMT_LIST, STMT_RANGE, STMT_WHILE, STMT_FORK, STMT_EXPR,
+ STMT_RETURN, STMT_TRY_EXCEPT, STMT_TRY_FINALLY, STMT_BREAK, STMT_CONTINUE
+};
+
+union Stmt_Data {
+ struct Stmt_Cond cond;
+ struct Stmt_List list;
+ struct Stmt_Range range;
+ struct Stmt_Loop loop;
+ struct Stmt_Fork fork;
+ struct Stmt_Catch catch;
+ struct Stmt_Finally finally;
+ Expr *expr;
+ int exit;
+};
+
+struct Stmt {
+ Stmt *next;
+ enum Stmt_Kind kind;
+ union Stmt_Data s;
+};
+
+
+extern void begin_code_allocation(void);
+extern void end_code_allocation(int);
+
+extern Stmt *alloc_stmt(enum Stmt_Kind);
+extern Cond_Arm *alloc_cond_arm(Expr *, Stmt *);
+extern Expr *alloc_expr(enum Expr_Kind);
+extern Expr *alloc_var(var_type);
+extern Expr *alloc_binary(enum Expr_Kind, Expr *, Expr *);
+extern Expr *alloc_verb(Expr *, Expr *, Arg_List *);
+extern Arg_List *alloc_arg_list(enum Arg_Kind, Expr *);
+extern Except_Arm *alloc_except(int, Arg_List *, Stmt *);
+extern Scatter *alloc_scatter(enum Scatter_Kind, int, Expr *);
+extern char *alloc_string(const char *);
+extern double *alloc_float(double);
+
+extern void dealloc_node(void *);
+extern void dealloc_string(char *);
+extern void free_stmt(Stmt *);
+
+#endif /* !AST_h */
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.3 1996/02/08 05:59:43 pavel
+ * Updated copyright notice for 1996. Added exponentiation expression, named
+ * WHILE loops, BREAK and CONTINUE statements, support for floating-point.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.2 1996/01/16 07:13:43 pavel
+ * Add support for scattering assignment. Release 1.8.0alpha6.
+ *
+ * Revision 2.1 1995/12/31 03:14:38 pavel
+ * Added EXPR_LENGTH. Release 1.8.0alpha4.
+ *
+ * Revision 2.0 1995/11/30 04:50:19 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.8 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.7 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.6 1992/08/31 22:21:43 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.5 1992/08/28 23:13:36 pjames
+ * Added ASGN_RANGE to assignment type enumeration.
+ * Added range arm to assigment union.
+ *
+ * Revision 1.4 1992/08/14 00:15:28 pavel
+ * Removed trailing comma in an enumeration.
+ *
+ * Revision 1.3 1992/08/14 00:01:11 pavel
+ * Converted to a typedef of `var_type' = `enum var_type'.
+ *
+ * Revision 1.2 1992/07/30 21:21:21 pjames
+ * Removed max_stack from AST structures.
+ *
+ * Revision 1.1 1992/07/20 23:23:12 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/bf_register.h b/bf_register.h
new file mode 100644
index 0000000..3a7a717
--- /dev/null
+++ b/bf_register.h
@@ -0,0 +1,55 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+extern void register_disassemble(void);
+extern void register_extensions(void);
+extern void register_execute(void);
+extern void register_functions(void);
+extern void register_list(void);
+extern void register_log(void);
+extern void register_numbers(void);
+extern void register_objects(void);
+extern void register_property(void);
+extern void register_server(void);
+extern void register_tasks(void);
+extern void register_verbs(void);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.2 1996/02/08 06:29:12 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.1 1996/01/16 07:28:09 pavel
+ * Added `register_functions()'. Release 1.8.0alpha6.
+ *
+ * Revision 2.0 1995/11/30 04:50:30 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.4 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.3 1992/09/08 22:04:15 pjames
+ * Updated to have new procedure names.
+ *
+ * Revision 1.2 1992/08/10 17:22:22 pjames
+ * Added register_bf_log(void);
+ *
+ * Revision 1.1 1992/07/20 23:23:12 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/client_bsd.c b/client_bsd.c
new file mode 100644
index 0000000..649a11e
--- /dev/null
+++ b/client_bsd.c
@@ -0,0 +1,119 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/* BSD/LOCAL MUD client */
+
+#include <errno.h>
+#include "my-socket.h"
+#include "my-stdio.h"
+#include "my-stdlib.h"
+#include "my-string.h"
+#include "my-sys-time.h"
+#include <sys/un.h>
+#include "my-types.h"
+#include "my-unistd.h"
+
+#include "config.h"
+#include "options.h"
+
+void
+main(int argc, char **argv)
+{
+ const char *connect_file = DEFAULT_CONNECT_FILE;
+ int s;
+ struct sockaddr_un address;
+
+ if (argc == 2)
+ connect_file = argv[1];
+ else if (argc != 1) {
+ fprintf(stderr, "Usage: %s [server-connect-file]\n", argv[0]);
+ exit(1);
+ }
+
+ if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+
+ address.sun_family = AF_UNIX;
+ strcpy(address.sun_path, connect_file);
+
+ if (connect(s, (struct sockaddr *) &address,
+ sizeof(address.sun_family) + strlen(connect_file)) < 0) {
+ perror("connect");
+ exit(1);
+ }
+
+ while (1) {
+ fd_set input;
+ char buffer[1024];
+
+ FD_ZERO(&input);
+ FD_SET(0, &input);
+ FD_SET(s, &input);
+
+ if (select(s + 1, (void *) &input, 0, 0, 0) < 0) {
+ if (errno != EINTR) {
+ perror("select");
+ exit(1);
+ }
+ } else {
+ if (FD_ISSET(0, &input))
+ write(s, buffer, read(0, buffer, sizeof(buffer)));
+ if (FD_ISSET(s, &input)) {
+ int count = read(s, buffer, sizeof(buffer));
+
+ if (count == 0)
+ break;
+ write(1, buffer, count);
+ }
+ }
+ }
+
+ exit(0);
+}
+
+char rcsid_client_bsd[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.2 1996/03/10 01:11:49 pavel
+ * Moved definition of DEFAULT_CONNECT_FILE to options.h. Release 1.8.0.
+ *
+ * Revision 2.1 1996/02/08 06:33:46 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:47:38 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.5 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.4 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.3 1992/10/17 20:17:13 pavel
+ * Fix up connect_file defaulting, to eliminate warnings from naive compilers.
+ *
+ * Revision 1.2 1992/10/06 18:15:34 pavel
+ * Added missing #include of my-stdlib.h, for exit().
+ *
+ * Revision 1.1 1992/09/23 22:35:05 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/client_sysv.c b/client_sysv.c
new file mode 100644
index 0000000..79b97f0
--- /dev/null
+++ b/client_sysv.c
@@ -0,0 +1,238 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/* SYSV/LOCAL MUD client */
+
+#include <errno.h>
+#include "my-fcntl.h"
+#include "my-signal.h"
+#include "my-stdio.h"
+#include "my-stdlib.h"
+#include "my-string.h"
+#include "my-types.h"
+#include "my-stat.h"
+#include "my-unistd.h"
+
+#include "config.h"
+#include "options.h"
+
+int
+set_non_blocking(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) < 0
+ || fcntl(fd, F_SETFL, flags | NONBLOCK_FLAG) < 0)
+ return 0;
+ else
+ return 1;
+}
+
+int
+set_blocking(int fd)
+{
+ int flags;
+
+ if ((flags = fcntl(fd, F_GETFL, 0)) < 0
+ || fcntl(fd, F_SETFL, flags & ~NONBLOCK_FLAG) < 0)
+ return 0;
+ else
+ return 1;
+}
+
+void
+kill_signal(int sig)
+{
+ set_blocking(0);
+ exit(0);
+}
+
+void
+write_all(int fd, const char *buffer, int length)
+{
+ while (length) {
+ int count = write(fd, buffer, length);
+
+ buffer += count;
+ length -= count;
+ }
+}
+
+void
+main(int argc, char **argv)
+{
+ const char *connect_file = DEFAULT_CONNECT_FILE;
+ int server_fifo;
+ const char *dir;
+ char c2s[1000], s2c[1000], connect[2010];
+ int pid = getpid();
+ int rfd, wfd;
+ int use_home_dir = 0;
+ char *prog_name = argv[0];
+
+ while (--argc && **(++argv) == '-') {
+ switch ((*argv)[1]) {
+ case 'h':
+ use_home_dir = 1;
+ break;
+
+ default:
+ goto usage;
+ }
+ }
+
+ if (argc == 1)
+ connect_file = argv[0];
+ else if (argc != 0)
+ goto usage;
+
+ signal(SIGTERM, kill_signal);
+ signal(SIGINT, kill_signal);
+ signal(SIGQUIT, kill_signal);
+
+ if (!use_home_dir || !(dir = getenv("HOME")))
+ dir = "/usr/tmp";
+
+ sprintf(c2s, "%s/.c2s-%05d", dir, (int) pid);
+ sprintf(s2c, "%s/.s2c-%05d", dir, (int) pid);
+
+ if (mkfifo(c2s, 0000) < 0 || chmod(c2s, 0644) < 0
+ || mkfifo(s2c, 0000) < 0 || chmod(s2c, 0622) < 0) {
+ perror("Creating personal FIFOs");
+ goto die;
+ }
+
+ if ((rfd = open(s2c, O_RDONLY | NONBLOCK_FLAG)) < 0) {
+ perror("Opening server-to-client FIFO");
+ goto die;
+ }
+
+ if ((server_fifo = open(connect_file, O_WRONLY | NONBLOCK_FLAG)) < 0) {
+ perror("Opening server FIFO");
+ goto die;
+ }
+
+ sprintf(connect, "\n%s %s\n", c2s, s2c);
+ write(server_fifo, connect, strlen(connect));
+ close(server_fifo);
+
+ if ((wfd = open(c2s, O_WRONLY)) < 0) {
+ perror("Opening client-to-server FIFO");
+ goto die;
+ }
+
+ remove(c2s);
+ remove(s2c);
+
+ fprintf(stderr, "[Connected to server]\n");
+
+ if (!set_non_blocking(0) || !set_non_blocking(rfd)) {
+ perror("Setting connection non-blocking");
+ exit(1);
+ }
+
+ while (1) {
+ char buffer[1024];
+ int count, did_some = 0;
+
+ count = read(0, buffer, sizeof(buffer));
+ if (count > 0) {
+ did_some = 1;
+ write_all(wfd, buffer, count);
+ }
+
+ count = read(rfd, buffer, sizeof(buffer));
+ if (count > 0) {
+ did_some = 1;
+ if (buffer[count - 1] == '\0') {
+ write_all(1, buffer, count - 1);
+ break;
+ } else
+ write_all(1, buffer, count);
+ }
+
+ if (!did_some)
+ sleep(1);
+ }
+
+ set_blocking(0);
+ exit(0);
+
+ die:
+ remove(c2s);
+ remove(s2c);
+ exit(1);
+
+ usage:
+ fprintf(stderr, "Usage: %s [-h] [server-connect-file]\n", prog_name);
+ exit(1);
+}
+
+char rcsid_client_sysv[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.3 1996/03/10 01:11:56 pavel
+ * Moved definition of DEFAULT_CONNECT_FILE to options.h. Release 1.8.0.
+ *
+ * Revision 2.2 1996/02/08 06:33:33 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.1 1995/12/30 23:58:51 pavel
+ * Fixed a longstanding bug where it never worked to specify a connect-file on
+ * the command line. Release 1.8.0alpha4.
+ *
+ * Revision 2.0 1995/11/30 04:47:54 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.10 1993/12/09 02:19:59 pavel
+ * Fixed bug whereby short writes would cause us to lose data.
+ *
+ * Revision 1.9 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.8 1992/10/23 19:17:22 pavel
+ * Moved macro definition of mkfifo() to my-stat.h.
+ * Added a `connected' message, printed when the client has finished the
+ * hand-shake with the server.
+ *
+ * Revision 1.7 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.6 1992/10/17 20:18:27 pavel
+ * Replace all uses of O_NDELAY by NONBLOCK_FLAG, to allow for the use of
+ * POSIX non-blocking where available.
+ *
+ * Revision 1.5 1992/10/06 20:33:57 pavel
+ * Added -h switch to make it create the client FIFOs in the user's home
+ * directory.
+ *
+ * Revision 1.4 1992/10/06 18:23:49 pavel
+ * Added missing #include of my-string.h.
+ *
+ * Revision 1.3 1992/10/02 18:41:24 pavel
+ * Now catches SIGINT, SIGTERM, and SIGQUIT in order to restore normal
+ * blocking behavior on stdin.
+ *
+ * Revision 1.2 1992/10/01 17:29:18 pavel
+ * Removed use of the BSD-specific select() call.
+ *
+ * Revision 1.1 1992/09/27 19:23:03 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/code_gen.c b/code_gen.c
new file mode 100644
index 0000000..84ef9f8
--- /dev/null
+++ b/code_gen.c
@@ -0,0 +1,1205 @@
+/******************************************************************************
+ Copyright (c) 1994, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include <limits.h>
+
+#include "ast.h"
+#include "exceptions.h"
+#include "opcode.h"
+#include "program.h"
+#include "storage.h"
+#include "structures.h"
+#include "utils.h"
+#include "version.h"
+
+/*** The reader will likely find it useful to consult the file
+ *** `MOOCodeSequences.txt' in this directory while reading the code in this
+ *** file.
+ ***/
+
+enum fixup_kind {
+ FIXUP_LITERAL, FIXUP_FORK, FIXUP_LABEL, FIXUP_VAR_REF, FIXUP_STACK
+};
+
+struct fixup {
+ enum fixup_kind kind;
+ unsigned pc;
+ unsigned value;
+ unsigned prev_literals, prev_forks, prev_var_refs, prev_labels,
+ prev_stacks;
+ int next; /* chain for compiling IF/ELSEIF arms */
+};
+typedef struct fixup Fixup;
+
+struct gstate {
+ unsigned total_var_refs; /* For duplicating an old bug... */
+ unsigned num_literals, max_literals;
+ Var *literals;
+ unsigned num_fork_vectors, max_fork_vectors;
+ Bytecodes *fork_vectors;
+};
+typedef struct gstate GState;
+
+struct loop {
+ int id;
+ Fixup top_label;
+ unsigned top_stack;
+ int bottom_label;
+ unsigned bottom_stack;
+};
+typedef struct loop Loop;
+
+struct state {
+ unsigned max_literal, max_fork, max_var_ref;
+ /* For telling how big the refs must be */
+ unsigned num_literals, num_forks, num_var_refs, num_labels,
+ num_stacks;
+ /* For computing the final vector length */
+ unsigned num_fixups, max_fixups;
+ Fixup *fixups;
+ unsigned num_bytes, max_bytes;
+ Byte *bytes;
+ unsigned cur_stack, max_stack;
+ unsigned saved_stack;
+ unsigned num_loops, max_loops;
+ Loop *loops;
+ GState *gstate;
+};
+typedef struct state State;
+
+static void
+init_gstate(GState *gstate)
+{
+ gstate->total_var_refs = 0;
+ gstate->num_literals = gstate->num_fork_vectors = 0;
+ gstate->max_literals = gstate->max_fork_vectors = 0;
+ gstate->fork_vectors = 0;
+ gstate->literals = 0;
+}
+
+static void
+free_gstate(GState gstate)
+{
+ if (gstate.literals)
+ myfree(gstate.literals, M_CODE_GEN);
+ if (gstate.fork_vectors)
+ myfree(gstate.fork_vectors, M_CODE_GEN);
+}
+
+static void
+init_state(State *state, GState *gstate)
+{
+ state->num_literals = state->num_forks = state->num_labels = 0;
+ state->num_var_refs = state->num_stacks = 0;
+
+ state->max_literal = state->max_fork = state->max_var_ref = 0;
+
+ state->num_fixups = 0;
+ state->max_fixups = 10;
+ state->fixups = mymalloc(sizeof(Fixup) * state->max_fixups, M_CODE_GEN);
+
+ state->num_bytes = 0;
+ state->max_bytes = 50;
+ state->bytes = mymalloc(sizeof(Byte) * state->max_bytes, M_BYTECODES);
+
+ state->cur_stack = state->max_stack = 0;
+ state->saved_stack = UINT_MAX;
+
+ state->num_loops = 0;
+ state->max_loops = 5;
+ state->loops = mymalloc(sizeof(Loop) * state->max_loops, M_CODE_GEN);
+
+ state->gstate = gstate;
+}
+
+static void
+free_state(State state)
+{
+ myfree(state.fixups, M_CODE_GEN);
+ myfree(state.bytes, M_BYTECODES);
+ myfree(state.loops, M_CODE_GEN);
+}
+
+static void
+emit_byte(Byte b, State *state)
+{
+ if (state->num_bytes == state->max_bytes) {
+ unsigned new_max = 2 * state->max_bytes;
+ Byte *new_bytes = mymalloc(sizeof(Byte) * new_max,
+ M_BYTECODES);
+ unsigned i;
+
+ for (i = 0; i < state->num_bytes; i++)
+ new_bytes[i] = state->bytes[i];
+
+ myfree(state->bytes, M_BYTECODES);
+ state->bytes = new_bytes;
+ state->max_bytes = new_max;
+ }
+
+ state->bytes[state->num_bytes++] = b;
+}
+
+static void
+emit_extended_byte(Byte b, State *state)
+{
+ emit_byte(OP_EXTENDED, state);
+ emit_byte(b, state);
+}
+
+static int
+add_known_fixup(Fixup f, State *state)
+{
+ int i;
+
+ if (state->num_fixups == state->max_fixups) {
+ unsigned new_max = 2 * state->max_fixups;
+ Fixup *new_fixups = mymalloc(sizeof(Fixup) * new_max,
+ M_CODE_GEN);
+
+ for (i = 0; i < state->num_fixups; i++)
+ new_fixups[i] = state->fixups[i];
+
+ myfree(state->fixups, M_CODE_GEN);
+ state->fixups = new_fixups;
+ state->max_fixups = new_max;
+ }
+
+ f.pc = state->num_bytes;
+ state->fixups[i = state->num_fixups++] = f;
+
+ emit_byte(0, state); /* a placeholder for the eventual value */
+
+ return i;
+}
+
+static int
+add_linked_fixup(enum fixup_kind kind, unsigned value, int next, State *state)
+{
+ Fixup f;
+
+ f.kind = kind;
+ f.value = value;
+ f.prev_literals = state->num_literals;
+ f.prev_forks = state->num_forks;
+ f.prev_var_refs = state->num_var_refs;
+ f.prev_labels = state->num_labels;
+ f.prev_stacks = state->num_stacks;
+ f.next = next;
+ return add_known_fixup(f, state);
+}
+
+static int
+add_fixup(enum fixup_kind kind, unsigned value, State *state)
+{
+ return add_linked_fixup(kind, value, -1, state);
+}
+
+static void
+add_literal(Var v, State *state)
+{
+ GState *gstate = state->gstate;
+ Var *literals = gstate->literals;
+ unsigned i;
+
+ for (i = 0; i < gstate->num_literals; i++)
+ if (v.type == literals[i].type /* no int/float coercion here */
+ && equality(v, literals[i], 1))
+ break;
+
+ if (i == gstate->num_literals) {
+ /* New literal to intern */
+ if (gstate->num_literals == gstate->max_literals) {
+ unsigned new_max = gstate->max_literals == 0
+ ? 5 : 2 * gstate->max_literals;
+ Var *new_literals = mymalloc(sizeof(Var) * new_max,
+ M_CODE_GEN);
+
+ if (gstate->literals) {
+ for (i = 0; i < gstate->num_literals; i++)
+ new_literals[i] = literals[i];
+
+ myfree(literals, M_CODE_GEN);
+ }
+
+ gstate->literals = new_literals;
+ gstate->max_literals = new_max;
+ }
+
+ gstate->literals[i = gstate->num_literals++] = var_ref(v);
+ }
+
+ add_fixup(FIXUP_LITERAL, i, state);
+ state->num_literals++;
+ if (i > state->max_literal)
+ state->max_literal = i;
+}
+
+static void
+add_fork(Bytecodes b, State *state)
+{
+ unsigned i;
+ GState *gstate = state->gstate;
+
+ if (gstate->num_fork_vectors == gstate->max_fork_vectors) {
+ unsigned new_max = gstate->max_fork_vectors == 0
+ ? 1 : 2 * gstate->max_fork_vectors;
+ Bytecodes *new_fv = mymalloc(sizeof(Bytecodes) * new_max,
+ M_CODE_GEN);
+
+ if (gstate->fork_vectors) {
+ for (i = 0; i < gstate->num_fork_vectors; i++)
+ new_fv[i] = gstate->fork_vectors[i];
+
+ myfree(gstate->fork_vectors, M_CODE_GEN);
+ }
+
+ gstate->fork_vectors = new_fv;
+ gstate->max_fork_vectors = new_max;
+ }
+
+ gstate->fork_vectors[i = gstate->num_fork_vectors++] = b;
+
+ add_fixup(FIXUP_FORK, i, state);
+ state->num_forks++;
+ if (i > state->max_fork)
+ state->max_fork = i;
+}
+
+static void
+add_var_ref(unsigned slot, State *state)
+{
+ add_fixup(FIXUP_VAR_REF, slot, state);
+ state->num_var_refs++;
+ if (slot > state->max_var_ref)
+ state->max_var_ref = slot;
+ state->gstate->total_var_refs++;
+}
+
+static int
+add_linked_label(int next, State *state)
+{
+ int label = add_linked_fixup(FIXUP_LABEL, 0, next, state);
+
+ state->num_labels++;
+ return label;
+}
+
+static int
+add_label(State *state)
+{
+ return add_linked_label(-1, state);
+}
+
+static void
+add_pseudo_label(unsigned value, State *state)
+{
+ Fixup f;
+
+ f.kind = FIXUP_LABEL;
+ f.value = value;
+ f.prev_literals = f.prev_forks = 0;
+ f.prev_var_refs = f.prev_labels = 0;
+ f.next = -1;
+
+ add_known_fixup(f, state);
+ state->num_labels++;
+}
+
+static int
+add_known_label(Fixup f, State *state)
+{
+ int label = add_known_fixup(f, state);
+
+ state->num_labels++;
+ return label;
+}
+
+static Fixup
+capture_label(State *state)
+{
+ Fixup f;
+
+ f.kind = FIXUP_LABEL;
+ f.value = state->num_bytes;
+ f.prev_literals = state->num_literals;
+ f.prev_forks = state->num_forks;
+ f.prev_var_refs = state->num_var_refs;
+ f.prev_labels = state->num_labels;
+ f.next = -1;
+
+ return f;
+}
+
+static void
+define_label(int label, State *state)
+{
+ unsigned value = state->num_bytes;
+
+ while (label != -1) {
+ Fixup *fixup = &(state->fixups[label]);
+
+ fixup->value = value;
+ fixup->prev_literals = state->num_literals;
+ fixup->prev_forks = state->num_forks;
+ fixup->prev_var_refs = state->num_var_refs;
+ fixup->prev_labels = state->num_labels;
+ label = fixup->next;
+ }
+}
+
+static void
+add_stack_ref(unsigned index, State *state)
+{
+ add_fixup(FIXUP_STACK, index, state);
+}
+
+static void
+push_stack(unsigned n, State *state)
+{
+ state->cur_stack += n;
+ if (state->cur_stack > state->max_stack)
+ state->max_stack = state->cur_stack;
+}
+
+static void
+pop_stack(unsigned n, State *state)
+{
+ state->cur_stack -= n;
+}
+
+static unsigned
+save_stack_top(State *state)
+{
+ unsigned old = state->saved_stack;
+
+ state->saved_stack = state->cur_stack - 1;
+
+ return old;
+}
+
+static unsigned
+saved_stack_top(State *state)
+{
+ return state->saved_stack;
+}
+
+static void
+restore_stack_top(unsigned old, State *state)
+{
+ state->saved_stack = old;
+}
+
+static void
+enter_loop(int id, Fixup top_label, unsigned top_stack,
+ int bottom_label, unsigned bottom_stack, State *state)
+{
+ int i;
+ Loop *loop;
+
+ if (state->num_loops == state->max_loops) {
+ unsigned new_max = 2 * state->max_loops;
+ Loop *new_loops = mymalloc(sizeof(Loop) * new_max,
+ M_CODE_GEN);
+
+ for (i = 0; i < state->num_loops; i++)
+ new_loops[i] = state->loops[i];
+
+ myfree(state->loops, M_CODE_GEN);
+ state->loops = new_loops;
+ state->max_loops = new_max;
+ }
+
+ loop = &(state->loops[state->num_loops++]);
+ loop->id = id;
+ loop->top_label = top_label;
+ loop->top_stack = top_stack;
+ loop->bottom_label = bottom_label;
+ loop->bottom_stack = bottom_stack;
+}
+
+static int
+exit_loop(State *state)
+{
+ return state->loops[--state->num_loops].bottom_label;
+}
+
+
+static void
+emit_var_op(Opcode op, unsigned slot, State *state)
+{
+ if (slot >= NUM_READY_VARS) {
+ emit_byte(op + NUM_READY_VARS, state);
+ add_var_ref(slot, state);
+ } else
+ emit_byte(op + slot, state);
+}
+
+static void generate_expr(Expr *, State *);
+
+static void
+generate_arg_list(Arg_List *args, State *state)
+{
+ if (!args) {
+ emit_byte(OP_MAKE_EMPTY_LIST, state);
+ push_stack(1, state);
+ } else {
+ Opcode normal_op = OP_MAKE_SINGLETON_LIST,
+ splice_op = OP_CHECK_LIST_FOR_SPLICE;
+ unsigned pop = 0;
+
+ for (; args; args = args->next) {
+ generate_expr(args->expr, state);
+ emit_byte(args->kind == ARG_NORMAL ? normal_op : splice_op, state);
+ pop_stack(pop, state);
+ normal_op = OP_LIST_ADD_TAIL;
+ splice_op = OP_LIST_APPEND;
+ pop = 1;
+ }
+ }
+}
+
+static void
+push_lvalue(Expr *expr, int indexed_above, State *state)
+{
+ unsigned old;
+
+ switch (expr->kind) {
+ case EXPR_RANGE:
+ push_lvalue(expr->e.range.base, 1, state);
+ old = save_stack_top(state);
+ generate_expr(expr->e.range.from, state);
+ generate_expr(expr->e.range.to, state);
+ restore_stack_top(old, state);
+ break;
+ case EXPR_INDEX:
+ push_lvalue(expr->e.bin.lhs, 1, state);
+ old = save_stack_top(state);
+ generate_expr(expr->e.bin.rhs, state);
+ restore_stack_top(old, state);
+ if (indexed_above) {
+ emit_byte(OP_PUSH_REF, state);
+ push_stack(1, state);
+ }
+ break;
+ case EXPR_ID:
+ if (indexed_above) {
+ emit_var_op(OP_PUSH, expr->e.id, state);
+ push_stack(1, state);
+ }
+ break;
+ case EXPR_PROP:
+ generate_expr(expr->e.bin.lhs, state);
+ generate_expr(expr->e.bin.rhs, state);
+ if (indexed_above) {
+ emit_byte(OP_PUSH_GET_PROP, state);
+ push_stack(1, state);
+ }
+ break;
+ default:
+ panic("Bad lvalue in PUSH_LVALUE()");
+ }
+}
+
+static void
+generate_codes(Arg_List *codes, State *state)
+{
+ if (codes)
+ generate_arg_list(codes, state);
+ else {
+ emit_byte(OPTIM_NUM_TO_OPCODE(0), state);
+ push_stack(1, state);
+ }
+}
+
+static void
+generate_expr(Expr *expr, State *state)
+{
+ switch (expr->kind) {
+ case EXPR_VAR:
+ {
+ Var v;
+
+ v = expr->e.var;
+ if (v.type == TYPE_INT && IN_OPTIM_NUM_RANGE(v.v.num))
+ emit_byte(OPTIM_NUM_TO_OPCODE(v.v.num), state);
+ else {
+ emit_byte(OP_IMM, state);
+ add_literal(v, state);
+ }
+ push_stack(1, state);
+ }
+ break;
+ case EXPR_ID:
+ emit_var_op(OP_PUSH, expr->e.id, state);
+ push_stack(1, state);
+ break;
+ case EXPR_AND:
+ case EXPR_OR:
+ {
+ int end_label;
+
+ generate_expr(expr->e.bin.lhs, state);
+ emit_byte(expr->kind == EXPR_AND ? OP_AND : OP_OR, state);
+ end_label = add_label(state);
+ pop_stack(1, state);
+ generate_expr(expr->e.bin.rhs, state);
+ define_label(end_label, state);
+ }
+ break;
+ case EXPR_NEGATE:
+ case EXPR_NOT:
+ generate_expr(expr->e.expr, state);
+ emit_byte(expr->kind == EXPR_NOT ? OP_NOT : OP_UNARY_MINUS, state);
+ break;
+ case EXPR_EQ:
+ case EXPR_NE:
+ case EXPR_GE:
+ case EXPR_GT:
+ case EXPR_LE:
+ case EXPR_LT:
+ case EXPR_IN:
+ case EXPR_PLUS:
+ case EXPR_MINUS:
+ case EXPR_TIMES:
+ case EXPR_DIVIDE:
+ case EXPR_MOD:
+ case EXPR_PROP:
+ {
+ Opcode op = OP_ADD; /* initialize to silence warning */
+
+ generate_expr(expr->e.bin.lhs, state);
+ generate_expr(expr->e.bin.rhs, state);
+ switch (expr->kind) {
+ case EXPR_EQ: op = OP_EQ; break;
+ case EXPR_NE: op = OP_NE; break;
+ case EXPR_GE: op = OP_GE; break;
+ case EXPR_GT: op = OP_GT; break;
+ case EXPR_LE: op = OP_LE; break;
+ case EXPR_LT: op = OP_LT; break;
+ case EXPR_IN: op = OP_IN; break;
+ case EXPR_PLUS: op = OP_ADD; break;
+ case EXPR_MINUS: op = OP_MINUS; break;
+ case EXPR_TIMES: op = OP_MULT; break;
+ case EXPR_DIVIDE: op = OP_DIV; break;
+ case EXPR_MOD: op = OP_MOD; break;
+ case EXPR_PROP: op = OP_GET_PROP; break;
+ case EXPR_INDEX: op = OP_REF; break;
+ default:
+ panic("Not a binary operator in GENERATE_EXPR()");
+ }
+ emit_byte(op, state);
+ pop_stack(1, state);
+ }
+ break;
+ case EXPR_EXP:
+ generate_expr(expr->e.bin.lhs, state);
+ generate_expr(expr->e.bin.rhs, state);
+ emit_extended_byte(EOP_EXP, state);
+ pop_stack(1, state);
+ break;
+ case EXPR_INDEX:
+ {
+ unsigned old;
+
+ generate_expr(expr->e.bin.lhs, state);
+ old = save_stack_top(state);
+ generate_expr(expr->e.bin.rhs, state);
+ restore_stack_top(old, state);
+ emit_byte(OP_REF, state);
+ pop_stack(1, state);
+ }
+ break;
+ case EXPR_RANGE:
+ {
+ unsigned old;
+
+ generate_expr(expr->e.range.base, state);
+ old = save_stack_top(state);
+ generate_expr(expr->e.range.from, state);
+ generate_expr(expr->e.range.to, state);
+ restore_stack_top(old, state);
+ emit_byte(OP_RANGE_REF, state);
+ pop_stack(2, state);
+ }
+ break;
+ case EXPR_LENGTH:
+ {
+ unsigned saved = saved_stack_top(state);
+
+ if (saved != UINT_MAX) {
+ emit_extended_byte(EOP_LENGTH, state);
+ add_stack_ref(saved, state);
+ push_stack(1, state);
+ } else
+ panic("Missing saved stack for `$' in GENERATE_EXPR()");
+ }
+ break;
+ case EXPR_LIST:
+ generate_arg_list(expr->e.list, state);
+ break;
+ case EXPR_CALL:
+ generate_arg_list(expr->e.call.args, state);
+ emit_byte(OP_BI_FUNC_CALL, state);
+ emit_byte(expr->e.call.func, state);
+ break;
+ case EXPR_VERB:
+ generate_expr(expr->e.verb.obj, state);
+ generate_expr(expr->e.verb.verb, state);
+ generate_arg_list(expr->e.verb.args, state);
+ emit_byte(OP_CALL_VERB, state);
+ pop_stack(2, state);
+ break;
+ case EXPR_COND:
+ {
+ int else_label, end_label;
+
+ generate_expr(expr->e.cond.condition, state);
+ emit_byte(OP_IF_QUES, state);
+ else_label = add_label(state);
+ pop_stack(1, state);
+ generate_expr(expr->e.cond.consequent, state);
+ emit_byte(OP_JUMP, state);
+ end_label = add_label(state);
+ pop_stack(1, state);
+ define_label(else_label, state);
+ generate_expr(expr->e.cond.alternate, state);
+ define_label(end_label, state);
+ }
+ break;
+ case EXPR_ASGN:
+ {
+ Expr *e = expr->e.bin.lhs;
+
+ if (e->kind == EXPR_SCATTER) {
+ int nargs = 0, nreq = 0, rest = -1;
+ unsigned done;
+ Scatter *sc;
+
+ generate_expr(expr->e.bin.rhs, state);
+ for (sc = e->e.scatter; sc; sc = sc->next) {
+ nargs++;
+ if (sc->kind == SCAT_REQUIRED)
+ nreq++;
+ else if (sc->kind == SCAT_REST)
+ rest = nargs;
+ }
+ if (rest == -1)
+ rest = nargs + 1;
+ emit_extended_byte(EOP_SCATTER, state);
+ emit_byte(nargs, state);
+ emit_byte(nreq, state);
+ emit_byte(rest, state);
+ for (sc = e->e.scatter; sc; sc = sc->next) {
+ add_var_ref(sc->id, state);
+ if (sc->kind != SCAT_OPTIONAL)
+ add_pseudo_label(0, state);
+ else if (!sc->expr)
+ add_pseudo_label(1, state);
+ else
+ sc->label = add_label(state);
+ }
+ done = add_label(state);
+ for (sc = e->e.scatter; sc; sc = sc->next)
+ if (sc->kind == SCAT_OPTIONAL && sc->expr) {
+ define_label(sc->label, state);
+ generate_expr(sc->expr, state);
+ emit_var_op(OP_PUT, sc->id, state);
+ emit_byte(OP_POP, state);
+ pop_stack(1, state);
+ }
+ define_label(done, state);
+ } else {
+ int is_indexed = 0;
+
+ push_lvalue(e, 0, state);
+ generate_expr(expr->e.bin.rhs, state);
+ if (e->kind == EXPR_RANGE || e->kind == EXPR_INDEX)
+ emit_byte(OP_PUT_TEMP, state);
+ while (1) {
+ switch (e->kind) {
+ case EXPR_RANGE:
+ emit_extended_byte(EOP_RANGESET, state);
+ pop_stack(3, state);
+ e = e->e.range.base;
+ is_indexed = 1;
+ continue;
+ case EXPR_INDEX:
+ emit_byte(OP_INDEXSET, state);
+ pop_stack(2, state);
+ e = e->e.bin.lhs;
+ is_indexed = 1;
+ continue;
+ case EXPR_ID:
+ emit_var_op(OP_PUT, e->e.id, state);
+ break;
+ case EXPR_PROP:
+ emit_byte(OP_PUT_PROP, state);
+ pop_stack(2, state);
+ break;
+ default:
+ panic("Bad lvalue in GENERATE_EXPR()");
+ }
+ break;
+ }
+ if (is_indexed) {
+ emit_byte(OP_POP, state);
+ emit_byte(OP_PUSH_TEMP, state);
+ }
+ }
+ }
+ break;
+ case EXPR_CATCH:
+ {
+ int handler_label, end_label;
+
+ generate_codes(expr->e.catch.codes, state);
+ emit_extended_byte(EOP_PUSH_LABEL, state);
+ handler_label = add_label(state);
+ push_stack(1, state);
+ emit_extended_byte(EOP_CATCH, state);
+ push_stack(1, state);
+ generate_expr(expr->e.expr, state);
+ emit_extended_byte(EOP_END_CATCH, state);
+ end_label = add_label(state);
+ pop_stack(3, state); /* codes, label, catch */
+ define_label(handler_label, state);
+ /* After this label, we still have a value on the stack, but now,
+ * instead of it being the value of the main expression, we have
+ * the exception tuple pushed before entering the handler.
+ */
+ if (expr->e.catch.except) {
+ emit_byte(OP_POP, state);
+ pop_stack(1, state);
+ generate_expr(expr->e.catch.except, state);
+ } else {
+ /* Select code from tuple */
+ emit_byte(OPTIM_NUM_TO_OPCODE(1), state);
+ emit_byte(OP_REF, state);
+ }
+ define_label(end_label, state);
+ }
+ break;
+ default:
+ panic("Can't happen in GENERATE_EXPR()");
+ }
+}
+
+static Bytecodes stmt_to_code(Stmt *, GState *);
+
+static void
+generate_stmt(Stmt *stmt, State *state)
+{
+ for (; stmt; stmt = stmt->next) {
+ switch (stmt->kind) {
+ case STMT_COND:
+ {
+ Opcode if_op = OP_IF;
+ int end_label = -1;
+ Cond_Arm *arms;
+
+ for (arms = stmt->s.cond.arms; arms; arms = arms->next) {
+ int else_label;
+
+ generate_expr(arms->condition, state);
+ emit_byte(if_op, state);
+ else_label = add_label(state);
+ pop_stack(1, state);
+ generate_stmt(arms->stmt, state);
+ emit_byte(OP_JUMP, state);
+ end_label = add_linked_label(end_label, state);
+ define_label(else_label, state);
+ if_op = OP_EIF;
+ }
+
+ if (stmt->s.cond.otherwise)
+ generate_stmt(stmt->s.cond.otherwise, state);
+ define_label(end_label, state);
+ }
+ break;
+ case STMT_LIST:
+ {
+ Fixup loop_top;
+ int end_label;
+
+ generate_expr(stmt->s.list.expr, state);
+ emit_byte(OPTIM_NUM_TO_OPCODE(1), state); /* loop list index */
+ push_stack(1, state);
+ loop_top = capture_label(state);
+ emit_byte(OP_FOR_LIST, state);
+ add_var_ref(stmt->s.list.id, state);
+ end_label = add_label(state);
+ enter_loop(stmt->s.list.id, loop_top, state->cur_stack,
+ end_label, state->cur_stack - 2, state);
+ generate_stmt(stmt->s.list.body, state);
+ end_label = exit_loop(state);
+ emit_byte(OP_JUMP, state);
+ add_known_label(loop_top, state);
+ define_label(end_label, state);
+ pop_stack(2, state);
+ }
+ break;
+ case STMT_RANGE:
+ {
+ Fixup loop_top;
+ int end_label;
+
+ generate_expr(stmt->s.range.from, state);
+ generate_expr(stmt->s.range.to, state);
+ loop_top = capture_label(state);
+ emit_byte(OP_FOR_RANGE, state);
+ add_var_ref(stmt->s.range.id, state);
+ end_label = add_label(state);
+ enter_loop(stmt->s.range.id, loop_top, state->cur_stack,
+ end_label, state->cur_stack - 2, state);
+ generate_stmt(stmt->s.range.body, state);
+ end_label = exit_loop(state);
+ emit_byte(OP_JUMP, state);
+ add_known_label(loop_top, state);
+ define_label(end_label, state);
+ pop_stack(2, state);
+ }
+ break;
+ case STMT_WHILE:
+ {
+ Fixup loop_top;
+ int end_label;
+
+ loop_top = capture_label(state);
+ generate_expr(stmt->s.loop.condition, state);
+ if (stmt->s.loop.id == -1)
+ emit_byte(OP_WHILE, state);
+ else {
+ emit_extended_byte(EOP_WHILE_ID, state);
+ add_var_ref(stmt->s.loop.id, state);
+ }
+ end_label = add_label(state);
+ pop_stack(1, state);
+ enter_loop(stmt->s.loop.id, loop_top, state->cur_stack,
+ end_label, state->cur_stack, state);
+ generate_stmt(stmt->s.loop.body, state);
+ end_label = exit_loop(state);
+ emit_byte(OP_JUMP, state);
+ add_known_label(loop_top, state);
+ define_label(end_label, state);
+ }
+ break;
+ case STMT_FORK:
+ generate_expr(stmt->s.fork.time, state);
+ if (stmt->s.fork.id >= 0)
+ emit_byte(OP_FORK_WITH_ID, state);
+ else
+ emit_byte(OP_FORK, state);
+ add_fork(stmt_to_code(stmt->s.fork.body, state->gstate), state);
+ if (stmt->s.fork.id >= 0)
+ add_var_ref(stmt->s.fork.id, state);
+ pop_stack(1, state);
+ break;
+ case STMT_EXPR:
+ generate_expr(stmt->s.expr, state);
+ emit_byte(OP_POP, state);
+ pop_stack(1, state);
+ break;
+ case STMT_RETURN:
+ if (stmt->s.expr) {
+ generate_expr(stmt->s.expr, state);
+ emit_byte(OP_RETURN, state);
+ pop_stack(1, state);
+ } else
+ emit_byte(OP_RETURN0, state);
+ break;
+ case STMT_TRY_EXCEPT:
+ {
+ int end_label, arm_count = 0;
+ Except_Arm *ex;
+
+ for (ex = stmt->s.catch.excepts; ex; ex = ex->next) {
+ generate_codes(ex->codes, state);
+ emit_extended_byte(EOP_PUSH_LABEL, state);
+ ex->label = add_label(state);
+ push_stack(1, state);
+ arm_count++;
+ }
+ emit_extended_byte(EOP_TRY_EXCEPT, state);
+ emit_byte(arm_count, state);
+ push_stack(1, state);
+ generate_stmt(stmt->s.catch.body, state);
+ emit_extended_byte(EOP_END_EXCEPT, state);
+ end_label = add_label(state);
+ pop_stack(2 * arm_count + 1, state); /* 2(codes,pc) + catch */
+ for (ex = stmt->s.catch.excepts; ex; ex = ex->next) {
+ define_label(ex->label, state);
+ push_stack(1, state); /* exception tuple */
+ if (ex->id >= 0)
+ emit_var_op(OP_PUT, ex->id, state);
+ emit_byte(OP_POP, state);
+ pop_stack(1, state);
+ generate_stmt(ex->stmt, state);
+ if (ex->next) {
+ emit_byte(OP_JUMP, state);
+ end_label = add_linked_label(end_label, state);
+ }
+ }
+ define_label(end_label, state);
+ }
+ break;
+ case STMT_TRY_FINALLY:
+ {
+ int handler_label;
+
+ emit_extended_byte(EOP_TRY_FINALLY, state);
+ handler_label = add_label(state);
+ push_stack(1, state);
+ generate_stmt(stmt->s.finally.body, state);
+ emit_extended_byte(EOP_END_FINALLY, state);
+ pop_stack(1, state); /* FINALLY marker */
+ define_label(handler_label, state);
+ push_stack(2, state); /* continuation value, reason */
+ generate_stmt(stmt->s.finally.handler, state);
+ emit_extended_byte(EOP_CONTINUE, state);
+ pop_stack(2, state);
+ }
+ break;
+ case STMT_BREAK:
+ case STMT_CONTINUE:
+ {
+ int i;
+ Loop *loop = 0; /* silence warnings */
+
+ if (stmt->s.exit == -1) {
+ emit_extended_byte(EOP_EXIT, state);
+ if (state->num_loops == 0)
+ panic("No loop to exit, in CODE_GEN!");
+ loop = &(state->loops[state->num_loops - 1]);
+ } else {
+ emit_extended_byte(EOP_EXIT_ID, state);
+ add_var_ref(stmt->s.exit, state);
+ for (i = state->num_loops - 1; i >= 0; i--)
+ if (state->loops[i].id == stmt->s.exit) {
+ loop = &(state->loops[i]);
+ break;
+ }
+ if (i < 0)
+ panic("Can't find loop in CONTINUE_LOOP!");
+ }
+
+ if (stmt->kind == STMT_CONTINUE) {
+ add_stack_ref(loop->top_stack, state);
+ add_known_label(loop->top_label, state);
+ } else {
+ add_stack_ref(loop->bottom_stack, state);
+ loop->bottom_label = add_linked_label(loop->bottom_label,
+ state);
+ }
+ }
+ break;
+ default:
+ panic("Can't happen in GENERATE_STMT()");
+ }
+ }
+}
+
+static unsigned
+max(unsigned a, unsigned b)
+{
+ return a > b ? a : b;
+}
+
+static unsigned
+ref_size(unsigned max)
+{
+ if (max <= 256)
+ return 1;
+ else if (max <= 256 * 256)
+ return 2;
+ else
+ return 4;
+}
+
+static Bytecodes
+stmt_to_code(Stmt *stmt, GState *gstate)
+{
+ State state;
+ Bytecodes bc;
+ unsigned old_i, new_i, fix_i;
+ Fixup *fixup;
+
+ init_state(&state, gstate);
+
+ generate_stmt(stmt, &state);
+ emit_byte(OP_DONE, &state);
+
+ if (state.cur_stack != 0)
+ panic("Stack not entirely popped in STMT_TO_CODE()");
+ if (state.saved_stack != UINT_MAX)
+ panic("Still a saved stack index in STMT_TO_CODE()");
+
+ /* The max()ing here with gstate->* is wrong (since that's a global
+ * cumulative count, and thus unrelated to the local maximum), but required
+ * in order to maintain the validity of old program counters stored for
+ * suspended tasks... */
+ bc.numbytes_literal = ref_size(max(state.max_literal,
+ gstate->num_literals));
+ bc.numbytes_fork = ref_size(max(state.max_fork,
+ gstate->num_fork_vectors));
+ bc.numbytes_var_name = ref_size(max(state.max_var_ref,
+ gstate->total_var_refs));
+
+ bc.size = state.num_bytes
+ + (bc.numbytes_literal - 1) * state.num_literals
+ + (bc.numbytes_fork - 1) * state.num_forks
+ + (bc.numbytes_var_name - 1) * state.num_var_refs;
+
+ if (bc.size <= 256)
+ bc.numbytes_label = 1;
+ else if (bc.size + state.num_labels <= 256 * 256)
+ bc.numbytes_label = 2;
+ else
+ bc.numbytes_label = 4;
+ bc.size += (bc.numbytes_label - 1) * state.num_labels;
+
+ bc.max_stack = state.max_stack;
+ bc.numbytes_stack = ref_size(state.max_stack);
+
+ bc.vector = mymalloc(sizeof(Byte) * bc.size, M_BYTECODES);
+
+ fixup = state.fixups;
+ fix_i = 0;
+
+ for (old_i = new_i = 0; old_i < state.num_bytes; old_i++) {
+ if (fix_i < state.num_fixups && fixup->pc == old_i) {
+ unsigned value, size = 0; /* initialized to silence warning */
+
+ value = fixup->value;
+ switch (fixup->kind) {
+ case FIXUP_LITERAL:
+ size = bc.numbytes_literal;
+ break;
+ case FIXUP_FORK:
+ size = bc.numbytes_fork;
+ break;
+ case FIXUP_VAR_REF:
+ size = bc.numbytes_var_name;
+ break;
+ case FIXUP_STACK:
+ size = bc.numbytes_stack;
+ break;
+ case FIXUP_LABEL:
+ value += fixup->prev_literals * (bc.numbytes_literal - 1)
+ + fixup->prev_forks * (bc.numbytes_fork - 1)
+ + fixup->prev_var_refs * (bc.numbytes_var_name - 1)
+ + fixup->prev_labels * (bc.numbytes_label - 1)
+ + fixup->prev_stacks * (bc.numbytes_stack - 1);
+ size = bc.numbytes_label;
+ break;
+ default:
+ panic("Can't happen #1 in STMT_TO_CODE()");
+ }
+
+ switch (size) {
+ case 4:
+ bc.vector[new_i++] = value >> 24;
+ bc.vector[new_i++] = value >> 16;
+ case 2:
+ bc.vector[new_i++] = value >> 8;
+ case 1:
+ bc.vector[new_i++] = value;
+ break;
+ default:
+ panic("Can't happen #2 in STMT_TO_CODE()");
+ }
+
+ fixup++;
+ fix_i++;
+ } else
+ bc.vector[new_i++] = state.bytes[old_i];
+ }
+
+ free_state(state);
+
+ return bc;
+}
+
+Program *
+generate_code(Stmt *stmt, DB_Version version)
+{
+ Program *prog = new_program();
+ GState gstate;
+
+ init_gstate(&gstate);
+
+ prog->main_vector = stmt_to_code(stmt, &gstate);
+ prog->version = version;
+
+ if (gstate.literals) {
+ unsigned i;
+
+ prog->literals = mymalloc(sizeof(Var) * gstate.num_literals,
+ M_LIT_LIST);
+ prog->num_literals = gstate.num_literals;
+ for (i = 0; i < gstate.num_literals; i++)
+ prog->literals[i] = gstate.literals[i];
+ } else {
+ prog->literals = 0;
+ prog->num_literals = 0;
+ }
+
+ if (gstate.fork_vectors) {
+ unsigned i;
+
+ prog->fork_vectors =
+ mymalloc(sizeof(Bytecodes) * gstate.num_fork_vectors,
+ M_FORK_VECTORS);
+ prog->fork_vectors_size = gstate.num_fork_vectors;
+ for (i = 0; i < gstate.num_fork_vectors; i++)
+ prog->fork_vectors[i] = gstate.fork_vectors[i];
+ } else {
+ prog->fork_vectors = 0;
+ prog->fork_vectors_size = 0;
+ }
+
+ free_gstate(gstate);
+
+ return prog;
+}
+
+char rcsid_code_gen[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.4 1996/02/08 07:21:08 pavel
+ * Renamed TYPE_NUM to TYPE_INT. Added support for exponentiation expression,
+ * named WHILE loop and BREAK and CONTINUE statement. Updated copyright
+ * notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.3 1996/01/16 07:17:36 pavel
+ * Add support for scattering assignment. Release 1.8.0alpha6.
+ *
+ * Revision 2.2 1995/12/31 03:10:47 pavel
+ * Added general support for managing stack references as another kind of
+ * fixup and for a single stack of remembered stack positions. Used that
+ * stack for remembering the positions of indexed/subranged values and for
+ * implementing the `$' expression. Release 1.8.0alpha4.
+ *
+ * Revision 2.1 1995/11/30 04:18:56 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 2.0 1995/11/30 04:16:54 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/code_gen.h b/code_gen.h
new file mode 100644
index 0000000..f465682
--- /dev/null
+++ b/code_gen.h
@@ -0,0 +1,37 @@
+/******************************************************************************
+ Copyright (c) 1994, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "ast.h"
+#include "program.h"
+#include "version.h"
+
+extern Program *generate_code(Stmt *, DB_Version);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.2 1996/02/08 06:28:58 pavel
+ * Made generate_code() version-dependent. Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.1 1995/11/30 05:04:34 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 2.0 1995/11/30 05:04:03 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..8ce0c37
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,319 @@
+
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/* If you're looking at this as `config.h.in', then this file is full of
+ * `#undef' preprocessor directives. The `configure' command, which generates
+ * `config.h' from `config.h.in', replaces appropriate ones of those `#undef's
+ * with `#define's, depending upon characteristics of the operating system,
+ * and comments out all of the others.
+ */
+
+#ifndef Config_H
+#define Config_H 1
+
+/* Some systems require that certain preprocessor symbols be defined in order
+ * for certain pieces of the C library to be available. If one of the
+ * following is defined, then your system is one such.
+ */
+
+#undef _ALL_SOURCE
+#undef _POSIX_SOURCE
+#undef _MINIX
+#undef _POSIX_1_SOURCE
+#undef _HPUX_SOURCE
+
+/* The following symbols describe whether or not certain functions are declared
+ * in header files on your system and, if so, where. If, on your system, the
+ * functions `memcmp', `memcpy', etc. are not declared in <string.h>, but are
+ * instead in <memory.h>, then NEED_MEMORY_H will be defined. Similarly, if
+ * `malloc', `free', etc. are not declared in <stdlib.h>, but are instead in
+ * <malloc.h>, then NEED_MALLOC_H will be defined. If your system has the file
+ * </sys/bsdtypes.h>, then NEED_BSDTYPES_H will be defined.
+ *
+ * The various NDECL_* symbols are defined if the named function or variable is
+ * not declared in its standard place, which is given in a comment after each
+ * symbol. In several of the cases below, the absence of a declaration for the
+ * named function or variable is taken in the server code to imply the similar
+ * absence of declarations for several other related functions or variables.
+ */
+
+#undef NEED_MEMORY_H
+#undef NEED_MALLOC_H
+#undef NEED_BSDTYPES_H
+#undef NEED_SELECT_H
+
+#undef NDECL_TOLOWER /* <ctype.h> */
+#undef NDECL_FCNTL /* <fcntl.h> */
+#undef NDECL_HTONL /* <netinet/in.h> */
+#undef NDECL_IOCTL /* <sys/ioctl.h> */
+#undef NDECL_POLL /* <poll.h> */
+#undef NDECL_KILL /* <signal.h> */
+#undef NDECL_SIGEMPTYSET /* <signal.h> */
+#undef NDECL_SIGPROCMASK /* <signal.h> */
+#undef NDECL_SIGRELSE /* <signal.h> */
+#undef NDECL_ACCEPT /* <sys/socket.h> */
+#undef NDECL_BIND /* <sys/socket.h> */
+#undef NDECL_SHUTDOWN /* <sys/socket.h> */
+#undef NDECL_FSTAT /* <sys/stat.h> */
+#undef NDECL_FCLOSE /* <stdio.h> */
+#undef NDECL_PERROR /* <stdio.h> */
+#undef NDECL_REMOVE /* <stdio.h> */
+#undef NDECL_VFPRINTF /* <stdio.h> */
+#undef NDECL_RANDOM /* <stdlib.h> */
+#undef NDECL_SRANDOM /* <stdlib.h> */
+#undef NDECL_STRTOD /* <stdlib.h> */
+#undef NDECL_STRTOL /* <stdlib.h> */
+#undef NDECL_STRTOUL /* <stdlib.h> */
+#undef NDECL_BZERO /* <string.h> */
+#undef NDECL_MEMCPY /* <string.h> */
+#undef NDECL_MEMSET /* <string.h> */
+#undef NDECL_STRERROR /* <string.h> */
+#undef NDECL_GETITIMER /* <sys/time.h> */
+#undef NDECL_SETITIMER /* <sys/time.h> */
+#undef NDECL_SELECT /* <sys/time.h> */
+#undef NDECL_STRFTIME /* <time.h> */
+#undef NDECL_TIME /* <time.h> */
+#undef NDECL_TZNAME /* <time.h> */
+#undef NDECL_T_OPEN /* <tiuser.h> */
+#undef NDECL_T_ERRLIST /* <tiuser.h> */
+#undef NDECL_FORK /* <unistd.h> */
+#undef NDECL_WAITPID /* <sys/wait.h> */
+
+/* Some systems declare bzero() in <stdlib.h> instead of in <string.h>. */
+
+#undef BZERO_IN_STDLIB_H
+
+/* Some header files are only present on certain kinds of UNIXes (e.g., BSD,
+ * System V, or POSIX). The following symbols are defined only if the named
+ * header file exists on your system.
+ */
+
+#undef HAVE_MACHINE_ENDIAN_H
+#undef HAVE_STDLIB_H
+#undef HAVE_SYS_CDEFS_H
+#undef HAVE_UNISTD_H
+
+/* Some POSIX-standard typedefs are not present in some systems. The following
+ * symbols are defined as aliases for their usual definitions if they are not
+ * defined on your system.
+ */
+
+#undef pid_t
+#undef mode_t
+#undef size_t
+
+/* To implement the LambdaMOO built-in function `ctime()', which converts a
+ * number of seconds since the POSIX `epoch' into a human-readable time/date
+ * string, the server code needs to be able to find out the name of the local
+ * time zone. This information is available in an obscure way from the ANSI
+ * standard function `strftime()', but many systems still don't implement it.
+ * The server has code to cope with this case, though, if your system maintains
+ * the time-zone name in one of two common places. Appropriate ones of the
+ * following symbols will be defined to tell the server how to get time-zone
+ * information on your system.
+ */
+
+#undef HAVE_STRFTIME
+#undef TM_IN_SYS_TIME
+#undef HAVE_TM_ZONE
+#undef HAVE_TZNAME
+
+/* Some compilers `almost' conform to the ANSI C standard, in that they provide
+ * the vast majority of the features and constructs used by the server code,
+ * but one common failing with which the code can cope is the lack of support
+ * for the `const' keyword. If your compiler doesn't support `const', then
+ * this symbol will be defined as a macro that expands into nothing, thus
+ * eliding all uses of the keyword in the code.
+ */
+
+#undef const
+
+/* Some sites have installed GCC improperly or incompletely, thereby requiring
+ * the server to be compiled with the `-traditional' switch. That disables the
+ * `const', `volatile' or `signed' keywords, which we need. Thus, for GCC, we
+ * do these little substitutions to always refer to its `hidden' names for
+ * these keywords.
+ */
+
+#if defined(__GNUC__) && !HAVE_SYS_CDEFS_H
+# define const __const__
+# define volatile __volatile__
+# define signed __signed__
+#endif
+
+/* Certain functions used by the server are `optional', in the sense that the
+ * server can provide its own definition if necessary. In some cases, there
+ * are a number of common ways to do the same thing, differing by system type
+ * (e.g., BSD, System V, or POSIX). The following symbols are defined if your
+ * system provides the named functions.
+ */
+
+#undef HAVE_CRYPT
+#undef HAVE_MATHERR
+#undef HAVE_MKFIFO
+#undef HAVE_REMOVE
+#undef HAVE_RENAME
+#undef HAVE_SELECT
+#undef HAVE_POLL
+#undef HAVE_STRERROR
+#undef HAVE_STRTOUL
+#undef HAVE_RANDOM
+#undef HAVE_LRAND48
+#undef HAVE_WAITPID
+#undef HAVE_WAIT2
+#undef HAVE_WAIT3
+#undef HAVE_SIGEMPTYSET
+#undef HAVE_SIGPROCMASK
+#undef HAVE_SIGSETMASK
+#undef HAVE_SIGRELSE
+
+/* It used to be very much the fashion in UNIX programming to make use of
+ * certain standard header files depend on the programmer having #include'd
+ * other standard header files first. This is obviously losing and, amazingly,
+ * is also losing favor, replaced by the notion that every header file should
+ * #include whatever other headers it requires, and that all headers should
+ * take steps to ensure that they are not #include'd more than once if that
+ * would be bad. The following symbols are defined if the named header files
+ * fail to adhere to the new style; the server code makes up for them by doing
+ * its own #include of the usual prerequisite.
+ */
+
+#undef ARPA_INET_H_NEEDS_HELP
+#undef SIGNAL_H_NEEDS_HELP
+#undef SYS_SOCKET_H_NEEDS_HELP
+#undef SYS_STAT_H_NEEDS_HELP
+#undef TIME_H_NEEDS_HELP
+
+/* Version 2 of the GNU C compiler includes `built-in' declarations for a
+ * number of standard functions from the ANSI header file <string.h>.
+ * Unfortunately, some systems declare these functions differently (and
+ * non-standardly) in their native header files, generating a warning about the
+ * conflict from GCC. If this is the case on your system, the following symbol
+ * will be defined, indicating that the server code should avoid your system's
+ * <string.h> file, using its own version instead.
+ */
+
+#undef USE_OWN_STRING_H
+
+/* On some systems, notably AIX, both <sys/ioctl.h> and <stropts.h> define the
+ * internal macro `_IO' in compatible but different ways. This means that no
+ * program can #include both files without getting an error. If this is so on
+ * your machine, then the following symbol will be defined and the server code
+ * will carefully #undef _IO between inclusions of the two files.
+ */
+
+#undef UNDEF_IO_IN_STROPTS_H
+
+/* The NS_SYSV/NP_LOCAL networking configuration of the server makes use of an
+ * operating system feature called `FIFO's for communication with clients of
+ * the MOO. In order to do this, the server needs some way to tell whether or
+ * not there's something ready to be read on a given FIFO at any given time.
+ * The BSD function `select()' usually works if it's available, but the System
+ * V equivalent, `poll()', sometimes does and sometimes doesn't, depending on
+ * the system. The POSIX definers, of course, took the coward's way out and
+ * didn't define any such function at all. On many systems, however, you can
+ * use the `fstat()' function to determine the number of bytes of data in a
+ * FIFO; this allows the use of a somewhat crocky but workable substitute for
+ * either `poll()' or `select()'. The following symbols are defined if the
+ * given function works to perform the necessary operation on FIFOs on your
+ * system.
+ */
+
+#undef FSTAT_WORKS_ON_FIFOS
+#undef POLL_WORKS_ON_FIFOS
+#undef SELECT_WORKS_ON_FIFOS
+
+/* The POSIX standard specifies a nice, simple, reliable method for performing
+ * non-blocking I/O, and a lot of systems actually implement it. Some systems,
+ * though (notably Ultrix 4.1), misleadingly define the preprocessor symbols
+ * that might make you *think* that they've implemented it, but really they
+ * don't. This symbol is defined if POSIX-style non-blocking actually works on
+ * your system.
+ */
+
+#undef POSIX_NONBLOCKING_WORKS
+
+/* The following code figures out how to express a 32-bit integer type on your
+ * machine.
+ */
+
+#include <limits.h>
+
+#if INT_MAX == 2147483647
+ typedef int int32;
+ typedef unsigned unsigned32;
+# define INT32_MAX INT_MAX
+#else
+# if LONG_MAX == 2147483647
+ typedef long int int32;
+ typedef unsigned long unsigned32;
+# define INT32_MAX LONG_MAX
+# else
+# error I cannot figure out how to express a 32-bit integer on your machine.
+# endif
+#endif
+
+#endif /* !Config_H */
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:06 nop
+/* Initial revision
+/*
+ * Revision 2.5 1996/02/11 00:41:40 pavel
+ * Added check for matherr(). Release 1.8.0beta2.
+ *
+ * Revision 2.4 1996/02/08 07:23:16 pavel
+ * Added NDECL_STRTOD. Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.3 1996/01/11 07:49:01 pavel
+ * Added HAVE_CRYPT. Release 1.8.0alpha5.
+ *
+ * Revision 2.2 1995/12/31 03:18:30 pavel
+ * Added SYS_STAT_H_NEEDS_HELP. Release 1.8.0alpha4.
+ *
+ * Revision 2.1 1995/12/28 00:57:13 pavel
+ * Added definition of INT32_MAX. Release 1.8.0alpha3.
+ *
+ * Revision 2.0 1995/11/30 05:17:32 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.6 1993/10/11 19:25:17 pavel
+ * Changed from config.h.in to raw_config.h.in, along with several new tests
+ * being added.
+ *
+ * Revision 1.5 1992/10/28 01:57:38 pavel
+ * Changed NDECL_VPRINTF to NDECL_VFPRINTF, which is the one we care about...
+ *
+ * Revision 1.4 1992/10/23 23:11:21 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.3 1992/10/23 19:19:22 pavel
+ * Added NEED_MALLOC_H, NDECL_{SIGEMPTYSET,FSTAT,PERROR,REMOVE,VPRINTF,MEMSET,
+ * FORK,WAITPID}, SELECT_WORKS_ON_FIFOS, and POSIX_NONBLOCKING_WORKS.
+ *
+ * Revision 1.2 1992/10/21 03:12:59 pavel
+ * Fixed up the comment leader string for this file.
+ *
+ * Revision 1.1 1992/10/21 03:06:52 pavel
+ * Initial RCS-controlled version.
+ */
+
+/* Local Variables: */
+/* mode: c */
+/* End: */
diff --git a/configure b/configure
new file mode 100755
index 0000000..6878b69
--- /dev/null
+++ b/configure
@@ -0,0 +1,2894 @@
+#!/bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf.
+# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Usage: configure [--srcdir=DIR] [--host=HOST] [--gas] [--nfp]
+# [--prefix=PREFIX] [--exec-prefix=PREFIX] [--with-PACKAGE[=VALUE]]
+# Ignores all args except --srcdir, --prefix, --exec-prefix, and
+# --with-PACKAGE[=VALUE] unless this script has special code to handle it.
+
+for arg
+do
+ # Handle --exec-prefix with a space before the argument.
+ if test x$next_exec_prefix = xyes; then exec_prefix=$arg; next_exec_prefix=
+ # Handle --host with a space before the argument.
+ elif test x$next_host = xyes; then next_host=
+ # Handle --prefix with a space before the argument.
+ elif test x$next_prefix = xyes; then prefix=$arg; next_prefix=
+ # Handle --srcdir with a space before the argument.
+ elif test x$next_srcdir = xyes; then srcdir=$arg; next_srcdir=
+ else
+ case $arg in
+ # For backward compatibility, recognize -exec-prefix and --exec_prefix.
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* | --exec=* | --exe=* | --ex=* | --e=*)
+ exec_prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- | --exec | --exe | --ex | --e)
+ next_exec_prefix=yes ;;
+
+ -gas | --gas | --ga | --g) ;;
+
+ -host=* | --host=* | --hos=* | --ho=* | --h=*) ;;
+ -host | --host | --hos | --ho | --h)
+ next_host=yes ;;
+
+ -nfp | --nfp | --nf) ;;
+
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ next_prefix=yes ;;
+
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=* | --s=*)
+ srcdir=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr | --s)
+ next_srcdir=yes ;;
+
+ -with-* | --with-*)
+ package=`echo $arg|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that aren't valid shell variable names.
+ if test -n "`echo $package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ echo "configure: $package: invalid package name" >&2; exit 1
+ fi
+ package=`echo $package| sed 's/-/_/g'`
+ case "$arg" in
+ *=*) val="`echo $arg|sed 's/[^=]*=//'`" ;;
+ *) val=1 ;;
+ esac
+ eval "with_$package='$val'" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb | --ver | --ve | --v)
+ verbose=yes ;;
+
+ *) ;;
+ esac
+ fi
+done
+
+trap 'rm -fr conftest* confdefs* core; exit 1' 1 3 15
+trap 'rm -f confdefs*' 0
+
+# NLS nuisances.
+# These must not be set unconditionally because not all systems understand
+# e.g. LANG=C (notably SCO).
+if test "${LC_ALL+set}" = 'set' ; then LC_ALL=C; export LC_ALL; fi
+if test "${LANG+set}" = 'set' ; then LANG=C; export LANG; fi
+
+rm -f conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+compile='${CC-cc} $CFLAGS conftest.c -o conftest $LIBS >/dev/null 2>&1'
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+unique_file=Makefile.in
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ srcdirdefaulted=yes
+ # Try the directory containing this script, then `..'.
+ prog=$0
+ confdir=`echo $prog|sed 's%/[^/][^/]*$%%'`
+ test "X$confdir" = "X$prog" && confdir=.
+ srcdir=$confdir
+ if test ! -r $srcdir/$unique_file; then
+ srcdir=..
+ fi
+fi
+if test ! -r $srcdir/$unique_file; then
+ if test x$srcdirdefaulted = xyes; then
+ echo "configure: Can not find sources in \`${confdir}' or \`..'." 1>&2
+ else
+ echo "configure: Can not find sources in \`${srcdir}'." 1>&2
+ fi
+ exit 1
+fi
+# Preserve a srcdir of `.' to avoid automounter screwups with pwd.
+# But we can't avoid them for `..', to make subdirectories work.
+case $srcdir in
+ .|/*|~*) ;;
+ *) srcdir=`cd $srcdir; pwd` ;; # Make relative path absolute.
+esac
+
+
+# Save the original args to write them into config.status later.
+configure_args="$*"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+for p in 'bison -y' byacc
+do
+if test -z "$YACC"; then
+ # Extract the first word of `$p', so it can be a program name with args.
+ set dummy $p; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ YACC="$p"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+
+test -n "$YACC" && test -n "$verbose" && echo " setting YACC to $YACC"
+
+test -n "$YACC" && break
+done
+test -n "$YACC" || YACC="yacc"
+
+if test -z "$CC"; then
+ # Extract the first word of `gcc', so it can be a program name with args.
+ set dummy gcc; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ CC="gcc"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$CC" && CC="cc"
+test -n "$CC" && test -n "$verbose" && echo " setting CC to $CC"
+
+# Find out if we are using GNU C, under whatever name.
+cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes
+#endif
+EOF
+${CC-cc} -E conftest.c > conftest.out 2>&1
+if egrep yes conftest.out >/dev/null 2>&1; then
+ GCC=1 # For later tests.
+fi
+rm -f conftest*
+
+echo checking how to run the C preprocessor
+if test -z "$CPP"; then
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and ``${CC-cc}'' will simply confuse
+ # make. It must be expanded now.
+ CPP="${CC-cc} -E"
+ cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+Syntax Error
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+test ".${verbose}" != "." && echo " setting CPP to $CPP"
+
+if test -n "$GCC"; then
+ echo checking whether -traditional is needed
+ pattern="Autoconf.*'x'"
+ prog='#include <sgtty.h>
+Autoconf TIOCGETP'
+ cat > conftest.c <<EOF
+#include "confdefs.h"
+$prog
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ need_trad=1
+
+fi
+rm -f conftest*
+
+
+ if test -z "$need_trad"; then
+ prog='#include <termio.h>
+Autoconf TCGETA'
+ cat > conftest.c <<EOF
+#include "confdefs.h"
+$prog
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ need_trad=1
+
+fi
+rm -f conftest*
+
+ fi
+ test -n "$need_trad" && CC="$CC -traditional"
+fi
+
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if __GNUC__==2
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ CC="$CC -Wall -Wwrite-strings"
+
+fi
+rm -f conftest*
+
+test -n "$GCC" && CC="$CC -g"
+echo checking how to run the C preprocessor
+if test -z "$CPP"; then
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and ``${CC-cc}'' will simply confuse
+ # make. It must be expanded now.
+ CPP="${CC-cc} -E"
+ cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+Syntax Error
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+test ".${verbose}" != "." && echo " setting CPP to $CPP"
+
+
+echo checking for NeXT
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#ifdef NeXT
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining _NEXT_SOURCE"
+echo "#define" _NEXT_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_NEXT_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_NEXT_SOURCE\${SEDdB}_NEXT_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_NEXT_SOURCE\${SEDuB}_NEXT_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_NEXT_SOURCE\${SEDeB}_NEXT_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+{
+test -n "$verbose" && \
+echo " defining _POSIX_SOURCE"
+echo "#define" _POSIX_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_POSIX_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_POSIX_SOURCE\${SEDdB}_POSIX_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_POSIX_SOURCE\${SEDuB}_POSIX_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_POSIX_SOURCE\${SEDeB}_POSIX_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+
+echo checking for the DEC Alpha running OSF/1
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if defined(__osf__) && defined(__alpha) && !defined(__GNUC__)
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ CC="$CC -Olimit 2500"
+
+fi
+rm -f conftest*
+
+
+echo checking for the SGI compiler
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if defined(sgi) && !defined(__GNUC__)
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ CC="$CC -Olimit 2500"
+
+fi
+rm -f conftest*
+
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if defined(sgi)
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ CC="$CC -U__EXTENSIONS__"
+
+fi
+rm -f conftest*
+
+echo checking for AIX
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#ifdef _AIX
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining _ALL_SOURCE"
+echo "#define" _ALL_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_ALL_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_ALL_SOURCE\${SEDdB}_ALL_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_ALL_SOURCE\${SEDuB}_ALL_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_ALL_SOURCE\${SEDeB}_ALL_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+
+echo checking for POSIXized ISC
+if test -d /etc/conf/kconfig.d &&
+ grep _POSIX_VERSION /usr/include/sys/unistd.h >/dev/null 2>&1
+then
+ ISC=1 # If later tests want to check for ISC.
+
+{
+test -n "$verbose" && \
+echo " defining _POSIX_SOURCE"
+echo "#define" _POSIX_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_POSIX_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_POSIX_SOURCE\${SEDdB}_POSIX_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_POSIX_SOURCE\${SEDuB}_POSIX_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_POSIX_SOURCE\${SEDeB}_POSIX_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+ if test -n "$GCC"; then
+ CC="$CC -posix"
+ else
+ CC="$CC -Xp"
+ fi
+fi
+
+echo checking for minix/config.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <minix/config.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+ MINIX=1
+
+fi
+rm -f conftest*
+
+# The Minix shell can't assign to the same variable on the same line!
+if test -n "$MINIX"; then
+
+{
+test -n "$verbose" && \
+echo " defining _POSIX_SOURCE"
+echo "#define" _POSIX_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_POSIX_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_POSIX_SOURCE\${SEDdB}_POSIX_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_POSIX_SOURCE\${SEDuB}_POSIX_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_POSIX_SOURCE\${SEDeB}_POSIX_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+{
+test -n "$verbose" && \
+echo " defining" _POSIX_1_SOURCE to be 2
+echo "#define" _POSIX_1_SOURCE 2 >> confdefs.h
+DEFS="$DEFS -D_POSIX_1_SOURCE=2"
+SEDDEFS="${SEDDEFS}\${SEDdA}_POSIX_1_SOURCE\${SEDdB}_POSIX_1_SOURCE\${SEDdC}2\${SEDdD}
+\${SEDuA}_POSIX_1_SOURCE\${SEDuB}_POSIX_1_SOURCE\${SEDuC}2\${SEDuD}
+\${SEDeA}_POSIX_1_SOURCE\${SEDeB}_POSIX_1_SOURCE\${SEDeC}2\${SEDeD}
+"
+}
+
+
+{
+test -n "$verbose" && \
+echo " defining _MINIX"
+echo "#define" _MINIX 1 >> confdefs.h
+DEFS="$DEFS -D_MINIX=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_MINIX\${SEDdB}_MINIX\${SEDdC}1\${SEDdD}
+\${SEDuA}_MINIX\${SEDuB}_MINIX\${SEDuC}1\${SEDuD}
+\${SEDeA}_MINIX\${SEDeB}_MINIX\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+
+LIBS_save="${LIBS}"
+LIBS="${LIBS} -lintl"
+have_lib=""
+echo checking for -lintl
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ have_lib="1"
+
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; LIBS="$LIBS -lintl"
+else
+ :;
+fi
+
+
+
+echo checking for A/UX
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if defined(AUX)
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining _POSIX_SOURCE"
+echo "#define" _POSIX_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_POSIX_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_POSIX_SOURCE\${SEDdB}_POSIX_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_POSIX_SOURCE\${SEDuB}_POSIX_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_POSIX_SOURCE\${SEDeB}_POSIX_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+
+echo checking for HP/UX
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if defined(__hpux)
+ yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining _HPUX_SOURCE"
+echo "#define" _HPUX_SOURCE 1 >> confdefs.h
+DEFS="$DEFS -D_HPUX_SOURCE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}_HPUX_SOURCE\${SEDdB}_HPUX_SOURCE\${SEDdC}1\${SEDdD}
+\${SEDuA}_HPUX_SOURCE\${SEDuB}_HPUX_SOURCE\${SEDuC}1\${SEDuD}
+\${SEDeA}_HPUX_SOURCE\${SEDeB}_HPUX_SOURCE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+
+echo "checking that the C compiler handles important ANSI C constructs"
+for opt in "" -Aa -Xa -ansi
+do
+SAVECC="$CC"
+CC="$CC $opt"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+int main(int argc, char *argv) { void *ptr; exit(0); }
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ have_ansi=1
+break
+
+else
+ CC="$SAVECC"
+fi
+rm -fr conftest*
+done
+if test -z "$have_ansi"; then
+echo ""
+echo "*** Sorry, but I can't figure out how to find an ANSI C compiler here."
+echo "*** Compiling this program requires such a compiler."
+exit 1
+fi
+
+
+echo checking for a working const keyword
+cat << EOF > conftest.c
+#ifdef __GNUC__
+#define const __const__
+#endif
+int foo(const char *x) { return 17 + (x - x); }
+int bar() { int x = foo("foo"); return x; }
+EOF
+if $CC -c conftest.c > conftest.out 2>&1; then
+ if test -s conftest.out; then
+
+{
+test -n "$verbose" && \
+echo " defining" const to be empty
+echo "#define" const >> confdefs.h
+DEFS="$DEFS -Dconst="
+SEDDEFS="${SEDDEFS}\${SEDdA}const\${SEDdB}const\${SEDdC}\${SEDdD}
+\${SEDuA}const\${SEDuB}const\${SEDuC}\${SEDuD}
+\${SEDeA}const\${SEDeB}const\${SEDeC}\${SEDeD}
+"
+}
+
+ fi
+else
+
+{
+test -n "$verbose" && \
+echo " defining" const to be empty
+echo "#define" const >> confdefs.h
+DEFS="$DEFS -Dconst="
+SEDDEFS="${SEDDEFS}\${SEDdA}const\${SEDdB}const\${SEDdC}\${SEDdD}
+\${SEDuA}const\${SEDuB}const\${SEDuC}\${SEDuD}
+\${SEDeA}const\${SEDeB}const\${SEDeC}\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+CPP=""
+echo checking how to run the C preprocessor
+if test -z "$CPP"; then
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and ``${CC-cc}'' will simply confuse
+ # make. It must be expanded now.
+ CPP="${CC-cc} -E"
+ cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <stdio.h>
+Syntax Error
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+test ".${verbose}" != "." && echo " setting CPP to $CPP"
+
+
+echo checking whether stdlib.h declares malloc and friends
+
+pattern="[^_a-zA-Z0-9]malloc *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <stdlib.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ echo checking for malloc.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <malloc.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining NEED_MALLOC_H"
+echo "#define" NEED_MALLOC_H 1 >> confdefs.h
+DEFS="$DEFS -DNEED_MALLOC_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NEED_MALLOC_H\${SEDdB}NEED_MALLOC_H\${SEDdC}1\${SEDdD}
+\${SEDuA}NEED_MALLOC_H\${SEDuB}NEED_MALLOC_H\${SEDuC}1\${SEDuD}
+\${SEDeA}NEED_MALLOC_H\${SEDeB}NEED_MALLOC_H\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+echo checking for sys/bsdtypes.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/bsdtypes.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining NEED_BSDTYPES_H"
+echo "#define" NEED_BSDTYPES_H 1 >> confdefs.h
+DEFS="$DEFS -DNEED_BSDTYPES_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NEED_BSDTYPES_H\${SEDdB}NEED_BSDTYPES_H\${SEDdC}1\${SEDdD}
+\${SEDuA}NEED_BSDTYPES_H\${SEDuB}NEED_BSDTYPES_H\${SEDuC}1\${SEDuD}
+\${SEDeA}NEED_BSDTYPES_H\${SEDeB}NEED_BSDTYPES_H\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+echo checking for sys/select.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/select.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining NEED_SELECT_H"
+echo "#define" NEED_SELECT_H 1 >> confdefs.h
+DEFS="$DEFS -DNEED_SELECT_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NEED_SELECT_H\${SEDdB}NEED_SELECT_H\${SEDdC}1\${SEDdD}
+\${SEDuA}NEED_SELECT_H\${SEDuB}NEED_SELECT_H\${SEDuC}1\${SEDuD}
+\${SEDeA}NEED_SELECT_H\${SEDeB}NEED_SELECT_H\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+echo checking for memory.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <memory.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining NEED_MEMORY_H"
+echo "#define" NEED_MEMORY_H 1 >> confdefs.h
+DEFS="$DEFS -DNEED_MEMORY_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NEED_MEMORY_H\${SEDdB}NEED_MEMORY_H\${SEDdC}1\${SEDdD}
+\${SEDuA}NEED_MEMORY_H\${SEDuB}NEED_MEMORY_H\${SEDuC}1\${SEDuD}
+\${SEDeA}NEED_MEMORY_H\${SEDeB}NEED_MEMORY_H\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+echo checking for pid_t in sys/types.h
+echo '#include "confdefs.h"
+#include <sys/types.h>' > conftest.c
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "pid_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining" pid_t to be int
+echo "#define" pid_t int >> confdefs.h
+DEFS="$DEFS -Dpid_t=int"
+SEDDEFS="${SEDDEFS}\${SEDdA}pid_t\${SEDdB}pid_t\${SEDdC}int\${SEDdD}
+\${SEDuA}pid_t\${SEDuB}pid_t\${SEDuC}int\${SEDuD}
+\${SEDeA}pid_t\${SEDeB}pid_t\${SEDeC}int\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+echo checking for mode_t in sys/types.h
+echo '#include "confdefs.h"
+#include <sys/types.h>' > conftest.c
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "mode_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining" mode_t to be int
+echo "#define" mode_t int >> confdefs.h
+DEFS="$DEFS -Dmode_t=int"
+SEDDEFS="${SEDDEFS}\${SEDdA}mode_t\${SEDdB}mode_t\${SEDdC}int\${SEDdD}
+\${SEDuA}mode_t\${SEDuB}mode_t\${SEDuC}int\${SEDuD}
+\${SEDeA}mode_t\${SEDeB}mode_t\${SEDeC}int\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+echo checking for size_t in sys/types.h
+echo '#include "confdefs.h"
+#include <sys/types.h>' > conftest.c
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "size_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining" size_t to be unsigned
+echo "#define" size_t unsigned >> confdefs.h
+DEFS="$DEFS -Dsize_t=unsigned"
+SEDDEFS="${SEDDEFS}\${SEDdA}size_t\${SEDdB}size_t\${SEDdC}unsigned\${SEDdD}
+\${SEDuA}size_t\${SEDuB}size_t\${SEDuC}unsigned\${SEDuD}
+\${SEDeA}size_t\${SEDeB}size_t\${SEDeC}unsigned\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+echo checking for struct tm in time.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <time.h>
+int main() { exit(0); }
+int t() { struct tm *tp; tp->tm_sec; }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining TM_IN_SYS_TIME"
+echo "#define" TM_IN_SYS_TIME 1 >> confdefs.h
+DEFS="$DEFS -DTM_IN_SYS_TIME=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}TM_IN_SYS_TIME\${SEDdB}TM_IN_SYS_TIME\${SEDdC}1\${SEDdD}
+\${SEDuA}TM_IN_SYS_TIME\${SEDuB}TM_IN_SYS_TIME\${SEDuC}1\${SEDuD}
+\${SEDeA}TM_IN_SYS_TIME\${SEDeB}TM_IN_SYS_TIME\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+decl='#include <sys/types.h>
+'
+case "$DEFS" in
+ *TM_IN_SYS_TIME*) decl="$decl
+#include <sys/time.h>
+" ;;
+ *) decl="$decl
+#include <time.h>
+" ;;
+esac
+echo checking for tm_zone in struct tm
+cat > conftest.c <<EOF
+#include "confdefs.h"
+$decl
+int main() { exit(0); }
+int t() { struct tm tm; tm.tm_zone; }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining HAVE_TM_ZONE"
+echo "#define" HAVE_TM_ZONE 1 >> confdefs.h
+DEFS="$DEFS -DHAVE_TM_ZONE=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}HAVE_TM_ZONE\${SEDdB}HAVE_TM_ZONE\${SEDdC}1\${SEDdD}
+\${SEDuA}HAVE_TM_ZONE\${SEDuB}HAVE_TM_ZONE\${SEDuC}1\${SEDuD}
+\${SEDeA}HAVE_TM_ZONE\${SEDeB}HAVE_TM_ZONE\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ no_tm_zone=1
+fi
+rm -f conftest*
+
+if test -n "$no_tm_zone"; then
+echo checking for tzname
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <time.h>
+#ifndef tzname /* For SGI. */
+extern char *tzname[]; /* RS6000 and others want it this way. */
+#endif
+int main() { exit(0); }
+int t() { atoi(*tzname); }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining HAVE_TZNAME"
+echo "#define" HAVE_TZNAME 1 >> confdefs.h
+DEFS="$DEFS -DHAVE_TZNAME=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}HAVE_TZNAME\${SEDdB}HAVE_TZNAME\${SEDdC}1\${SEDdD}
+\${SEDuA}HAVE_TZNAME\${SEDuB}HAVE_TZNAME\${SEDuC}1\${SEDuD}
+\${SEDeA}HAVE_TZNAME\${SEDeB}HAVE_TZNAME\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+fi
+
+
+for func in sqrt
+do
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ SAVELIBS="$LIBS"
+ for lib in -lm /lib/libm.a "-static -lm"
+ do
+ LIBS="$LIBS $lib"
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+ break
+
+else
+ rm -rf conftest*
+ LIBS="$SAVELIBS"
+fi
+rm -f conftest*
+
+ done
+
+fi
+rm -f conftest*
+
+done
+
+
+for func in mkfifo waitpid sigemptyset
+do
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ SAVELIBS="$LIBS"
+ for lib in -lposix /lib/libposix.a
+ do
+ LIBS="$LIBS $lib"
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+ break
+
+else
+ rm -rf conftest*
+ LIBS="$SAVELIBS"
+fi
+rm -f conftest*
+
+ done
+
+fi
+rm -f conftest*
+
+done
+
+
+for func in accept
+do
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ SAVELIBS="$LIBS"
+ for lib in "-lsocket -lnsl" -lsocket -linet
+ do
+ LIBS="$LIBS $lib"
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+ break
+
+else
+ rm -rf conftest*
+ LIBS="$SAVELIBS"
+fi
+rm -f conftest*
+
+ done
+
+fi
+rm -f conftest*
+
+done
+
+
+for func in t_open
+do
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ SAVELIBS="$LIBS"
+ for lib in -lnsl -lnsl_s
+ do
+ LIBS="$LIBS $lib"
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+ break
+
+else
+ rm -rf conftest*
+ LIBS="$SAVELIBS"
+fi
+rm -f conftest*
+
+ done
+
+fi
+rm -f conftest*
+
+done
+
+
+for func in crypt
+do
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+
+else
+ rm -rf conftest*
+ SAVELIBS="$LIBS"
+ for lib in -lcrypt -lcrypt_d
+ do
+ LIBS="$LIBS $lib"
+ echo checking for $func
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$func) || defined (__stub___$func)
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char $func(); $func();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining $var"
+echo "#define" $var 1 >> confdefs.h
+DEFS="$DEFS -D$var=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}$var\${SEDdB}$var\${SEDdC}1\${SEDdD}
+\${SEDuA}$var\${SEDuB}$var\${SEDuC}1\${SEDuD}
+\${SEDeA}$var\${SEDeB}$var\${SEDeC}1\${SEDeD}
+"
+}
+
+ break
+
+else
+ rm -rf conftest*
+ LIBS="$SAVELIBS"
+fi
+rm -f conftest*
+
+ done
+
+fi
+rm -f conftest*
+
+done
+
+for hdr in unistd.h sys/cdefs.h stdlib.h tiuser.h machine/endian.h
+do
+trhdr=HAVE_`echo $hdr | tr '[a-z]./' '[A-Z]__'`
+echo checking for ${hdr}
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <${hdr}>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining ${trhdr}"
+echo "#define" ${trhdr} 1 >> confdefs.h
+DEFS="$DEFS -D${trhdr}=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}${trhdr}\${SEDdB}${trhdr}\${SEDdC}1\${SEDdD}
+\${SEDuA}${trhdr}\${SEDuB}${trhdr}\${SEDuC}1\${SEDuD}
+\${SEDeA}${trhdr}\${SEDeB}${trhdr}\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+done
+
+for func in remove rename poll select strerror strftime strtoul matherr
+do
+trfunc=HAVE_`echo $func | tr '[a-z]' '[A-Z]'`
+echo checking for ${func}
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_${func}) || defined (__stub___${func})
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char ${func}(); ${func}();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining ${trfunc}"
+echo "#define" ${trfunc} 1 >> confdefs.h
+DEFS="$DEFS -D${trfunc}=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}${trfunc}\${SEDdB}${trfunc}\${SEDdC}1\${SEDdD}
+\${SEDuA}${trfunc}\${SEDuB}${trfunc}\${SEDuC}1\${SEDuD}
+\${SEDeA}${trfunc}\${SEDeB}${trfunc}\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+done
+
+for func in random lrand48 wait3 wait2 sigsetmask sigprocmask sigrelse
+do
+trfunc=HAVE_`echo $func | tr '[a-z]' '[A-Z]'`
+echo checking for ${func}
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <assert.h>
+int main() { exit(0); }
+int t() {
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_${func}) || defined (__stub___${func})
+choke me
+#else
+/* Override any gcc2 internal prototype to avoid an error. */
+extern char ${func}(); ${func}();
+#endif
+ }
+EOF
+if eval $compile; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining ${trfunc}"
+echo "#define" ${trfunc} 1 >> confdefs.h
+DEFS="$DEFS -D${trfunc}=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}${trfunc}\${SEDdB}${trfunc}\${SEDdC}1\${SEDdD}
+\${SEDuA}${trfunc}\${SEDuB}${trfunc}\${SEDuC}1\${SEDuD}
+\${SEDeA}${trfunc}\${SEDeB}${trfunc}\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in tolower
+do
+echo "checking whether $func is declared in ctype.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <ctype.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in fcntl
+do
+echo "checking whether $func is declared in fcntl.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <fcntl.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in htonl
+do
+echo "checking whether $func is declared in netinet/in.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <netinet/in.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in ioctl
+do
+echo "checking whether $func is declared in sys/ioctl.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/ioctl.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in poll
+do
+echo "checking whether $func is declared in poll.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <poll.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in kill sigemptyset sigprocmask sigrelse
+do
+echo "checking whether $func is declared in signal.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <signal.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in accept bind shutdown
+do
+echo "checking whether $func is declared in sys/socket.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/socket.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in fstat
+do
+echo "checking whether $func is declared in sys/stat.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/stat.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in fclose perror remove vfprintf
+do
+echo "checking whether $func is declared in stdio.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <stdio.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in random srandom strtod strtol strtoul
+do
+echo "checking whether $func is declared in stdlib.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <stdlib.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in bzero memcpy memset strerror
+do
+echo "checking whether $func is declared in string.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <string.h>
+#if NEED_MEMORY_H
+#include <memory.h>
+#endif
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+echo checking for bzero being declared in stdlib.h
+
+pattern="[^_a-zA-Z0-9]bzero *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <stdlib.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining BZERO_IN_STDLIB_H"
+echo "#define" BZERO_IN_STDLIB_H 1 >> confdefs.h
+DEFS="$DEFS -DBZERO_IN_STDLIB_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}BZERO_IN_STDLIB_H\${SEDdB}BZERO_IN_STDLIB_H\${SEDdC}1\${SEDdD}
+\${SEDuA}BZERO_IN_STDLIB_H\${SEDuB}BZERO_IN_STDLIB_H\${SEDuC}1\${SEDuD}
+\${SEDeA}BZERO_IN_STDLIB_H\${SEDeB}BZERO_IN_STDLIB_H\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -f conftest*
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in getitimer setitimer select
+do
+echo "checking whether $func is declared in sys/time.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/time.h>
+#if NEED_SELECT_H
+#include <sys/types.h>
+#include <sys/select.h>
+#endif
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in strftime time
+do
+echo "checking whether $func is declared in time.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <time.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for var in tzname
+do
+echo "checking whether $var is declared in time.h"
+
+pattern="[^_a-zA-Z0-9]$var"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <time.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $var | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $var | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $var | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in t_open
+do
+echo "checking whether $func is declared in tiuser.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <tiuser.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for var in t_errlist
+do
+echo "checking whether $var is declared in tiuser.h"
+
+pattern="[^_a-zA-Z0-9]$var"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <tiuser.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $var | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $var | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $var | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $var | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in fork
+do
+echo "checking whether $func is declared in unistd.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <unistd.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+trfrom='[a-z]' trto='[A-Z]'
+for func in waitpid
+do
+echo "checking whether $func is declared in sys/wait.h"
+
+pattern="[^_a-zA-Z0-9]$func *\("
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/wait.h>
+
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ :
+else
+ rm -rf conftest*
+ {
+test -n "$verbose" && \
+echo " defining NDECL_`echo $func | tr "$trfrom" "$trto"`"
+echo "#define" NDECL_`echo $func | tr "$trfrom" "$trto"` 1 >> confdefs.h
+DEFS="$DEFS -DNDECL_`echo $func | tr "$trfrom" "$trto"`=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDdC}1\${SEDdD}
+\${SEDuA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDuC}1\${SEDuD}
+\${SEDeA}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeB}NDECL_`echo $func | tr "$trfrom" "$trto"`\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+done
+
+
+
+trfrom='[a-z]./' trto='[A-Z]__'
+echo checking for self-sufficiency of arpa/inet.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <arpa/inet.h>
+struct in_addr foo;
+
+int main() { exit(0); }
+int t() { }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining `echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP"
+echo "#define" `echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP 1 >> confdefs.h
+DEFS="$DEFS -D`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdB}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdC}1\${SEDdD}
+\${SEDuA}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuB}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuC}1\${SEDuD}
+\${SEDeA}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeB}`echo arpa/inet.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+
+
+trfrom='[a-z]./' trto='[A-Z]__'
+echo checking for self-sufficiency of signal.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <signal.h>
+
+
+int main() { exit(0); }
+int t() { }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining `echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP"
+echo "#define" `echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP 1 >> confdefs.h
+DEFS="$DEFS -D`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdB}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdC}1\${SEDdD}
+\${SEDuA}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuB}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuC}1\${SEDuD}
+\${SEDeA}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeB}`echo signal.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+
+
+trfrom='[a-z]./' trto='[A-Z]__'
+echo checking for self-sufficiency of sys/socket.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/socket.h>
+
+
+int main() { exit(0); }
+int t() { }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining `echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP"
+echo "#define" `echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP 1 >> confdefs.h
+DEFS="$DEFS -D`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdB}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdC}1\${SEDdD}
+\${SEDuA}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuB}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuC}1\${SEDuD}
+\${SEDeA}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeB}`echo sys/socket.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+
+
+trfrom='[a-z]./' trto='[A-Z]__'
+echo checking for self-sufficiency of sys/stat.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/stat.h>
+
+
+int main() { exit(0); }
+int t() { }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining `echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP"
+echo "#define" `echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP 1 >> confdefs.h
+DEFS="$DEFS -D`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdB}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdC}1\${SEDdD}
+\${SEDuA}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuB}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuC}1\${SEDuD}
+\${SEDeA}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeB}`echo sys/stat.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+
+
+trfrom='[a-z]./' trto='[A-Z]__'
+echo checking for self-sufficiency of time.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <time.h>
+
+
+int main() { exit(0); }
+int t() { }
+EOF
+if eval $compile; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining `echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP"
+echo "#define" `echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP 1 >> confdefs.h
+DEFS="$DEFS -D`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdB}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDdC}1\${SEDdD}
+\${SEDuA}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuB}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDuC}1\${SEDuD}
+\${SEDeA}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeB}`echo time.h | tr "$trfrom" "$trto"`_NEEDS_HELP\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+
+
+echo checking for string.h declaring some functions incorrectly
+echo "
+#include <sys/types.h>
+#include <string.h>
+#if NEED_MEMORY_H
+#include <memory.h>
+#endif
+" > conftest.c
+$CC $DEFS conftest.c -o conftest $LIBS > conftest.out 2>&1
+if grep "conflicting types for built-in function" conftest.out >/dev/null; then
+
+{
+test -n "$verbose" && \
+echo " defining USE_OWN_STRING_H"
+echo "#define" USE_OWN_STRING_H 1 >> confdefs.h
+DEFS="$DEFS -DUSE_OWN_STRING_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}USE_OWN_STRING_H\${SEDdB}USE_OWN_STRING_H\${SEDdC}1\${SEDdD}
+\${SEDuA}USE_OWN_STRING_H\${SEDuB}USE_OWN_STRING_H\${SEDuC}1\${SEDuD}
+\${SEDeA}USE_OWN_STRING_H\${SEDeB}USE_OWN_STRING_H\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+echo "checking for incompatibility between <sys/ioctl.h> and <stropts.h>"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#include <sys/ioctl.h>
+#include <stropts.h>
+
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ rm -rf conftest*
+
+{
+test -n "$verbose" && \
+echo " defining UNDEF_IO_IN_STROPTS_H"
+echo "#define" UNDEF_IO_IN_STROPTS_H 1 >> confdefs.h
+DEFS="$DEFS -DUNDEF_IO_IN_STROPTS_H=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}UNDEF_IO_IN_STROPTS_H\${SEDdB}UNDEF_IO_IN_STROPTS_H\${SEDdC}1\${SEDdD}
+\${SEDuA}UNDEF_IO_IN_STROPTS_H\${SEDuB}UNDEF_IO_IN_STROPTS_H\${SEDuC}1\${SEDuD}
+\${SEDeA}UNDEF_IO_IN_STROPTS_H\${SEDeB}UNDEF_IO_IN_STROPTS_H\${SEDeC}1\${SEDeD}
+"
+}
+
+fi
+rm -f conftest*
+
+echo "checking whether or not fstat() can tell how much data is in a FIFO"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+main()
+{
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, result; struct stat st;
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || write(wfd, "foo", 3) != 3
+ || fstat(rfd, &st) < 0
+ || st.st_size != 3);
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+
+{
+test -n "$verbose" && \
+echo " defining FSTAT_WORKS_ON_FIFOS"
+echo "#define" FSTAT_WORKS_ON_FIFOS 1 >> confdefs.h
+DEFS="$DEFS -DFSTAT_WORKS_ON_FIFOS=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}FSTAT_WORKS_ON_FIFOS\${SEDdB}FSTAT_WORKS_ON_FIFOS\${SEDdC}1\${SEDdD}
+\${SEDuA}FSTAT_WORKS_ON_FIFOS\${SEDuB}FSTAT_WORKS_ON_FIFOS\${SEDuC}1\${SEDuD}
+\${SEDeA}FSTAT_WORKS_ON_FIFOS\${SEDeB}FSTAT_WORKS_ON_FIFOS\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -fr conftest*
+
+echo "checking whether or not select() can be used on FIFOs"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifndef FD_ZERO
+#define NFDBITS (sizeof(fd_set)*8)
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1L<<((n)%NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1L<<((n)%NFDBITS)))
+#endif /* FD_ZERO */
+main()
+{
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, result; fd_set input; struct timeval tv;
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || (FD_ZERO(&input), FD_SET(rfd, &input),
+ select(rfd + 1, &input, 0, 0, &tv) != 0)
+ || write(wfd, "foo", 3) != 3
+ || (FD_ZERO(&input), FD_SET(rfd, &input),
+ select(rfd + 1, &input, 0, 0, &tv) != 1)
+ || !FD_ISSET(rfd, &input));
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+
+{
+test -n "$verbose" && \
+echo " defining SELECT_WORKS_ON_FIFOS"
+echo "#define" SELECT_WORKS_ON_FIFOS 1 >> confdefs.h
+DEFS="$DEFS -DSELECT_WORKS_ON_FIFOS=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}SELECT_WORKS_ON_FIFOS\${SEDdB}SELECT_WORKS_ON_FIFOS\${SEDdC}1\${SEDdD}
+\${SEDuA}SELECT_WORKS_ON_FIFOS\${SEDuB}SELECT_WORKS_ON_FIFOS\${SEDuC}1\${SEDuD}
+\${SEDeA}SELECT_WORKS_ON_FIFOS\${SEDeB}SELECT_WORKS_ON_FIFOS\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -fr conftest*
+
+echo "checking whether or not poll() can be used on FIFOs"
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+main()
+{
+ int rfd, wfd, result; struct pollfd fds[1];
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || write(wfd, "foo", 3) != 3
+ || (fds[0].fd = rfd, fds[0].events = POLLIN, poll(fds, 1, 1) != 1)
+ || (fds[0].revents & POLLIN) == 0);
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+
+{
+test -n "$verbose" && \
+echo " defining POLL_WORKS_ON_FIFOS"
+echo "#define" POLL_WORKS_ON_FIFOS 1 >> confdefs.h
+DEFS="$DEFS -DPOLL_WORKS_ON_FIFOS=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}POLL_WORKS_ON_FIFOS\${SEDdB}POLL_WORKS_ON_FIFOS\${SEDdC}1\${SEDdD}
+\${SEDuA}POLL_WORKS_ON_FIFOS\${SEDuB}POLL_WORKS_ON_FIFOS\${SEDuC}1\${SEDuD}
+\${SEDeA}POLL_WORKS_ON_FIFOS\${SEDeB}POLL_WORKS_ON_FIFOS\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -fr conftest*
+
+echo checking whether POSIX-style non-blocking I/O works
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+handler(int sig) { }
+main ()
+{ /* Testing a POSIX feature, so assume FIFOs */
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, flags, result; char buffer[10];
+ unlink("/tmp/conftest-fifo");
+ signal(SIGALRM, handler);
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NONBLOCK)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || (flags = fcntl(rfd, F_GETFL, 0)) < 0
+ || fcntl(rfd, F_SETFL, flags | O_NONBLOCK) < 0
+ || (alarm(3), read(rfd, buffer, 10) >= 0)
+ || (alarm(0), errno != EAGAIN));
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+
+{
+test -n "$verbose" && \
+echo " defining POSIX_NONBLOCKING_WORKS"
+echo "#define" POSIX_NONBLOCKING_WORKS 1 >> confdefs.h
+DEFS="$DEFS -DPOSIX_NONBLOCKING_WORKS=1"
+SEDDEFS="${SEDDEFS}\${SEDdA}POSIX_NONBLOCKING_WORKS\${SEDdB}POSIX_NONBLOCKING_WORKS\${SEDdC}1\${SEDdD}
+\${SEDuA}POSIX_NONBLOCKING_WORKS\${SEDuB}POSIX_NONBLOCKING_WORKS\${SEDuC}1\${SEDuD}
+\${SEDeA}POSIX_NONBLOCKING_WORKS\${SEDeB}POSIX_NONBLOCKING_WORKS\${SEDeC}1\${SEDeD}
+"
+}
+
+
+fi
+rm -fr conftest*
+
+echo checking which MOO networking configurations are likely to work...
+
+NETWORK_CONFIGURATIONS="NP_SINGLE"
+
+echo checking for sys/socket.h
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/socket.h>
+EOF
+err=`eval "($CPP conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ rm -rf conftest*
+
+
+ NETWORK_CONFIGURATIONS="$NETWORK_CONFIGURATIONS NS_BSD/NP_LOCAL"
+ if test -z "$have_telnet"; then
+ # Extract the first word of `telnet', so it can be a program name with args.
+ set dummy telnet; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ have_telnet="yes"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$have_telnet" && have_telnet="no"
+test -n "$have_telnet" && test -n "$verbose" && echo " setting have_telnet to $have_telnet"
+
+ test x$have_telnet = xyes &&
+ NETWORK_CONFIGURATIONS="$NETWORK_CONFIGURATIONS NS_BSD/NP_TCP"
+
+fi
+rm -f conftest*
+
+cat > conftest.c <<EOF
+#include "confdefs.h"
+#include <sys/stat.h>
+#if defined(S_IFIFO) && !defined(NeXT) && \
+ (SELECT_WORKS_ON_FIFOS || POLL_WORKS_ON_FIFOS || FSTAT_WORKS_ON_FIFOS)
+ yes-there-are-FIFOs
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes-there-are-FIFOs" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+
+ NETWORK_CONFIGURATIONS="$NETWORK_CONFIGURATIONS NS_SYSV/NP_LOCAL"
+
+fi
+rm -f conftest*
+
+cat > conftest.c <<EOF
+#include "confdefs.h"
+
+#if HAVE_TIUSER_H && HAVE_T_OPEN && HAVE_POLL
+yes
+#endif
+
+EOF
+eval "$CPP conftest.c > conftest.out 2>&1"
+if egrep "yes" conftest.out >/dev/null 2>&1; then
+ rm -rf conftest*
+ test -r /dev/tcp &&
+ NETWORK_CONFIGURATIONS="$NETWORK_CONFIGURATIONS NS_SYSV/NP_TCP"
+
+fi
+rm -f conftest*
+
+
+echo "----------------------------------------------------------------------"
+echo "| The following networking configurations will probably work on your"
+echo "| system; any configuration *not* listed here almost certainly will"
+echo "| *not* work on your system:"
+echo "|"
+echo "| $NETWORK_CONFIGURATIONS"
+echo "----------------------------------------------------------------------"
+
+# Set default prefixes.
+if test -n "$prefix"; then
+ test -z "$exec_prefix" && exec_prefix='${prefix}'
+ prsub="s%^prefix\\([ ]*\\)=\\([ ]*\\).*$%prefix\\1=\\2$prefix%"
+fi
+if test -n "$exec_prefix"; then
+ prsub="$prsub
+s%^exec_prefix\\([ ]*\\)=\\([ ]*\\).*$%exec_prefix\\1=\\2$exec_prefix%"
+fi
+# Quote sed substitution magic chars in DEFS.
+cat >conftest.def <<EOF
+$DEFS
+EOF
+escape_ampersand_and_backslash='s%[&\\]%\\&%g'
+DEFS=`sed "$escape_ampersand_and_backslash" <conftest.def`
+rm -f conftest.def
+# Substitute for predefined variables.
+
+trap 'rm -f config.status; exit 1' 1 3 15
+echo creating config.status
+rm -f config.status
+cat > config.status <<EOF
+#!/bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $configure_args
+
+for arg
+do
+ case "\$arg" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo running \${CONFIG_SHELL-/bin/sh} $0 $configure_args
+ exec \${CONFIG_SHELL-/bin/sh} $0 $configure_args ;;
+ *) echo "Usage: config.status --recheck" 2>&1; exit 1 ;;
+ esac
+done
+
+trap 'rm -fr Makefile config.h conftest*; exit 1' 1 3 15
+YACC='$YACC'
+CC='$CC'
+CPP='$CPP'
+have_telnet='$have_telnet'
+LIBS='$LIBS'
+srcdir='$srcdir'
+prefix='$prefix'
+exec_prefix='$exec_prefix'
+prsub='$prsub'
+extrasub='$extrasub'
+EOF
+cat >> config.status <<\EOF
+
+top_srcdir=$srcdir
+
+CONFIG_FILES=${CONFIG_FILES-"Makefile"}
+for file in .. ${CONFIG_FILES}; do if test "x$file" != x..; then
+ srcdir=$top_srcdir
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ dir=`echo $file|sed 's%/[^/][^/]*$%%'`
+ if test "$dir" != "$file"; then
+ test "$top_srcdir" != . && srcdir=$top_srcdir/$dir
+ test ! -d $dir && mkdir $dir
+ fi
+ echo creating $file
+ rm -f $file
+ echo "# Generated automatically from `echo $file|sed 's|.*/||'`.in by configure." > $file
+ sed -e "
+$prsub
+$extrasub
+s%@YACC@%$YACC%g
+s%@CC@%$CC%g
+s%@CPP@%$CPP%g
+s%@have_telnet@%$have_telnet%g
+s%@LIBS@%$LIBS%g
+s%@srcdir@%$srcdir%g
+s%@DEFS@%-DHAVE_CONFIG_H%" $top_srcdir/${file}.in >> $file
+fi; done
+
+CONFIG_HEADERS=${CONFIG_HEADERS-"config.h"}
+for file in .. ${CONFIG_HEADERS}; do if test "x$file" != x..; then
+echo creating $file
+
+# These sed commands are put into SEDDEFS when defining a macro.
+# They are broken into pieces to make the sed script easier to manage.
+# They are passed to sed as "A NAME B NAME C VALUE D", where NAME
+# is the cpp macro being defined and VALUE is the value it is being given.
+# Each defining turns into a single global substitution command.
+#
+# SEDd sets the value in "#define NAME VALUE" lines.
+SEDdA='s@^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+SEDdB='\([ ][ ]*\)[^ ]*@\1#\2'
+SEDdC='\3'
+SEDdD='@g'
+# SEDu turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+SEDuA='s@^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+SEDuB='\([ ]\)@\1#\2define\3'
+SEDuC=' '
+SEDuD='\4@g'
+# SEDe turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+SEDeA='s@^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+SEDeB='$@\1#\2define\3'
+SEDeC=' '
+SEDeD='@g'
+rm -f conftest.sed
+EOF
+# Turn off quoting long enough to insert the sed commands.
+rm -f conftest.sh
+cat > conftest.sh <<EOF
+$SEDDEFS
+EOF
+
+# Break up $SEDDEFS (now in conftest.sh) because some shells have a limit
+# on the size of here documents.
+
+# Maximum number of lines to put in a single here document.
+maxshlines=9
+
+while :
+do
+ # wc gives bogus results for an empty file on some systems.
+ lines=`grep -c . conftest.sh`
+ if test -z "$lines" || test "$lines" -eq 0; then break; fi
+ rm -f conftest.s1 conftest.s2
+ sed ${maxshlines}q conftest.sh > conftest.s1 # Like head -20.
+ sed 1,${maxshlines}d conftest.sh > conftest.s2 # Like tail +21.
+ # Write a limited-size here document to append to conftest.sed.
+ echo 'cat >> conftest.sed <<CONFEOF' >> config.status
+ cat conftest.s1 >> config.status
+ echo 'CONFEOF' >> config.status
+ rm -f conftest.s1 conftest.sh
+ mv conftest.s2 conftest.sh
+done
+rm -f conftest.sh
+
+# Now back to your regularly scheduled config.status.
+cat >> config.status <<\EOF
+# This sed command replaces #undef's with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it in
+# config.h.
+cat >> conftest.sed <<\CONFEOF
+s,^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*,/* & */,
+CONFEOF
+rm -f conftest.h
+# Break up the sed commands because old seds have small limits.
+maxsedlines=20
+cp $top_srcdir/$file.in conftest.h1
+while :
+do
+ lines=`grep -c . conftest.sed`
+ if test -z "$lines" || test "$lines" -eq 0; then break; fi
+ rm -f conftest.s1 conftest.s2 conftest.h2
+ sed ${maxsedlines}q conftest.sed > conftest.s1 # Like head -20.
+ sed 1,${maxsedlines}d conftest.sed > conftest.s2 # Like tail +21.
+ sed -f conftest.s1 < conftest.h1 > conftest.h2
+ rm -f conftest.s1 conftest.h1 conftest.sed
+ mv conftest.h2 conftest.h1
+ mv conftest.s2 conftest.sed
+done
+rm -f conftest.sed conftest.h
+echo "/* $file. Generated automatically by configure. */" > conftest.h
+cat conftest.h1 >> conftest.h
+rm -f conftest.h1
+if cmp -s $file conftest.h 2>/dev/null; then
+ # The file exists and we would not be changing it.
+ echo "$file is unchanged"
+ rm -f conftest.h
+else
+ rm -f $file
+ mv conftest.h $file
+fi
+fi; done
+
+
+
+exit 0
+EOF
+chmod +x config.status
+${CONFIG_SHELL-/bin/sh} config.status
+
+
+# $Log$
+# Revision 1.1 1997/03/03 03:45:05 nop
+# Initial revision
+#
+# Revision 2.8 1996/03/19 07:16:46 pavel
+# Added one more option for trying to enable ANSI C compilation.
+# Release 1.8.0p2.
+#
+# Revision 2.7 1996/03/10 01:21:41 pavel
+# Increased DEC Alpha -Olimit to 2500 from 1000. Release 1.8.0.
+#
+# Revision 2.6 1996/02/11 00:43:22 pavel
+# Added check for matherr(). Release 1.8.0beta2.
+#
+# Revision 2.5 1996/02/08 07:22:30 pavel
+# Added checks for SGI machines and for strtod() being undeclared. Updated
+# copyright notice for 1996. Release 1.8.0beta1.
+#
+# Revision 2.4 1996/01/11 07:47:54 pavel
+# Added a handy form-feed between the macro definitions and the main script,
+# to make it easier to navigate with Emacs. Release 1.8.0alpha5.
+#
+# Revision 2.3 1995/12/31 03:17:07 pavel
+# Added test for <sys/stat.h> needing help. Release 1.8.0alpha4.
+#
+# Revision 2.2 1995/12/28 00:49:41 pavel
+# On NeXT, we now define _POSIX_SOURCE too. Release 1.8.0alpha3.
+#
+# Revision 2.1 1995/12/11 08:05:45 pavel
+# Added support for finding `crypt()' in `-lcrypt'. Release 1.8.0alpha2.
+#
+# Revision 2.0 1995/11/30 05:09:41 pavel
+# New baseline version, corresponding to release 1.8.0alpha1.
+#
+# Revision 1.5 1992/10/28 01:56:41 pavel
+# Fixed NDECL test to look for `vfprintf' instead of the unused `vprintf'...
+#
+# Revision 1.4 1992/10/23 23:13:18 pavel
+# Added copyright notice.
+#
+# Revision 1.3 1992/10/23 19:23:52 pavel
+# Added MOO_AUX, MOO_MALLOC_H, MOO_FUNC_DECL_CHECK.
+# Generalized MOO_HAVE_FUNC_LIBS to take multiple function names.
+# Refined MOO_CONST to check for warnings about `reserved' keywords.
+# Added MOO_NDECL_FUNCS checks for sigemptyset, perror, remove, vprintf,
+# bzero, memset, fork, fstat, and waitpid.
+# Simplified test for conflicting built-in function declarations.
+# Added SELECT_WORKS_ON_FIFOS and POSIX_NONBLOCKING_WORKS tests.
+#
+# Revision 1.2 1992/10/21 03:09:41 pavel
+# Added this log.
+
+# Local Variables:
+# mode: c
+# End:
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..d814f46
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,574 @@
+dnl Process this file with `autoconf' to create `configure'.
+dnl
+dnl Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+dnl Portions of this code were written by Stephen White, aka ghond.
+dnl Use and copying of this software and preparation of derivative works based
+dnl upon this software are permitted. Any distribution of this software or
+dnl derivative works must comply with all applicable United States export
+dnl control laws. This software is made available AS IS, and Xerox Corporation
+dnl makes no warranty about the software, its performance or its conformity to
+dnl any specification. Any person obtaining a copy of this software is
+dnl requested to send their name and post office or electronic mail address to:
+dnl Pavel Curtis
+dnl Xerox PARC
+dnl 3333 Coyote Hill Rd.
+dnl Palo Alto, CA 94304
+dnl Pavel@Xerox.Com
+dnl
+AC_INIT(Makefile.in)
+AC_CONFIG_HEADER(config.h)
+
+dnl ***************************************************************************
+dnl MOO_AUX
+dnl Defines _POSIX_SOURCE if this machine is running A/UX; this appears to be
+dnl necessary in order to get declarations of POSIX functions.
+define(MOO_AUX, [
+echo checking for A/UX
+AC_PROGRAM_EGREP(yes, [
+#if defined(AUX)
+ yes
+#endif
+], AC_DEFINE(_POSIX_SOURCE))])
+
+dnl ***************************************************************************
+dnl MOO_HPUX
+dnl Defines _HPUX_SOURCE if this machine is running HP/UX; this appears to be
+dnl necessary in order to get declarations of POSIX functions.
+define(MOO_HPUX, [
+echo checking for HP/UX
+AC_PROGRAM_EGREP(yes, [
+#if defined(__hpux)
+ yes
+#endif
+], AC_DEFINE(_HPUX_SOURCE))])
+
+dnl ***************************************************************************
+dnl MOO_ALPHA
+dnl If this machine is a DEC Alpha running OSF/1, and we're not using GCC, then
+dnl add `-Olimit 1000' to the `cc' switches, to allow the compiler to run the
+dnl optimizer over large functions, like the main MOO interpreter loop.
+define(MOO_ALPHA, [
+echo checking for the DEC Alpha running OSF/1
+AC_PROGRAM_EGREP(yes, [
+#if defined(__osf__) && defined(__alpha) && !defined(__GNUC__)
+ yes
+#endif
+], CC="$CC -Olimit 2500")])
+
+dnl ***************************************************************************
+dnl MOO_SGI
+dnl If this machine is an SGI running IRIX, and we're not using GCC, then
+dnl add `-Olimit 2500' to the `cc' switches, to allow the compiler to run the
+dnl optimizer over large functions, like the main MOO interpreter loop. Also,
+dnl even if we are using GCC, undefine __EXTENSIONS__ to keep from seeing a
+dnl bunch of interfering declarations.
+define(MOO_SGI, [
+echo checking for the SGI compiler
+AC_PROGRAM_EGREP(yes, [
+#if defined(sgi) && !defined(__GNUC__)
+ yes
+#endif
+], CC="$CC -Olimit 2500")
+AC_PROGRAM_EGREP(yes, [
+#if defined(sgi)
+ yes
+#endif
+], CC="$CC -U__EXTENSIONS__")])
+
+dnl ***************************************************************************
+dnl MOO_NEXT
+dnl On NeXT, we need to make sure that _NEXT_SOURCE and _POSIX_SOURCE are
+dnl defined.
+define(MOO_NEXT, [
+echo checking for NeXT
+AC_PROGRAM_EGREP(yes, [
+#ifdef NeXT
+ yes
+#endif
+], AC_DEFINE(_NEXT_SOURCE)
+ AC_DEFINE(_POSIX_SOURCE))])
+
+dnl ***************************************************************************
+dnl MOO_MALLOC_H
+dnl Define NEED_MALLOC_H if `malloc()' is not declared in <stdlib.h> and
+dnl <malloc.h> exists.
+define(MOO_MALLOC_H, [
+echo checking whether stdlib.h declares malloc and friends
+MOO_FUNC_DECL_CHECK(stdlib.h, malloc, ,
+ AC_HEADER_CHECK(malloc.h, AC_DEFINE(NEED_MALLOC_H)))])
+
+dnl ***************************************************************************
+dnl MOO_FUNC_DECL_CHECK(header, func,
+dnl if-declared [, if-not-declared[, extra-hdr]])
+dnl Do `if-declared' is `func' is declared in `header', and if-not-declared
+dnl otherwise. If `extra-hdr' is provided, it is added after the #include of
+dnl `header'.
+define(MOO_FUNC_DECL_CHECK, [
+changequote(,)dnl
+pattern="[^_a-zA-Z0-9]$2 *\("
+changequote([,])dnl
+AC_PROGRAM_EGREP($pattern, [
+#include <$1>
+$5
+], $3, $4)])
+
+dnl ***************************************************************************
+dnl MOO_NDECL_FUNCS(header, func1 func2 ...[, extra-hdr])
+dnl Defines NDECL_func1, NDECL_func2, ... if they are not declared in header.
+dnl
+define(MOO_NDECL_FUNCS, [
+changequote(,)dnl
+trfrom='[a-z]' trto='[A-Z]'
+changequote([,])dnl
+for func in $2
+do
+echo "checking whether $func is declared in $1"
+MOO_FUNC_DECL_CHECK($1, $func, ,
+ AC_DEFINE(NDECL_`echo $func | tr "$trfrom" "$trto"`), $3)
+done
+])
+
+dnl ***************************************************************************
+dnl MOO_VAR_DECL_CHECK(header, variable,
+dnl if-declared [, if-not-declared[, extra-hdr]])
+dnl Do `if-declared' is `variable' is declared in `header', and if-not-declared
+dnl otherwise. If `extra-hdr' is provided, it is added after the #include of
+dnl `header'.
+define(MOO_VAR_DECL_CHECK, [
+changequote(,)dnl
+pattern="[^_a-zA-Z0-9]$2"
+changequote([,])dnl
+AC_PROGRAM_EGREP($pattern, [
+#include <$1>
+$5
+], $3, $4)])
+
+dnl ***************************************************************************
+dnl MOO_NDECL_VARS(header, var1 var2 ...[, extra-hdr])
+dnl Defines NDECL_var1, NDECL_var2, ... if they are not declared in header.
+dnl
+define(MOO_NDECL_VARS, [
+changequote(,)dnl
+trfrom='[a-z]' trto='[A-Z]'
+changequote([,])dnl
+for var in $2
+do
+echo "checking whether $var is declared in $1"
+MOO_VAR_DECL_CHECK($1, $var, ,
+ AC_DEFINE(NDECL_`echo $var | tr "$trfrom" "$trto"`), $3)
+done
+])
+
+dnl ***************************************************************************
+dnl MOO_HEADER_STANDS_ALONE(header [, extra-code])
+dnl Defines header_NEEDS_HELP if can't be compiled all by itself.
+define(MOO_HEADER_STANDS_ALONE, [
+changequote(,)dnl
+trfrom='[a-z]./' trto='[A-Z]__'
+changequote([,])dnl
+AC_COMPILE_CHECK(self-sufficiency of $1, [
+#include <$1>
+$2
+], , , AC_DEFINE(`echo $1 | tr "$trfrom" "$trto"`_NEEDS_HELP))
+])
+
+dnl ***************************************************************************
+dnl MOO_HAVE_FUNC_LIBS(func1 func2 ..., lib1 "lib2a lib2b" lib3 ...)
+dnl For each `func' in turn, if `func' is defined using the current LIBS value,
+dnl leave LIBS alone. Otherwise, try adding each of the given libs to LIBS in
+dnl turn, stopping when one of them succeeds in providing `func'. Define
+dnl HAVE_func if `func' is eventually found.
+define(MOO_HAVE_FUNC_LIBS, [
+for func in $1
+do
+ changequote(,)dnl
+ trfrom='[a-z]' trto='[A-Z]'
+ var=HAVE_`echo $func | tr "$trfrom" "$trto"`
+ changequote([,])dnl
+ AC_FUNC_CHECK($func, AC_DEFINE($var), [
+ SAVELIBS="$LIBS"
+ for lib in $2
+ do
+ LIBS="$LIBS $lib"
+ AC_FUNC_CHECK($func, [AC_DEFINE($var)
+ break],
+ LIBS="$SAVELIBS")
+ done
+ ])
+done
+])
+
+dnl ***************************************************************************
+dnl MOO_HAVE_HEADER_DIRS(header1 header2 ..., dir1 dir2 ...)
+dnl For each `header' in turn, if `header' is found using the current CC value
+dnl leave CC alone. Otherwise, try adding each of the given `dir's to CC in
+dnl turn, stopping when one of them succeeds in providing `header'. Define
+dnl HAVE_header if `header' is eventually found.
+define(MOO_HAVE_HEADER_DIRS, [
+for hdr in $1
+do
+ changequote(,)dnl
+ trfrom='[a-z]./' trto='[A-Z]__'
+ var=HAVE_`echo $hdr | tr "$trfrom" "$trto"`
+ changequote([,])dnl
+ AC_HEADER_CHECK($hdr, AC_DEFINE($var), [
+ SAVECC="$CC"
+ for dir in $2
+ do
+ CC="$CC $dir"
+ AC_HEADER_CHECK($hdr, [AC_DEFINE($var)
+ break],
+ CC="$SAVECC")
+ done
+ ])
+done
+])
+
+dnl ***************************************************************************
+dnl MOO_CONST
+dnl Check whether or not the C compiler can cope with simple uses of the ANSI C
+dnl `const' keyword, defining `const' as the empty string if it can't.
+define(MOO_CONST, [
+echo checking for a working const keyword
+cat << EOF > conftest.c
+#ifdef __GNUC__
+#define const __const__
+#endif
+int foo(const char *x) { return 17 + (x - x); }
+int bar() { int x = foo("foo"); return x; }
+EOF
+if $CC -c conftest.c > conftest.out 2>&1; then
+ if test -s conftest.out; then
+ AC_DEFINE(const,)
+ fi
+else
+ AC_DEFINE(const,)
+fi
+rm -f conftest*
+])
+
+dnl ***************************************************************************
+dnl MOO_ANSI_C
+dnl Check whether or not the C compiler handles ANSI C (i.e., allows function
+dnl prototypes and the `void *' type) and try to make it do so by adding
+dnl command-line options like -Aa and -Xa, which some compilers require. If
+dnl nothing works, abort configuration.
+define(MOO_ANSI_C, [
+echo "checking that the C compiler handles important ANSI C constructs"
+for opt in "" -Aa -Xa -ansi
+do
+SAVECC="$CC"
+CC="$CC $opt"
+AC_TEST_PROGRAM([
+int main(int argc, char *argv) { void *ptr; exit(0); }
+],
+[have_ansi=1
+break],
+[CC="$SAVECC"])
+done
+if test -z "$have_ansi"; then
+echo ""
+echo "*** Sorry, but I can't figure out how to find an ANSI C compiler here."
+echo "*** Compiling this program requires such a compiler."
+exit 1
+fi
+])
+
+dnl ***************************************************************************
+dnl Start of actual configuration tests
+dnl ***************************************************************************
+
+AC_PROG_YACC
+AC_PROG_CC
+AC_GCC_TRADITIONAL
+AC_PROGRAM_EGREP(yes, [
+#if __GNUC__==2
+ yes
+#endif
+], [CC="$CC -Wall -Wwrite-strings"])
+test -n "$GCC" && CC="$CC -g"
+AC_PROG_CPP
+MOO_NEXT
+MOO_ALPHA
+MOO_SGI
+AC_AIX
+AC_ISC_POSIX
+AC_MINIX
+AC_SCO_INTL
+MOO_AUX
+MOO_HPUX
+MOO_ANSI_C
+MOO_CONST
+CPP=""
+AC_PROG_CPP
+MOO_MALLOC_H
+AC_HEADER_CHECK(sys/bsdtypes.h, AC_DEFINE(NEED_BSDTYPES_H))
+AC_HEADER_CHECK(sys/select.h, AC_DEFINE(NEED_SELECT_H))
+AC_HEADER_CHECK(memory.h, AC_DEFINE(NEED_MEMORY_H))
+AC_PID_T
+AC_MODE_T
+AC_SIZE_T
+AC_STRUCT_TM
+AC_TIMEZONE
+MOO_HAVE_FUNC_LIBS(sqrt, -lm /lib/libm.a "-static -lm")
+MOO_HAVE_FUNC_LIBS(mkfifo waitpid sigemptyset, -lposix /lib/libposix.a)
+MOO_HAVE_FUNC_LIBS(accept, "-lsocket -lnsl" -lsocket -linet)
+MOO_HAVE_FUNC_LIBS(t_open, -lnsl -lnsl_s)
+MOO_HAVE_FUNC_LIBS(crypt, -lcrypt -lcrypt_d)
+AC_HAVE_HEADERS(unistd.h sys/cdefs.h stdlib.h tiuser.h machine/endian.h)
+AC_HAVE_FUNCS(remove rename poll select strerror strftime strtoul matherr)
+AC_HAVE_FUNCS(random lrand48 wait3 wait2 sigsetmask sigprocmask sigrelse)
+MOO_NDECL_FUNCS(ctype.h, tolower)
+MOO_NDECL_FUNCS(fcntl.h, fcntl)
+MOO_NDECL_FUNCS(netinet/in.h, htonl)
+MOO_NDECL_FUNCS(sys/ioctl.h, ioctl)
+MOO_NDECL_FUNCS(poll.h, poll)
+MOO_NDECL_FUNCS(signal.h, kill sigemptyset sigprocmask sigrelse)
+MOO_NDECL_FUNCS(sys/socket.h, accept bind shutdown)
+MOO_NDECL_FUNCS(sys/stat.h, fstat)
+MOO_NDECL_FUNCS(stdio.h, fclose perror remove vfprintf)
+MOO_NDECL_FUNCS(stdlib.h, random srandom strtod strtol strtoul)
+MOO_NDECL_FUNCS(string.h, bzero memcpy memset strerror, [
+#if NEED_MEMORY_H
+#include <memory.h>
+#endif
+])
+echo checking for bzero being declared in stdlib.h
+MOO_FUNC_DECL_CHECK(stdlib.h, bzero, AC_DEFINE(BZERO_IN_STDLIB_H))
+MOO_NDECL_FUNCS(sys/time.h, getitimer setitimer select, [
+#if NEED_SELECT_H
+#include <sys/types.h>
+#include <sys/select.h>
+#endif
+])
+MOO_NDECL_FUNCS(time.h, strftime time)
+MOO_NDECL_VARS(time.h, tzname)
+MOO_NDECL_FUNCS(tiuser.h, t_open)
+MOO_NDECL_VARS(tiuser.h, t_errlist)
+MOO_NDECL_FUNCS(unistd.h, fork)
+MOO_NDECL_FUNCS(sys/wait.h, waitpid)
+
+MOO_HEADER_STANDS_ALONE(arpa/inet.h, [struct in_addr foo;])
+MOO_HEADER_STANDS_ALONE(signal.h)
+MOO_HEADER_STANDS_ALONE(sys/socket.h)
+MOO_HEADER_STANDS_ALONE(sys/stat.h)
+MOO_HEADER_STANDS_ALONE(time.h)
+
+dnl ***************************************************************************
+echo checking for string.h declaring some functions incorrectly
+echo "
+#include <sys/types.h>
+#include <string.h>
+#if NEED_MEMORY_H
+#include <memory.h>
+#endif
+" > conftest.c
+$CC $DEFS conftest.c -o conftest $LIBS > conftest.out 2>&1
+if grep "conflicting types for built-in function" conftest.out >/dev/null; then
+ AC_DEFINE(USE_OWN_STRING_H)
+fi
+rm -f conftest*
+
+dnl ***************************************************************************
+echo "checking for incompatibility between <sys/ioctl.h> and <stropts.h>"
+AC_TEST_CPP([
+#include <sys/ioctl.h>
+#include <stropts.h>
+], , AC_DEFINE(UNDEF_IO_IN_STROPTS_H))
+
+dnl ***************************************************************************
+echo "checking whether or not fstat() can tell how much data is in a FIFO"
+AC_TEST_PROGRAM([#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+main()
+{
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, result; struct stat st;
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || write(wfd, "foo", 3) != 3
+ || fstat(rfd, &st) < 0
+ || st.st_size != 3);
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+], AC_DEFINE(FSTAT_WORKS_ON_FIFOS))
+
+dnl ***************************************************************************
+echo "checking whether or not select() can be used on FIFOs"
+AC_TEST_PROGRAM([#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#ifndef FD_ZERO
+#define NFDBITS (sizeof(fd_set)*8)
+#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p)))
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1L<<((n)%NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1L<<((n)%NFDBITS)))
+#endif /* FD_ZERO */
+main()
+{
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, result; fd_set input; struct timeval tv;
+ tv.tv_sec = 2;
+ tv.tv_usec = 0;
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || (FD_ZERO(&input), FD_SET(rfd, &input),
+ select(rfd + 1, &input, 0, 0, &tv) != 0)
+ || write(wfd, "foo", 3) != 3
+ || (FD_ZERO(&input), FD_SET(rfd, &input),
+ select(rfd + 1, &input, 0, 0, &tv) != 1)
+ || !FD_ISSET(rfd, &input));
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+], AC_DEFINE(SELECT_WORKS_ON_FIFOS))
+
+dnl ***************************************************************************
+echo "checking whether or not poll() can be used on FIFOs"
+AC_TEST_PROGRAM([#include <sys/types.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+main()
+{
+ int rfd, wfd, result; struct pollfd fds[1];
+ unlink("/tmp/conftest-fifo");
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NDELAY)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || write(wfd, "foo", 3) != 3
+ || (fds[0].fd = rfd, fds[0].events = POLLIN, poll(fds, 1, 1) != 1)
+ || (fds[0].revents & POLLIN) == 0);
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+], AC_DEFINE(POLL_WORKS_ON_FIFOS))
+
+dnl ***************************************************************************
+echo checking whether POSIX-style non-blocking I/O works
+AC_TEST_PROGRAM([#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+handler(int sig) { }
+main ()
+{ /* Testing a POSIX feature, so assume FIFOs */
+#ifdef NeXT
+/* The NeXT claims to have FIFOs, but using them panics the kernel... */
+ exit(-1);
+#endif
+ int rfd, wfd, flags, result; char buffer[10];
+ unlink("/tmp/conftest-fifo");
+ signal(SIGALRM, handler);
+ result = (mknod("/tmp/conftest-fifo", 0666 | S_IFIFO, 0) < 0
+ || (rfd = open("/tmp/conftest-fifo", O_RDONLY | O_NONBLOCK)) < 0
+ || (wfd = open("/tmp/conftest-fifo", O_WRONLY)) < 0
+ || (flags = fcntl(rfd, F_GETFL, 0)) < 0
+ || fcntl(rfd, F_SETFL, flags | O_NONBLOCK) < 0
+ || (alarm(3), read(rfd, buffer, 10) >= 0)
+ || (alarm(0), errno != EAGAIN));
+ unlink("/tmp/conftest-fifo");
+ exit(result);
+}
+], AC_DEFINE(POSIX_NONBLOCKING_WORKS))
+
+dnl ***************************************************************************
+echo checking which MOO networking configurations are likely to work...
+define(MOO_ADD_NET_CONFIG,[
+ NETWORK_CONFIGURATIONS="$NETWORK_CONFIGURATIONS $1"])
+NETWORK_CONFIGURATIONS="NP_SINGLE"
+
+AC_HEADER_CHECK(sys/socket.h, [
+ MOO_ADD_NET_CONFIG(NS_BSD/NP_LOCAL)
+ AC_PROGRAM_CHECK(have_telnet, telnet, yes, no)
+ test x$have_telnet = xyes && MOO_ADD_NET_CONFIG(NS_BSD/NP_TCP)])
+AC_PROGRAM_EGREP(yes-there-are-FIFOs, [#include <sys/stat.h>
+#if defined(S_IFIFO) && !defined(NeXT) && \
+ (SELECT_WORKS_ON_FIFOS || POLL_WORKS_ON_FIFOS || FSTAT_WORKS_ON_FIFOS)
+ yes-there-are-FIFOs
+#endif
+], MOO_ADD_NET_CONFIG(NS_SYSV/NP_LOCAL))
+AC_PROGRAM_EGREP(yes, [
+#if HAVE_TIUSER_H && HAVE_T_OPEN && HAVE_POLL
+yes
+#endif
+], test -r /dev/tcp && MOO_ADD_NET_CONFIG(NS_SYSV/NP_TCP))
+
+echo "----------------------------------------------------------------------"
+echo "| The following networking configurations will probably work on your"
+echo "| system; any configuration *not* listed here almost certainly will"
+echo "| *not* work on your system:"
+echo "|"
+echo "| $NETWORK_CONFIGURATIONS"
+echo "----------------------------------------------------------------------"
+
+AC_OUTPUT(Makefile)
+
+# $Log$
+# Revision 1.1 1997/03/03 03:45:05 nop
+# Initial revision
+#
+# Revision 2.8 1996/03/19 07:16:46 pavel
+# Added one more option for trying to enable ANSI C compilation.
+# Release 1.8.0p2.
+#
+# Revision 2.7 1996/03/10 01:21:41 pavel
+# Increased DEC Alpha -Olimit to 2500 from 1000. Release 1.8.0.
+#
+# Revision 2.6 1996/02/11 00:43:22 pavel
+# Added check for matherr(). Release 1.8.0beta2.
+#
+# Revision 2.5 1996/02/08 07:22:30 pavel
+# Added checks for SGI machines and for strtod() being undeclared. Updated
+# copyright notice for 1996. Release 1.8.0beta1.
+#
+# Revision 2.4 1996/01/11 07:47:54 pavel
+# Added a handy form-feed between the macro definitions and the main script,
+# to make it easier to navigate with Emacs. Release 1.8.0alpha5.
+#
+# Revision 2.3 1995/12/31 03:17:07 pavel
+# Added test for <sys/stat.h> needing help. Release 1.8.0alpha4.
+#
+# Revision 2.2 1995/12/28 00:49:41 pavel
+# On NeXT, we now define _POSIX_SOURCE too. Release 1.8.0alpha3.
+#
+# Revision 2.1 1995/12/11 08:05:45 pavel
+# Added support for finding `crypt()' in `-lcrypt'. Release 1.8.0alpha2.
+#
+# Revision 2.0 1995/11/30 05:09:41 pavel
+# New baseline version, corresponding to release 1.8.0alpha1.
+#
+# Revision 1.5 1992/10/28 01:56:41 pavel
+# Fixed NDECL test to look for `vfprintf' instead of the unused `vprintf'...
+#
+# Revision 1.4 1992/10/23 23:13:18 pavel
+# Added copyright notice.
+#
+# Revision 1.3 1992/10/23 19:23:52 pavel
+# Added MOO_AUX, MOO_MALLOC_H, MOO_FUNC_DECL_CHECK.
+# Generalized MOO_HAVE_FUNC_LIBS to take multiple function names.
+# Refined MOO_CONST to check for warnings about `reserved' keywords.
+# Added MOO_NDECL_FUNCS checks for sigemptyset, perror, remove, vprintf,
+# bzero, memset, fork, fstat, and waitpid.
+# Simplified test for conflicting built-in function declarations.
+# Added SELECT_WORKS_ON_FIFOS and POSIX_NONBLOCKING_WORKS tests.
+#
+# Revision 1.2 1992/10/21 03:09:41 pavel
+# Added this log.
+
+# Local Variables:
+# mode: c
+# End:
diff --git a/db.h b/db.h
new file mode 100644
index 0000000..a11fbae
--- /dev/null
+++ b/db.h
@@ -0,0 +1,547 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#ifndef DB_h
+#define DB_h 1
+
+#include "config.h"
+#include "program.h"
+#include "structures.h"
+
+
+/**** file system ****/
+
+extern const char *db_usage_string(void);
+ /* Returns a string describing the database
+ * command-line arguments.
+ */
+
+extern int db_initialize(int *pargc, char ***pargv);
+ /* (*pargc) and (*pargv) refer to the database
+ * command-line arguments and perhaps others.
+ * Returns true (and sets (*pargc) and (*pargv)
+ * to refer to any extra arguments) if the
+ * database args were valid.
+ */
+
+extern int db_load(void);
+ /* Does any necessary long-running preparations
+ * of the database, such as loading significant
+ * amounts of data into memory, returning true
+ * iff this operation is successful.
+ */
+
+enum db_flush_type {
+ FLUSH_IF_FULL, /* Do output only if there's `too much' changed
+ * data in memory now. */
+ FLUSH_ONE_SECOND, /* Do output for up to about one second. */
+ FLUSH_ALL_NOW, /* Do all pending output, forking the server
+ * if necessary. */
+ FLUSH_PANIC /* Do all pending output; the server is going
+ * down in emergency mode. */
+ };
+
+extern int db_flush(enum db_flush_type);
+ /* Flush some amount of the changed portion of
+ * the database to disk, as indicated by the
+ * argument. Returns true on success.
+ */
+
+extern int32 db_disk_size(void);
+ /* Return the total size, in bytes, of the most
+ * recent full representation of the database
+ * as one or more disk files. Returns -1 if,
+ * for some reason, no such on-disk
+ * representation is currently available.
+ */
+
+extern void db_shutdown(void);
+ /* Shut down the database module, flushing all
+ * pending database changes to disk and only
+ * returning after this is done.
+ */
+
+/**** objects ****/
+
+extern Objid db_create_object(void);
+ /* Creates a new object with parent & location
+ * == #-1. Returns new object's id number.
+ */
+
+extern Objid db_last_used_objid(void);
+extern void db_reset_last_used_objid(void);
+ /* The former returns the highest object number
+ * ever returned by db_create_object(). The
+ * latter resets that high-water mark to the
+ * highest object number referring to a
+ * currently-valid object.
+ */
+
+extern void db_destroy_object(Objid);
+ /* Destroys object, freeing all associated
+ * storage. The object's parent and location
+ * must == #-1 and it must not have any
+ * children or contents.
+ */
+
+extern Objid db_renumber_object(Objid);
+ /* Renumbers object to have the lowest free
+ * object number. Returns its new number.
+ */
+
+extern int valid(Objid);
+
+extern int db_object_bytes(Objid);
+ /* Returns the number of bytes of memory
+ * currently required in order to represent the
+ * given object and all of its verbs and
+ * properties.
+ */
+
+/**** object attributes ****/
+
+extern Objid db_object_owner(Objid);
+extern void db_set_object_owner(Objid oid, Objid owner);
+
+extern const char *db_object_name(Objid);
+extern void db_set_object_name(Objid oid, const char *name);
+ /* These functions do not change the reference
+ * count of the name they accept/return. Thus,
+ * the caller should str_ref() the name if the
+ * reference is to be persistent.
+ */
+
+extern Objid db_object_parent(Objid);
+extern int db_count_children(Objid);
+extern int db_for_all_children(Objid,
+ int (*)(void *, Objid),
+ void *);
+ /* The outcome is unspecified if any of the
+ * following functions are called during a call
+ * to db_for_all_children():
+ * db_create_object()
+ * db_destroy_object()
+ * db_renumber_object()
+ * db_change_parent()
+ */
+extern int db_change_parent(Objid oid, Objid parent);
+ /* db_change_parent() returns true (and
+ * actually changes the parent of OID) iff
+ * neither OID nor any of its descendents
+ * defines any properties with the same names
+ * as properties defined on PARENT or any of
+ * its ancestors.
+ */
+
+extern Objid db_object_location(Objid);
+extern int db_count_contents(Objid);
+extern int db_for_all_contents(Objid,
+ int (*)(void *, Objid),
+ void *);
+ /* The outcome is unspecified if any of the
+ * following functions are called during a call
+ * to db_for_all_contects():
+ * db_destroy_object()
+ * db_renumber_object()
+ * db_change_location()
+ */
+extern void db_change_location(Objid oid, Objid location);
+
+/* NOTE: New flags must always be added to the end of this list, rather than
+ * replacing one of the obsolete ones, since old databases might have
+ * old objects around that still have that flag set.
+ */
+typedef enum {
+ FLAG_USER, FLAG_PROGRAMMER, FLAG_WIZARD, FLAG_OBSOLETE_1,
+ FLAG_READ, FLAG_WRITE, FLAG_OBSOLETE_2, FLAG_FERTILE
+} db_object_flag;
+
+extern int db_object_has_flag(Objid, db_object_flag);
+extern void db_set_object_flag(Objid, db_object_flag);
+extern void db_clear_object_flag(Objid, db_object_flag);
+
+extern int db_object_allows(Objid oid, Objid progr,
+ db_object_flag flag);
+ /* Returns true iff either OID has FLAG or
+ * PROGR either is a wizard or owns OID; that
+ * is, iff PROGR's authority is sufficient to
+ * be allowed to do the indicated operation on
+ * OID.
+ */
+
+extern int is_wizard(Objid oid);
+extern int is_programmer(Objid oid);
+extern int is_user(Objid oid);
+
+extern Var db_all_users(void);
+ /* db_all_users() does not change the reference
+ * count of the `users' list maintained by this
+ * module. The caller should thus var_ref() it
+ * to make it persistent.
+ */
+
+
+/**** properties *****/
+
+typedef enum {
+ PF_READ = 01,
+ PF_WRITE = 02,
+ PF_CHOWN = 04
+} db_prop_flag;
+
+extern int db_add_propdef(Objid oid, const char *pname,
+ Var value, Objid owner,
+ unsigned flags);
+ /* Returns true (and actually adds the property
+ * to OID) iff (1) no property named PNAME
+ * already exists on OID or one of its
+ * ancestors or descendants, and (2) PNAME does
+ * not name any built-in property. This
+ * function does not change the reference count
+ * of VALUE, so the caller should var_ref() it
+ * if the caller's reference is to persist.
+ * FLAGS should be the result of ORing together
+ * zero or more elements of `db_prop_flag'.
+ */
+
+extern int db_rename_propdef(Objid oid, const char *old,
+ const char *new);
+ /* Returns true (and actually renames the
+ * propdef on OID) iff (1) a propdef with the
+ * name OLD existed on OID, (2) no property
+ * named NEW already exists on OID or one of
+ * its ancestors or descendants, and (3) NEW
+ * does not name any built-in property. If
+ * condition (1) holds and OLD == NEW, then
+ * this is a no-op that returns true.
+ */
+
+extern int db_delete_propdef(Objid, const char *);
+ /* Returns true iff a propdef with the given
+ * name existed on the object (i.e., was there
+ * to be deleted).
+ */
+
+extern int db_count_propdefs(Objid);
+extern int db_for_all_propdefs(Objid,
+ int (*)(void *, const char *),
+ void *);
+ /* db_for_all_propdefs() does not change the
+ * reference counts of the property names
+ * passed to the given callback function.
+ * Thus, the caller should str_ref() the names
+ * if the references are to be persistent.
+ */
+
+enum bi_prop {
+ BP_NONE = 0,
+ BP_NAME, BP_OWNER,
+ BP_PROGRAMMER, BP_WIZARD,
+ BP_R, BP_W, BP_F,
+ BP_LOCATION, BP_CONTENTS
+};
+
+typedef struct {
+ enum bi_prop built_in; /* true iff property is a built-in one */
+ Objid definer; /* if !built_in, the object defining prop */
+ void *ptr; /* null iff property not found */
+} db_prop_handle;
+
+extern db_prop_handle db_find_property(Objid oid, const char *name,
+ Var *value);
+ /* Returns a handle on the named property of
+ * the given object. If `value' is non-null,
+ * then the value of the named property (after
+ * skipping over any `clear' slots up the
+ * ancestor list) will be stored through it.
+ * For non-built-in properties, the reference
+ * count of the value is not changed by this
+ * function, so the caller should var_ref() it
+ * if it is to be persistent; some built-in
+ * properties have freshly generated values, so
+ * the caller should free_var() the value in
+ * the built-in case if it is *not* to be
+ * persistent. The `ptr' in the result of this
+ * function will be null iff there is no such
+ * property on the object. The `built_in'
+ * field in the result is true iff the named
+ * property is one built into all objects, in
+ * which case it specifies which such property
+ * it is. The returned handle is very
+ * volatile; only the routines declared
+ * immediately below as taking a
+ * `db_prop_handle' argument are guaranteed to
+ * leave the handle intact.
+ */
+
+extern Var db_property_value(db_prop_handle);
+extern void db_set_property_value(db_prop_handle, Var);
+ /* For non-built-in properties, these functions
+ * do not change the reference count of the
+ * value they accept/return, so the caller
+ * should var_ref() the value if it is to be
+ * persistent. Some built-in properties have
+ * freshly generated values, so the caller
+ * should free_var() the value in the built-in
+ * case if it is *not* to be persistent.
+ * The server will panic if
+ * `db_set_property_value()' is called with an
+ * illegal value for a built-in property.
+ */
+
+extern Objid db_property_owner(db_prop_handle);
+extern void db_set_property_owner(db_prop_handle, Objid);
+ /* These functions may not be called for
+ * built-in properties.
+ */
+
+extern unsigned db_property_flags(db_prop_handle);
+extern void db_set_property_flags(db_prop_handle, unsigned);
+ /* The property flags are the result of ORing
+ * together zero or more elements of
+ * `db_prop_flag', defined above. These
+ * functions may not be called for built-in
+ * properties.
+ */
+
+extern int db_property_allows(db_prop_handle, Objid,
+ db_prop_flag);
+ /* Returns true iff either the property has the
+ * flag or the object either is a wizard or
+ * owns the property; that is, iff the object's
+ * authority is sufficient to be allowed to do
+ * the indicated operation on the property.
+ * This function may not be called for built-in
+ * properties.
+ */
+
+
+/**** verbs ****/
+
+typedef enum {
+ VF_READ = 01,
+ VF_WRITE = 02,
+ VF_EXEC = 04,
+ VF_DEBUG = 010
+} db_verb_flag;
+
+typedef enum {
+ ASPEC_NONE, ASPEC_ANY, ASPEC_THIS
+} db_arg_spec;
+
+typedef enum {
+ PREP_ANY = -2,
+ PREP_NONE = -1,
+ Other_Preps = 0 /* Others are indices into DB-internal table */
+} db_prep_spec;
+
+extern db_prep_spec db_find_prep(int argc, char *argv[],
+ int *first, int *last);
+ /* Look for a prepositional phrase in the
+ * ARGC-element sequence ARGV, returning
+ * PREP_NONE if none was found. If FIRST and
+ * LAST are both non-null, then the indices
+ * of the first and last word in the phrase
+ * are stored through them. If either FIRST
+ * or LAST is null, then the match will fail
+ * unless the entire sequence is a phrase.
+ */
+
+extern db_prep_spec db_match_prep(const char *);
+ /* Try to recognize the given string as a
+ * prepositional phrase, returning the
+ * appropriate db_prep_spec on success and
+ * PREP_NONE on failure. The string may be of
+ * the form
+ * [#]<digit>+
+ * (an optional hash mark followed by one or
+ * more decimal digits). If the number so
+ * represented is a legitimate index for a
+ * prepositional phrase in the DB-internal
+ * table, the corresponding db_prep_spec is
+ * returned, or PREP_NONE otherwise.
+ */
+
+extern const char *db_unparse_prep(db_prep_spec);
+ /* Returns a string giving the human-readable
+ * name(s) for the given preposition
+ * specification. The returned string may not
+ * be dynamically allocated, so the caller
+ * should str_dup() it if it is to be
+ * persistent.
+ */
+
+extern void db_add_verb(Objid oid, const char *vnames,
+ Objid owner, unsigned flags,
+ db_arg_spec dobj, db_prep_spec prep,
+ db_arg_spec iobj);
+ /* This function does not change the reference
+ * count of the NAMES string it accepts. Thus,
+ * the caller should str_ref() it if it is to
+ * be persistent.
+ */
+
+extern int db_count_verbs(Objid);
+extern int db_for_all_verbs(Objid,
+ int (*)(void *, const char *),
+ void *);
+ /* db_for_all_verbs() does not change the
+ * reference counts of the verb names
+ * passed to the given callback function.
+ * Thus, the caller should str_ref() the names
+ * if the references are to be persistent.
+ */
+
+typedef struct {
+ void *ptr;
+} db_verb_handle;
+
+extern db_verb_handle db_find_command_verb(Objid oid, const char *verb,
+ db_arg_spec dobj, unsigned prep,
+ db_arg_spec iobj);
+ /* Returns a handle on the first matching
+ * verb found defined on OID or one of its
+ * ancestors. A matching verb has a name
+ * matching VERB, a dobj spec equal either to
+ * ASPEC_ANY or DOBJ, a prep spec equal either
+ * to PREP_ANY or PREP, and an iobj spec equal
+ * either to ASPEC_ANY or IOBJ. The `ptr' in
+ * the result is null iff there is no such
+ * verb. The returned handle is very volatile;
+ * only the routines declared below as taking a
+ * `db_verb_handle' argument are guaranteed to
+ * leave the handle intact.
+ */
+
+extern db_verb_handle db_find_callable_verb(Objid oid, const char *verb);
+ /* Returns a handle on the first verb found
+ * defined on OID or one of its ancestors with
+ * a name matching VERB (and, for now, the
+ * VF_EXEC flag set). The `ptr' in the result
+ * is null iff there is no such verb. The
+ * returned handle is very volatile; only the
+ * routines declared below as taking a
+ * `db_verb_handle' argument are guaranteed to
+ * leave the handle intact.
+ */
+
+extern db_verb_handle db_find_defined_verb(Objid oid, const char *verb,
+ int allow_numbers);
+ /* Returns a handle on the first verb found
+ * defined on OID with a name matching VERB
+ * (or, if ALLOW_NUMBERS is true and VERB has
+ * the form of a decimal natural number, the
+ * zero-based VERB'th verb defined on OID,
+ * whichever comes first). The `ptr' in the
+ * result is null iff there is no such verb.
+ * The returned handle is very volatile; only
+ * the routines declared below as taking a
+ * `db_verb_handle' argument are guaranteed to
+ * leave the handle intact.
+ */
+
+extern db_verb_handle db_find_indexed_verb(Objid oid, unsigned index);
+ /* Returns a handle on the 1-based INDEX'th
+ * verb defined on OID. The `ptr' in the
+ * result is null iff there is no such verb.
+ * The returned handle is very volatile; only
+ * the routines declared below as taking a
+ * `db_verb_handle' argument are guaranteed to
+ * leave the handle intact.
+ */
+
+extern Objid db_verb_definer(db_verb_handle);
+ /* Returns the object on which the given verb
+ * is defined.
+ */
+
+extern const char *db_verb_names(db_verb_handle);
+extern void db_set_verb_names(db_verb_handle, const char *);
+ /* These functions do not change the reference
+ * count of the string they accept/return.
+ * Thus, the caller should str_ref() it if it
+ * is to be persistent.
+ */
+
+extern Objid db_verb_owner(db_verb_handle);
+extern void db_set_verb_owner(db_verb_handle, Objid);
+
+extern unsigned db_verb_flags(db_verb_handle);
+extern void db_set_verb_flags(db_verb_handle, unsigned);
+
+extern Program *db_verb_program(db_verb_handle);
+extern void db_set_verb_program(db_verb_handle, Program *);
+ /* These functions do not change the reference
+ * count of the program they accept/return.
+ * Thus, the caller should program_ref() it if
+ * it is to be persistent.
+ */
+
+extern void db_verb_arg_specs(db_verb_handle h,
+ db_arg_spec *dobj,
+ db_prep_spec *prep,
+ db_arg_spec *iobj);
+extern void db_set_verb_arg_specs(db_verb_handle h,
+ db_arg_spec dobj,
+ db_prep_spec prep,
+ db_arg_spec iobj);
+
+extern int db_verb_allows(db_verb_handle, Objid, db_verb_flag);
+ /* Returns true iff either the verb has the
+ * flag or the object either is a wizard or
+ * owns the verb; that is, iff the object's
+ * authority is sufficient to be allowed to do
+ * the indicated operation on the verb.
+ */
+
+extern void db_delete_verb(db_verb_handle);
+ /* Deletes the given verb entirely. This
+ * db_verb_handle may not be used again after
+ * this call returns.
+ */
+
+#endif /* !DB_h */
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.5 1996/05/12 21:31:37 pavel
+ * Added documentation of the memory-management policy for db_add_verb().
+ * Release 1.8.0p5.
+ *
+ * Revision 2.4 1996/02/11 00:47:04 pavel
+ * Enhanced db_find_property() to report the defining object of the found
+ * property. Release 1.8.0beta2.
+ *
+ * Revision 2.3 1996/02/08 06:28:33 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.2 1995/12/28 00:30:27 pavel
+ * Added db_disk_size(). Changed db_delete_verb() to unbundle how the verb is
+ * found. Added support to db_find_defined_verb() for suppressing old
+ * numeric-string behavior. Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:56:12 pavel
+ * Added `db_object_bytes()' and `db_rename_propdef()'. Clarified
+ * reference-counting behavior of built-in property references.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 05:05:11 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ */
diff --git a/db_file.c b/db_file.c
new file mode 100644
index 0000000..2b179b9
--- /dev/null
+++ b/db_file.c
@@ -0,0 +1,757 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for initializing, loading, dumping, and shutting down the database
+ *****************************************************************************/
+
+#include "my-stat.h"
+#include "my-stdio.h"
+#include "my-stdlib.h"
+
+#include "config.h"
+#include "db.h"
+#include "db_io.h"
+#include "db_private.h"
+#include "exceptions.h"
+#include "list.h"
+#include "log.h"
+#include "options.h"
+#include "server.h"
+#include "storage.h"
+#include "streams.h"
+#include "tasks.h"
+#include "timers.h"
+#include "version.h"
+
+static char *input_db_name, *dump_db_name;
+static int dump_generation = 0;
+static const char *header_format_string
+ = "** LambdaMOO Database, Format Version %u **\n";
+
+DB_Version dbio_input_version;
+
+
+/*********** Verb and property I/O ***********/
+
+static void
+read_verbdef(Verbdef *v)
+{
+ v->name = str_dup(dbio_read_string());
+ v->owner = dbio_read_objid();
+ v->perms = dbio_read_num();
+ v->prep = dbio_read_num();
+ v->next = 0;
+ v->program = 0;
+}
+
+static void
+write_verbdef(Verbdef *v)
+{
+ dbio_write_string(v->name);
+ dbio_write_objid(v->owner);
+ dbio_write_num(v->perms);
+ dbio_write_num(v->prep);
+}
+
+static Propdef
+read_propdef()
+{
+ char *name = str_dup(dbio_read_string());
+ return dbpriv_new_propdef(name);
+}
+
+static void
+write_propdef(Propdef *p)
+{
+ dbio_write_string(p->name);
+}
+
+static void
+read_propval(Pval *p)
+{
+ p->var = dbio_read_var();
+ p->owner = dbio_read_objid();
+ p->perms = dbio_read_num();
+}
+
+static void
+write_propval(Pval *p)
+{
+ dbio_write_var(p->var);
+ dbio_write_objid(p->owner);
+ dbio_write_num(p->perms);
+}
+
+
+/*********** Object I/O ***********/
+
+static int
+read_object(void)
+{
+ Objid oid;
+ Object *o;
+ char s[20];
+ int i;
+ Verbdef *v, **prevv;
+ int nprops;
+
+ if (dbio_scanf("#%d", &oid) != 1 || oid != db_last_used_objid() + 1)
+ return 0;
+ dbio_read_line(s, sizeof(s));
+
+ if (strcmp(s, " recycled\n") == 0) {
+ dbpriv_new_recycled_object();
+ return 1;
+ } else if (strcmp(s, "\n") != 0)
+ return 0;
+
+ o = dbpriv_new_object();
+ o->name = str_dup(dbio_read_string());
+ (void) dbio_read_string(); /* discard old handles string */
+ o->flags = dbio_read_num();
+
+ o->owner = dbio_read_objid();
+
+ o->location = dbio_read_objid();
+ o->contents = dbio_read_objid();
+ o->next = dbio_read_objid();
+
+ o->parent = dbio_read_objid();
+ o->child = dbio_read_objid();
+ o->sibling = dbio_read_objid();
+
+ o->verbdefs = 0;
+ prevv = &(o->verbdefs);
+ for (i = dbio_read_num(); i > 0; i--) {
+ v = mymalloc(sizeof(Verbdef), M_VERBDEF);
+ read_verbdef(v);
+ *prevv = v;
+ prevv = &(v->next);
+ }
+
+ o->propdefs.cur_length = 0;
+ o->propdefs.max_length = 0;
+ o->propdefs.l = 0;
+ if ((i = dbio_read_num()) != 0) {
+ o->propdefs.l = mymalloc(i * sizeof(Propdef), M_PROPDEF);
+ o->propdefs.cur_length = i;
+ o->propdefs.max_length = i;
+ for (i = 0; i < o->propdefs.cur_length; i++)
+ o->propdefs.l[i] = read_propdef();
+ }
+
+ nprops = dbio_read_num();
+ if (nprops)
+ o->propval = mymalloc(nprops * sizeof(Pval), M_PVAL);
+ else
+ o->propval = 0;
+
+ for (i = 0; i < nprops; i++) {
+ read_propval(o->propval + i);
+ }
+
+ return 1;
+}
+
+static void
+write_object(Objid oid)
+{
+ Object *o;
+ Verbdef *v;
+ int i;
+ int nverbdefs, nprops;
+
+ if (!valid(oid)) {
+ dbio_printf("#%d recycled\n", oid);
+ return;
+ }
+ o = dbpriv_find_object(oid);
+
+ dbio_printf("#%d\n", oid);
+ dbio_write_string(o->name);
+ dbio_write_string(""); /* placeholder for old handles string */
+ dbio_write_num(o->flags);
+
+ dbio_write_objid(o->owner);
+
+ dbio_write_objid(o->location);
+ dbio_write_objid(o->contents);
+ dbio_write_objid(o->next);
+
+ dbio_write_objid(o->parent);
+ dbio_write_objid(o->child);
+ dbio_write_objid(o->sibling);
+
+ for (v = o->verbdefs, nverbdefs = 0; v; v = v->next)
+ nverbdefs++;
+
+ dbio_write_num(nverbdefs);
+ for (v = o->verbdefs; v; v = v->next)
+ write_verbdef(v);
+
+ dbio_write_num(o->propdefs.cur_length);
+ for (i = 0; i < o->propdefs.cur_length; i++)
+ write_propdef(&o->propdefs.l[i]);
+
+ nprops = dbpriv_count_properties(oid);
+
+ dbio_write_num(nprops);
+ for (i = 0; i < nprops; i++)
+ write_propval(o->propval + i);
+}
+
+
+/*********** File-level Input ***********/
+
+static int
+validate_hierarchies()
+{
+ Objid oid, log_oid;
+ Objid size = db_last_used_objid() + 1;
+ int broken = 0;
+ int fixed_nexts = 0;
+
+ oklog("VALIDATING the object hierarchies ...\n");
+
+# define PROGRESS_INTERVAL 10000
+# define MAYBE_LOG_PROGRESS \
+ { \
+ if (oid == log_oid) { \
+ log_oid += PROGRESS_INTERVAL; \
+ oklog("VALIDATE: Done through #%d ...\n", oid); \
+ } \
+ }
+
+ oklog("VALIDATE: Phase 1: Check for invalid objects ...\n");
+ for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) {
+ Object *o = dbpriv_find_object(oid);
+
+ MAYBE_LOG_PROGRESS;
+ if (o) {
+ if (o->location == NOTHING && o->next != NOTHING) {
+ o->next = NOTHING;
+ fixed_nexts++;
+ }
+
+# define CHECK(field, name) \
+ { \
+ if (o->field != NOTHING \
+ && !dbpriv_find_object(o->field)) { \
+ errlog("VALIDATE: #%d.%s = #%d <invalid> ... fixed.\n", \
+ oid, name, o->field); \
+ o->field = NOTHING; \
+ } \
+ }
+
+ CHECK(parent, "parent");
+ CHECK(child, "child");
+ CHECK(sibling, "sibling");
+ CHECK(location, "location");
+ CHECK(contents, "contents");
+ CHECK(next, "next");
+
+# undef CHECK
+ }
+ }
+
+ if (fixed_nexts != 0)
+ errlog("VALIDATE: Fixed %d should-be-null next pointer(s) ...\n",
+ fixed_nexts);
+
+ oklog("VALIDATE: Phase 2: Check for cycles ...\n");
+ for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) {
+ Object *o = dbpriv_find_object(oid);
+
+ MAYBE_LOG_PROGRESS;
+ if (o) {
+# define CHECK(start, field, name) \
+ { \
+ Objid oid2 = start; \
+ int count = 0; \
+ for (; oid2 != NOTHING \
+ ; oid2 = dbpriv_find_object(oid2)->field) { \
+ if (++count > size) { \
+ errlog("VALIDATE: Cycle in `%s' chain of #%d\n",\
+ name, oid); \
+ broken = 1; \
+ break; \
+ } \
+ } \
+ }
+
+ CHECK(o->parent, parent, "parent");
+ CHECK(o->child, sibling, "child");
+ CHECK(o->location, location, "location");
+ CHECK(o->contents, next, "contents");
+
+# undef CHECK
+ }
+ }
+
+ if (broken) /* Can't continue if cycles found */
+ return 0;
+
+ oklog("VALIDATE: Phase 3: Check for inconsistencies ...\n");
+ for (oid = 0, log_oid = PROGRESS_INTERVAL; oid < size; oid++) {
+ Object *o = dbpriv_find_object(oid);
+
+ MAYBE_LOG_PROGRESS;
+ if (o) {
+# define CHECK(up, up_name, down, down_name, across) \
+ { \
+ Objid up = o->up; \
+ Objid oid2; \
+ \
+ /* Is oid in its up's down list? */ \
+ if (up != NOTHING) { \
+ for (oid2 = dbpriv_find_object(up)->down; \
+ oid2 != NOTHING; \
+ oid2 = dbpriv_find_object(oid2)->across) { \
+ if (oid2 == oid) /* found it */ \
+ break; \
+ } \
+ if (oid2 == NOTHING) { /* didn't find it */ \
+ errlog("VALIDATE: #%d not in %s (#%d)'s %s list.\n", \
+ oid, up_name, up, down_name); \
+ broken = 1; \
+ } \
+ } \
+ }
+
+ CHECK(parent, "parent", child, "child", sibling);
+ CHECK(location, "location", contents, "contents", next);
+
+# undef CHECK
+
+# define CHECK(up, down, down_name, across) \
+ { \
+ Objid oid2; \
+ \
+ for (oid2 = o->down; \
+ oid2 != NOTHING; \
+ oid2 = dbpriv_find_object(oid2)->across) { \
+ if (dbpriv_find_object(oid2)->up != oid) { \
+ errlog( \
+ "VALIDATE: #%d erroneously on #%d's %s list.\n", \
+ oid2, oid, down_name); \
+ broken = 1; \
+ } \
+ } \
+ }
+
+ CHECK(parent, child, "child", sibling);
+ CHECK(location, contents, "contents", next);
+
+# undef CHECK
+ }
+ }
+
+ oklog("VALIDATING the object hierarchies ... finished.\n");
+ return !broken;
+}
+
+static const char *
+fmt_verb_name(void *data)
+{
+ db_verb_handle *h = data;
+ static Stream *s = 0;
+
+ if (!s)
+ s = new_stream(40);
+
+ stream_printf(s, "#%d:%s", db_verb_definer(*h), db_verb_names(*h));
+ return reset_stream(s);
+}
+
+static int
+read_db_file(void)
+{
+ Objid oid;
+ int nobjs, nprogs, nusers;
+ Var user_list;
+ int i, vnum, dummy;
+ db_verb_handle h;
+ Program *program;
+
+ if (dbio_scanf(header_format_string, &dbio_input_version) != 1)
+ dbio_input_version = DBV_Prehistory;
+
+ if (!check_version(dbio_input_version)) {
+ errlog("READ_DB_FILE: Unknown DB version number: %d\n",
+ dbio_input_version);
+ return 0;
+ }
+
+ /* I use a `dummy' variable here and elsewhere instead of the `*'
+ * assignment-suppression syntax of `scanf' because it allows more
+ * straightforward error checking; unfortunately, the standard says that
+ * suppressed assignments are not counted in determining the returned value
+ * of `scanf'...
+ */
+ if (dbio_scanf("%d\n%d\n%d\n%d\n",
+ &nobjs, &nprogs, &dummy, &nusers) != 4) {
+ errlog("READ_DB_FILE: Bad header\n");
+ return 0;
+ }
+
+ user_list = new_list(nusers);
+ for (i = 1; i <= nusers; i++) {
+ user_list.v.list[i].type = TYPE_OBJ;
+ user_list.v.list[i].v.obj = dbio_read_objid();
+ }
+ dbpriv_set_all_users(user_list);
+
+ oklog("LOADING: Reading %d objects...\n", nobjs);
+ for (i = 1; i <= nobjs; i++) {
+ if (!read_object()) {
+ errlog("READ_DB_FILE: Bad object #%d.\n", i - 1);
+ return 0;
+ }
+ if (i % 10000 == 0 || i == nobjs)
+ oklog("LOADING: Done reading %d objects ...\n", i);
+ }
+
+ if (!validate_hierarchies()) {
+ errlog("READ_DB_FILE: Errors in object hierarchies.\n");
+ return 0;
+ }
+
+ oklog("LOADING: Reading %d MOO verb programs...\n", nprogs);
+ for (i = 1; i <= nprogs; i++) {
+ if (dbio_scanf("#%d:%d\n", &oid, &vnum) != 2) {
+ errlog("READ_DB_FILE: Bad program header, i = %d.\n", i);
+ return 0;
+ }
+ if (!valid(oid)) {
+ errlog("READ_DB_FILE: Verb for non-existant object: #%d:%d.\n",
+ oid, vnum);
+ return 0;
+ }
+ h = db_find_indexed_verb(oid, vnum + 1); /* DB file is 0-based. */
+ if (!h.ptr) {
+ errlog("READ_DB_FILE: Unknown verb index: #%d:%d.\n", oid, vnum);
+ return 0;
+ }
+ program = dbio_read_program(dbio_input_version, fmt_verb_name, &h);
+ if (!program) {
+ errlog("READ_DB_FILE: Unparsable program #%d:%d.\n", oid, vnum);
+ return 0;
+ }
+ db_set_verb_program(h, program);
+ if (i % 5000 == 0 || i == nprogs)
+ oklog("LOADING: Done reading %d verb programs...\n", i);
+ }
+
+ oklog("LOADING: Reading forked and suspended tasks...\n");
+ if (!read_task_queue()) {
+ errlog("READ_DB_FILE: Can't read task queue.\n");
+ return 0;
+ }
+
+ oklog("LOADING: Reading list of formerly active connections...\n");
+ if (!read_active_connections()) {
+ errlog("DB_READ: Can't read active connections.\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*********** File-level Output ***********/
+
+static int
+write_db_file(const char *reason)
+{
+ Objid oid;
+ Objid max_oid = db_last_used_objid();
+ Verbdef *v;
+ Var user_list;
+ int i;
+ volatile int nprogs = 0;
+ volatile int success = 1;
+
+ for (oid = 0; oid <= max_oid; oid++) {
+ if (valid(oid))
+ for (v = dbpriv_find_object(oid)->verbdefs; v; v = v->next)
+ if (v->program)
+ nprogs++;
+ }
+
+ user_list = db_all_users();
+
+ TRY
+ dbio_printf(header_format_string, current_version);
+ dbio_printf("%d\n%d\n%d\n%d\n",
+ max_oid + 1, nprogs, 0, user_list.v.list[0].v.num);
+ for (i = 1; i <= user_list.v.list[0].v.num; i++)
+ dbio_write_objid(user_list.v.list[i].v.obj);
+ oklog("%s: Writing %d objects...\n", reason, max_oid + 1);
+ for (oid = 0; oid <= max_oid; oid++) {
+ write_object(oid);
+ if ((oid + 1) % 10000 == 0 || oid == max_oid)
+ oklog("%s: Done writing %d objects...\n", reason, oid + 1);
+ }
+ oklog("%s: Writing %d MOO verb programs...\n", reason, nprogs);
+ for (i = 0, oid = 0; oid <= max_oid; oid++)
+ if (valid(oid)) {
+ int vcount = 0;
+
+ for (v = dbpriv_find_object(oid)->verbdefs; v; v = v->next) {
+ if (v->program) {
+ dbio_printf("#%d:%d\n", oid, vcount);
+ dbio_write_program(v->program);
+ if (++i % 5000 == 0 || i == nprogs)
+ oklog("%s: Done writing %d verb programs...\n",
+ reason, i);
+ }
+ vcount++;
+ }
+ }
+
+ oklog("%s: Writing forked and suspended tasks...\n", reason);
+ write_task_queue();
+ oklog("%s: Writing list of formerly active connections...\n", reason);
+ write_active_connections();
+ EXCEPT (dbpriv_dbio_failed)
+ success = 0;
+ ENDTRY;
+
+ return success;
+}
+
+typedef enum { DUMP_SHUTDOWN, DUMP_CHECKPOINT, DUMP_PANIC } Dump_Reason;
+const char *reason_names[] = { "DUMPING", "CHECKPOINTING", "PANIC-DUMPING" };
+
+static int
+dump_database(Dump_Reason reason)
+{
+ Stream *s = new_stream(100);
+ char *temp_name;
+ FILE *f;
+ int success;
+
+ retryDumping:
+
+ stream_printf(s, "%s.#%d#", dump_db_name, dump_generation);
+ remove(reset_stream(s)); /* Remove previous checkpoint */
+
+ if (reason == DUMP_PANIC)
+ stream_printf(s, "%s.PANIC", dump_db_name);
+ else {
+ dump_generation++;
+ stream_printf(s, "%s.#%d#", dump_db_name, dump_generation);
+ }
+ temp_name = reset_stream(s);
+
+ oklog("%s on %s ...\n", reason_names[reason], temp_name);
+
+#ifdef UNFORKED_CHECKPOINTS
+ reset_command_history();
+#else
+ if (reason == DUMP_CHECKPOINT) {
+ switch (fork_server("checkpointer")) {
+ case FORK_PARENT:
+ reset_command_history();
+ free_stream(s);
+ return 1;
+ case FORK_ERROR:
+ free_stream(s);
+ return 0;
+ case FORK_CHILD:
+ set_server_cmdline("(MOO checkpointer)");
+ break;
+ }
+ }
+#endif
+
+ success = 1;
+ if ((f = fopen(temp_name, "w")) != 0) {
+ dbpriv_set_dbio_output(f);
+ if (!write_db_file(reason_names[reason])) {
+ log_perror("Trying to dump database");
+ fclose(f);
+ remove(temp_name);
+ if (reason == DUMP_CHECKPOINT) {
+ errlog("Abandoning checkpoint attempt...\n");
+ success = 0;
+ } else {
+ int retry_interval = 60;
+
+ errlog("Waiting %d seconds and retrying dump...\n",
+ retry_interval);
+ timer_sleep(retry_interval);
+ goto retryDumping;
+ }
+ } else {
+ fclose(f);
+ oklog("%s on %s finished\n", reason_names[reason], temp_name);
+ if (reason != DUMP_PANIC) {
+ remove(dump_db_name);
+ if (rename(temp_name, dump_db_name) != 0) {
+ log_perror("Renaming temporary dump file");
+ success = 0;
+ }
+ }
+ }
+ } else {
+ log_perror("Opening temporary dump file");
+ success = 0;
+ }
+
+ free_stream(s);
+
+#ifndef UNFORKED_CHECKPOINTS
+ if (reason == DUMP_CHECKPOINT)
+ /* We're a child, so we'd better go away. */
+ exit(!success);
+#endif
+
+ return success;
+}
+
+
+/*********** External interface ***********/
+
+const char *
+db_usage_string(void)
+{
+ return "input-db-file output-db-file";
+}
+
+static FILE *input_db;
+
+int
+db_initialize(int *pargc, char ***pargv)
+{
+ FILE *f;
+
+ if (*pargc < 2)
+ return 0;
+
+ input_db_name = str_dup((*pargv)[0]);
+ dump_db_name = str_dup((*pargv)[1]);
+ *pargc -= 2;
+ *pargv += 2;
+
+ if (!(f = fopen(input_db_name, "r"))) {
+ fprintf(stderr, "Cannot open input database file: %s\n",
+ input_db_name);
+ return 0;
+ }
+
+ input_db = f;
+ dbpriv_build_prep_table();
+
+ return 1;
+}
+
+int
+db_load(void)
+{
+ dbpriv_set_dbio_input(input_db);
+
+ oklog("LOADING: %s\n", input_db_name);
+ if (!read_db_file()) {
+ errlog("DB_LOAD: Cannot load database!\n");
+ return 0;
+ }
+ oklog("LOADING: %s done, will dump new database on %s\n",
+ input_db_name, dump_db_name);
+
+ fclose(input_db);
+ return 1;
+}
+
+int
+db_flush(enum db_flush_type type)
+{
+ int success = 0;
+
+ switch(type) {
+ case FLUSH_IF_FULL:
+ case FLUSH_ONE_SECOND:
+ success = 1;
+ break;
+
+ case FLUSH_ALL_NOW:
+ success = dump_database(DUMP_CHECKPOINT);
+ break;
+
+ case FLUSH_PANIC:
+ success = dump_database(DUMP_PANIC);
+ break;
+ }
+
+ return success;
+}
+
+int32
+db_disk_size(void)
+{
+ struct stat st;
+
+ if ((dump_generation == 0 || stat(dump_db_name, &st) < 0)
+ && stat(input_db_name, &st) < 0)
+ return -1;
+ else
+ return st.st_size;
+}
+
+void
+db_shutdown()
+{
+ dump_database(DUMP_SHUTDOWN);
+}
+
+char rcsid_db_file[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.5 1996/04/08 01:07:21 pavel
+ * Changed a boot-time error message to go directly to stderr, instead of
+ * through the logging package. Release 1.8.0p3.
+ *
+ * Revision 2.4 1996/02/08 07:20:18 pavel
+ * Renamed err/logf() to errlog/oklog(). Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.3 1995/12/31 03:27:54 pavel
+ * Added missing #include "options.h". Release 1.8.0alpha4.
+ *
+ * Revision 2.2 1995/12/28 00:51:39 pavel
+ * Added db_disk_size(). Added support for printing location of
+ * MOO-compilation warnings and errors during loading. More slight
+ * improvements to load-time progress messages. Added dump-time progress
+ * messages. Added init-time call to build preposition table.
+ * Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:55:01 pavel
+ * Added missing #include of "my-stdlib.h". Slightly improved clarity of the
+ * progress messages during DB loading.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:19:37 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:19:11 pavel
+ * Initial revision
+ */
diff --git a/db_io.c b/db_io.c
new file mode 100644
index 0000000..e53a13f
--- /dev/null
+++ b/db_io.c
@@ -0,0 +1,438 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for use by non-DB modules with persistent state stored in the DB
+ *****************************************************************************/
+
+#include "my-ctype.h"
+#include <float.h>
+#include "my-stdarg.h"
+#include "my-stdio.h"
+#include "my-stdlib.h"
+
+#include "db_io.h"
+#include "db_private.h"
+#include "exceptions.h"
+#include "list.h"
+#include "log.h"
+#include "numbers.h"
+#include "parser.h"
+#include "storage.h"
+#include "streams.h"
+#include "structures.h"
+#include "unparse.h"
+#include "version.h"
+
+
+/*********** Input ***********/
+
+static FILE *input;
+
+void
+dbpriv_set_dbio_input(FILE *f)
+{
+ input = f;
+}
+
+void
+dbio_read_line(char *s, int n)
+{
+ fgets(s, n, input);
+}
+
+int
+dbio_scanf(const char *format, ...)
+{
+ va_list args;
+ int count;
+ const char *ptr;
+
+ va_start(args, format);
+ /* The following line would be nice, but unfortunately those darlings on
+ * the ANSI C committee apparently didn't feel it worthwhile to include
+ * support for functions wrapping `scanf' *even though* they included
+ * symmetric such support for functions wrapping `printf'. (*sigh*)
+ * Fortunately, we only use a small fraction of the full functionality of
+ * scanf in the server, so it's not unbearably unpleasant to have to
+ * reimplement it here.
+ */
+ /* count = vfscanf(input, format, args); */
+
+ count = 0;
+ for (ptr = format; *ptr; ptr++) {
+ int c, n, *ip;
+ unsigned *up;
+ char *cp;
+
+ if (isspace(*ptr)) {
+ do
+ c = fgetc(input);
+ while (isspace(c));
+ ungetc(c, input);
+ } else if (*ptr != '%') {
+ do
+ c = fgetc(input);
+ while (isspace(c));
+
+ if (c == EOF)
+ return count ? count : EOF;
+ else if (c != *ptr) {
+ ungetc(c, input);
+ return count;
+ }
+ } else
+ switch (*++ptr) {
+ case 'd':
+ ip = va_arg(args, int *);
+ n = fscanf(input, "%d", ip);
+ goto finish;
+ case 'u':
+ up = va_arg(args, unsigned *);
+ n = fscanf(input, "%u", up);
+ goto finish;
+ case 'c':
+ cp = va_arg(args, char *);
+ n = fscanf(input, "%c", cp);
+ finish:
+ if (n == 1)
+ count++;
+ else if (n == 0)
+ return count;
+ else /* n == EOF */
+ return count ? count : EOF;
+ break;
+ default:
+ panic("DBIO_SCANF: Unsupported directive!");
+ }
+ }
+
+ va_end(args);
+
+ return count;
+}
+
+int
+dbio_read_num(void)
+{
+ char s[20];
+ char *p;
+ int i;
+
+ fgets(s, 20, input);
+ i = strtol(s, &p, 10);
+ if (isspace(*s) || *p != '\n')
+ errlog("DBIO_READ_NUM: Bad number: \"%s\" at file pos. %ld\n",
+ s, ftell(input));
+ return i;
+}
+
+double
+dbio_read_float(void)
+{
+ char s[40];
+ char *p;
+ double d;
+
+ fgets(s, 40, input);
+ d = strtod(s, &p);
+ if (isspace(*s) || *p != '\n')
+ errlog("DBIO_READ_FLOAT: Bad number: \"%s\" at file pos. %ld\n",
+ s, ftell(input));
+ return d;
+}
+
+Objid
+dbio_read_objid(void)
+{
+ return dbio_read_num();
+}
+
+const char *
+dbio_read_string(void)
+{
+ static Stream *str = 0;
+ static char buffer[1024];
+ int len, used_stream = 0;
+
+ if (str == 0)
+ str = new_stream(1024);
+
+ try_again:
+ fgets(buffer, sizeof(buffer), input);
+ len = strlen(buffer);
+ if (len == sizeof(buffer) - 1 && buffer[len - 1] != '\n') {
+ stream_add_string(str, buffer);
+ used_stream = 1;
+ goto try_again;
+ }
+
+ if (buffer[len - 1] == '\n')
+ buffer[len - 1] = '\0';
+
+ if (used_stream) {
+ stream_add_string(str, buffer);
+ return reset_stream(str);
+ } else
+ return buffer;
+}
+
+Var
+dbio_read_var(void)
+{
+ Var r;
+ int i, l = dbio_read_num();
+
+ if (l == (int) TYPE_ANY && dbio_input_version == DBV_Prehistory)
+ l = TYPE_NONE; /* Old encoding for VM's empty temp register
+ * and any as-yet unassigned variables.
+ */
+ r.type = (var_type) l;
+ switch (l) {
+ case TYPE_CLEAR:
+ case TYPE_NONE:
+ break;
+ case TYPE_STR:
+ r.v.str = str_dup(dbio_read_string());
+ break;
+ case TYPE_OBJ:
+ case TYPE_ERR:
+ case TYPE_INT:
+ case TYPE_CATCH:
+ case TYPE_FINALLY:
+ r.v.num = dbio_read_num();
+ break;
+ case TYPE_FLOAT:
+ r = new_float(dbio_read_float());
+ break;
+ case TYPE_LIST:
+ l = dbio_read_num();
+ r = new_list(l);
+ for (i = 0; i < l; i++)
+ r.v.list[i + 1] = dbio_read_var();
+ break;
+ default:
+ errlog("DBIO_READ_VAR: Unknown type (%d) at DB file pos. %ld\n",
+ l, ftell(input));
+ r = zero;
+ break;
+ }
+ return r;
+}
+
+struct state {
+ char prev_char;
+ const char *(*fmtr)(void *);
+ void *data;
+};
+
+static const char *
+program_name(struct state *s)
+{
+ if (!s->fmtr)
+ return s->data;
+ else
+ return (*s->fmtr)(s->data);
+}
+
+static void
+my_error(void *data, const char *msg)
+{
+ errlog("PARSER: Error in %s:\n", program_name(data));
+ errlog(" %s\n", msg);
+}
+
+static void
+my_warning(void *data, const char *msg)
+{
+ oklog("PARSER: Warning in %s:\n", program_name(data));
+ oklog(" %s\n", msg);
+}
+
+static int
+my_getc(void *data)
+{
+ struct state *s = data;
+ int c;
+
+ c = fgetc(input);
+ if (c == '.' && s->prev_char == '\n') {
+ /* end-of-verb marker in DB */
+ c = fgetc(input); /* skip next newline */
+ return EOF;
+ }
+ if (c == EOF)
+ my_error(data, "Unexpected EOF");
+ s->prev_char = c;
+ return c;
+}
+
+static Parser_Client parser_client = { my_error, my_warning, my_getc };
+
+Program *
+dbio_read_program(DB_Version version, const char *(*fmtr)(void *), void *data)
+{
+ struct state s;
+
+ s.prev_char = '\n';
+ s.fmtr = fmtr;
+ s.data = data;
+ return parse_program(version, parser_client, &s);
+}
+
+
+/*********** Output ***********/
+
+Exception dbpriv_dbio_failed;
+
+static FILE *output;
+
+void
+dbpriv_set_dbio_output(FILE *f)
+{
+ output = f;
+}
+
+void
+dbio_printf(const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ if (vfprintf(output, format, args) < 0)
+ RAISE(dbpriv_dbio_failed, 0);
+ va_end(args);
+}
+
+void
+dbio_write_num(int n)
+{
+ dbio_printf("%d\n", n);
+}
+
+void
+dbio_write_float(double d)
+{
+ static const char *fmt = 0;
+ static char buffer[10];
+
+ if (!fmt) {
+ sprintf(buffer, "%%.%dg\n", DBL_DIG + 4);
+ fmt = buffer;
+ }
+
+ dbio_printf(fmt, d);
+}
+
+void
+dbio_write_objid(Objid oid)
+{
+ dbio_write_num(oid);
+}
+
+void
+dbio_write_string(const char *s)
+{
+ dbio_printf("%s\n", s ? s : "");
+}
+
+void
+dbio_write_var(Var v)
+{
+ int i;
+
+ dbio_write_num((int) v.type);
+ switch (v.type) {
+ case TYPE_CLEAR:
+ case TYPE_NONE:
+ break;
+ case TYPE_STR:
+ dbio_write_string(v.v.str);
+ break;
+ case TYPE_OBJ:
+ case TYPE_ERR:
+ case TYPE_INT:
+ case TYPE_CATCH:
+ case TYPE_FINALLY:
+ dbio_write_num(v.v.num);
+ break;
+ case TYPE_FLOAT:
+ dbio_write_float(*v.v.fnum);
+ break;
+ case TYPE_LIST:
+ dbio_write_num(v.v.list[0].v.num);
+ for (i = 0; i < v.v.list[0].v.num; i++)
+ dbio_write_var(v.v.list[i + 1]);
+ break;
+ }
+}
+
+static void
+receiver(void *data, const char *line)
+{
+ dbio_printf("%s\n", line);
+}
+
+void
+dbio_write_program(Program *program)
+{
+ unparse_program(program, receiver, 0, 1, 0, MAIN_VECTOR);
+ dbio_printf(".\n");
+}
+
+void
+dbio_write_forked_program(Program *program, int f_index)
+{
+ unparse_program(program, receiver, 0, 1, 0, f_index);
+ dbio_printf(".\n");
+}
+
+char rcsid_db_io[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.5 1996/03/19 07:16:12 pavel
+ * Increased precision of floating-point numbers printed in the DB file.
+ * Release 1.8.0p2.
+ *
+ * Revision 2.4 1996/03/10 01:04:16 pavel
+ * Increased the precision of printed floating-point numbers by two digits.
+ * Release 1.8.0.
+ *
+ * Revision 2.3 1996/02/08 07:19:15 pavel
+ * Renamed err/logf() to errlog/oklog() and TYPE_NUM to TYPE_INT. Added
+ * dbio_read/write_float(). Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.2 1995/12/28 00:44:51 pavel
+ * Added support for receiving MOO-compilation warnings during loading and for
+ * printing useful error and warning messages in the log.
+ * Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:59:50 pavel
+ * Fixed broken #includes. Removed another silly use of `unsigned'.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:20:10 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:19:56 pavel
+ * Initial revision
+ */
diff --git a/db_io.h b/db_io.h
new file mode 100644
index 0000000..17fa579
--- /dev/null
+++ b/db_io.h
@@ -0,0 +1,115 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for use by non-DB modules with persistent state stored in the DB
+ *****************************************************************************/
+
+#include "program.h"
+#include "structures.h"
+#include "version.h"
+
+/*********** Input ***********/
+
+extern DB_Version dbio_input_version;
+ /* What DB-format version are we reading? */
+
+extern void dbio_read_line(char *s, int n);
+ /* Reads at most N-1 characters through the
+ * next newline into S, terminating S with a
+ * null. (Like the `fgets()' function.)
+ */
+
+extern int dbio_scanf(const char *format, ...);
+
+extern int dbio_read_num(void);
+extern Objid dbio_read_objid(void);
+extern double dbio_read_float(void);
+
+extern const char *dbio_read_string(void);
+ /* The returned string is in private storage of
+ * the DBIO module, so the caller should
+ * str_dup() it if it is to persist.
+ */
+
+extern Var dbio_read_var(void);
+ /* The DBIO module retains no references to
+ * the returned value, so freeing it is
+ * entirely the responsibility of the caller.
+ */
+
+extern Program *dbio_read_program(DB_Version version,
+ const char *(*fmtr)(void *),
+ void *data);
+ /* FMTR is called with DATA to produce a human-
+ * understandable identifier for the program
+ * being read, for use in any error/warning
+ * messages. If FMTR is null, then DATA should
+ * be the required string.
+ */
+
+
+/*********** Output ***********/
+
+/* NOTE: All output routines can raise a (private) exception if they are unable
+ * to write all of the requested output (e.g., because there is no more space
+ * on disk). The DB module catches this exception and retries the DB dump
+ * after performing appropriate notifications, waiting, and/or fixes. Callers
+ * should thus be prepared for any call to these routines to fail to return
+ * normally, using TRY ... FINALLY ... if necessary to recover from such an
+ * event.
+ */
+
+extern void dbio_printf(const char *format, ...);
+
+extern void dbio_write_num(int);
+extern void dbio_write_objid(Objid);
+extern void dbio_write_float(double);
+
+extern void dbio_write_string(const char *);
+ /* The given string should not contain any
+ * newline characters.
+ */
+
+extern void dbio_write_var(Var);
+
+extern void dbio_write_program(Program *);
+extern void dbio_write_forked_program(Program *prog, int f_index);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.3 1996/02/08 06:28:21 pavel
+ * Added dbio_input_version, dbio_read/write_float(). Made dbio_read_program
+ * version-dependent. Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.2 1995/12/28 00:46:52 pavel
+ * Added support for printing location of MOO-compilation warnings and errors
+ * during loading. Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 08:00:11 pavel
+ * Removed another silly use of `unsigned'.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 05:05:29 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 05:05:21 pavel
+ * Initial revision
+ */
diff --git a/db_objects.c b/db_objects.c
new file mode 100644
index 0000000..e34a65b
--- /dev/null
+++ b/db_objects.c
@@ -0,0 +1,569 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for manipulating DB objects
+ *****************************************************************************/
+
+#include "config.h"
+#include "db.h"
+#include "db_private.h"
+#include "list.h"
+#include "program.h"
+#include "storage.h"
+#include "utils.h"
+
+static Object **objects;
+static int num_objects = 0;
+static int max_objects = 0;
+
+static Var all_users;
+
+
+/*********** Objects qua objects ***********/
+
+Object *
+dbpriv_find_object(Objid oid)
+{
+ if (oid < 0 || oid >= num_objects)
+ return 0;
+ else
+ return objects[oid];
+}
+
+int
+valid(Objid oid)
+{
+ return dbpriv_find_object(oid) != 0;
+}
+
+Objid
+db_last_used_objid(void)
+{
+ return num_objects - 1;
+}
+
+void
+db_reset_last_used_objid(void)
+{
+ while (!objects[num_objects - 1])
+ num_objects--;
+}
+
+static void
+ensure_new_object(void)
+{
+ if (max_objects == 0) {
+ max_objects = 100;
+ objects = mymalloc(max_objects * sizeof(Object *), M_OBJECT_TABLE);
+ }
+
+ if (num_objects >= max_objects) {
+ int i;
+ Object **new;
+
+ new = mymalloc(max_objects * 2 * sizeof(Object *), M_OBJECT_TABLE);
+ for (i = 0; i < max_objects; i++)
+ new[i] = objects[i];
+ myfree(objects, M_OBJECT_TABLE);
+ objects = new;
+ max_objects *= 2;
+ }
+}
+
+Object *
+dbpriv_new_object(void)
+{
+ Object *o;
+
+ ensure_new_object();
+ o = objects[num_objects] = mymalloc(sizeof(Object), M_OBJECT);
+ o->id = num_objects;
+ num_objects++;
+
+ return o;
+}
+
+void
+dbpriv_new_recycled_object(void)
+{
+ ensure_new_object();
+ objects[num_objects++] = 0;
+}
+
+Objid
+db_create_object(void)
+{
+ Object *o;
+ Objid oid;
+
+ o = dbpriv_new_object();
+ oid = o->id;
+
+ o->name = str_dup("");
+ o->flags = 0;
+ o->parent = o->child = o->sibling = NOTHING;
+ o->location = o->contents = o->next = NOTHING;
+
+ o->propval = 0;
+
+ o->propdefs.max_length = 0;
+ o->propdefs.cur_length = 0;
+ o->propdefs.l = 0;
+
+ o->verbdefs = 0;
+
+ return oid;
+}
+
+void
+db_destroy_object(Objid oid)
+{
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v, *w;
+ int i;
+
+ if (!o)
+ panic("DB_DESTROY_OBJECT: Invalid object!");
+
+ if (o->location != NOTHING || o->contents != NOTHING
+ || o->parent != NOTHING || o->child != NOTHING)
+ panic("DB_DESTROY_OBJECT: Not a barren orphan!");
+
+ if (is_user(oid)) {
+ Var t;
+
+ t.type = TYPE_OBJ;
+ t.v.obj = oid;
+ all_users = setremove(all_users, t);
+ }
+
+ free_str(o->name);
+
+ for (i = 0; i < o->propdefs.cur_length; i++) {
+ /* As an orphan, the only properties on this object are the ones
+ * defined on it directly, so these two arrays must be the same length.
+ */
+ free_str(o->propdefs.l[i].name);
+ free_var(o->propval[i].var);
+ }
+ if (o->propval)
+ myfree(o->propval, M_PVAL);
+ if (o->propdefs.l)
+ myfree(o->propdefs.l, M_PROPDEF);
+
+ for (v = o->verbdefs; v; v = w) {
+ if (v->program)
+ free_program(v->program);
+ free_str(v->name);
+ w = v->next;
+ myfree(v, M_VERBDEF);
+ }
+
+ myfree(objects[oid], M_OBJECT);
+ objects[oid] = 0;
+}
+
+Objid
+db_renumber_object(Objid old)
+{
+ Objid new;
+ Object *o;
+
+ for (new = 0; new < old; new++) {
+ if (objects[new] == 0) {
+ /* Change the identity of the object. */
+ o = objects[new] = objects[old];
+ objects[old] = 0;
+ objects[new]->id = new;
+
+ /* Fix up the parent/children hierarchy */
+ {
+ Objid oid, *oidp;
+
+ if (o->parent != NOTHING) {
+ oidp = &objects[o->parent]->child;
+ while (*oidp != old && *oidp != NOTHING)
+ oidp = &objects[*oidp]->sibling;
+ if (*oidp == NOTHING)
+ panic("Object not in parent's children list");
+ *oidp = new;
+ }
+
+ for (oid = o->child;
+ oid != NOTHING;
+ oid = objects[oid]->sibling)
+ objects[oid]->parent = new;
+ }
+
+ /* Fix up the location/contents hierarchy */
+ {
+ Objid oid, *oidp;
+
+ if (o->location != NOTHING) {
+ oidp = &objects[o->location]->contents;
+ while (*oidp != old && *oidp != NOTHING)
+ oidp = &objects[*oidp]->next;
+ if (*oidp == NOTHING)
+ panic("Object not in location's contents list");
+ *oidp = new;
+ }
+
+ for (oid = o->contents;
+ oid != NOTHING;
+ oid = objects[oid]->next)
+ objects[oid]->location = new;
+ }
+
+ /* Fix up the list of users, if necessary */
+ if (is_user(new)) {
+ int i;
+
+ for (i = 1; i <= all_users.v.list[0].v.num; i++)
+ if (all_users.v.list[i].v.obj == old) {
+ all_users.v.list[i].v.obj = new;
+ break;
+ }
+ }
+
+ /* Fix the owners of verbs, properties and objects */
+ {
+ Objid oid;
+
+ for (oid = 0; oid < num_objects; oid++) {
+ Object *o = objects[oid];
+ Verbdef *v;
+ Pval *p;
+ int i, count;
+
+ if (!o)
+ continue;
+
+ if (o->owner == new)
+ o->owner = NOTHING;
+ else if (o->owner == old)
+ o->owner = new;
+
+ for (v = o->verbdefs; v; v = v->next)
+ if (v->owner == new)
+ v->owner = NOTHING;
+ else if (v->owner == old)
+ v->owner = new;
+
+ count = dbpriv_count_properties(oid);
+ p = o->propval;
+ for (i = 0; i < count; i++)
+ if (p[i].owner == new)
+ p[i].owner = NOTHING;
+ else if (p[i].owner == old)
+ p[i].owner = new;
+ }
+ }
+
+ return new;
+ }
+ }
+
+ /* There are no recycled objects less than `old', so keep its number. */
+ return old;
+}
+
+int
+db_object_bytes(Objid oid)
+{
+ Object *o = objects[oid];
+ int i, len, count;
+ Verbdef *v;
+
+ count = sizeof(Object) + sizeof(Object *);
+ count += strlen(o->name) + 1;
+
+ for (v = o->verbdefs; v; v = v->next) {
+ count += sizeof(Verbdef);
+ count += strlen(v->name) + 1;
+ if (v->program)
+ count += program_bytes(v->program);
+ }
+
+ count += sizeof(Propdef) * o->propdefs.cur_length;
+ for (i = 0; i < o->propdefs.cur_length; i++)
+ count += strlen(o->propdefs.l[i].name) + 1;
+
+ len = dbpriv_count_properties(oid);
+ count += (sizeof(Pval) - sizeof(Var)) * len;
+ for (i = 0; i < len; i++)
+ count += value_bytes(o->propval[i].var);
+
+ return count;
+}
+
+
+/*********** Object attributes ***********/
+
+Objid
+db_object_owner(Objid oid)
+{
+ return objects[oid]->owner;
+}
+
+void
+db_set_object_owner(Objid oid, Objid owner)
+{
+ objects[oid]->owner = owner;
+}
+
+const char *
+db_object_name(Objid oid)
+{
+ return objects[oid]->name;
+}
+
+void
+db_set_object_name(Objid oid, const char *name)
+{
+ Object *o = objects[oid];
+
+ if (o->name)
+ free_str(o->name);
+ o->name = name;
+}
+
+Objid
+db_object_parent(Objid oid)
+{
+ return objects[oid]->parent;
+}
+
+int
+db_count_children(Objid oid)
+{
+ Objid c;
+ int i = 0;
+
+ for (c = objects[oid]->child; c != NOTHING; c = objects[c]->sibling)
+ i++;
+
+ return i;
+}
+
+int
+db_for_all_children(Objid oid, int (*func)(void *, Objid), void *data)
+{
+ Objid c;
+
+ for (c = objects[oid]->child; c != NOTHING; c = objects[c]->sibling)
+ if (func(data, c))
+ return 1;
+
+ return 0;
+}
+
+#define LL_REMOVE(where, listname, what, nextname) { \
+ Objid lid; \
+ if (objects[where]->listname == what) \
+ objects[where]->listname = objects[what]->nextname; \
+ else { \
+ for (lid = objects[where]->listname; lid != NOTHING; \
+ lid = objects[lid]->nextname) { \
+ if (objects[lid]->nextname == what) { \
+ objects[lid]->nextname = objects[what]->nextname; \
+ break; \
+ } \
+ } \
+ } \
+ objects[what]->nextname = NOTHING; \
+}
+
+#define LL_APPEND(where, listname, what, nextname) { \
+ Objid lid; \
+ if (objects[where]->listname == NOTHING) { \
+ objects[where]->listname = what; \
+ } else { \
+ for (lid = objects[where]->listname; \
+ objects[lid]->nextname != NOTHING; \
+ lid = objects[lid]->nextname) \
+ ; \
+ objects[lid]->nextname = what; \
+ } \
+ objects[what]->nextname = NOTHING; \
+}
+
+int
+db_change_parent(Objid oid, Objid parent)
+{
+ Objid old_parent;
+
+ if (!dbpriv_check_properties_for_chparent(oid, parent))
+ return 0;
+
+ old_parent = objects[oid]->parent;
+
+ if (old_parent != NOTHING)
+ LL_REMOVE(old_parent, child, oid, sibling);
+
+ if (parent != NOTHING)
+ LL_APPEND(parent, child, oid, sibling);
+
+ objects[oid]->parent = parent;
+ dbpriv_fix_properties_after_chparent(oid, old_parent);
+
+ return 1;
+}
+
+Objid
+db_object_location(Objid oid)
+{
+ return objects[oid]->location;
+}
+
+int
+db_count_contents(Objid oid)
+{
+ Objid c;
+ int i = 0;
+
+ for (c = objects[oid]->contents; c != NOTHING; c = objects[c]->next)
+ i++;
+
+ return i;
+}
+
+int
+db_for_all_contents(Objid oid, int (*func)(void *, Objid), void *data)
+{
+ Objid c;
+
+ for (c = objects[oid]->contents; c != NOTHING; c = objects[c]->next)
+ if (func(data, c))
+ return 1;
+
+ return 0;
+}
+
+void
+db_change_location(Objid oid, Objid location)
+{
+ Objid old_location = objects[oid]->location;
+
+ if (valid(old_location))
+ LL_REMOVE(old_location, contents, oid, next);
+
+ if (valid(location))
+ LL_APPEND(location, contents, oid, next);
+
+ objects[oid]->location = location;
+}
+
+int
+db_object_has_flag(Objid oid, db_object_flag f)
+{
+ return (objects[oid]->flags & (1 << f)) != 0;
+}
+
+void
+db_set_object_flag(Objid oid, db_object_flag f)
+{
+ objects[oid]->flags |= (1 << f);
+ if (f == FLAG_USER) {
+ Var v;
+
+ v.type = TYPE_OBJ;
+ v.v.obj = oid;
+ all_users = setadd(all_users, v);
+ }
+}
+
+void
+db_clear_object_flag(Objid oid, db_object_flag f)
+{
+ objects[oid]->flags &= ~(1 << f);
+ if (f == FLAG_USER) {
+ Var v;
+
+ v.type = TYPE_OBJ;
+ v.v.obj = oid;
+ all_users = setremove(all_users, v);
+ }
+}
+
+int
+db_object_allows(Objid oid, Objid progr, db_object_flag f)
+{
+ return (progr == db_object_owner(oid)
+ || is_wizard(progr)
+ || db_object_has_flag(oid, f));
+}
+
+int
+is_wizard(Objid oid)
+{
+ return valid(oid) && db_object_has_flag(oid, FLAG_WIZARD);
+}
+
+int
+is_programmer(Objid oid)
+{
+ return valid(oid) && db_object_has_flag(oid, FLAG_PROGRAMMER);
+}
+
+int
+is_user(Objid oid)
+{
+ return valid(oid) && db_object_has_flag(oid, FLAG_USER);
+}
+
+Var
+db_all_users(void)
+{
+ return all_users;
+}
+
+void
+dbpriv_set_all_users(Var v)
+{
+ all_users = v;
+}
+
+char rcsid_db_objects[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.5 1996/04/08 00:42:11 pavel
+ * Adjusted computation in `db_object_bytes()' to account for change in the
+ * definition of `value_bytes()'. Release 1.8.0p3.
+ *
+ * Revision 2.4 1996/02/08 07:18:13 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.3 1996/01/16 07:23:45 pavel
+ * Fixed object-array overrun when a recycled object is right on the boundary.
+ * Release 1.8.0alpha6.
+ *
+ * Revision 2.2 1996/01/11 07:30:53 pavel
+ * Fixed memory-smash bug in db_renumber_object(). Release 1.8.0alpha5.
+ *
+ * Revision 2.1 1995/12/11 08:08:28 pavel
+ * Added `db_object_bytes()'. Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:20:51 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:20:41 pavel
+ * Initial revision
+ */
diff --git a/db_private.h b/db_private.h
new file mode 100644
index 0000000..0e10d6d
--- /dev/null
+++ b/db_private.h
@@ -0,0 +1,159 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Private interface for internal communication in the DB implementation
+ *****************************************************************************/
+
+#include "config.h"
+#include "exceptions.h"
+#include "program.h"
+#include "structures.h"
+
+typedef struct Verbdef Verbdef;
+
+struct Verbdef {
+ const char *name;
+ Program *program;
+ Objid owner;
+ short perms;
+ short prep;
+ Verbdef *next;
+};
+
+typedef struct Proplist Proplist;
+typedef struct Propdef Propdef;
+
+struct Propdef {
+ const char *name;
+ int hash;
+};
+
+struct Proplist {
+ int max_length;
+ int cur_length;
+ Propdef *l;
+};
+
+typedef struct Pval {
+ Var var;
+ Objid owner;
+ short perms;
+} Pval;
+
+typedef struct Object {
+ Objid id;
+ Objid owner;
+ Objid location;
+ Objid contents;
+ Objid next;
+
+ Objid parent;
+ Objid child;
+ Objid sibling;
+
+
+ const char *name;
+ int flags;
+
+ Verbdef *verbdefs;
+ Proplist propdefs;
+ Pval *propval;
+} Object;
+
+/*********** Objects ***********/
+
+extern void dbpriv_set_all_users(Var);
+ /* Initialize the list returned by
+ * db_all_users().
+ */
+
+extern Object *dbpriv_new_object(void);
+ /* Creates a new object, assigning it a number,
+ * but doesn't fill in any of the fields other
+ * than `id'.
+ */
+
+extern void dbpriv_new_recycled_object(void);
+ /* Does the equivalent of creating and
+ * destroying an object, with the net effect of
+ * using up the next available object number.
+ */
+
+extern Object *dbpriv_find_object(Objid);
+ /* Returns 0 if given object is not valid.
+ */
+
+/*********** Properties ***********/
+
+extern Propdef dbpriv_new_propdef(const char *name);
+
+extern int dbpriv_count_properties(Objid);
+
+extern int dbpriv_check_properties_for_chparent(Objid oid,
+ Objid new_parent);
+ /* Return true iff NEW_PARENT defines no
+ * properties that are also defined by either
+ * OID or any of OID's descendants.
+ */
+
+extern void dbpriv_fix_properties_after_chparent(Objid oid,
+ Objid old_parent);
+ /* OID has just had its parent changed away
+ * from OLD_PARENT. Fix up the properties of
+ * OID and its descendants, removing obsolete
+ * ones and adding clear new ones, as
+ * appropriate for its new parent.
+ */
+
+/*********** Verbs ***********/
+
+extern void dbpriv_build_prep_table(void);
+ /* Should be called once near the beginning of
+ * the world, to initialize the
+ * prepositional-phrase matching table.
+ */
+
+/*********** DBIO ***********/
+
+extern Exception dbpriv_dbio_failed;
+ /* Raised by DBIO in case of failure (e.g.,
+ * running out of disk space for the dump).
+ */
+
+extern void dbpriv_set_dbio_input(FILE *);
+extern void dbpriv_set_dbio_output(FILE *);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.3 1996/02/08 06:27:28 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.2 1995/12/31 03:18:05 pavel
+ * Removed a few more uses of `unsigned'. Release 1.8.0alpha4.
+ *
+ * Revision 2.1 1995/12/28 00:57:51 pavel
+ * Added dbpriv_build_prep_table(). Release 1.8.0alpha3.
+ *
+ * Revision 2.0 1995/11/30 05:05:53 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 05:05:38 pavel
+ * Initial revision
+ */
diff --git a/db_properties.c b/db_properties.c
new file mode 100644
index 0000000..69116f4
--- /dev/null
+++ b/db_properties.c
@@ -0,0 +1,700 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for manipulating properties on DB objects
+ *****************************************************************************/
+
+#include "config.h"
+#include "db.h"
+#include "db_private.h"
+#include "list.h"
+#include "storage.h"
+#include "utils.h"
+
+Propdef
+dbpriv_new_propdef(const char *name)
+{
+ Propdef newprop;
+
+ newprop.name = str_ref(name);
+ newprop.hash = str_hash(name);
+ return newprop;
+}
+
+int
+dbpriv_count_properties(Objid oid)
+{
+ Object *o;
+ int nprops = 0;
+
+ for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
+ nprops += o->propdefs.cur_length;
+
+ return nprops;
+}
+
+static int
+property_defined_at_or_below(const char *pname, int phash, Objid oid)
+{
+ /* Return true iff some descendant of OID defines a property named PNAME.
+ */
+ Objid c;
+ Proplist *props = &dbpriv_find_object(oid)->propdefs;
+ int length = props->cur_length;
+ int i;
+
+ for (i = 0; i < length; i++)
+ if (props->l[i].hash == phash
+ && !mystrcasecmp(props->l[i].name, pname))
+ return 1;
+
+ for (c = dbpriv_find_object(oid)->child;
+ c != NOTHING;
+ c = dbpriv_find_object(c)->sibling)
+ if (property_defined_at_or_below(pname, phash, c))
+ return 1;
+
+ return 0;
+}
+
+static void
+insert_prop(Objid oid, int pos, Pval pval)
+{
+ Pval *new_propval;
+ Object *o;
+ int i, nprops;
+
+ nprops = dbpriv_count_properties(oid);
+ new_propval = mymalloc(nprops * sizeof(Pval), M_PVAL);
+
+ o = dbpriv_find_object(oid);
+
+ for (i = 0; i < pos; i++)
+ new_propval[i] = o->propval[i];
+
+ new_propval[pos] = pval;
+ new_propval[pos].var = var_ref(pval.var);
+ if (new_propval[pos].perms & PF_CHOWN)
+ new_propval[pos].owner = o->owner;
+
+ for (i = pos + 1; i < nprops; i++)
+ new_propval[i] = o->propval[i - 1];
+
+ if (o->propval)
+ myfree(o->propval, M_PVAL);
+ o->propval = new_propval;
+}
+
+static void
+insert_prop_recursively(Objid root, int root_pos, Pval pv)
+{
+ Objid c;
+
+ insert_prop(root, root_pos, pv);
+ pv.var.type = TYPE_CLEAR; /* do after initial insert_prop so only
+ children will be TYPE_CLEAR */
+ for (c = dbpriv_find_object(root)->child;
+ c != NOTHING;
+ c = dbpriv_find_object(c)->sibling) {
+ int new_prop_count = dbpriv_find_object(c)->propdefs.cur_length;
+
+ insert_prop_recursively(c, new_prop_count + root_pos, pv);
+ }
+}
+
+int
+db_add_propdef(Objid oid, const char *pname, Var value, Objid owner,
+ unsigned flags)
+{
+ Object *o;
+ Pval pval;
+ int i;
+ db_prop_handle h;
+
+ h = db_find_property(oid, pname, 0);
+
+ if (h.ptr || property_defined_at_or_below(pname, str_hash(pname), oid))
+ return 0;
+
+ o = dbpriv_find_object(oid);
+ if (o->propdefs.cur_length == o->propdefs.max_length) {
+ Propdef *old_props = o->propdefs.l;
+ int new_size = (o->propdefs.max_length == 0
+ ? 8 : 2 * o->propdefs.max_length);
+
+ o->propdefs.l = mymalloc(new_size * sizeof(Propdef), M_PROPDEF);
+ for (i = 0; i < o->propdefs.max_length; i++)
+ o->propdefs.l[i] = old_props[i];
+ o->propdefs.max_length = new_size;
+
+ if (old_props)
+ myfree(old_props, M_PROPDEF);
+ }
+
+ o->propdefs.l[o->propdefs.cur_length++] = dbpriv_new_propdef(pname);
+
+ pval.var = value;
+ pval.owner = owner;
+ pval.perms = flags;
+
+ insert_prop_recursively(oid, o->propdefs.cur_length - 1, pval);
+
+ return 1;
+}
+
+int
+db_rename_propdef(Objid oid, const char *old, const char *new)
+{
+ Proplist *props = &dbpriv_find_object(oid)->propdefs;
+ int hash = str_hash(old);
+ int count = props->cur_length;
+ int i;
+ db_prop_handle h;
+
+ for (i = 0; i < count; i++) {
+ Propdef p;
+
+ p = props->l[i];
+ if (p.hash == hash && !mystrcasecmp(p.name, old)) {
+ if (mystrcasecmp(old, new) != 0) { /* Not changing just the case */
+ h = db_find_property(oid, new, 0);
+ if (h.ptr
+ || property_defined_at_or_below(new, str_hash(new), oid))
+ return 0;
+ }
+ free_str(props->l[i].name);
+ props->l[i].name = str_ref(new);
+ props->l[i].hash = str_hash(new);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+remove_prop(Objid oid, int pos)
+{
+ Pval *new_propval;
+ Object *o;
+ int i, nprops;
+
+ o = dbpriv_find_object(oid);
+ nprops = dbpriv_count_properties(oid);
+
+ free_var(o->propval[pos].var); /* free deleted property */
+
+ if (nprops) {
+ new_propval = mymalloc(nprops * sizeof(Pval), M_PVAL);
+ for (i = 0; i < pos; i++)
+ new_propval[i] = o->propval[i];
+ for (i = pos; i < nprops; i++)
+ new_propval[i] = o->propval[i + 1];
+ } else
+ new_propval = 0;
+
+ if (o->propval)
+ myfree(o->propval, M_PVAL);
+ o->propval = new_propval;
+}
+
+static void
+remove_prop_recursively(Objid root, int root_pos)
+{
+ Objid c;
+
+ remove_prop(root, root_pos);
+ for (c = dbpriv_find_object(root)->child;
+ c != NOTHING;
+ c = dbpriv_find_object(c)->sibling) {
+ int new_prop_count = dbpriv_find_object(c)->propdefs.cur_length;
+
+ remove_prop_recursively(c, new_prop_count + root_pos);
+ }
+}
+
+int
+db_delete_propdef(Objid oid, const char *pname)
+{
+ Proplist *props = &dbpriv_find_object(oid)->propdefs;
+ int hash = str_hash(pname);
+ int count = props->cur_length;
+ int max = props->max_length;
+ int i, j;
+
+ for (i = 0; i < count; i++) {
+ Propdef p;
+
+ p = props->l[i];
+ if (p.hash == hash && !mystrcasecmp(p.name, pname)) {
+ if (p.name)
+ free_str(p.name);
+
+ if (max > 8 && props->cur_length <= ((max * 3) / 8)) {
+ int new_size = max / 2;
+ Propdef *new_props;
+
+ new_props = mymalloc(new_size * sizeof(Propdef), M_PROPDEF);
+
+ for (j = 0; j < i; j++)
+ new_props[j] = props->l[j];
+ for (j = i + 1; j < count; j++)
+ new_props[j - 1] = props->l[j];
+
+ myfree(props->l, M_PROPDEF);
+ props->l = new_props;
+ props->max_length = new_size;
+ } else
+ for (j = i + 1; j < count; j++)
+ props->l[j - 1] = props->l[j];
+
+ props->cur_length--;
+ remove_prop_recursively(oid, i);
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int
+db_count_propdefs(Objid oid)
+{
+ return dbpriv_find_object(oid)->propdefs.cur_length;
+}
+
+int
+db_for_all_propdefs(Objid oid, int (*func)(void *, const char *), void *data)
+{
+ int i;
+ Object *o = dbpriv_find_object(oid);
+ int len = o->propdefs.cur_length;
+
+ for (i = 0; i < len; i++)
+ if (func(data, o->propdefs.l[i].name))
+ return 1;
+
+ return 0;
+}
+
+struct contents_data {
+ Var r;
+ int i;
+};
+
+static int
+add_to_list(void *data, Objid c)
+{
+ struct contents_data *d = data;
+
+ d->i++;
+ d->r.v.list[d->i].type = TYPE_OBJ;
+ d->r.v.list[d->i].v.obj = c;
+
+ return 0;
+}
+
+static void
+get_bi_value(db_prop_handle h, Var *value)
+{
+ Objid oid = *((Objid *) h.ptr);
+
+ switch (h.built_in) {
+ case BP_NAME:
+ value->type = TYPE_STR;
+ value->v.str = str_ref(db_object_name(oid));
+ break;
+ case BP_OWNER:
+ value->type = TYPE_OBJ;
+ value->v.obj = db_object_owner(oid);
+ break;
+ case BP_PROGRAMMER:
+ value->type = TYPE_INT;
+ value->v.num = db_object_has_flag(oid, FLAG_PROGRAMMER);
+ break;
+ case BP_WIZARD:
+ value->type = TYPE_INT;
+ value->v.num = db_object_has_flag(oid, FLAG_WIZARD);
+ break;
+ case BP_R:
+ value->type = TYPE_INT;
+ value->v.num = db_object_has_flag(oid, FLAG_READ);
+ break;
+ case BP_W:
+ value->type = TYPE_INT;
+ value->v.num = db_object_has_flag(oid, FLAG_WRITE);
+ break;
+ case BP_F:
+ value->type = TYPE_INT;
+ value->v.num = db_object_has_flag(oid, FLAG_FERTILE);
+ break;
+ case BP_LOCATION:
+ value->type = TYPE_OBJ;
+ value->v.obj = db_object_location(oid);
+ break;
+ case BP_CONTENTS:
+ {
+ struct contents_data d;
+
+ d.r = new_list(db_count_contents(oid));
+ d.i = 0;
+ db_for_all_contents(oid, add_to_list, &d);
+
+ *value = d.r;
+ }
+ break;
+ default:
+ panic("Unknown built-in property in GET_BI_VALUE!");
+ }
+}
+
+db_prop_handle
+db_find_property(Objid oid, const char *name, Var *value)
+{
+ static struct {
+ const char *name;
+ enum bi_prop prop;
+ int hash;
+ } ptable[] = {
+ {"name", BP_NAME, 0},
+ {"owner", BP_OWNER, 0},
+ {"programmer", BP_PROGRAMMER, 0},
+ {"wizard", BP_WIZARD, 0},
+ {"r", BP_R, 0},
+ {"w", BP_W, 0},
+ {"f", BP_F, 0},
+ {"location", BP_LOCATION, 0},
+ {"contents", BP_CONTENTS, 0}
+ };
+ static int ptable_init = 0;
+ int i, n;
+ db_prop_handle h;
+ int hash = str_hash(name);
+ Object *o;
+
+ if (!ptable_init) {
+ for (i = 0; i < Arraysize(ptable); i++)
+ ptable[i].hash = str_hash(ptable[i].name);
+ ptable_init = 1;
+ }
+
+ for (i = 0; i < Arraysize(ptable); i++) {
+ if (ptable[i].hash == hash && !mystrcasecmp(name, ptable[i].name)) {
+ static Objid ret;
+
+ ret = oid;
+ h.built_in = ptable[i].prop;
+ h.definer = NOTHING;
+ h.ptr = &ret;
+ if (value)
+ get_bi_value(h, value);
+ return h;
+ }
+ }
+
+ h.built_in = BP_NONE;
+ n = 0;
+ for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent)) {
+ Proplist *props = &(o->propdefs);
+ Propdef *defs = props->l;
+ int length = props->cur_length;
+
+ for (i = 0; i < length; i++, n++) {
+ if (defs[i].hash == hash
+ && !mystrcasecmp(defs[i].name, name)) {
+ Pval *prop;
+
+ h.definer = o->id;
+ o = dbpriv_find_object(oid);
+ prop = h.ptr = o->propval + n;
+
+ if (value) {
+ while (prop->var.type == TYPE_CLEAR) {
+ n -= o->propdefs.cur_length;
+ o = dbpriv_find_object(o->parent);
+ prop = o->propval + n;
+ }
+ *value = prop->var;
+ }
+ return h;
+ }
+ }
+ }
+
+ h.ptr = 0;
+ return h;
+}
+
+Var
+db_property_value(db_prop_handle h)
+{
+ Var value;
+
+ if (h.built_in)
+ get_bi_value(h, &value);
+ else {
+ Pval *prop = h.ptr;
+
+ value = prop->var;
+ }
+
+ return value;
+}
+
+void
+db_set_property_value(db_prop_handle h, Var value)
+{
+ if (!h.built_in) {
+ Pval *prop = h.ptr;
+
+ free_var(prop->var);
+ prop->var = value;
+ } else {
+ Objid oid = *((Objid *) h.ptr);
+ db_object_flag flag;
+
+ switch (h.built_in) {
+ case BP_NAME:
+ if (value.type != TYPE_STR)
+ goto complain;
+ db_set_object_name(oid, value.v.str);
+ break;
+ case BP_OWNER:
+ if (value.type != TYPE_OBJ)
+ goto complain;
+ db_set_object_owner(oid, value.v.obj);
+ break;
+ case BP_PROGRAMMER:
+ flag = FLAG_PROGRAMMER;
+ goto finish_flag;
+ case BP_WIZARD:
+ flag = FLAG_WIZARD;
+ goto finish_flag;
+ case BP_R:
+ flag = FLAG_READ;
+ goto finish_flag;
+ case BP_W:
+ flag = FLAG_WRITE;
+ goto finish_flag;
+ case BP_F:
+ flag = FLAG_FERTILE;
+ finish_flag:
+ if (is_true(value))
+ db_set_object_flag(oid, flag);
+ else
+ db_clear_object_flag(oid, flag);
+ free_var(value);
+ break;
+ case BP_LOCATION:
+ case BP_CONTENTS:
+ complain:
+ panic("Inappropriate value in DB_SET_PROPERTY_VALUE!");
+ break;
+ default:
+ panic("Unknown built-in property in DB_SET_PROPERTY_VALUE!");
+ }
+ }
+}
+
+Objid
+db_property_owner(db_prop_handle h)
+{
+ if (h.built_in) {
+ panic("Built-in property in DB_PROPERTY_OWNER!");
+ return NOTHING;
+ } else {
+ Pval *prop = h.ptr;
+
+ return prop->owner;
+ }
+}
+
+void
+db_set_property_owner(db_prop_handle h, Objid oid)
+{
+ if (h.built_in)
+ panic("Built-in property in DB_SET_PROPERTY_OWNER!");
+ else {
+ Pval *prop = h.ptr;
+
+ prop->owner = oid;
+ }
+}
+
+unsigned
+db_property_flags(db_prop_handle h)
+{
+ if (h.built_in) {
+ panic("Built-in property in DB_PROPERTY_FLAGS!");
+ return 0;
+ } else {
+ Pval *prop = h.ptr;
+
+ return prop->perms;
+ }
+}
+
+void
+db_set_property_flags(db_prop_handle h, unsigned flags)
+{
+ if (h.built_in)
+ panic("Built-in property in DB_SET_PROPERTY_FLAGS!");
+ else {
+ Pval *prop = h.ptr;
+
+ prop->perms = flags;
+ }
+}
+
+int
+db_property_allows(db_prop_handle h, Objid progr, db_prop_flag flag)
+{
+ return ((db_property_flags(h) & flag)
+ || progr == db_property_owner(h)
+ || is_wizard(progr));
+}
+
+static void
+fix_props(Objid oid, int parent_local, int old, int new, int common)
+{
+ Object *me = dbpriv_find_object(oid);
+ Object *parent = dbpriv_find_object(me->parent);
+ Pval *new_propval;
+ int local = parent_local;
+ int i;
+ Objid c;
+
+ local += me->propdefs.cur_length;
+
+ for (i = local; i < local + old; i++)
+ free_var(me->propval[i].var);
+
+ if (local + new + common != 0) {
+ new_propval = mymalloc((local + new + common) * sizeof(Pval), M_PVAL);
+ for (i = 0; i < local; i++)
+ new_propval[i] = me->propval[i];
+ for (i = 0; i < new; i++) {
+ Pval pv;
+
+ pv = parent->propval[parent_local + i];
+ new_propval[local + i] = pv;
+ new_propval[local + i].var.type = TYPE_CLEAR;
+ if (pv.perms & PF_CHOWN)
+ new_propval[local + i].owner = me->owner;
+ }
+ for (i = 0; i < common; i++)
+ new_propval[local + new + i] = me->propval[local + old + i];
+ } else
+ new_propval = 0;
+
+ if (me->propval)
+ myfree(me->propval, M_PVAL);
+ me->propval = new_propval;
+
+ for (c = me->child; c != NOTHING; c = dbpriv_find_object(c)->sibling)
+ fix_props(c, local, old, new, common);
+}
+
+int
+dbpriv_check_properties_for_chparent(Objid oid, Objid new_parent)
+{
+ Object *o;
+ int i;
+
+ for (o = dbpriv_find_object(new_parent);
+ o;
+ o = dbpriv_find_object(o->parent)) {
+ Proplist *props = &o->propdefs;
+
+ for (i = 0; i < props->cur_length; i++)
+ if (property_defined_at_or_below(props->l[i].name,
+ props->l[i].hash,
+ oid))
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+dbpriv_fix_properties_after_chparent(Objid oid, Objid old_parent)
+{
+ Objid o1, o2, common, new_parent;
+ int common_props, old_props, new_props;
+
+ /* Find the nearest common ancestor between old & new parent */
+ new_parent = db_object_parent(oid);
+ common = NOTHING;
+ for (o1 = new_parent; o1 != NOTHING; o1 = db_object_parent(o1))
+ for (o2 = old_parent; o2 != NOTHING; o2 = db_object_parent(o2))
+ if (o1 == o2) {
+ common = o1;
+ goto endouter;
+ }
+ endouter:
+
+ if (common != NOTHING)
+ common_props = dbpriv_count_properties(common);
+ else
+ common_props = 0;
+
+ old_props = dbpriv_count_properties(old_parent) - common_props;
+ new_props = dbpriv_count_properties(new_parent) - common_props;
+
+ fix_props(oid, 0, old_props, new_props, common_props);
+}
+
+char rcsid_db_properties[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.6 1996/04/08 01:08:32 pavel
+ * Fixed `db_rename_propdef()' to allow case-only changes. Release 1.8.0p3.
+ *
+ * Revision 2.5 1996/02/11 00:46:48 pavel
+ * Enhanced db_find_property() to report the defining object of the found
+ * property. Release 1.8.0beta2.
+ *
+ * Revision 2.4 1996/02/08 07:18:02 pavel
+ * Renamed TYPE_NUM to TYPE_INT. Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.3 1995/12/31 03:27:40 pavel
+ * Removed a few more uses of `unsigned'. Reordered things in
+ * db_delete_propdef() to fix an occasional memory smash.
+ * Release 1.8.0alpha4.
+ *
+ * Revision 2.2 1995/12/28 00:41:34 pavel
+ * Made *all* built-in property references return fresh value references.
+ * Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:52:27 pavel
+ * Added support for renaming propdefs.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:21:13 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:21:02 pavel
+ * Initial revision
+ */
diff --git a/db_verbs.c b/db_verbs.c
new file mode 100644
index 0000000..578026d
--- /dev/null
+++ b/db_verbs.c
@@ -0,0 +1,573 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Routines for manipulating verbs on DB objects
+ *****************************************************************************/
+
+#include <ctype.h>
+#include "my-stdlib.h"
+#include "my-string.h"
+
+#include "config.h"
+#include "db.h"
+#include "db_private.h"
+#include "parse_cmd.h"
+#include "program.h"
+#include "storage.h"
+#include "utils.h"
+
+
+/*********** Prepositions ***********/
+
+#define MAXPPHRASE 3 /* max. number of words in a prepositional phrase */
+
+/* NOTE: New prepositional phrases should only be added to this list at the
+ * end, never in the middle, and no entries should every be removed from this
+ * list; the list indices are stored raw in the DB file.
+ */
+static const char *prep_list[] = {
+ "with/using",
+ "at/to",
+ "in front of",
+ "in/inside/into",
+ "on top of/on/onto/upon",
+ "out of/from inside/from",
+ "over",
+ "through",
+ "under/underneath/beneath",
+ "behind",
+ "beside",
+ "for/about",
+ "is",
+ "as",
+ "off/off of",
+};
+
+#define NPREPS Arraysize(prep_list)
+
+typedef struct pt_entry {
+ int nwords;
+ char *words[MAXPPHRASE];
+ struct pt_entry *next;
+} pt_entry;
+
+struct pt_entry *prep_table[NPREPS];
+
+void
+dbpriv_build_prep_table(void)
+{
+ int i, j;
+ int nwords;
+ char **words;
+ char cprep[100];
+ const char *p;
+ char *t;
+ pt_entry *current_alias, **prev;
+
+ for (i = 0; i < NPREPS; i++) {
+ p = prep_list[i];
+ prev = &prep_table[i];
+ while (*p) {
+ t = cprep;
+ if (*p == '/')
+ p++;
+ while (*p && *p != '/')
+ *t++ = *p++;
+ *t = '\0';
+
+ /* This call to PARSE_INTO_WORDS() is the reason that this function
+ * is called from DB_INITIALIZE() instead of from the first call to
+ * DB_FIND_PREP(), below. You see, PARSE_INTO_WORDS() isn't
+ * re-entrant, and DB_FIND_PREP() is called from code that's still
+ * using the results of a previous call to PARSE_INTO_WORDS()...
+ */
+ words = parse_into_words(cprep, &nwords);
+
+ current_alias = mymalloc(sizeof(struct pt_entry), M_PREP);
+ current_alias->nwords = nwords;
+ current_alias->next = 0;
+ for (j = 0; j < nwords; j++)
+ current_alias->words[j] = str_dup(words[j]);
+ *prev = current_alias;
+ prev = &current_alias->next;
+ }
+ }
+}
+
+db_prep_spec
+db_find_prep(int argc, char *argv[], int *first, int *last)
+{
+ pt_entry *alias;
+ int i, j, k;
+ int exact_match = (first == 0 || last == 0);
+
+ for (i = 0; i < argc; i++) {
+ for (j = 0; j < NPREPS; j++) {
+ for (alias = prep_table[j]; alias; alias = alias->next) {
+ if (i + alias->nwords <= argc) {
+ for (k = 0; k < alias->nwords; k++) {
+ if (mystrcasecmp(argv[i + k], alias->words[k]))
+ break;
+ }
+ if (k == alias->nwords
+ && (! exact_match || i + k == argc)) {
+ if (! exact_match) {
+ *first = i;
+ *last = i + alias->nwords - 1;
+ }
+ return (db_prep_spec) j;
+ }
+ }
+ }
+ }
+ if (exact_match)
+ break;
+ }
+ return PREP_NONE;
+}
+
+db_prep_spec
+db_match_prep(const char *prepname)
+{
+ db_prep_spec prep;
+ int argc;
+ char *ptr;
+ char **argv;
+ char *s, first;
+
+ s = str_dup(prepname);
+ first = s[0];
+ if (first == '#')
+ first = (++s)[0];
+ prep = strtol(s, &ptr, 10);
+ if (*ptr == '\0') {
+ free_str(s);
+ if (!isdigit(first) || prep >= NPREPS)
+ return PREP_NONE;
+ else
+ return prep;
+ }
+
+ if ((ptr = strchr(s, '/')) != '\0')
+ *ptr = '\0';
+
+ argv = parse_into_words(s, &argc);
+ prep = db_find_prep(argc, argv, 0, 0);
+ free_str(s);
+ return prep;
+}
+
+const char *
+db_unparse_prep(db_prep_spec prep)
+{
+ if (prep == PREP_NONE)
+ return "none";
+ else if (prep == PREP_ANY)
+ return "any";
+ else
+ return prep_list[prep];
+}
+
+
+/*********** Verbs ***********/
+
+#define DOBJSHIFT 4
+#define IOBJSHIFT 6
+#define OBJMASK 0x3
+#define PERMMASK 0xF
+
+void
+db_add_verb(Objid oid, const char *vnames, Objid owner, unsigned flags,
+ db_arg_spec dobj, db_prep_spec prep, db_arg_spec iobj)
+{
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v, *newv;
+
+ newv = mymalloc(sizeof(Verbdef), M_VERBDEF);
+ newv->name = vnames;
+ newv->owner = owner;
+ newv->perms = flags | (dobj << DOBJSHIFT) | (iobj << IOBJSHIFT);
+ newv->prep = prep;
+ newv->next = 0;
+ newv->program = 0;
+ if (o->verbdefs) {
+ for (v = o->verbdefs; v->next; v = v->next)
+ ;
+ v->next = newv;
+ } else
+ o->verbdefs = newv;
+}
+
+static Verbdef *
+find_verbdef_by_name(Object *o, const char *vname, int check_x_bit)
+{
+ Verbdef *v;
+
+ for (v = o->verbdefs; v; v = v->next)
+ if (verbcasecmp(v->name, vname)
+ && (!check_x_bit || (v->perms & VF_EXEC)))
+ break;
+
+ return v;
+}
+
+int
+db_count_verbs(Objid oid)
+{
+ int count = 0;
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v;
+
+ for (v = o->verbdefs; v; v = v->next)
+ count++;
+
+ return count;
+}
+
+int
+db_for_all_verbs(Objid oid,
+ int (*func)(void *data, const char *vname),
+ void *data)
+{
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v;
+
+ for (v = o->verbdefs; v; v = v->next)
+ if ((*func)(data, v->name))
+ return 1;
+
+ return 0;
+}
+
+typedef struct { /* Non-null db_verb_handles point to these */
+ Objid definer;
+ Verbdef *verbdef;
+} handle;
+
+void
+db_delete_verb(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+ Objid oid = h->definer;
+ Verbdef *v = h->verbdef;
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *vv;
+
+ vv = o->verbdefs;
+ if (vv == v)
+ o->verbdefs = v->next;
+ else {
+ while (vv->next != v)
+ vv = vv->next;
+ vv->next = v->next;
+ }
+
+ if (v->program)
+ free_program(v->program);
+ if (v->name)
+ free_str(v->name);
+ myfree(v, M_VERBDEF);
+}
+
+db_verb_handle
+db_find_command_verb(Objid oid, const char *verb,
+ db_arg_spec dobj, unsigned prep, db_arg_spec iobj)
+{
+ Object *o;
+ Verbdef *v;
+ static handle h;
+ db_verb_handle vh;
+
+ for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
+ for (v = o->verbdefs; v; v = v->next) {
+ db_arg_spec vdobj = (v->perms >> DOBJSHIFT) & OBJMASK;
+ db_arg_spec viobj = (v->perms >> IOBJSHIFT) & OBJMASK;
+
+ if (verbcasecmp(v->name, verb)
+ && (vdobj == ASPEC_ANY || vdobj == dobj)
+ && (v->prep == PREP_ANY || v->prep == prep)
+ && (viobj == ASPEC_ANY || viobj == iobj)) {
+ h.definer = o->id;
+ h.verbdef = v;
+ vh.ptr = &h;
+
+ return vh;
+ }
+ }
+
+ vh.ptr = 0;
+
+ return vh;
+}
+
+db_verb_handle
+db_find_callable_verb(Objid oid, const char *verb)
+{
+ Object *o;
+ Verbdef *v;
+ static handle h;
+ db_verb_handle vh;
+
+ for (o = dbpriv_find_object(oid); o; o = dbpriv_find_object(o->parent))
+ if ((v = find_verbdef_by_name(o, verb, 1)) != 0) {
+ h.definer = o->id;
+ h.verbdef = v;
+ vh.ptr = &h;
+
+ return vh;
+ }
+
+ vh.ptr = 0;
+
+ return vh;
+}
+
+db_verb_handle
+db_find_defined_verb(Objid oid, const char *vname, int allow_numbers)
+{
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v;
+ char *p;
+ int num, i;
+ static handle h;
+ db_verb_handle vh;
+
+ if (!allow_numbers ||
+ (num = strtol(vname, &p, 10),
+ (isspace(*vname) || *p != '\0')))
+ num = -1;
+
+ for (i = 0, v = o->verbdefs; v; v = v->next, i++)
+ if (i == num || verbcasecmp(v->name, vname))
+ break;
+
+ if (v) {
+ h.definer = o->id;
+ h.verbdef = v;
+ vh.ptr = &h;
+
+ return vh;
+ }
+
+ vh.ptr = 0;
+
+ return vh;
+}
+
+db_verb_handle
+db_find_indexed_verb(Objid oid, unsigned index)
+{
+ Object *o = dbpriv_find_object(oid);
+ Verbdef *v;
+ unsigned i;
+ static handle h;
+ db_verb_handle vh;
+
+ for (v = o->verbdefs, i = 0; v; v = v->next)
+ if (++i == index) {
+ h.definer = o->id;
+ h.verbdef = v;
+ vh.ptr = &h;
+
+ return vh;
+ }
+
+ vh.ptr = 0;
+
+ return vh;
+}
+
+Objid
+db_verb_definer(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h)
+ return h->definer;
+
+ panic("DB_VERB_DEFINER: Null handle!");
+ return 0;
+}
+
+const char *
+db_verb_names(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h)
+ return h->verbdef->name;
+
+ panic("DB_VERB_NAMES: Null handle!");
+ return 0;
+}
+
+void
+db_set_verb_names(db_verb_handle vh, const char *names)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ if (h->verbdef->name)
+ free_str(h->verbdef->name);
+ h->verbdef->name = names;
+ } else
+ panic("DB_SET_VERB_NAMES: Null handle!");
+}
+
+Objid
+db_verb_owner(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h)
+ return h->verbdef->owner;
+
+ panic("DB_VERB_OWNER: Null handle!");
+ return 0;
+}
+
+void
+db_set_verb_owner(db_verb_handle vh, Objid owner)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h)
+ h->verbdef->owner = owner;
+ else
+ panic("DB_SET_VERB_OWNER: Null handle!");
+}
+
+unsigned
+db_verb_flags(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h)
+ return h->verbdef->perms & PERMMASK;
+
+ panic("DB_VERB_FLAGS: Null handle!");
+ return 0;
+}
+
+void
+db_set_verb_flags(db_verb_handle vh, unsigned flags)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ h->verbdef->perms &= ~PERMMASK;
+ h->verbdef->perms |= flags;
+ } else
+ panic("DB_SET_VERB_FLAGS: Null handle!");
+}
+
+Program *
+db_verb_program(db_verb_handle vh)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ Program *p = h->verbdef->program;
+
+ return p ? p : null_program();
+ }
+
+ panic("DB_VERB_PROGRAM: Null handle!");
+ return 0;
+}
+
+void
+db_set_verb_program(db_verb_handle vh, Program *program)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ if (h->verbdef->program)
+ free_program(h->verbdef->program);
+ h->verbdef->program = program;
+ } else
+ panic("DB_SET_VERB_PROGRAM: Null handle!");
+}
+
+void
+db_verb_arg_specs(db_verb_handle vh,
+ db_arg_spec *dobj, db_prep_spec *prep, db_arg_spec *iobj)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ *dobj = (h->verbdef->perms >> DOBJSHIFT) & OBJMASK;
+ *prep = h->verbdef->prep;
+ *iobj = (h->verbdef->perms >> IOBJSHIFT) & OBJMASK;
+ } else
+ panic("DB_VERB_ARG_SPECS: Null handle!");
+}
+
+void
+db_set_verb_arg_specs(db_verb_handle vh,
+ db_arg_spec dobj, db_prep_spec prep, db_arg_spec iobj)
+{
+ handle *h = (handle *) vh.ptr;
+
+ if (h) {
+ h->verbdef->perms = ((h->verbdef->perms & PERMMASK)
+ | (dobj << DOBJSHIFT)
+ | (iobj << IOBJSHIFT));
+ h->verbdef->prep = prep;
+ } else
+ panic("DB_SET_VERB_ARG_SPECS: Null handle!");
+}
+
+int
+db_verb_allows(db_verb_handle h, Objid progr, db_verb_flag flag)
+{
+ return ((db_verb_flags(h) & flag)
+ || progr == db_verb_owner(h)
+ || is_wizard(progr));
+}
+
+
+char rcsid_db_verbs[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.4 1996/05/12 21:32:23 pavel
+ * Changed db_add_verb() not to bump the reference count of the given
+ * verb-names string. Release 1.8.0p5.
+ *
+ * Revision 2.3 1996/02/08 07:17:40 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.2 1995/12/28 00:29:03 pavel
+ * Broke out the building of the preposition table into a separate function,
+ * called during initialization, to avoid an storage-overwriting bug. Changed
+ * db_delete_verb() to unbundle how the verb is found. Changed
+ * db_find_defined_verb() to allow suppressing old numeric-string behavior.
+ * Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 08:11:07 pavel
+ * Made verb programs never be NULL any more. Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:21:33 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:21:20 pavel
+ * Initial revision
+ */
diff --git a/decompile.c b/decompile.c
new file mode 100644
index 0000000..bb94116
--- /dev/null
+++ b/decompile.c
@@ -0,0 +1,1015 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "ast.h"
+#include "decompile.h"
+#include "exceptions.h"
+#include "opcode.h"
+#include "program.h"
+#include "storage.h"
+#include "utils.h"
+
+static Program *program;
+static Expr **expr_stack;
+static int top_expr_stack;
+
+static Byte *hot_byte;
+static void *hot_node;
+static enum { TOP, ENDBODY, BOTTOM, DONE } hot_position;
+
+static int lineno;
+
+static void
+push_expr(Expr *expr)
+{
+ expr_stack[top_expr_stack++] = expr;
+}
+
+static Expr *
+pop_expr(void)
+{
+ if (!top_expr_stack)
+ panic("Empty expr stack in DECOMPILE!");
+ return expr_stack[--top_expr_stack];
+}
+
+#define ADD_STMT(stmt) \
+do { \
+ Stmt *temp = stmt; \
+ \
+ *stmt_sink = temp; \
+ stmt_sink = &(temp->next); \
+ stmt_start = ptr - bc.vector;\
+} while (0);
+
+#define ADD_ARM(arm) \
+do { \
+ Cond_Arm *temp = arm; \
+ \
+ *arm_sink = temp; \
+ arm_sink = &(temp->next); \
+} while (0);
+
+#define READ_BYTES(n) \
+ (ptr += n, \
+ (n == 1 \
+ ? ptr[-1] \
+ : (n == 2 \
+ ? ((unsigned) ptr[-2] << 8) \
+ + ptr[-1] \
+ : ((unsigned) ptr[-4] << 24) \
+ + ((unsigned) ptr[-3] << 16) \
+ + ((unsigned) ptr[-2] << 8) \
+ + ptr[-1])))
+
+#define READ_LABEL() READ_BYTES(bc.numbytes_label)
+#define READ_LITERAL() program->literals[READ_BYTES(bc.numbytes_literal)]
+#define READ_FORK() program->fork_vectors[READ_BYTES(bc.numbytes_fork)]
+#define READ_ID() READ_BYTES(bc.numbytes_var_name)
+#define READ_STACK() READ_BYTES(bc.numbytes_stack)
+
+#define READ_JUMP(is_hot) read_jump(bc.numbytes_label, &ptr, &is_hot)
+
+static unsigned
+read_jump(unsigned numbytes_label, Byte **p, int *is_hot)
+{
+ Byte *ptr = *p;
+ unsigned label;
+
+ *is_hot = (ptr == hot_byte);
+ if (*ptr++ != OP_JUMP)
+ panic("Missing JUMP in DECOMPILE!");
+
+ label = READ_BYTES(numbytes_label);
+ *p = ptr;
+
+ return label;
+}
+
+#define HOT(is_hot, n) (node = n, is_hot ? (hot_node = node) : node)
+#define HOT1(is_hot, kid, n) HOT(is_hot || hot_node == kid, n)
+#define HOT2(is_hot, kid1, kid2, n) \
+ HOT1(is_hot || hot_node == kid1, kid2, n)
+#define HOT3(is_hot, kid1, kid2, kid3, n) \
+ HOT2(is_hot || hot_node == kid1, kid2, kid3, n)
+#define HOT4(is_hot, kid1, kid2, kid3, kid4, n) \
+ HOT3(is_hot || hot_node == kid1, kid2, kid3, kid4, n)
+
+#define HOT_OP(n) HOT(op_hot, n)
+#define HOT_OP1(kid, n) HOT1(op_hot, kid, n)
+#define HOT_OP2(kid1, kid2, n) HOT2(op_hot, kid1, kid2, n)
+#define HOT_OP3(kid1, kid2, kid3, n) \
+ HOT3(op_hot, kid1, kid2, kid3, n)
+
+#define HOT_POS(is_hot, n, pos) \
+ if (is_hot) { hot_node = n; hot_position = pos; }
+#define HOT_BOTTOM(is_hot, n) HOT_POS(is_hot, n, BOTTOM)
+
+#define DECOMPILE(bc, start, end, stmt_sink, arm_sink) \
+ (ptr = decompile(bc, start, end, stmt_sink, arm_sink))
+
+static Byte *
+decompile(Bytecodes bc, Byte *start, Byte *end, Stmt **stmt_sink,
+ Cond_Arm **arm_sink)
+{
+ /*** The reader will likely find it useful to consult the file
+ *** `MOOCodeSequences.txt' in this directory while reading this function.
+ ***/
+
+ Byte *ptr = start;
+ unsigned stmt_start = start - bc.vector;
+ unsigned jump_len = bc.numbytes_label + 1;
+ Stmt *s;
+ Expr *e;
+ enum Expr_Kind kind;
+ void *node;
+ int asgn_hot = 0;
+
+ if (stmt_sink)
+ *stmt_sink = 0;
+ if (arm_sink)
+ *arm_sink = 0;
+
+ while (ptr < end) {
+ int op_hot = (ptr == hot_byte);
+ Opcode op = *ptr++;
+
+ if (IS_PUSH_n(op)) {
+ e = alloc_expr(EXPR_ID);
+ e->e.id = PUSH_n_INDEX(op);
+ push_expr(HOT_OP(e));
+ continue;
+ } else if (IS_PUT_n(op)) {
+ e = alloc_expr(EXPR_ID);
+ e->e.id = PUT_n_INDEX(op);
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT_OP1(e->e.bin.rhs, e));
+ continue;
+ } else if (IS_OPTIM_NUM_OPCODE(op)) {
+ e = alloc_var(TYPE_INT);
+ e->e.var.v.num = OPCODE_TO_OPTIM_NUM(op);
+ push_expr(HOT_OP(e));
+ continue;
+ }
+
+ switch (op) {
+ case OP_IF:
+ {
+ unsigned next = READ_LABEL();
+ Expr *condition = pop_expr();
+ Stmt *arm_stmts;
+ Cond_Arm *arm;
+ unsigned done;
+ int jump_hot;
+
+ s = alloc_stmt(STMT_COND);
+ DECOMPILE(bc, ptr, bc.vector + next - jump_len, &arm_stmts, 0);
+ arm = s->s.cond.arms = alloc_cond_arm(condition, arm_stmts);
+ HOT_OP1(condition, arm);
+ done = READ_JUMP(jump_hot);
+ HOT_BOTTOM(jump_hot, arm);
+ DECOMPILE(bc, ptr, bc.vector + done, &(s->s.cond.otherwise),
+ &(arm->next));
+
+ ADD_STMT(s);
+ }
+ break;
+ case OP_EIF:
+ {
+ unsigned next = READ_LABEL();
+ Expr *condition = pop_expr();
+ Stmt *arm_stmts;
+ Cond_Arm *arm;
+ unsigned done;
+ int jump_hot;
+
+ DECOMPILE(bc, ptr, bc.vector + next - jump_len, &arm_stmts, 0);
+ ADD_ARM(arm = alloc_cond_arm(condition, arm_stmts));
+ HOT_OP1(condition, arm);
+ done = READ_JUMP(jump_hot);
+ HOT_BOTTOM(jump_hot, arm);
+ if (bc.vector + done != end)
+ panic("ELSEIF jumps to wrong place in DECOMPILE!");
+ stmt_start = ptr - bc.vector;
+ }
+ break;
+ case OP_FOR_LIST:
+ {
+ unsigned top = (ptr - 1) - bc.vector;
+ int id = READ_ID();
+ unsigned done = READ_LABEL();
+ Expr *one = pop_expr();
+ Expr *list = pop_expr();
+ int jump_hot;
+
+ if (one->kind != EXPR_VAR
+ || one->e.var.type != TYPE_INT
+ || one->e.var.v.num != 1)
+ panic("Not a literal one in DECOMPILE!");
+ else
+ dealloc_node(one);
+ s = alloc_stmt(STMT_LIST);
+ s->s.list.id = id;
+ s->s.list.expr = list;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.list.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("FOR_LIST jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP2(one, list, s));
+ }
+ break;
+ case OP_FOR_RANGE:
+ {
+ unsigned top = (ptr - 1) - bc.vector;
+ int id = READ_ID();
+ unsigned done = READ_LABEL();
+ Expr *to = pop_expr();
+ Expr *from = pop_expr();
+ int jump_hot;
+
+ s = alloc_stmt(STMT_RANGE);
+ s->s.range.id = id;
+ s->s.range.from = from;
+ s->s.range.to = to;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.range.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("FOR_RANGE jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP2(from, to, s));
+ }
+ break;
+ case OP_WHILE:
+ s = alloc_stmt(STMT_WHILE);
+ s->s.loop.id = -1;
+ finish_while:
+ {
+ unsigned top = stmt_start;
+ unsigned done = READ_LABEL();
+ Expr *condition = pop_expr();
+ int jump_hot;
+
+ s->s.loop.condition = condition;
+ DECOMPILE(bc, ptr, bc.vector + done - jump_len,
+ &(s->s.loop.body), 0);
+ if (top != READ_JUMP(jump_hot))
+ panic("WHILE jumps to wrong place in DECOMPILE!");
+ HOT_BOTTOM(jump_hot, s);
+ ADD_STMT(HOT_OP1(condition, s));
+ }
+ break;
+ case OP_FORK:
+ case OP_FORK_WITH_ID:
+ {
+ Bytecodes fbc;
+ int id;
+ Expr *time = pop_expr();
+
+ fbc = READ_FORK();
+ id = (op == OP_FORK ? -1 : READ_ID());
+ s = alloc_stmt(STMT_FORK);
+ s->s.fork.id = id;
+ s->s.fork.time = time;
+ (void) decompile(fbc, fbc.vector, fbc.vector + fbc.size,
+ &(s->s.fork.body), 0);
+ HOT_BOTTOM(hot_byte == fbc.vector + fbc.size - 1, s);
+ ADD_STMT(HOT_OP1(time, s));
+ }
+ break;
+ case OP_POP:
+ s = alloc_stmt(STMT_EXPR);
+ e = s->s.expr = pop_expr();
+ ADD_STMT(HOT_OP1(e, s));
+ break;
+ case OP_RETURN:
+ case OP_RETURN0:
+ s = alloc_stmt(STMT_RETURN);
+ e = s->s.expr = (op == OP_RETURN ? pop_expr() : 0);
+ ADD_STMT(HOT(op_hot || (e && e == hot_node), s));
+ break;
+ case OP_DONE:
+ if (ptr != end)
+ panic("DONE not at end in DECOMPILE!");
+ break;
+ case OP_IMM:
+ e = alloc_expr(EXPR_VAR);
+ e->e.var = var_ref(READ_LITERAL());
+ push_expr(HOT_OP(e));
+ break;
+ case OP_G_PUSH:
+ e = alloc_expr(EXPR_ID);
+ e->e.id = READ_ID();
+ push_expr(HOT_OP(e));
+ break;
+ case OP_AND:
+ case OP_OR:
+ {
+ unsigned done = READ_LABEL();
+
+ e = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + done, 0, 0);
+ if (ptr != bc.vector + done)
+ panic("AND/OR jumps to wrong place in DECOMPILE!");
+ e = alloc_binary(op == OP_AND ? EXPR_AND : EXPR_OR,
+ e, pop_expr());
+ push_expr(HOT_OP2(e->e.bin.lhs, e->e.bin.rhs, e));
+ }
+ break;
+ case OP_UNARY_MINUS:
+ case OP_NOT:
+ e = alloc_expr(op == OP_NOT ? EXPR_NOT : EXPR_NEGATE);
+ e->e.expr = pop_expr();
+ push_expr(HOT_OP1(e->e.expr, e));
+ break;
+ case OP_GET_PROP:
+ case OP_PUSH_GET_PROP:
+ kind = EXPR_PROP;
+ goto finish_binary;
+ case OP_EQ:
+ kind = EXPR_EQ;
+ goto finish_binary;
+ case OP_NE:
+ kind = EXPR_NE;
+ goto finish_binary;
+ case OP_LT:
+ kind = EXPR_LT;
+ goto finish_binary;
+ case OP_LE:
+ kind = EXPR_LE;
+ goto finish_binary;
+ case OP_GT:
+ kind = EXPR_GT;
+ goto finish_binary;
+ case OP_GE:
+ kind = EXPR_GE;
+ goto finish_binary;
+ case OP_IN:
+ kind = EXPR_IN;
+ goto finish_binary;
+ case OP_ADD:
+ kind = EXPR_PLUS;
+ goto finish_binary;
+ case OP_MINUS:
+ kind = EXPR_MINUS;
+ goto finish_binary;
+ case OP_MULT:
+ kind = EXPR_TIMES;
+ goto finish_binary;
+ case OP_DIV:
+ kind = EXPR_DIVIDE;
+ goto finish_binary;
+ case OP_MOD:
+ kind = EXPR_MOD;
+ goto finish_binary;
+ case OP_REF:
+ case OP_PUSH_REF:
+ kind = EXPR_INDEX;
+ finish_binary:
+ e = pop_expr();
+ e = alloc_binary(kind, pop_expr(), e);
+ push_expr(HOT_OP2(e->e.bin.lhs, e->e.bin.rhs, e));
+ break;
+ case OP_RANGE_REF:
+ {
+ Expr *e1 = pop_expr();
+ Expr *e2 = pop_expr();
+
+ e = alloc_expr(EXPR_RANGE);
+ e->e.range.base = pop_expr();
+ e->e.range.from = e2;
+ e->e.range.to = e1;
+ push_expr(HOT_OP3(e1, e2, e->e.range.base, e));
+ }
+ break;
+ case OP_BI_FUNC_CALL:
+ {
+ Expr *a = pop_expr();
+
+ if (a->kind != EXPR_LIST)
+ panic("Missing arglist for BI_FUNC_CALL in DECOMPILE!");
+ e = alloc_expr(EXPR_CALL);
+ e->e.call.args = a->e.list;
+ dealloc_node(a);
+ e->e.call.func = READ_BYTES(1);
+ push_expr(HOT_OP1(a, e));
+ }
+ break;
+ case OP_CALL_VERB:
+ {
+ Expr *a = pop_expr();
+ Expr *e2 = pop_expr();
+
+ if (a->kind != EXPR_LIST)
+ panic("Missing arglist for CALL_VERB in DECOMPILE!");
+ e = alloc_verb(pop_expr(), e2, a->e.list);
+ dealloc_node(a);
+ push_expr(HOT_OP3(e->e.verb.obj, a, e2, e));
+ }
+ break;
+ case OP_IF_QUES:
+ {
+ unsigned label = READ_LABEL();
+ int jump_hot;
+
+ e = alloc_expr(EXPR_COND);
+ e->e.cond.condition = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + label - jump_len, 0, 0);
+ label = READ_JUMP(jump_hot);
+ e->e.cond.consequent = pop_expr();
+ DECOMPILE(bc, ptr, bc.vector + label, 0, 0);
+ if (ptr != bc.vector + label)
+ panic("THEN jumps to wrong place in DECOMPILE!");
+ e->e.cond.alternate = pop_expr();
+ push_expr(HOT3(op_hot || jump_hot, e->e.cond.condition,
+ e->e.cond.consequent, e->e.cond.alternate,
+ e));
+ }
+ break;
+ case OP_PUT_TEMP:
+ /* Ignore; following RANGESET or INDEXSET does the work */
+ if (op_hot)
+ asgn_hot = 1;
+ break;
+ case OP_INDEXSET:
+ /* Most of the lvalue has already been constructed on the stack.
+ * Add the final indexing.
+ */
+ {
+ Expr *rvalue = pop_expr();
+ Expr *index = pop_expr();
+
+ e = alloc_binary(EXPR_INDEX, pop_expr(), index);
+ push_expr(HOT3(op_hot || asgn_hot,
+ e->e.bin.lhs, index, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ finish_indexed_assignment:
+ /* The remainder of this complex assignment code sequence is
+ * useless for the purpose of decompilation.
+ */
+ asgn_hot = 0;
+ while (*ptr != OP_PUSH_TEMP) {
+ if (ptr == hot_byte) /* it's our assignment expression */
+ hot_node = expr_stack[top_expr_stack];
+ ptr++;
+ }
+ ptr++;
+ break;
+ case OP_G_PUT:
+ e = alloc_expr(EXPR_ID);
+ e->e.id = READ_ID();
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT_OP1(e->e.bin.rhs, e));
+ break;
+ case OP_PUT_PROP:
+ {
+ Expr *rvalue = pop_expr();
+
+ e = pop_expr();
+ e = alloc_binary(EXPR_PROP, pop_expr(), e);
+ push_expr(HOT_OP3(e->e.bin.lhs, e->e.bin.rhs, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ break;
+ case OP_MAKE_EMPTY_LIST:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = 0;
+ push_expr(HOT_OP(e));
+ break;
+ case OP_MAKE_SINGLETON_LIST:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = alloc_arg_list(ARG_NORMAL, pop_expr());
+ push_expr(HOT_OP1(e->e.list->expr, e));
+ break;
+ case OP_CHECK_LIST_FOR_SPLICE:
+ e = alloc_expr(EXPR_LIST);
+ e->e.list = alloc_arg_list(ARG_SPLICE, pop_expr());
+ push_expr(HOT_OP1(e->e.list->expr, e));
+ break;
+ case OP_LIST_ADD_TAIL:
+ case OP_LIST_APPEND:
+ {
+ Expr *list;
+ Arg_List *a;
+
+ e = pop_expr();
+ list = pop_expr();
+ if (list->kind != EXPR_LIST)
+ panic("Missing list expression in DECOMPILE!");
+ for (a = list->e.list; a->next; a = a->next)
+ ;
+ a->next = alloc_arg_list(op == OP_LIST_APPEND ? ARG_SPLICE
+ : ARG_NORMAL, e);
+ push_expr(HOT_OP1(e, list));
+ }
+ break;
+ case OP_PUSH_TEMP:
+ case OP_JUMP:
+ panic("Unexpected opcode in DECOMPILE!");
+ break;
+ case OP_EXTENDED:
+ {
+ Extended_Opcode eop = *ptr++;
+
+ switch (eop) {
+ case EOP_RANGESET:
+ /* Most of the lvalue has already been constructed on the
+ * stack. Add the final subranging.
+ */
+ {
+ Expr *rvalue = pop_expr();
+
+ e = alloc_expr(EXPR_RANGE);
+ e->e.range.to = pop_expr();
+ e->e.range.from = pop_expr();
+ e->e.range.base = pop_expr();
+ push_expr(HOT4(op_hot || asgn_hot,
+ e->e.range.base, e->e.range.from,
+ e->e.range.to, rvalue,
+ alloc_binary(EXPR_ASGN, e, rvalue)));
+ }
+ goto finish_indexed_assignment;
+ case EOP_LENGTH:
+ READ_STACK();
+ e = alloc_expr(EXPR_LENGTH);
+ push_expr(HOT_OP(e));
+ break;
+ case EOP_EXP:
+ kind = EXPR_EXP;
+ goto finish_binary;
+ case EOP_SCATTER:
+ {
+ Scatter *sc, **scp;
+ int nargs = *ptr++;
+ int rest = (ptr++, *ptr++); /* skip nreq */
+ int *next_label = 0;
+ int i, done, is_hot = op_hot;
+
+ for (i = 1, scp = &sc;
+ i <= nargs;
+ i++, scp = &((*scp)->next)) {
+ int id = READ_ID();
+ int label = READ_LABEL();
+
+ *scp = alloc_scatter(i == rest ? SCAT_REST :
+ label == 0 ? SCAT_REQUIRED
+ : SCAT_OPTIONAL,
+ id, 0);
+ if (label > 1) {
+ (*scp)->label = label;
+ if (next_label)
+ *next_label = label;
+ next_label = &((*scp)->next_label);
+ } else
+ (*scp)->label = 0;
+ }
+ done = READ_LABEL();
+ if (next_label)
+ *next_label = done;
+ e = alloc_expr(EXPR_SCATTER);
+ e->e.scatter = sc;
+ for (sc = e->e.scatter; sc; sc = sc->next)
+ if (sc->label) {
+ Expr *defallt;
+
+ if (ptr != bc.vector + sc->label)
+ panic("Misplaced default in DECOMPILE!");
+ DECOMPILE(bc, ptr,
+ bc.vector + sc->next_label - 1,
+ 0, 0);
+ defallt = pop_expr();
+ HOT1(is_hot, defallt, e);
+ if (defallt->kind != EXPR_ASGN
+ || defallt->e.bin.lhs->kind != EXPR_ID
+ || defallt->e.bin.lhs->e.id != sc->id)
+ panic("Wrong variable in DECOMPILE!");
+ sc->expr = defallt->e.bin.rhs;
+ dealloc_node(defallt->e.bin.lhs);
+ dealloc_node(defallt);
+ is_hot = (is_hot || ptr == hot_byte);
+ if (*ptr++ != OP_POP)
+ panic("Missing default POP in DECOMPILE!");
+ }
+ e = alloc_binary(EXPR_ASGN, e, pop_expr());
+ push_expr(HOT2(is_hot, e->e.bin.lhs, e->e.bin.rhs, e));
+ if (ptr != bc.vector + done)
+ panic("Not at end of scatter in DECOMPILE!");
+ }
+ break;
+ case EOP_PUSH_LABEL:
+ e = alloc_var(TYPE_INT);
+ e->e.var.v.num = READ_LABEL();
+ push_expr(HOT_OP(e));
+ break;
+ case EOP_CATCH:
+ {
+ Expr *label_expr = pop_expr();
+ Expr *codes = pop_expr();
+ Expr *try_expr, *default_expr = 0;
+ Arg_List *a = 0; /* silence warning */
+ int label, done;
+ int is_hot = op_hot;
+
+ if (label_expr->kind != EXPR_VAR
+ || label_expr->e.var.type != TYPE_INT)
+ panic("Not a catch label in DECOMPILE!");
+ label = label_expr->e.var.v.num;
+ dealloc_node(label_expr);
+ if (codes->kind == EXPR_LIST)
+ a = codes->e.list;
+ else if (codes->kind == EXPR_VAR
+ && codes->e.var.type == TYPE_INT
+ && codes->e.var.v.num == 0)
+ a = 0;
+ else
+ panic("Not a codes expression in DECOMPILE!");
+ dealloc_node(codes);
+ DECOMPILE(bc, ptr, end, 0, 0);
+ is_hot = (is_hot || ptr++ == hot_byte);
+ if (*ptr++ != EOP_END_CATCH)
+ panic("Missing END_CATCH in DECOMPILE!");
+ done = READ_LABEL();
+ try_expr = pop_expr();
+ if (ptr != bc.vector + label)
+ panic("Misplaced handler in DECOMPILE!");
+ is_hot = (is_hot || ptr == hot_byte);
+ op = *ptr++;
+ if (op == OPTIM_NUM_TO_OPCODE(1)) {
+ /* No default expression */
+ is_hot = (is_hot || ptr == hot_byte);
+ if (*ptr++ != OP_REF)
+ panic("Missing REF in DECOMPILE!");
+ default_expr = 0;
+ } else if (op == OP_POP) {
+ /* There's a default expression */
+ DECOMPILE(bc, ptr, bc.vector + done, 0, 0);
+ default_expr = pop_expr();
+ } else
+ panic("Bad default expression in DECOMPILE!");
+ if (ptr != bc.vector + done)
+ panic("CATCH ends in wrong place in DECOMPILE!");
+ e = alloc_expr(EXPR_CATCH);
+ e->e.catch.try = try_expr;
+ e->e.catch.codes = a;
+ e->e.catch.except = default_expr;
+ push_expr(HOT3(is_hot || (default_expr
+ && default_expr == hot_node),
+ label_expr, codes, try_expr,
+ e));
+ }
+ break;
+ case EOP_END_CATCH:
+ /* Early exit; main logic is in CATCH case, above. */
+ return ptr - 2;
+ case EOP_TRY_EXCEPT:
+ {
+ Expr *label_expr;
+ Except_Arm *ex;
+ Arg_List *a = 0; /* silence warning */
+ int count = *ptr++, label;
+ unsigned done;
+
+ s = HOT_OP(alloc_stmt(STMT_TRY_EXCEPT));
+ s->s.catch.excepts = 0;
+ while (count--) {
+ label_expr = pop_expr();
+ if (label_expr->kind != EXPR_VAR
+ || label_expr->e.var.type != TYPE_INT)
+ panic("Not an except label in DECOMPILE!");
+ label = label_expr->e.var.v.num;
+ dealloc_node(label_expr);
+ e = pop_expr();
+ if (e->kind == EXPR_LIST)
+ a = e->e.list;
+ else if (e->kind == EXPR_VAR
+ && e->e.var.type == TYPE_INT
+ && e->e.var.v.num == 0)
+ a = 0;
+ else
+ panic("Not a codes expression in DECOMPILE!");
+ dealloc_node(e);
+ ex = alloc_except(-1, a, 0);
+ ex->label = label;
+ ex->next = s->s.catch.excepts;
+ HOT2(0, label_expr, e, ex);
+ s->s.catch.excepts = ex;
+ }
+ DECOMPILE(bc, ptr, end, &(s->s.catch.body), 0);
+ HOT_POS(ptr++ == hot_byte, s, ENDBODY);
+ if (*ptr++ != EOP_END_EXCEPT)
+ panic("Missing END_EXCEPT in DECOMPILE!");
+ done = READ_LABEL();
+ for (ex = s->s.catch.excepts; ex; ex = ex->next) {
+ Byte *stop;
+ int jump_hot = 0;
+
+ if (ex->label != ptr - bc.vector)
+ panic("Not at start of handler in DECOMPILE!");
+ op_hot = (ptr == hot_byte);
+ op = *ptr++;
+ if (op == OP_G_PUT) {
+ ex->id = READ_ID();
+ op = *ptr++;
+ } else if (IS_PUT_n(op)) {
+ ex->id = PUT_n_INDEX(op);
+ op = *ptr++;
+ }
+ HOT(op_hot || ptr - 1 == hot_byte, ex);
+ if (op != OP_POP)
+ panic("Missing POP in DECOMPILE!");
+ if (ex->next)
+ stop = bc.vector + ex->next->label - jump_len;
+ else
+ stop = bc.vector + done;
+ DECOMPILE(bc, ptr, stop, &(ex->stmt), 0);
+ if (ex->next && READ_JUMP(jump_hot) != done)
+ panic("EXCEPT jumps to wrong place in "
+ "DECOMPILE!");
+ HOT_BOTTOM(jump_hot, ex);
+ }
+ if (ptr - bc.vector != done)
+ panic("EXCEPTS end in wrong place in DECOMPILE!");
+ ADD_STMT(s);
+ }
+ break;
+ case EOP_END_EXCEPT:
+ /* Early exit; main logic is in TRY_EXCEPT case, above. */
+ return ptr - 2;
+ case EOP_TRY_FINALLY:
+ {
+ int label = READ_LABEL();
+
+ s = HOT_OP(alloc_stmt(STMT_TRY_FINALLY));
+ DECOMPILE(bc, ptr, end, &(s->s.finally.body), 0);
+ HOT_POS(ptr++ == hot_byte, s, ENDBODY);
+ if (*ptr++ != EOP_END_FINALLY)
+ panic("Missing END_FINALLY in DECOMPILE!");
+ if (ptr - bc.vector != label)
+ panic("FINALLY handler in wrong place in "
+ "DECOMPILE!");
+ DECOMPILE(bc, ptr, end, &(s->s.finally.handler), 0);
+ HOT_BOTTOM(ptr++ == hot_byte, s);
+ if (*ptr++ != EOP_CONTINUE)
+ panic("Missing CONTINUE in DECOMPILE!");
+ ADD_STMT(s);
+ }
+ break;
+ case EOP_END_FINALLY:
+ case EOP_CONTINUE:
+ /* Early exit; main logic is in TRY_FINALLY case, above. */
+ return ptr - 2;
+ case EOP_WHILE_ID:
+ s = alloc_stmt(STMT_WHILE);
+ s->s.loop.id = READ_ID();
+ goto finish_while;
+ case EOP_EXIT:
+ case EOP_EXIT_ID:
+ s = alloc_stmt(STMT_BREAK);
+ if (eop == EOP_EXIT_ID)
+ s->s.exit = READ_ID();
+ else
+ s->s.exit = -1;
+ READ_STACK();
+ if (READ_LABEL() < ptr - bc.vector)
+ s->kind = STMT_CONTINUE;
+ ADD_STMT(HOT_OP(s));
+ break;
+ default:
+ panic("Unknown extended opcode in DECOMPILE!");
+ }
+ }
+ break;
+ default:
+ panic("Unknown opcode in DECOMPILE!");
+ }
+ }
+
+ if (ptr != end)
+ panic("Overshot end in DECOMPILE!");
+
+ return ptr;
+}
+
+static Stmt *
+program_to_tree(Program *prog, int vector, int pc_vector, int pc)
+{
+ Stmt *result;
+ Bytecodes bc;
+ int i, sum;
+
+ program = prog;
+ bc = (pc_vector == MAIN_VECTOR
+ ? program->main_vector
+ : program->fork_vectors[pc_vector]);
+
+ if (pc < 0)
+ hot_byte = 0;
+ else if (pc < bc.size)
+ hot_byte = bc.vector + pc;
+ else
+ panic("Illegal PC in FIND_LINE_NUMBER!");
+
+ hot_node = 0;
+ hot_position = (pc == bc.size - 1 ? DONE : TOP);
+
+ sum = program->main_vector.max_stack;
+ for (i = 0; i < program->fork_vectors_size; i++)
+ sum += program->fork_vectors[i].max_stack;
+ expr_stack = mymalloc(sum * sizeof(Expr *), M_DECOMPILE);
+ top_expr_stack = 0;
+
+ bc = (vector == MAIN_VECTOR
+ ? program->main_vector
+ : program->fork_vectors[vector]);
+
+ begin_code_allocation();
+ decompile(bc, bc.vector, bc.vector + bc.size, &result, 0);
+ end_code_allocation(0);
+
+ myfree(expr_stack, M_DECOMPILE);
+
+ return result;
+}
+
+Stmt *
+decompile_program(Program *prog, int vector)
+{
+ return program_to_tree(prog, vector, MAIN_VECTOR, -1);
+}
+
+static int
+find_hot_node(Stmt *stmt)
+{
+ /* Invariants: on entry, lineno is number of first line of STMT
+ * on exit, if result is true,
+ * lineno is line number of hot_node/position
+ * else, lineno is number of first line after STMT
+ */
+
+ for (; stmt; stmt = stmt->next) {
+ if (stmt == hot_node && hot_position == TOP)
+ return 1;
+
+ switch (stmt->kind) {
+ case STMT_COND:
+ {
+ Cond_Arm *arm;
+
+ for (arm = stmt->s.cond.arms; arm; arm = arm->next) {
+ if (arm == hot_node && hot_position == TOP)
+ return 1;
+ lineno++; /* advance to arm body */
+ if (find_hot_node(arm->stmt))
+ return 1;
+ else if (arm == hot_node && hot_position == BOTTOM) {
+ lineno--; /* Blame last line of arm */
+ return 1;
+ }
+ }
+ if (stmt->s.cond.otherwise) {
+ lineno++; /* Skip `else' line */
+ if (find_hot_node(stmt->s.cond.otherwise))
+ return 1;
+ }
+ }
+ break;
+ case STMT_LIST:
+ lineno++; /* Skip `for' line */
+ if (find_hot_node(stmt->s.list.body))
+ return 1;
+ break;
+ case STMT_RANGE:
+ lineno++; /* Skip `for' line */
+ if (find_hot_node(stmt->s.range.body))
+ return 1;
+ break;
+ case STMT_WHILE:
+ lineno++; /* Skip `while' line */
+ if (find_hot_node(stmt->s.loop.body))
+ return 1;
+ break;
+ case STMT_FORK:
+ lineno++; /* Skip `fork' line */
+ if (find_hot_node(stmt->s.fork.body))
+ return 1;
+ break;
+ case STMT_EXPR:
+ case STMT_RETURN:
+ case STMT_BREAK:
+ case STMT_CONTINUE:
+ /* Nothing more to do */
+ break;
+ case STMT_TRY_EXCEPT:
+ {
+ Except_Arm *ex;
+
+ lineno++; /* Skip `try' line */
+ if (find_hot_node(stmt->s.catch.body))
+ return 1;
+ if (stmt == hot_node && hot_position == ENDBODY) {
+ lineno--; /* blame it on last line of body */
+ return 1;
+ }
+ for (ex = stmt->s.catch.excepts; ex; ex = ex->next) {
+ if (ex == hot_node && hot_position == TOP)
+ return 1;
+ lineno++; /* skip `except' line */
+ if (find_hot_node(ex->stmt))
+ return 1;
+ if (ex == hot_node && hot_position == BOTTOM) {
+ lineno--; /* blame last line of handler */
+ return 1;
+ }
+ }
+ }
+ break;
+ case STMT_TRY_FINALLY:
+ lineno++; /* Skip `try' line */
+ if (find_hot_node(stmt->s.finally.body)
+ || (stmt == hot_node && hot_position == ENDBODY))
+ return 1;
+ lineno++; /* Skip `finally' line */
+ if (find_hot_node(stmt->s.finally.handler))
+ return 1;
+ break;
+ default:
+ panic("Unknown statement kind in FIND_HOT_NODE!");
+ }
+
+ if (stmt == hot_node && hot_position == BOTTOM)
+ return 1;
+
+ lineno++; /* Count this statement */
+ }
+
+ return 0;
+}
+
+int
+find_line_number(Program *prog, int vector, int pc)
+{
+ Stmt *tree = program_to_tree(prog, MAIN_VECTOR, vector, pc);
+
+ lineno = prog->first_lineno;
+ find_hot_node(tree);
+ free_stmt(tree);
+
+ if (!hot_node && hot_position != DONE)
+ panic("Can't do job in FIND_LINE_NUMBER!");
+
+ return lineno;
+}
+
+char rcsid_decompile[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.6 1996/03/10 01:17:48 pavel
+ * Removed a automatic structure initialization. Release 1.8.0.
+ *
+ * Revision 2.5 1996/02/08 07:17:28 pavel
+ * Renamed TYPE_NUM to TYPE_INT. Fixed bug in handling of OP_EIF
+ * vs. stmt_start. Added support for exponentiation expression, named WHILE
+ * loop and BREAK and CONTINUE statement. Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.4 1996/01/16 07:12:42 pavel
+ * Add EOP_SCATTER handling. Release 1.8.0alpha6.
+ *
+ * Revision 2.3 1995/12/31 03:11:29 pavel
+ * Added support for the `$' expression. Release 1.8.0alpha4.
+ *
+ * Revision 2.2 1995/12/28 00:37:38 pavel
+ * Fixed line number bug in `if' statements. Fixed memory leak in line-number
+ * finder. Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 07:58:23 pavel
+ * Fixed bug in decompilation of try-except-endtry. Added distinction between
+ * vector and pc_vector in program_to_tree(), to clear up bugs in
+ * forked-program accounting.
+ *
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 04:25:59 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 04:25:40 pavel
+ * Initial revision
+ */
diff --git a/decompile.h b/decompile.h
new file mode 100644
index 0000000..8760802
--- /dev/null
+++ b/decompile.h
@@ -0,0 +1,36 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "ast.h"
+#include "program.h"
+
+extern Stmt *decompile_program(Program *program, int vector);
+extern int find_line_number(Program *program, int vector, int pc);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 06:27:19 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 05:06:15 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 05:06:05 pavel
+ * Initial revision
+ */
diff --git a/disassemble.c b/disassemble.c
new file mode 100644
index 0000000..d67e9ef
--- /dev/null
+++ b/disassemble.c
@@ -0,0 +1,496 @@
+/******************************************************************************
+ Copyright (c) 1994, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "my-stdio.h"
+
+#include "bf_register.h"
+#include "config.h"
+#include "db.h"
+#include "functions.h"
+#include "list.h"
+#include "opcode.h"
+#include "program.h"
+#include "storage.h"
+#include "streams.h"
+#include "unparse.h"
+#include "utils.h"
+#include "verbs.h"
+
+const char *mnemonics[256], *ext_mnemonics[256];
+
+struct mapping {
+ unsigned value;
+ const char *name;
+};
+
+struct mapping mappings[] = {
+ {OP_IF, "IF"},
+ {OP_WHILE, "WHILE"},
+ {OP_EIF, "ELSEIF"},
+ {OP_FORK, "FORK"},
+ {OP_FORK_WITH_ID, "FORK_NAMED"},
+ {OP_FOR_LIST, "FOR_LIST"},
+ {OP_FOR_RANGE, "FOR_RANGE"},
+ {OP_INDEXSET, "INDEXSET"},
+ {OP_PUSH_GET_PROP, "PUSH_GET_PROP"},
+ {OP_GET_PROP, "GET_PROP"},
+ {OP_CALL_VERB, "CALL_VERB"},
+ {OP_PUT_PROP, "PUT_PROP"},
+ {OP_BI_FUNC_CALL, "CALL_FUNC"},
+ {OP_IF_QUES, "IF_EXPR"},
+ {OP_REF, "INDEX"},
+ {OP_RANGE_REF, "RANGE"},
+ {OP_MAKE_SINGLETON_LIST, "MAKE_SINGLETON_LIST"},
+ {OP_CHECK_LIST_FOR_SPLICE, "CHECK_LIST_FOR_SPLICE"},
+ {OP_MULT, "MULTIPLY"},
+ {OP_DIV, "DIVIDE"},
+ {OP_MOD, "MOD"},
+ {OP_ADD, "ADD"},
+ {OP_MINUS, "SUBTRACT"},
+ {OP_EQ, "EQ"},
+ {OP_NE, "NE"},
+ {OP_LT, "LT"},
+ {OP_LE, "LE"},
+ {OP_GT, "GT"},
+ {OP_GE, "GE"},
+ {OP_IN, "IN"},
+ {OP_AND, "AND"},
+ {OP_OR, "OR"},
+ {OP_UNARY_MINUS, "NEGATE"},
+ {OP_NOT, "NOT"},
+ {OP_G_PUT, "PUT"},
+ {OP_G_PUSH, "PUSH"},
+ {OP_IMM, "PUSH_LITERAL"},
+ {OP_MAKE_EMPTY_LIST, "MAKE_EMPTY_LIST"},
+ {OP_LIST_ADD_TAIL, "LIST_ADD_TAIL"},
+ {OP_LIST_APPEND, "LIST_APPEND"},
+ {OP_PUSH_REF, "PUSH_INDEX"},
+ {OP_PUT_TEMP, "PUT_TEMP"},
+ {OP_PUSH_TEMP, "PUSH_TEMP"},
+ {OP_JUMP, "JUMP"},
+ {OP_RETURN, "RETURN"},
+ {OP_RETURN0, "RETURN 0"},
+ {OP_DONE, "DONE"},
+ {OP_POP, "POP"}};
+
+struct mapping ext_mappings[] = {
+ {EOP_RANGESET, "RANGESET"},
+ {EOP_LENGTH, "LENGTH"},
+ {EOP_PUSH_LABEL, "PUSH_LABEL"},
+ {EOP_SCATTER, "SCATTER"},
+ {EOP_EXP, "EXPONENT"},
+ {EOP_CATCH, "CATCH"},
+ {EOP_END_CATCH, "END_CATCH"},
+ {EOP_TRY_EXCEPT, "TRY_EXCEPT"},
+ {EOP_END_EXCEPT, "END_EXCEPT"},
+ {EOP_TRY_FINALLY, "TRY_FINALLY"},
+ {EOP_END_FINALLY, "END_FINALLY"},
+ {EOP_CONTINUE, "CONTINUE"},
+ {EOP_WHILE_ID, "WHILE_ID"},
+ {EOP_EXIT, "EXIT"},
+ {EOP_EXIT_ID, "EXIT_ID"}};
+
+static void
+initialize_tables(void)
+{
+ static int tables_initialized = 0;
+ unsigned i;
+
+ if (tables_initialized)
+ return;
+
+ for (i = 0; i < 256; i++) {
+ mnemonics[i] = "*** Unknown opcode ***";
+ ext_mnemonics[i] = "*** Unknown extended opcode ***";
+ }
+
+ for (i = 0; i < Arraysize(mappings); i++)
+ mnemonics[mappings[i].value] = mappings[i].name;
+
+ for (i = 0; i < Arraysize(ext_mappings); i++)
+ ext_mnemonics[ext_mappings[i].value] = ext_mappings[i].name;
+
+ tables_initialized = 1;
+}
+
+typedef void (*Printer)(const char *, void *);
+static Printer print;
+static void *print_data;
+static int bytes_width, max_bytes_width;
+
+static void
+output(Stream *s)
+{
+ (*print)(reset_stream(s), print_data);
+}
+
+static void
+new_insn(Stream *s, unsigned pc)
+{
+ stream_printf(s, "%3d:", pc);
+ bytes_width = max_bytes_width;
+}
+
+static unsigned
+add_bytes(Stream *s, Byte *vector, unsigned pc, unsigned length)
+{
+ unsigned arg = 0, b;
+
+ while (length--) {
+ if (bytes_width == 0) {
+ output(s);
+ stream_add_string(s, " ");
+ bytes_width = max_bytes_width;
+ }
+ b = vector[pc++];
+ stream_printf(s, " %03d", b);
+ arg = (arg << 8) + b;
+ bytes_width--;
+ }
+
+ return arg;
+}
+
+static void
+finish_insn(Stream *s, Stream *insn)
+{
+ while (bytes_width--)
+ stream_add_string(s, " ");
+ stream_add_string(s, reset_stream(insn));
+ output(s);
+}
+
+static void
+disassemble(Program *prog, Printer p, void *data)
+{
+ Stream *s = new_stream(100);
+ Stream *insn = new_stream(50);
+ int i, l;
+ unsigned pc;
+ Bytecodes bc;
+ const char *ptr;
+ const char **names = prog->var_names;
+ unsigned tmp, num_names = prog->num_var_names;
+# define NAMES(i) (tmp = i, \
+ tmp < num_names ? names[tmp] \
+ : "*** Unknown variable ***")
+ Var *literals = prog->literals;
+
+ initialize_tables();
+ print = p;
+ print_data = data;
+ stream_printf(s, "Language version number: %d", (int) prog->version);
+ output(s);
+ stream_printf(s, "First line number: %d", prog->first_lineno);
+ output(s);
+
+ for (i = -1; i < 0 || i < prog->fork_vectors_size; i++) {
+ output(s);
+ if (i == -1) {
+ stream_printf(s, "Main code vector:");
+ output(s);
+ stream_printf(s, "=================");
+ output(s);
+ bc = prog->main_vector;
+ } else {
+ stream_printf(s, "Forked code vector %d:", i);
+ l = stream_length(s);
+ output(s);
+ while (l--)
+ stream_add_char(s, '=');
+ output(s);
+ bc = prog->fork_vectors[i];
+ }
+
+ stream_printf(s, "[Bytes for labels = %d, literals = %d, ",
+ bc.numbytes_label, bc.numbytes_literal);
+ stream_printf(s, "forks = %d, variables = %d, stack refs = %d]",
+ bc.numbytes_fork, bc.numbytes_var_name,
+ bc.numbytes_stack);
+ output(s);
+ stream_printf(s, "[Maximum stack size = %d]", bc.max_stack);
+ output(s);
+
+ max_bytes_width = 5;
+
+ for (pc = 0; pc < bc.size;) {
+ Byte b;
+ unsigned arg;
+# define ADD_BYTES(n) (arg = add_bytes(s, bc.vector, pc, n), \
+ pc += n, \
+ arg)
+ unsigned a1, a2;
+
+ new_insn(s, pc);
+ b = add_bytes(s, bc.vector, pc++, 1);
+ if (b != OP_EXTENDED)
+ stream_add_string(insn, COUNT_TICK(b) ? " * " : " ");
+ if (IS_OPTIM_NUM_OPCODE(b))
+ stream_printf(insn, "NUM %d", OPCODE_TO_OPTIM_NUM(b));
+ else if (IS_PUSH_n(b))
+ stream_printf(insn, "PUSH %s", NAMES(PUSH_n_INDEX(b)));
+ else if (IS_PUT_n(b))
+ stream_printf(insn, "PUT %s", NAMES(PUT_n_INDEX(b)));
+ else if (b == OP_EXTENDED) {
+ b = ADD_BYTES(1);
+ stream_add_string(insn, COUNT_EOP_TICK(b) ? " * " : " ");
+ stream_add_string(insn, ext_mnemonics[b]);
+ switch ((Extended_Opcode) b) {
+ case EOP_WHILE_ID:
+ a1 = ADD_BYTES(bc.numbytes_var_name);
+ a2 = ADD_BYTES(bc.numbytes_label);
+ stream_printf(insn, " %s %d", NAMES(a1), a2);
+ break;
+ case EOP_EXIT_ID:
+ stream_printf(insn, " %s",
+ NAMES(ADD_BYTES(bc.numbytes_var_name)));
+ /* fall thru */
+ case EOP_EXIT:
+ a1 = ADD_BYTES(bc.numbytes_stack);
+ a2 = ADD_BYTES(bc.numbytes_label);
+ stream_printf(insn, " %d %d", a1, a2);
+ break;
+ case EOP_PUSH_LABEL: case EOP_END_CATCH:
+ case EOP_END_EXCEPT: case EOP_TRY_FINALLY:
+ stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_label));
+ break;
+ case EOP_TRY_EXCEPT:
+ stream_printf(insn, " %d", ADD_BYTES(1));
+ break;
+ case EOP_LENGTH:
+ stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_stack));
+ break;
+ case EOP_SCATTER:
+ {
+ int i, nargs = ADD_BYTES(1);
+
+ a1 = ADD_BYTES(1);
+ a2 = ADD_BYTES(1);
+ stream_printf(insn, " %d/%d/%d:", nargs, a1, a2);
+ for (i = 0; i < nargs; i++) {
+ a1 = ADD_BYTES(bc.numbytes_var_name);
+ a2 = ADD_BYTES(bc.numbytes_label);
+ stream_printf(insn, " %s/%d", NAMES(a1), a2);
+ }
+ stream_printf(insn, " %d",
+ ADD_BYTES(bc.numbytes_label));
+ }
+ break;
+ default:
+ break;
+ }
+ } else {
+ stream_add_string(insn, mnemonics[b]);
+ switch ((Opcode) b) {
+ case OP_IF: case OP_IF_QUES: case OP_EIF:
+ case OP_AND: case OP_OR: case OP_JUMP:
+ case OP_WHILE:
+ stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_label));
+ break;
+ case OP_FORK:
+ stream_printf(insn, " %d", ADD_BYTES(bc.numbytes_fork));
+ break;
+ case OP_FORK_WITH_ID:
+ a1 = ADD_BYTES(bc.numbytes_fork);
+ a2 = ADD_BYTES(bc.numbytes_var_name);
+ stream_printf(insn, " %d %s", a1, NAMES(a2));
+ break;
+ case OP_FOR_LIST:
+ case OP_FOR_RANGE:
+ a1 = ADD_BYTES(bc.numbytes_var_name);
+ a2 = ADD_BYTES(bc.numbytes_label);
+ stream_printf(insn, " %s %d", NAMES(a1), a2);
+ break;
+ case OP_G_PUSH:
+ case OP_G_PUT:
+ stream_printf(insn, " %s",
+ NAMES(ADD_BYTES(bc.numbytes_var_name)));
+ break;
+ case OP_IMM:
+ {
+ Var v;
+
+ v = literals[ADD_BYTES(bc.numbytes_literal)];
+ switch (v.type) {
+ case TYPE_OBJ:
+ stream_printf(insn, " #%d", v.v.obj);
+ break;
+ case TYPE_INT:
+ stream_printf(insn, " %d", v.v.num);
+ break;
+ case TYPE_STR:
+ stream_add_string(insn, " \"");
+ for (ptr = v.v.str; *ptr; ptr++) {
+ if (*ptr == '"' || *ptr == '\\')
+ stream_add_char(insn, '\\');
+ stream_add_char(insn, *ptr);
+ }
+ stream_add_char(insn, '"');
+ break;
+ case TYPE_ERR:
+ stream_printf(insn, " %s", error_name(v.v.err));
+ break;
+ default:
+ stream_printf(insn, " <literal type = %d>",
+ v.type);
+ break;
+ }
+ }
+ break;
+ case OP_BI_FUNC_CALL:
+ stream_printf(insn, " %s", name_func_by_num(ADD_BYTES(1)));
+ default:
+ break;
+ }
+ }
+
+ finish_insn(s, insn);
+ }
+ }
+
+ free_stream(s);
+ free_stream(insn);
+}
+
+static void
+print_line(const char *line, void *data)
+{
+ FILE *f = data;
+
+ fprintf(f, "%s\n", line);
+}
+
+void
+disassemble_to_file(FILE *fp, Program *prog)
+{
+ disassemble(prog, print_line, fp);
+}
+
+void
+disassemble_to_stderr(Program *prog)
+{
+ disassemble_to_file(stderr, prog);
+}
+
+struct data {
+ char **lines;
+ int used, max;
+};
+
+static void
+add_line(const char *line, void *data)
+{
+ struct data *d = data;
+
+ if (d->used >= d->max) {
+ int new_max = (d->max == 0 ? 20 : d->max * 2);
+ char **new = mymalloc(sizeof(char **) * new_max, M_DISASSEMBLE);
+ int i;
+
+ for (i = 0; i < d->used; i++)
+ new[i] = d->lines[i];
+ if (d->lines)
+ myfree(d->lines, M_DISASSEMBLE);
+ d->lines = new;
+ d->max = new_max;
+ }
+
+ d->lines[d->used++] = str_dup(line);
+}
+
+static package
+bf_disassemble(Var arglist, Byte next, void *vdata, Objid progr)
+{
+ Objid oid = arglist.v.list[1].v.obj;
+ Var desc = arglist.v.list[2];
+ db_verb_handle h;
+ struct data data;
+ Var r;
+ int i;
+ enum error e;
+
+ if ((e = validate_verb_descriptor(desc)) != E_NONE
+ || (e = E_INVARG, !valid(oid))) {
+ free_var(arglist);
+ return make_error_pack(e);
+ }
+
+ h = find_described_verb(oid, desc);
+ free_var(arglist);
+
+ if (!h.ptr)
+ return make_error_pack(E_VERBNF);
+ if (!db_verb_allows(h, progr, VF_READ))
+ return make_error_pack(E_PERM);
+
+ data.lines = 0;
+ data.used = data.max = 0;
+ disassemble(db_verb_program(h), add_line, &data);
+ r = new_list(data.used);
+ for (i = 1; i <= data.used; i++) {
+ r.v.list[i].type = TYPE_STR;
+ r.v.list[i].v.str = data.lines[i - 1];
+ }
+ if (data.lines)
+ myfree(data.lines, M_DISASSEMBLE);
+ return make_var_pack(r);
+}
+
+void
+register_disassemble(void)
+{
+ register_function("disassemble", 2, 2, bf_disassemble, TYPE_OBJ, TYPE_ANY);
+}
+
+char rcsid_disassemble[] = "$Id";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.6 1996/04/08 01:09:40 pavel
+ * Added missing mnemonic-table entry for EOP_PUSH_LABEL. Release 1.8.0p3.
+ *
+ * Revision 2.5 1996/02/08 07:15:40 pavel
+ * Added support for exponentiation expression, named WHILE loop, and BREAK
+ * and CONTINUE statements. Removed redundant error-value unparsing code.
+ * Added printing of language version number. Added support for EOPs that
+ * cost ticks. Renamed TYPE_NUM to TYPE_INT. Added disassemble_to_file().
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.4 1996/01/16 07:16:38 pavel
+ * Add EOP_SCATTER handling. Added support for line-wrapping in the display
+ * of the bytes of an instruction. Release 1.8.0alpha6.
+ *
+ * Revision 2.3 1995/12/31 00:07:49 pavel
+ * Added support for EOP_LENGTH and stack references in general.
+ * Release 1.8.0alpha4.
+ *
+ * Revision 2.2 1995/12/28 00:31:29 pavel
+ * Added support for numeric verb descriptors in disassemble() built-in.
+ * Release 1.8.0alpha3.
+ *
+ * Revision 2.1 1995/12/11 08:10:32 pavel
+ * Accounted for verb programs never being NULL any more.
+ * Release 1.8.0alpha2.
+ *
+ * Revision 2.0 1995/11/30 05:00:54 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 05:00:43 pavel
+ * Initial revision
+ */
diff --git a/disassemble.h b/disassemble.h
new file mode 100644
index 0000000..add3e0b
--- /dev/null
+++ b/disassemble.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+ Copyright (c) 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "my-stdio.h"
+
+#include "program.h"
+
+extern void disassemble_to_file(FILE *fp, Program *program);
+extern void disassemble_to_stderr(Program *program);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 06:27:06 pavel
+ * Added disassemble_to_file(). Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 05:06:29 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.1 1995/11/30 05:06:21 pavel
+ * Initial revision
+ */
diff --git a/eval_env.c b/eval_env.c
new file mode 100644
index 0000000..582fd7b
--- /dev/null
+++ b/eval_env.c
@@ -0,0 +1,131 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "config.h"
+#include "eval_env.h"
+#include "storage.h"
+#include "structures.h"
+#include "sym_table.h"
+#include "utils.h"
+
+Var *
+new_rt_env(unsigned size)
+{
+ Var *ret = mymalloc(size * sizeof(Var), M_RT_ENV);
+ unsigned i;
+
+ for (i = 0; i < size; i++)
+ ret[i].type = TYPE_NONE;
+
+ return ret;
+}
+
+void
+free_rt_env(Var *rt_env, unsigned size)
+{
+ register unsigned i;
+
+ for (i = 0; i < size; i++)
+ free_var(rt_env[i]);
+ myfree((void *) rt_env, M_RT_ENV);
+}
+
+Var *
+copy_rt_env(Var *from, unsigned size)
+{
+ unsigned i;
+
+ Var *ret = new_rt_env(size);
+ for (i = 0; i < size; i++)
+ ret[i] = var_ref(from[i]);
+ return ret;
+}
+
+void
+fill_in_rt_consts(Var *env, DB_Version version)
+{
+ Var v;
+
+ v.type = TYPE_INT;
+ v.v.num = (int) TYPE_ERR; env[SLOT_ERR] = var_ref(v);
+ v.v.num = (int) TYPE_INT; env[SLOT_NUM] = var_ref(v);
+ v.v.num = (int) TYPE_STR; env[SLOT_STR] = var_ref(v);
+ v.v.num = (int) TYPE_OBJ; env[SLOT_OBJ] = var_ref(v);
+ v.v.num = (int) TYPE_LIST; env[SLOT_LIST] = var_ref(v);
+
+ if (version >= DBV_Float) {
+ v.v.num = (int) TYPE_INT; env[SLOT_INT] = var_ref(v);
+ v.v.num = (int) TYPE_FLOAT; env[SLOT_FLOAT] = var_ref(v);
+ }
+}
+
+void
+set_rt_env_obj(Var *env, int slot, Objid o)
+{
+ Var v;
+ v.type = TYPE_OBJ;
+ v.v.obj = o;
+ env[slot] = var_ref(v);
+}
+
+void
+set_rt_env_str(Var *env, int slot, const char *s)
+{
+ Var v;
+ v.type = TYPE_STR;
+ v.v.str = s;
+ env[slot] = v;
+}
+
+void
+set_rt_env_var(Var *env, int slot, Var v)
+{
+ env[slot] = v;
+}
+
+char rcsid_rt_env[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 07:13:03 pavel
+ * Renamed TYPE_NUM to TYPE_INT. Made fill_in_rt_consts() version-dependent.
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:21:51 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.6 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.5 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.4 1992/10/17 20:24:21 pavel
+ * Global rename of strdup->str_dup, strref->str_ref, vardup->var_dup, and
+ * varref->var_ref.
+ *
+ * Revision 1.3 1992/08/31 22:23:15 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.2 1992/08/28 15:59:54 pjames
+ * Changed vardup to varref.
+ *
+ * Revision 1.1 1992/08/10 16:20:00 pjames
+ * Initial RCS-controlled version
+ */
diff --git a/eval_env.h b/eval_env.h
new file mode 100644
index 0000000..25b2a26
--- /dev/null
+++ b/eval_env.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#ifndef Eval_env_h
+#define Eval_env_h 1
+
+#include "config.h"
+#include "structures.h"
+#include "version.h"
+
+extern Var *new_rt_env(unsigned size);
+extern void free_rt_env(Var *rt_env, unsigned size);
+extern Var *copy_rt_env(Var *from, unsigned size);
+
+void set_rt_env_obj(Var *env, int slot, Objid o);
+void set_rt_env_str(Var *env, int slot, const char *s);
+void set_rt_env_var(Var *env, int slot, Var v);
+
+void fill_in_rt_consts(Var *env, DB_Version);
+
+#endif
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 06:26:34 pavel
+ * Made fill_in_rt_consts() version-dependent. Updated copyright notice for
+ * 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:50:38 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.4 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.3 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.2 1992/08/31 22:22:36 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.1 1992/08/10 16:20:00 pjames
+ * Initial RCS-controlled version
+ */
diff --git a/eval_vm.c b/eval_vm.c
new file mode 100644
index 0000000..8b7f26f
--- /dev/null
+++ b/eval_vm.c
@@ -0,0 +1,172 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "config.h"
+#include "db_io.h"
+#include "decompile.h"
+#include "eval_vm.h"
+#include "execute.h"
+#include "log.h"
+#include "options.h"
+#include "storage.h"
+#include "structures.h"
+#include "tasks.h"
+
+/**** external functions ****/
+
+vm
+new_vm(int task_id, int stack_size)
+{
+ vm the_vm = mymalloc(sizeof(vmstruct), M_VM);
+
+ the_vm->task_id = task_id;
+ the_vm->activ_stack = mymalloc(sizeof(activation) * stack_size, M_VM);
+
+ return the_vm;
+}
+
+void
+free_vm(vm the_vm, int stack_too)
+{
+ int i;
+
+ if (stack_too)
+ for (i = the_vm->top_activ_stack; i >= 0; i--)
+ free_activation(the_vm->activ_stack[i], 1);
+ myfree(the_vm->activ_stack, M_VM);
+ myfree(the_vm, M_VM);
+}
+
+activation
+top_activ(vm the_vm)
+{
+ return the_vm->activ_stack[the_vm->top_activ_stack];
+}
+
+Objid
+progr_of_cur_verb(vm the_vm)
+{
+ return top_activ(the_vm).progr;
+}
+
+unsigned
+suspended_lineno_of_vm(vm the_vm)
+{
+ activation top;
+
+ top = top_activ(the_vm);
+ return find_line_number(top.prog, (the_vm->top_activ_stack == 0
+ ? the_vm->root_activ_vector
+ : MAIN_VECTOR),
+ top.error_pc);
+}
+
+/**** read/write data base ****/
+
+void
+write_vm(vm the_vm)
+{
+ unsigned i;
+
+ dbio_printf("%u %d %u %u\n",
+ the_vm->top_activ_stack, the_vm->root_activ_vector,
+ the_vm->func_id, the_vm->max_stack_size);
+
+ for (i = 0; i <= the_vm->top_activ_stack; i++)
+ write_activ(the_vm->activ_stack[i]);
+}
+
+vm
+read_vm(int task_id)
+{
+ unsigned i, top, func_id, max;
+ int vector;
+ char c;
+ vm the_vm;
+
+ if (dbio_scanf("%u %d %u%c", &top, &vector, &func_id, &c) != 4
+ || (c == ' '
+ ? dbio_scanf("%u%c", &max, &c) != 2 || c != '\n'
+ : (max = DEFAULT_MAX_STACK_DEPTH, c != '\n'))) {
+ errlog("READ_VM: Bad vm header\n");
+ return 0;
+ }
+
+ the_vm = new_vm(task_id, top + 1);
+ the_vm->max_stack_size = max;
+ the_vm->top_activ_stack = top;
+ the_vm->root_activ_vector = vector;
+ the_vm->func_id = func_id;
+
+ for (i = 0; i <= top; i++)
+ if (!read_activ(&the_vm->activ_stack[i],
+ i == 0 ? vector : MAIN_VECTOR)) {
+ errlog("READ_VM: Bad activ number %d\n", i);
+ return 0;
+ }
+
+ return the_vm;
+}
+
+char rcsid_eval_vm[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.2 1996/02/08 07:12:16 pavel
+ * Renamed err/logf() to errlog/oklog(). Updated copyright notice for 1996.
+ * Release 1.8.0beta1.
+ *
+ * Revision 2.1 1995/12/31 03:19:43 pavel
+ * Added missing #include "options.h". Release 1.8.0alpha4.
+ *
+ * Revision 2.0 1995/11/30 04:22:07 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.10 1993/12/21 23:40:11 pavel
+ * Add new `outcome' result from the interpreter.
+ *
+ * Revision 1.9 1993/08/11 06:03:12 pavel
+ * Fixed mistaken %d->%u change.
+ *
+ * Revision 1.8 1993/08/11 02:58:03 pavel
+ * Changed some bogus %d's to %u's in calls to *scanf() and *printf(), guided
+ * by warnings from GCC...
+ *
+ * Revision 1.7 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.6 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.5 1992/10/17 20:25:59 pavel
+ * Changed return-type of read_vm() from `char' to `int', for systems that use
+ * unsigned chars.
+ *
+ * Revision 1.4 1992/09/25 21:12:12 pjames
+ * Passed correct pc (error_pc) to get_lineno.
+ *
+ * Revision 1.3 1992/09/02 18:43:11 pavel
+ * Added mechanism for resuming a read()ing task with an error.
+ *
+ * Revision 1.2 1992/08/31 22:22:24 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.1 1992/08/10 16:20:00 pjames
+ * Initial RCS-controlled version
+ */
diff --git a/eval_vm.h b/eval_vm.h
new file mode 100644
index 0000000..910360c
--- /dev/null
+++ b/eval_vm.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "config.h"
+#include "execute.h"
+#include "structures.h"
+
+extern vm read_vm(int task_id);
+extern void write_vm(vm);
+
+extern vm new_vm(int task_id, int stack_size);
+extern void free_vm(vm the_vm, int stack_too);
+
+extern activation top_activ(vm);
+extern Objid progr_of_cur_verb(vm);
+extern unsigned suspended_lineno_of_vm(vm);
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 06:26:06 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:50:51 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.6 1993/12/21 23:41:30 pavel
+ * Add new `outcome' result from the interpreter.
+ *
+ * Revision 1.5 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.4 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.3 1992/10/17 20:26:16 pavel
+ * Changed return-type of read_vm() from `char' to `int', for systems that use
+ * unsigned chars.
+ *
+ * Revision 1.2 1992/08/31 22:22:56 pjames
+ * Changed some `char *'s to `const char *'
+ *
+ * Revision 1.1 1992/08/10 16:20:00 pjames
+ * Initial RCS-controlled version
+ */
diff --git a/exceptions.c b/exceptions.c
new file mode 100644
index 0000000..c799609
--- /dev/null
+++ b/exceptions.c
@@ -0,0 +1,87 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/* Copyright 1989 Digital Equipment Corporation. */
+/* Distributed only by permission. */
+/*****************************************************************************/
+/* File: exceptions.c */
+/* Taken originally from: */
+/* Implementing Exceptions in C */
+/* Eric S. Roberts */
+/* Research Report #40 */
+/* DEC Systems Research Center */
+/* March 21, 1989 */
+/* Modified slightly by Pavel Curtis for use in the LambdaMOO server code. */
+/* ------------------------------------------------------------------------- */
+/* Implementation of the C exception handler. Most of the real work is */
+/* done in the macros defined in the exceptions.h header file. */
+/*****************************************************************************/
+
+#include <setjmp.h>
+
+#include "exceptions.h"
+
+ES_CtxBlock *ES_exceptionStack = 0;
+
+Exception ANY;
+
+void
+ES_RaiseException(Exception *exception, int value)
+{
+ ES_CtxBlock *cb, *xb;
+ int i;
+
+ for (xb = ES_exceptionStack; xb; xb = xb->link) {
+ for (i = 0; i < xb->nx; i++) {
+ if (xb->array[i] == exception || xb->array[i] == &ANY)
+ goto doneSearching;
+ }
+ }
+
+ doneSearching:
+ if (!xb)
+ panic("Unhandled exception!");
+
+ for (cb = ES_exceptionStack; cb != xb && !cb->finally; cb = cb->link)
+ ;
+ ES_exceptionStack = cb;
+ cb->id = exception;
+ cb->value = value;
+ longjmp((void *) cb->jmp, 1);
+}
+
+char rcsid_exceptions[] = "$Id$";
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:44:59 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 07:11:32 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:23:03 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.3 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.2 1992/07/21 00:01:23 pavel
+ * Added rcsid_<filename-root> declaration to hold the RCS ident. string.
+ *
+ * Revision 1.1 1992/07/20 23:23:12 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/exceptions.h b/exceptions.h
new file mode 100644
index 0000000..81d238a
--- /dev/null
+++ b/exceptions.h
@@ -0,0 +1,178 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+/* Copyright 1989 Digital Equipment Corporation. */
+/* Distributed only by permission. */
+/*****************************************************************************/
+/* File: exceptions.h */
+/* Taken originally from: */
+/* Implementing Exceptions in C */
+/* Eric S. Roberts */
+/* Research Report #40 */
+/* DEC Systems Research Center */
+/* March 21, 1989 */
+/* Modified slightly by Pavel Curtis for use in the LambdaMOO server code. */
+/* ------------------------------------------------------------------------- */
+/* The exceptions package provides a general exception handling mechanism */
+/* for use with C that is portable across a variety of compilers and */
+/* operating systems. The design of this facility is based on the */
+/* exception handling mechanism used in the Modula-2+ language at DEC/SRC */
+/* and is described in detail in the paper cited above. */
+/* For more background on the underlying motivation for this design, see */
+/* SRC Research Report #3. */
+/*****************************************************************************/
+
+/*
+ * Syntax: Exception my_exception;
+ *
+ * TRY
+ * stmts;
+ * EXCEPT (my_exception) [ANY matches all exceptions]
+ * stmts; [int exception_value available here]
+ * ...
+ * ENDTRY
+ *
+ * RAISE(my_exception, value);
+ *
+ * TRY
+ * stmts;
+ * FINALLY
+ * stmts;
+ * ENDTRY
+ */
+
+#ifndef Exceptions_H
+#define Exceptions_H 1
+
+#include <setjmp.h>
+
+#include "config.h"
+
+#define ES_MaxExceptionsPerScope 10
+
+typedef enum ES_Value { ES_Initialize, ES_EvalBody, ES_Exception } ES_Value;
+
+typedef struct { int junk; } Exception; /* Only addr. of exception is used. */
+
+typedef volatile struct ES_CtxBlock ES_CtxBlock;
+struct ES_CtxBlock {
+ jmp_buf jmp;
+ int nx;
+ Exception *array[ES_MaxExceptionsPerScope];
+ Exception *id;
+ int value;
+ int finally;
+ ES_CtxBlock *link;
+};
+
+extern Exception ANY;
+extern ES_CtxBlock *ES_exceptionStack;
+extern void ES_RaiseException(Exception *exception, int value);
+
+#define RAISE(e, v) ES_RaiseException(&e, v)
+
+
+#define TRY \
+ { \
+ ES_CtxBlock ES_ctx; \
+ volatile ES_Value ES_es = ES_Initialize; \
+ \
+ ES_ctx.nx = 0; \
+ ES_ctx.finally = 0; \
+ ES_ctx.link = ES_exceptionStack; \
+ ES_exceptionStack = &ES_ctx; \
+ \
+ if (setjmp((void *) ES_ctx.jmp) != 0) \
+ ES_es = ES_Exception; \
+ \
+ while (1) { \
+ if (ES_es == ES_EvalBody) { \
+ /* TRY body goes here */
+
+
+#define EXCEPT(e) \
+ /* TRY body or handler goes here */ \
+ if (ES_es == ES_EvalBody) \
+ ES_exceptionStack = ES_ctx.link; \
+ break; \
+ } \
+ if (ES_es == ES_Initialize) { \
+ if (ES_ctx.nx >= ES_MaxExceptionsPerScope) \
+ panic("Too many EXCEPT clauses!"); \
+ ES_ctx.array[ES_ctx.nx++] = &e; \
+ } else if (ES_ctx.id == &e || &e == &ANY) { \
+ int exception_value = ES_ctx.value; \
+ \
+ ES_exceptionStack = ES_ctx.link; \
+ exception_value = exception_value; \
+ /* avoid warnings */ \
+ /* handler goes here */
+
+
+#define FINALLY \
+ /* TRY body goes here */ \
+ } \
+ if (ES_es == ES_Initialize) \
+ ES_ctx.finally = 1; \
+ else { \
+ ES_exceptionStack = ES_ctx.link; \
+ /* FINALLY body goes here */ \
+
+
+#define ENDTRY \
+ /* FINALLY body or handler goes here */ \
+ if (ES_ctx.finally && ES_es == ES_Exception) \
+ ES_RaiseException((Exception *) ES_ctx.id, \
+ (int) ES_ctx.value); \
+ break; \
+ } \
+ ES_es = ES_EvalBody; \
+ } \
+ }
+
+
+/* The exceptions package doesn't provide this function, but it calls it */
+/* whenever a fatal error occurs: */
+/* 1) Too many EXCEPT clauses in a single TRY construct. */
+/* 2) An unhandled exception is RAISEd. */
+
+extern void panic(const char *message);
+
+#endif /* !Exceptions_H */
+
+/* $Log$
+/* Revision 1.1 1997/03/03 03:45:02 nop
+/* Initial revision
+/*
+ * Revision 2.1 1996/02/08 06:25:56 pavel
+ * Updated copyright notice for 1996. Release 1.8.0beta1.
+ *
+ * Revision 2.0 1995/11/30 04:51:06 pavel
+ * New baseline version, corresponding to release 1.8.0alpha1.
+ *
+ * Revision 1.4 1992/10/23 23:03:47 pavel
+ * Added copyright notice.
+ *
+ * Revision 1.3 1992/10/21 03:02:35 pavel
+ * Converted to use new automatic configuration system.
+ *
+ * Revision 1.2 1992/09/23 17:10:48 pavel
+ * Added protection against this file being included more than once.
+ *
+ * Revision 1.1 1992/07/20 23:23:12 pavel
+ * Initial RCS-controlled version.
+ */
diff --git a/execute.c b/execute.c
new file mode 100644
index 0000000..7abbae5
--- /dev/null
+++ b/execute.c
@@ -0,0 +1,2850 @@
+/******************************************************************************
+ Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
+ Portions of this code were written by Stephen White, aka ghond.
+ Use and copying of this software and preparation of derivative works based
+ upon this software are permitted. Any distribution of this software or
+ derivative works must comply with all applicable United States export
+ control laws. This software is made available AS IS, and Xerox Corporation
+ makes no warranty about the software, its performance or its conformity to
+ any specification. Any person obtaining a copy of this software is requested
+ to send their name and post office or electronic mail address to:
+ Pavel Curtis
+ Xerox PARC
+ 3333 Coyote Hill Rd.
+ Palo Alto, CA 94304
+ Pavel@Xerox.Com
+ *****************************************************************************/
+
+#include "my-string.h"
+
+#include "config.h"
+#include "db.h"
+#include "db_io.h"
+#include "decompile.h"
+#include "eval_env.h"
+#include "eval_vm.h"
+#include "exceptions.h"
+#include "execute.h"
+#include "functions.h"
+#include "list.h"
+#include "log.h"
+#include "numbers.h"
+#include "opcode.h"
+#include "options.h"
+#include "parse_cmd.h"
+#include "server.h"
+#include "storage.h"
+#include "streams.h"
+#include "structures.h"
+#include "sym_table.h"
+#include "tasks.h"
+#include "timers.h"
+#include "utils.h"
+#include "version.h"
+
+/* the following globals are the guts of the virtual machine: */
+static activation *activ_stack = 0;
+static int max_stack_size = 0;
+static unsigned top_activ_stack; /* points to top-of-stack
+ (last-occupied-slot),
+ not next-empty-slot */
+static int root_activ_vector; /* root_activ_vector == MAIN_VECTOR
+ iff root activation is main
+ vector */
+
+/* these globals are not part of the vm because they get re-initialized
+ after a suspend */
+static int ticks_remaining;
+int task_timed_out;
+static int interpreter_is_running = 0;
+static int task_killed;
+static Timer_ID task_alarm_id;
+static task_kind current_task_kind;
+
+static const char *handler_verb_name; /* For in-DB traceback handling */
+static Var handler_verb_args;
+
+/* macros to ease indexing into activation stack */
+#define RUN_ACTIV activ_stack[top_activ_stack]
+#define CALLER_ACTIV activ_stack[top_activ_stack - 1]
+
+/**** error handling ****/
+
+typedef enum { /* Reasons for executing a FINALLY handler */
+ /* These constants are stored in the DB, so don't change the order... */
+ FIN_FALL_THRU, FIN_RAISE, FIN_UNCAUGHT, FIN_RETURN,
+ FIN_ABORT, /* This doesn't actually get you into a FINALLY... */
+ FIN_EXIT
+} Finally_Reason;
+
+void
+print_error_backtrace(const char *msg, void (*output)(const char *))
+{
+ int t;
+ Stream *str;
+
+ if (!interpreter_is_running)
+ return;
+ str = new_stream(100);
+ for (t = top_activ_stack; t >= 0; t--) {
+ if (t != top_activ_stack)
+ stream_printf(str, "... called from ");
+ stream_printf(str, "#%d:%s", activ_stack[t].vloc,
+ activ_stack[t].verbname);
+ if (activ_stack[t].vloc != activ_stack[t].this)
+ stream_printf(str, " (this == #%d)", activ_stack[t].this);
+
+ stream_printf(str, ", line %d",
+ find_line_number(activ_stack[t].prog,
+ (t == 0 ? root_activ_vector
+ : MAIN_VECTOR),
+ activ_stack[t].error_pc));
+ if (t == top_activ_stack)
+ stream_printf(str, ": %s", msg);
+ output(reset_stream(str));
+ if (t > 0 && activ_stack[t].bi_func_pc) {
+ stream_printf(str, "... called from built-in function %s()",
+ name_func_by_num(activ_stack[t].bi_func_id));
+ output(reset_stream(str));
+ }
+ }
+ output("(End of traceback)");
+ free_stream(str);
+}
+
+void
+output_to_log(const char *line)
+{
+ oklog("%s\n", line);
+}
+
+static Var backtrace_list;
+
+static void
+output_to_list(const char *line)
+{
+ Var str;
+
+ str.type = TYPE_STR;
+ str.v.str = str_dup(line);
+ backtrace_list = listappend(backtrace_list, str);
+}
+
+static Var
+error_backtrace_list(const char *msg)
+{
+ backtrace_list = new_list(0);
+ print_error_backtrace(msg, output_to_list);
+ return backtrace_list;
+}
+
+static enum error
+suspend_task(package p)
+{
+ vm the_vm = new_vm(current_task_id, top_activ_stack + 1);
+ int i;
+ enum error e;
+
+ the_vm->max_stack_size = max_stack_size;
+ the_vm->top_activ_stack = top_activ_stack;
+ the_vm->root_activ_vector = root_activ_vector;
+ the_vm->func_id = 0; /* shouldn't need func_id; */
+ for (i = 0; i <= top_activ_stack; i++)
+ the_vm->activ_stack[i] = activ_stack[i];
+
+ e = (*p.u.susp.proc)(the_vm, p.u.susp.data);
+ if (e != E_NONE)
+ free_vm(the_vm, 0);
+ return e;
+}
+
+static int raise_error(package p, enum outcome *outcome);
+
+static int
+unwind_stack(Finally_Reason why, Var value, enum outcome *outcome)
+{
+ /* Returns true iff the entire stack was unwound and the interpreter
+ * should stop, in which case *outcome is the correct outcome to return. */
+ Var code = (why == FIN_RAISE ? value.v.list[1] : zero);
+
+ for (;;) { /* loop over activations */
+ activation *a = &(activ_stack[top_activ_stack]);
+ void *bi_func_data = 0;
+ int bi_func_pc;
+ unsigned bi_func_id = 0;
+ Objid player;
+ Var v, *goal = a->base_rt_stack;
+
+ if (why == FIN_EXIT)
+ goal += value.v.list[1].v.num;
+ while (a->top_rt_stack > goal) { /* loop over rt stack */
+ a->top_rt_stack--;
+ v = *(a->top_rt_stack);
+ if (why != FIN_ABORT && v.type == TYPE_FINALLY) {
+ /* FINALLY handler */
+ a->pc = v.v.num;
+ v.type = TYPE_INT;
+ v.v.num = why;
+ *(a->top_rt_stack++) = v;
+ *(a->top_rt_stack++) = value;
+ return 0;
+ } else if (why == FIN_RAISE && v.type == TYPE_CATCH) {
+ /* TRY-EXCEPT or `expr ! ...' handler */
+ Var *new_top = a->top_rt_stack - 2 * v.v.num;
+ Var *vv;
+ int found = 0;
+
+ for (vv = new_top; vv < a->top_rt_stack; vv += 2) {
+ if (!found && (vv->type != TYPE_LIST
+ || ismember(code, *vv, 0))) {
+ found = 1;
+ v = *(vv + 1);
+ if (v.type != TYPE_INT)
+ panic("Non-numeric PC value on stack!");
+ a->pc = v.v.num;
+ }
+ free_var(*vv);
+ }
+
+ a->top_rt_stack = new_top;
+ if (found) {
+ *(a->top_rt_stack++) = value;
+ return 0;
+ }
+ } else
+ free_var(v);
+ }
+ if (why == FIN_EXIT) {
+ a->pc = value.v.list[2].v.num;
+ free_var(value);
+ return 0;
+ }
+
+ bi_func_pc = a->bi_func_pc;
+ if (bi_func_pc) {
+ bi_func_id = a->bi_func_id;
+ bi_func_data = a->bi_func_data;
+ }
+ player = a->player;
+ free_activation(*a, 0); /* 0 == don't free bi_func_data */
+
+ if (top_activ_stack == 0) { /* done */
+ if (outcome)
+ *outcome = (why == FIN_RETURN
+ ? OUTCOME_DONE
+ : OUTCOME_ABORTED);
+ return 1;
+ }
+ top_activ_stack--;
+
+ if (bi_func_pc != 0) { /* Must unwind through a built-in function */
+ package p;
+
+ if (why == FIN_RETURN) {
+ a = &(activ_stack[top_activ_stack]);
+ p = call_bi_func(bi_func_id, value, bi_func_pc, a->progr,
+ bi_func_data);
+ switch (p.kind) {
+ case BI_RETURN:
+ *(a->top_rt_stack++) = p.u.ret;
+ return 0;
+ case BI_RAISE:
+ if (a->debug)
+ return raise_error(p, outcome);
+ else {
+ *(a->top_rt_stack++) = p.u.raise.code;
+ free_str(p.u.raise.msg);
+ free_var(p.u.raise.value);
+ return 0;
+ }
+ case BI_SUSPEND:
+ {
+ enum error e = suspend_task(p);
+
+ if (e == E_NONE) {
+ if (outcome)
+ *outcome = OUTCOME_BLOCKED;
+ return 1;
+ } else {
+ value.type = TYPE_ERR;
+ value.v.err = e;
+ return unwind_stack(FIN_RAISE, value, outcome);
+ }
+ }
+ case BI_CALL:
+ a = &(activ_stack[top_activ_stack]); /* TOS has changed */
+ a->bi_func_id = bi_func_id;
+ a->bi_func_pc = p.u.call.pc;
+ a->bi_func_data = p.u.call.data;
+ return 0;
+ }
+ } else {
+ /* Built-in functions receive zero as a `returned value' on
+ * errors and aborts, all further calls they make are short-
+ * circuited with an immediate return of zero, and any errors
+ * they raise are squelched. This is compatible with older,
+ * pre-error-handling versions of the server, and thus
+ * acceptible for the existing built-ins. It is conceivable
+ * that this model will have to be revisited at some point in
+ * the future.
+ */
+ do {
+ p = call_bi_func(bi_func_id, zero, bi_func_pc, a->progr,
+ bi_func_data);
+ switch (p.kind) {
+ case BI_RETURN:
+ free_var(p.u.ret);
+ break;
+ case BI_RAISE:
+ free_var(p.u.raise.code);
+ free_str(p.u.raise.msg);
+ free_var(p.u.raise.value);
+ break;
+ case BI_SUSPEND:
+ break;
+ case BI_CALL:
+ free_activation(activ_stack[top_activ_stack--], 0);
+ bi_func_pc = p.u.call.pc;
+ bi_func_data = p.u.call.data;
+ break;
+ }
+ } while (p.kind == BI_CALL && bi_func_pc != 0); /* !tailcall */
+ }
+ } else if (why == FIN_RETURN) { /* Push the value on the stack & go */
+ a = &(activ_stack[top_activ_stack]);
+ *(a->top_rt_stack++) = value;
+ return 0;
+ }
+ }
+}
+
+static int
+find_handler_activ(Var code)
+{
+ /* Returns the index of the hottest activation with an active exception
+ * handler for the given code.
+ */
+ int frame;
+
+ for (frame = top_activ_stack; frame >= 0; frame--) {
+ activation *a = &(activ_stack[frame]);
+ Var *v, *vv;
+
+ for (v = a->top_rt_stack - 1; v >= a->base_rt_stack; v--)
+ if (v->type == TYPE_CATCH) {
+ for (vv = v - 2 * v->v.num; vv < v; vv += 2)
+ if (vv->type != TYPE_LIST || ismember(code, *vv, 0))
+ return frame;
+ v -= 2 * v->v.num;
+ }
+ }
+
+ return -1;
+}
+
+static Var
+make_stack_list(activation *stack, int start, int end, int include_end,
+ int root_vector, int line_numbers_too)
+{
+ Var r;
+ int count = 0, i, j;
+
+ for (i = end; i >= start; i--) {
+ if (include_end || i != end)
+ count++;
+ if (i != start && stack[i].bi_func_pc)
+ count++;
+ }
+
+ r = new_list(count);
+ j = 1;
+ for (i = end; i >= start; i--) {
+ Var v;
+
+ if (include_end || i != end) {
+ v = r.v.list[j++] = new_list(line_numbers_too ? 6 : 5);
+ v.v.list[1].type = TYPE_OBJ;
+ v.v.list[1].v.obj = stack[i].this;
+ v.v.list[2].type = TYPE_STR;
+ v.v.list[2].v.str = str_ref(stack[i].verb);
+ v.v.list[3].type = TYPE_OBJ;
+ v.v.list[3].v.obj = stack[i].progr;
+ v.v.list[4].type = TYPE_OBJ;
+ v.v.list[4].v.obj = stack[i].vloc;
+ v.v.list[5].type = TYPE_OBJ;
+ v.v.list[5].v.obj = stack[i].player;
+ if (line_numbers_too) {
+ v.v.list[6].type = TYPE_INT;
+ v.v.list[6].v.num = find_line_number(stack[i].prog,
+ (i == 0 ? root_vector
+ : MAIN_VECTOR),
+ stack[i].error_pc);
+ }
+ }
+
+ if (i != start && stack[i].bi_func_pc) {
+ v = r.v.list[j++] = new_list(line_numbers_too ? 6 : 5);
+ v.v.list[1].type = TYPE_OBJ;
+ v.v.list[1].v.obj = NOTHING;
+ v.v.list[2].type = TYPE_STR;
+ v.v.list[2].v.str = str_dup(name_func_by_num(stack[i].bi_func_id));
+ v.v.list[3].type = TYPE_OBJ;
+ v.v.list[3].v.obj = NOTHING;
+ v.v.list[4].type = TYPE_OBJ;
+ v.v.list[4].v.obj = NOTHING;
+ v.v.list[5].type = TYPE_OBJ;
+ v.v.list[5].v.obj = stack[i].player;
+ if (line_numbers_too) {
+ v.v.list[6].type = TYPE_INT;
+ v.v.list[6].v.num = stack[i].bi_func_pc;
+ }
+ }
+ }
+
+ return r;
+}
+
+static void
+save_handler_info(const char *vname, Var args)
+{
+ handler_verb_name = vname;
+ free_var(handler_verb_args);
+ handler_verb_args = args;
+}
+
+static int
+raise_error(package p, enum outcome *outcome)
+{
+ /* ASSERT: p.kind == BI_RAISE */
+ int handler_activ = find_handler_activ(p.u.raise.code);
+ Finally_Reason why;
+ Var value;
+
+ if (handler_activ >= 0) { /* handler found */
+ why = FIN_RAISE;
+ value = new_list(4);
+ } else { /* uncaught exception */
+ why = FIN_UNCAUGHT;
+ value = new_list(5);
+ value.v.list[5] = error_backtrace_list(p.u.raise.msg);
+ handler_activ = 0; /* get entire stack in list */
+ }
+ value.v.list[1] = p.u.raise.code;
+ value.v.list[2].type = TYPE_STR;
+ value.v.list[2].v.str = p.u.raise.msg;
+ value.v.list[3] = p.u.raise.value;
+ value.v.list[4] = make_stack_list(activ_stack, handler_activ,
+ top_activ_stack, 1,
+ root_activ_vector, 1);
+
+ if (why == FIN_UNCAUGHT) {
+ save_handler_info("handle_uncaught_error", value);
+ value = zero;
+ }
+
+ return unwind_stack(why, value, outcome);
+}
+
+static void
+abort_task(int is_ticks)
+{
+ Var value;
+ const char *msg = (is_ticks ? "Task ran out of ticks"
+ : "Task ran out of seconds");
+
+ value = new_list(3);
+ value.v.list[1].type = TYPE_STR;
+ value.v.list[1].v.str = str_dup(is_ticks ? "ticks" : "seconds");
+ value.v.list[2] = make_stack_list(activ_stack, 0, top_activ_stack, 1,
+ root_activ_vector, 1);
+ value.v.list[3] = error_backtrace_list(msg);
+ save_handler_info("handle_task_timeout", value);
+ unwind_stack(FIN_ABORT, zero, 0);
+}
+
+/**** activation manipulation ****/
+
+static int
+push_activation(void)
+{
+ if (top_activ_stack < max_stack_size - 1) {
+ top_activ_stack++;
+ return 1;
+ } else
+ return 0;
+}
+
+void
+free_activation(activation a, char data_too)
+{
+ Var *i;
+
+ free_rt_env(a.rt_env, a.prog->num_var_names);
+
+ for (i = a.base_rt_stack; i < a.top_rt_stack; i++)
+ free_var(*i);
+ myfree((void *) a.base_rt_stack, M_RT_STACK);
+ free_var(a.temp);
+ free_str(a.verb);
+ free_str(a.verbname);
+
+ free_program(a.prog);
+
+ if (data_too && a.bi_func_pc && a.bi_func_data)
+ free_data(a.bi_func_data);
+ /* else bi_func_state will be later freed by bi_function */
+}
+
+
+/** Set up another activation for calling a verb
+ does not change the vm in case of any error **/
+
+enum error
+call_verb(Objid this, const char *vname, Var args, int do_pass)
+{
+ /* if call succeeds, args will be consumed. If call fails, args
+ will NOT be consumed -- it must therefore be freed by caller */
+ /* vname will never be consumed */
+
+ /* will only return E_MAXREC, E_INVIND, E_VERBNF, or E_NONE */
+ /* returns an error if there is one, and does not change the vm in that
+ case, else sets up the activ_stack for the verb call and then returns
+ E_NONE */
+
+ Objid where;
+ db_verb_handle h;
+ Program *program;
+ Var *env;
+ Var v;
+
+ if (do_pass)
+ if (!valid(RUN_ACTIV.vloc))
+ return E_INVIND;
+ else
+ where = db_object_parent(RUN_ACTIV.vloc);
+ else
+ where = this;
+
+ if (!valid(where))
+ return E_INVIND;
+ h = db_find_callable_verb(where, vname);
+ if (!h.ptr)
+ return E_VERBNF;
+ else if (!push_activation())
+ return E_MAXREC;
+
+ program = db_verb_program(h);
+ vname = str_dup(vname); /* ensure that vname is heap-allocated */
+ RUN_ACTIV.prog = program_ref(program);
+ RUN_ACTIV.this = this;
+ RUN_ACTIV.progr = db_verb_owner(h);
+ RUN_ACTIV.vloc = db_verb_definer(h);
+ RUN_ACTIV.verb = str_ref(vname);
+ RUN_ACTIV.verbname = str_ref(db_verb_names(h));
+ RUN_ACTIV.debug = (db_verb_flags(h) & VF_DEBUG);
+
+ RUN_ACTIV.top_rt_stack = RUN_ACTIV.base_rt_stack
+ = mymalloc (program->main_vector.max_stack * sizeof(Var), M_RT_STACK);
+ RUN_ACTIV.pc = 0;
+ RUN_ACTIV.error_pc = 0;
+ RUN_ACTIV.bi_func_pc = 0;
+ RUN_ACTIV.temp.type = TYPE_NONE;
+
+ RUN_ACTIV.rt_env = env = new_rt_env(RUN_ACTIV.prog->num_var_names);
+
+ fill_in_rt_consts(env, program->version);
+
+ set_rt_env_obj(env, SLOT_THIS, this);
+ set_rt_env_obj(env, SLOT_CALLER, CALLER_ACTIV.this);
+
+#define ENV_COPY(slot) \
+ set_rt_env_var(env, slot, var_ref(CALLER_ACTIV.rt_env[slot]))
+
+ ENV_COPY(SLOT_ARGSTR);
+ ENV_COPY(SLOT_DOBJ);
+ ENV_COPY(SLOT_DOBJSTR);
+ ENV_COPY(SLOT_PREPSTR);
+ ENV_COPY(SLOT_IOBJ);
+ ENV_COPY(SLOT_IOBJSTR);
+
+ if (is_wizard(CALLER_ACTIV.progr) &&
+ (CALLER_ACTIV.rt_env[SLOT_PLAYER].type == TYPE_OBJ))
+ ENV_COPY(SLOT_PLAYER);
+ else
+ set_rt_env_obj(env, SLOT_PLAYER, CALLER_ACTIV.player);
+ RUN_ACTIV.player = env[SLOT_PLAYER].v.obj;
+
+#undef ENV_COPY
+
+ v.type = TYPE_STR;
+ v.v.str = vname;
+ set_rt_env_var(env, SLOT_VERB, v); /* no var_dup */
+ set_rt_env_var(env, SLOT_ARGS, args); /* no var_dup */
+
+ return E_NONE;
+}
+
+static int
+rangeset_check(int end, int from, int to)
+{
+ if (from > end+1 || to < 0)
+ return 1;
+ return 0;
+}
+
+static int
+bi_prop_protected(enum bi_prop prop, Objid progr)
+{
+ const char *pname = 0; /* silence warning */
+
+ if (is_wizard(progr))
+ return 0;
+
+ switch (prop) {
+ case BP_NAME: pname = "protect_name"; break;
+ case BP_OWNER: pname = "protect_owner"; break;
+ case BP_PROGRAMMER: pname = "protect_programmer"; break;
+ case BP_WIZARD: pname = "protect_wizard"; break;
+ case BP_R: pname = "protect_r"; break;
+ case BP_W: pname = "protect_w"; break;
+ case BP_F: pname = "protect_f"; break;
+ case BP_LOCATION: pname = "protect_location"; break;
+ case BP_CONTENTS: pname = "protect_contents"; break;
+ default:
+ panic("Can't happen in BI_PROP_PROTECTED!");
+ }
+
+ return server_flag_option(pname);
+}
+
+/**
+ the main interpreter -- run()
+ everything is just an entry point to it
+**/
+
+static enum outcome
+run(enum error resumption_error, Var *result) /* runs the_vm */
+{
+ /* If the returned value is OUTCOME_DONE and RESULT is non-NULL, then
+ * *RESULT is the value returned by the top frame.
+ */
+
+ /* bc, bv, rts are distinguished as the state variables of run()
+ their value capture the state of the running between OP_ cases */
+ Bytecodes bc;
+ Byte *bv, *error_bv;
+ Var *rts; /* next empty slot */
+ enum Opcode op;
+ Var error_var;
+ enum outcome outcome;
+
+ /** a bunch of macros that work *ONLY* inside run() **/
+
+/* helping macros about the runtime_stack. */
+#define POP() (*(--rts))
+#define PUSH(v) (*(rts++) = v)
+#define PUSH_REF(v) PUSH(var_ref(v))
+#define TOP_RT_VALUE (*(rts - 1))
+#define NEXT_TOP_RT_VALUE (*(rts - 2))
+
+#define READ_BYTES(bv, nb) \
+ ( bv += nb, \
+ (nb == 1 \
+ ? bv[-1] \
+ : (nb == 2 \
+ ? ((unsigned) bv[-2] << 8) + bv[-1] \
+ : (((unsigned) bv[-4] << 24) \
+ + ((unsigned) bv[-3] << 16) \
+ + ((unsigned) bv[-2] << 8) \
+ + bv[-1]))))
+
+#define LOAD_STATE_VARIABLES() \
+do { \
+ bc = ( (top_activ_stack != 0 || root_activ_vector == MAIN_VECTOR) \
+ ? RUN_ACTIV.prog->main_vector \
+ : RUN_ACTIV.prog->fork_vectors[root_activ_vector]); \
+ bv = bc.vector + RUN_ACTIV.pc; \
+ error_bv = bc.vector + RUN_ACTIV.error_pc; \
+ rts = RUN_ACTIV.top_rt_stack; /* next empty slot */ \
+} while (0)
+
+#define STORE_STATE_VARIABLES() \
+do { \
+ RUN_ACTIV.pc = bv - bc.vector; \
+ RUN_ACTIV.error_pc = error_bv - bc.vector; \
+ RUN_ACTIV.top_rt_stack = rts; \
+} while (0)
+
+#define RAISE_ERROR(the_err) \
+do { \
+ if (RUN_ACTIV.debug) { \
+ STORE_STATE_VARIABLES(); \
+ if (raise_error(make_error_pack(the_err), 0)) \
+ return OUTCOME_ABORTED; \
+ else { \
+ LOAD_STATE_VARIABLES(); \
+ goto next_opcode; \
+ } \
+ } \
+} while (0)
+
+#define PUSH_ERROR(the_err) \
+do { \
+ RAISE_ERROR(the_err); /* may not return */ \
+ error_var.type = TYPE_ERR; \
+ error_var.v.err = the_err; \
+ PUSH(error_var); \
+} while (0)
+
+#define JUMP(label) (bv = bc.vector + label)
+
+/* end of major run() macros */
+
+ LOAD_STATE_VARIABLES();
+
+ if (resumption_error != E_NONE) {
+ error_bv = bv;
+ PUSH_ERROR(resumption_error);
+ }
+
+ for (;;) {
+ next_opcode:
+ op = *bv;
+ error_bv = bv;
+ bv++;
+
+ if (COUNT_TICK(op))
+ ticks_remaining--;
+
+ if (task_killed) {
+ STORE_STATE_VARIABLES();
+ unwind_stack(FIN_ABORT, zero, 0);
+ return OUTCOME_ABORTED;
+ }
+
+ if (ticks_remaining <= 0) {
+ STORE_STATE_VARIABLES();
+ abort_task(1);
+ return OUTCOME_ABORTED;
+ }
+
+ if (task_timed_out) {
+ STORE_STATE_VARIABLES();
+ abort_task(0);
+ return OUTCOME_ABORTED;
+ }
+
+ switch (op) {
+
+ case OP_IF_QUES:
+ case OP_IF:
+ case OP_WHILE:
+ case OP_EIF:
+ do_test:
+ {
+ Var cond;
+
+ cond = POP();
+ if (!is_true(cond)) /* jump if false */
+ JUMP(READ_BYTES(bv, bc.numbytes_label));
+ else {
+ bv += bc.numbytes_label;
+ }
+ free_var(cond);
+ }
+ break;
+
+ case OP_JUMP:
+ JUMP(READ_BYTES(bv, bc.numbytes_label));
+ break;
+
+ case OP_FOR_LIST:
+ {
+ unsigned id = READ_BYTES(bv, bc.numbytes_var_name);
+ unsigned lab = READ_BYTES(bv, bc.numbytes_label);
+ Var count, list;
+
+ count = TOP_RT_VALUE; /* will be a integer */
+ list = NEXT_TOP_RT_VALUE; /* should be a list */
+ if (list.type != TYPE_LIST) {
+ RAISE_ERROR(E_TYPE);
+ free_var(POP());
+ free_var(POP());
+ JUMP(lab);
+ } else if (count.v.num > list.v.list[0].v.num /* size */) {
+ free_var(POP());
+ free_var(POP());
+ JUMP(lab);
+ } else {
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = var_ref(list.v.list[count.v.num]);
+ count.v.num++; /* increment count */
+ TOP_RT_VALUE = count;
+ }
+ }
+ break;
+
+ case OP_FOR_RANGE:
+ {
+ unsigned id = READ_BYTES(bv, bc.numbytes_var_name);
+ unsigned lab = READ_BYTES(bv, bc.numbytes_label);
+ Var from, to;
+
+ to = TOP_RT_VALUE;
+ from = NEXT_TOP_RT_VALUE;
+ if ((to.type != TYPE_INT && to.type != TYPE_OBJ)
+ || to.type != from.type) {
+ RAISE_ERROR(E_TYPE);
+ free_var(POP());
+ free_var(POP());
+ JUMP(lab);
+ } else if (to.type == TYPE_INT
+ ? from.v.num > to.v.num
+ : from.v.obj > to.v.obj) {
+ free_var(POP());
+ free_var(POP());
+ JUMP(lab);
+ } else {
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = var_ref(from);
+ if (to.type == TYPE_INT)
+ from.v.num++;
+ else
+ from.v.obj++;
+ NEXT_TOP_RT_VALUE = from;
+ }
+ }
+ break;
+
+ case OP_POP:
+ free_var(POP());
+ break;
+
+ case OP_IMM:
+ {
+ int slot = READ_BYTES(bv, bc.numbytes_literal);
+ PUSH_REF(RUN_ACTIV.prog->literals[slot]);
+ }
+ break;
+
+ case OP_MAKE_EMPTY_LIST:
+ {
+ Var list;
+
+ list = new_list(0);
+ PUSH(list);
+ }
+ break;
+
+ case OP_LIST_ADD_TAIL:
+ {
+ Var tail, list;
+
+ tail = POP(); /* whatever */
+ list = POP(); /* should be list */
+ if (list.type != TYPE_LIST) {
+ free_var(list);
+ free_var(tail);
+ PUSH_ERROR(E_TYPE);
+ } else
+ PUSH(listappend(list, tail));
+ }
+ break;
+
+ case OP_LIST_APPEND:
+ {
+ Var tail, list;
+
+ tail = POP(); /* second, should be list */
+ list = POP(); /* first, should be list */
+ if (tail.type != TYPE_LIST || list.type != TYPE_LIST) {
+ free_var(tail);
+ free_var(list);
+ PUSH_ERROR(E_TYPE);
+ } else
+ PUSH(listconcat(list, tail));
+ }
+ break;
+
+ case OP_INDEXSET:
+ {
+ Var value, index, list;
+
+ value = POP(); /* rhs value */
+ index = POP(); /* index, should be integer */
+ list = POP(); /* lhs except last index, should be list or str */
+ /* whole thing should mean list[index] = value */
+ if ((list.type != TYPE_LIST && list.type != TYPE_STR)
+ || index.type != TYPE_INT
+ || (list.type == TYPE_STR && value.type != TYPE_STR)) {
+ free_var(value);
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_TYPE);
+ } else if (index.v.num < 1
+ || (list.type == TYPE_LIST
+ && index.v.num > list.v.list[0].v.num /* size */)
+ || (list.type == TYPE_STR
+ && index.v.num > (int) strlen(list.v.str))) {
+ free_var(value);
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_RANGE);
+ } else if (list.type == TYPE_STR && strlen(value.v.str) != 1) {
+ free_var(value);
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_INVARG);
+ } else if (list.type == TYPE_LIST) {
+ PUSH(listset(var_dup(list), value, index.v.num));
+ free_var(list);
+ } else { /* TYPE_STR */
+ char *tmp_str = str_dup(list.v.str);
+ free_str(list.v.str);
+ tmp_str[index.v.num - 1] = value.v.str[0];
+ list.v.str = tmp_str;
+ free_var(value);
+ PUSH(list);
+ }
+ }
+ break;
+
+ case OP_MAKE_SINGLETON_LIST:
+ {
+ Var list;
+
+ list = new_list(1);
+ list.v.list[1] = POP();
+ PUSH(list);
+ }
+ break;
+
+ case OP_CHECK_LIST_FOR_SPLICE:
+ if (TOP_RT_VALUE.type != TYPE_LIST) {
+ free_var(POP());
+ PUSH_ERROR(E_TYPE);
+ }
+ /* no op if top-rt-stack is a list */
+ break;
+
+ case OP_PUT_TEMP:
+ RUN_ACTIV.temp = var_ref(TOP_RT_VALUE);
+ break;
+
+ case OP_PUSH_TEMP:
+ PUSH(RUN_ACTIV.temp);
+ RUN_ACTIV.temp.type = TYPE_NONE;
+ break;
+
+ case OP_EQ:
+ case OP_NE:
+ {
+ Var rhs, lhs, ans;
+
+ rhs = POP();
+ lhs = POP();
+ ans.type = TYPE_INT;
+ ans.v.num = (op == OP_EQ
+ ? equality(rhs, lhs, 0)
+ : !equality(rhs, lhs, 0));
+ PUSH(ans);
+ free_var(rhs);
+ free_var(lhs);
+ }
+ break;
+
+ case OP_GT:
+ case OP_LT:
+ case OP_GE:
+ case OP_LE:
+ {
+ Var rhs, lhs, ans;
+ int comparison;
+
+ rhs = POP();
+ lhs = POP();
+ if ((lhs.type == TYPE_INT || lhs.type == TYPE_FLOAT)
+ && (rhs.type == TYPE_INT || rhs.type == TYPE_FLOAT)) {
+ ans = compare_numbers(lhs, rhs);
+ if (ans.type == TYPE_ERR) {
+ free_var(rhs);
+ free_var(lhs);
+ PUSH_ERROR(ans.v.err);
+ } else {
+ comparison = ans.v.num;
+ goto finish_comparison;
+ }
+ } else if (rhs.type != lhs.type || rhs.type == TYPE_LIST) {
+ free_var(rhs);
+ free_var(lhs);
+ PUSH_ERROR(E_TYPE);
+ } else {
+ switch(rhs.type) {
+ case TYPE_INT:
+ comparison = compare_integers(lhs.v.num, rhs.v.num);
+ break;
+ case TYPE_OBJ:
+ comparison = compare_integers(lhs.v.obj, rhs.v.obj);
+ break;
+ case TYPE_ERR:
+ comparison = ((int) lhs.v.err) - ((int) rhs.v.err);
+ break;
+ case TYPE_STR:
+ comparison = mystrcasecmp(lhs.v.str, rhs.v.str);
+ break;
+ default:
+ errlog("RUN: Impossible type in comparison: %d\n",
+ rhs.type);
+ comparison = 0;
+ }
+
+ finish_comparison:
+ ans.type = TYPE_INT;
+ switch (op) {
+ case OP_LT:
+ ans.v.num = (comparison < 0);
+ break;
+ case OP_LE:
+ ans.v.num = (comparison <= 0);
+ break;
+ case OP_GT:
+ ans.v.num = (comparison > 0);
+ break;
+ case OP_GE:
+ ans.v.num = (comparison >= 0);
+ break;
+ default:
+ errlog("RUN: Imposible opcode in comparison: %d\n", op);
+ break;
+ }
+ PUSH(ans);
+ free_var(rhs);
+ free_var(lhs);
+ }
+ }
+ break;
+
+ case OP_IN:
+ {
+ Var lhs, rhs, ans;
+
+ rhs = POP(); /* should be list */
+ lhs = POP(); /* lhs, any type */
+ if (rhs.type != TYPE_LIST) {
+ free_var(rhs);
+ free_var(lhs);
+ PUSH_ERROR(E_TYPE);
+ } else {
+ ans.type = TYPE_INT;
+ ans.v.num = ismember(lhs, rhs, 0);
+ PUSH(ans);
+ free_var(rhs);
+ free_var(lhs);
+ }
+ }
+ break;
+
+ case OP_MULT:
+ case OP_MINUS:
+ case OP_DIV:
+ case OP_MOD:
+ {
+ Var lhs, rhs, ans;
+
+ rhs = POP(); /* should be number */
+ lhs = POP(); /* should be number */
+ if ((lhs.type == TYPE_INT || lhs.type == TYPE_FLOAT)
+ && (rhs.type == TYPE_INT || rhs.type == TYPE_FLOAT)) {
+ switch (op) {
+ case OP_MULT: ans = do_multiply(lhs, rhs); break;
+ case OP_MINUS: ans = do_subtract(lhs, rhs); break;
+ case OP_DIV: ans = do_divide(lhs, rhs); break;
+ case OP_MOD: ans = do_modulus(lhs, rhs); break;
+ default:
+ errlog("RUN: Impossible opcode in arith ops: %d\n", op);
+ break;
+ }
+ } else {
+ ans.type = TYPE_ERR;
+ ans.v.err = E_TYPE;
+ }
+ free_var(rhs);
+ free_var(lhs);
+ if (ans.type == TYPE_ERR)
+ PUSH_ERROR(ans.v.err);
+ else
+ PUSH(ans);
+ }
+ break;
+
+ case OP_ADD:
+ {
+ Var rhs, lhs, ans;
+
+ rhs = POP();
+ lhs = POP();
+ if ((lhs.type == TYPE_INT || lhs.type == TYPE_FLOAT)
+ && (rhs.type == TYPE_INT || rhs.type == TYPE_FLOAT))
+ ans = do_add(lhs, rhs);
+ else if (lhs.type == TYPE_STR && rhs.type == TYPE_STR) {
+ char *str;
+
+ str = mymalloc((strlen(rhs.v.str) + strlen(lhs.v.str) + 1)
+ * sizeof(char), M_STRING);
+ sprintf(str, "%s%s", lhs.v.str, rhs.v.str);
+ ans.type = TYPE_STR;
+ ans.v.str = str;
+ } else {
+ ans.type = TYPE_ERR;
+ ans.v.err = E_TYPE;
+ }
+ free_var(rhs);
+ free_var(lhs);
+
+ if (ans.type == TYPE_ERR)
+ PUSH_ERROR(ans.v.err);
+ else
+ PUSH(ans);
+ }
+ break;
+
+ case OP_AND:
+ case OP_OR:
+ {
+ Var lhs;
+ unsigned lab = READ_BYTES(bv, bc.numbytes_label);
+
+ lhs = TOP_RT_VALUE;
+ if ( (op == OP_AND && !is_true(lhs))
+ || (op == OP_OR && is_true(lhs))) /* short-circuit */
+ JUMP(lab);
+ else {
+ free_var(POP());
+ }
+ }
+ break;
+
+ case OP_NOT:
+ {
+ Var arg, ans;
+
+ arg = POP();
+ ans.type = TYPE_INT;
+ ans.v.num = !is_true(arg);
+ PUSH(ans);
+ free_var(arg);
+ }
+ break;
+
+ case OP_UNARY_MINUS:
+ {
+ Var arg, ans;
+
+ arg = POP();
+ if (arg.type == TYPE_INT) {
+ ans.type = TYPE_INT;
+ ans.v.num = -arg.v.num;
+ } else if (arg.type == TYPE_FLOAT)
+ ans = new_float(-*arg.v.fnum);
+ else {
+ free_var(arg);
+ PUSH_ERROR(E_TYPE);
+ break;
+ }
+
+ PUSH(ans);
+ free_var(arg);
+ }
+ break;
+
+ case OP_REF:
+ {
+ Var index, list;
+
+ index = POP(); /* should be integer */
+ list = POP(); /* should be list or string */
+
+ if (index.type != TYPE_INT ||
+ (list.type != TYPE_LIST && list.type != TYPE_STR)) {
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_TYPE);
+ }
+ else if (list.type == TYPE_LIST) {
+ if (index.v.num <= 0 || index.v.num > list.v.list[0].v.num) {
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_RANGE);
+ } else {
+ PUSH(var_ref(list.v.list[index.v.num]));
+ free_var(index);
+ free_var(list);
+ }
+ } else { /* list.type == TYPE_STR */
+ if (index.v.num <= 0
+ || index.v.num > (int) strlen(list.v.str)) {
+ free_var(index);
+ free_var(list);
+ PUSH_ERROR(E_RANGE);
+ } else {
+ PUSH(strget(list, index));
+ free_var(index);
+ free_var(list);
+ }
+ }
+ }
+ break;
+
+ case OP_PUSH_REF:
+ {
+ Var index, list;
+
+ index = TOP_RT_VALUE;
+ list = NEXT_TOP_RT_VALUE;
+
+ if (index.type != TYPE_INT || list.type != TYPE_LIST) {
+ PUSH_ERROR(E_TYPE);
+ } else if (index.v.num <= 0 ||
+ index.v.num > list.v.list[0].v.num) {
+ PUSH_ERROR(E_RANGE);
+ } else
+ PUSH(var_ref(list.v.list[index.v.num]));
+ }
+ break;
+
+ case OP_RANGE_REF:
+ {
+ Var base, from, to;
+
+ to = POP(); /* should be integer */
+ from = POP(); /* should be integer */
+ base = POP(); /* should be list or string */
+
+ if ((base.type != TYPE_LIST && base.type != TYPE_STR)
+ || to.type != TYPE_INT || from.type != TYPE_INT) {
+ free_var(to);
+ free_var(from);
+ PUSH_ERROR(E_TYPE);
+ } else {
+ int len = (base.type == TYPE_STR ? strlen(base.v.str)
+ : base.v.list[0].v.num);
+ if (from.v.num <= to.v.num
+ && (from.v.num <= 0 || from.v.num > len
+ || to.v.num <= 0 || to.v.num > len)) {
+ free_var(to);
+ free_var(from);
+ free_var(base);
+ PUSH_ERROR(E_RANGE);
+ } else {
+ PUSH((base.type == TYPE_STR
+ ? substr(base, from.v.num, to.v.num)
+ : sublist(base, from.v.num, to.v.num)));
+ /* base freed by substr/sublist */
+ free_var(from);
+ free_var(to);
+ }
+ }
+ }
+ break;
+
+ case OP_G_PUT:
+ {
+ unsigned id = READ_BYTES(bv, bc.numbytes_var_name);
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = var_ref(TOP_RT_VALUE);
+ }
+ break;
+
+ case OP_G_PUSH:
+ {
+ Var value;
+
+ value = RUN_ACTIV.rt_env[READ_BYTES(bv, bc.numbytes_var_name)];
+ if (value.type == TYPE_NONE)
+ PUSH_ERROR(E_VARNF);
+ else
+ PUSH_REF(value);
+ }
+ break;
+
+ case OP_GET_PROP:
+ {
+ Var propname, obj, prop;
+
+ propname = POP(); /* should be string */
+ obj = POP(); /* should be objid */
+ if (propname.type != TYPE_STR || obj.type != TYPE_OBJ) {
+ free_var(propname);
+ free_var(obj);
+ PUSH_ERROR(E_TYPE);
+ } else if (!valid(obj.v.obj)) {
+ free_var(propname);
+ free_var(obj);
+ PUSH_ERROR(E_INVIND);
+ } else {
+ db_prop_handle h;
+
+ h = db_find_property(obj.v.obj, propname.v.str, &prop);
+ if (!h.ptr)
+ PUSH_ERROR(E_PROPNF);
+ else if (h.built_in
+ ? bi_prop_protected(h.built_in, RUN_ACTIV.progr)
+ : !db_property_allows(h, RUN_ACTIV.progr, PF_READ))
+ PUSH_ERROR(E_PERM);
+ else if (h.built_in)
+ PUSH(prop); /* it's already freshly allocated */
+ else
+ PUSH_REF(prop);
+ free_var(propname);
+ free_var(obj);
+ }
+ }
+ break;
+
+ case OP_PUSH_GET_PROP:
+ {
+ Var propname, obj, prop;
+
+ propname = TOP_RT_VALUE;
+ obj = NEXT_TOP_RT_VALUE;
+ if (propname.type != TYPE_STR || obj.type != TYPE_OBJ)
+ PUSH_ERROR(E_TYPE);
+ else if (!valid(obj.v.obj))
+ PUSH_ERROR(E_INVIND);
+ else {
+ db_prop_handle h;
+
+ h = db_find_property(obj.v.obj, propname.v.str, &prop);
+ if (!h.ptr)
+ PUSH_ERROR(E_PROPNF);
+ else if (h.built_in
+ ? bi_prop_protected(h.built_in, RUN_ACTIV.progr)
+ : !db_property_allows(h, RUN_ACTIV.progr, PF_READ))
+ PUSH_ERROR(E_PERM);
+ else if (h.built_in)
+ PUSH(prop);
+ else
+ PUSH_REF(prop);
+ }
+ }
+ break;
+
+ case OP_PUT_PROP:
+ {
+ Var obj, propname, rhs;
+
+ rhs = POP(); /* any type */
+ propname = POP(); /* should be string */
+ obj = POP(); /* should be objid */
+ if (obj.type != TYPE_OBJ || propname.type != TYPE_STR) {
+ free_var(rhs);
+ free_var(propname);
+ free_var(obj);
+ PUSH_ERROR(E_TYPE);
+ } else if (!valid(obj.v.obj)) {
+ free_var(rhs);
+ free_var(propname);
+ free_var(obj);
+ PUSH_ERROR(E_INVIND);
+ } else {
+ db_prop_handle h;
+ enum error err = E_NONE;
+ Objid progr = RUN_ACTIV.progr;
+
+ h = db_find_property(obj.v.obj, propname.v.str, 0);
+ if (!h.ptr)
+ err = E_PROPNF;
+ else {
+ switch (h.built_in) {
+ case BP_NONE: /* Not a built-in property */
+ if (!db_property_allows(h, progr, PF_WRITE))
+ err = E_PERM;
+ break;
+ case BP_NAME:
+ if (rhs.type != TYPE_STR)
+ err = E_TYPE;
+ else if (!is_wizard(progr)
+ && (is_user(obj.v.obj)
+ || progr != db_object_owner(obj.v.obj)))
+ err = E_PERM;
+ break;
+ case BP_OWNER:
+ if (rhs.type != TYPE_OBJ)
+ err = E_TYPE;
+ else if (!is_wizard(progr))
+ err = E_PERM;
+ break;
+ case BP_PROGRAMMER:
+ case BP_WIZARD:
+ if (!is_wizard(progr))
+ err = E_PERM;
+ else if (h.built_in == BP_WIZARD
+ && !is_true(rhs) != !is_wizard(obj.v.obj)) {
+ /* Notify only on changes in state; the !'s above
+ * serve to canonicalize the truth values.
+ */
+ /* First make sure traceback will be accurate. */
+ STORE_STATE_VARIABLES();
+ oklog("%sWIZARDED: #%d by programmer #%d\n",
+ is_wizard(obj.v.obj) ? "DE" : "",
+ obj.v.obj, progr);
+ print_error_backtrace(is_wizard(obj.v.obj)
+ ? "Wizard bit unset."
+ : "Wizard bit set.",
+ output_to_log);
+ }
+ break;
+ case BP_R:
+ case BP_W:
+ case BP_F:
+ if (progr != db_object_owner(obj.v.obj)
+ && !is_wizard(progr))
+ err = E_PERM;
+ break;
+ case BP_LOCATION:
+ case BP_CONTENTS:
+ err = E_PERM;
+ break;
+ default:
+ panic("Unknown built-in property in OP_PUT_PROP!");
+ }
+ }
+
+ if (err == E_NONE) {
+ db_set_property_value(h, var_ref(rhs));
+ PUSH(rhs);
+ } else {
+ free_var(rhs);
+ PUSH_ERROR(err);
+ }
+ free_var(propname);
+ free_var(obj);
+ }
+ }
+ break;
+
+ case OP_FORK:
+ case OP_FORK_WITH_ID:
+ {
+ Var time;
+ unsigned id = 0, f_index;
+
+ time = POP();
+ f_index = READ_BYTES(bv, bc.numbytes_fork);
+ if (op == OP_FORK_WITH_ID)
+ id = READ_BYTES(bv, bc.numbytes_var_name);
+ if (time.type != TYPE_INT) {
+ free_var(time);
+ RAISE_ERROR(E_TYPE);
+ } else if (time.v.num < 0) {
+ free_var(time);
+ RAISE_ERROR(E_INVARG);
+ } else {
+ Var *copied_rt_env;
+ Var task_id;
+
+ copied_rt_env = copy_rt_env(RUN_ACTIV.rt_env,
+ RUN_ACTIV.prog->num_var_names);
+ task_id = enqueue_forked_task(program_ref(RUN_ACTIV.prog),
+ RUN_ACTIV, copied_rt_env,
+ f_index, time.v.num);
+ if (task_id.type == TYPE_ERR) {
+ free_rt_env(copied_rt_env, RUN_ACTIV.prog->num_var_names);
+ RAISE_ERROR(task_id.v.err);
+ } else if (op == OP_FORK_WITH_ID) {
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = task_id;
+ free_var(copied_rt_env[id]);
+ copied_rt_env[id] = task_id;
+ }
+ }
+ }
+ break;
+
+ case OP_CALL_VERB:
+ {
+ enum error err;
+ Var args, verb, obj;
+
+ args = POP(); /* args, should be list */
+ verb = POP(); /* verbname, should be string */
+ obj = POP(); /* objid, should be obj */
+
+ if (args.type != TYPE_LIST || verb.type != TYPE_STR
+ || obj.type != TYPE_OBJ)
+ err = E_TYPE;
+ else if (!valid(obj.v.obj))
+ err = E_INVIND;
+ else {
+ STORE_STATE_VARIABLES();
+ err = call_verb(obj.v.obj, verb.v.str, args, 0);
+ /* if there is no error, RUN_ACTIV is now the CALLEE's.
+ args will be consumed in the new rt_env */
+ /* if there is an error, then RUN_ACTIV is unchanged, and
+ args is not consumed in this case */
+ LOAD_STATE_VARIABLES();
+ }
+ free_var(obj);
+ free_var(verb);
+
+ if (err != E_NONE) { /* there is an error, RUN_ACTIV unchanged,
+ args must be freed */
+ free_var(args);
+ PUSH_ERROR(err);
+ }
+ }
+ break;
+
+ case OP_RETURN:
+ case OP_RETURN0:
+ case OP_DONE:
+ {
+ Var ret_val;
+
+ if (op == OP_RETURN)
+ ret_val = POP();
+ else
+ ret_val = zero;
+
+ STORE_STATE_VARIABLES();
+ if (unwind_stack(FIN_RETURN, ret_val, &outcome)) {
+ if (result && outcome == OUTCOME_DONE)
+ *result = ret_val;
+ else
+ free_var(ret_val);
+ return outcome;
+ }
+ LOAD_STATE_VARIABLES();
+ }
+ break;
+
+ case OP_BI_FUNC_CALL:
+ {
+ unsigned func_id;
+ Var args;
+
+ func_id = READ_BYTES(bv, 1); /* 1 == numbytes of func_id */
+ args = POP(); /* should be list */
+ if (args.type != TYPE_LIST) {
+ free_var(args);
+ PUSH_ERROR(E_TYPE);
+ } else {
+ package p;
+
+ STORE_STATE_VARIABLES();
+ p = call_bi_func(func_id, args, 1, RUN_ACTIV.progr, 0);
+ LOAD_STATE_VARIABLES();
+
+ switch (p.kind) {
+ case BI_RETURN:
+ PUSH(p.u.ret);
+ break;
+ case BI_RAISE:
+ if (RUN_ACTIV.debug) {
+ if (raise_error(p, 0))
+ return OUTCOME_ABORTED;
+ else
+ LOAD_STATE_VARIABLES();
+ } else {
+ PUSH(p.u.raise.code);
+ free_str(p.u.raise.msg);
+ free_var(p.u.raise.value);
+ }
+ break;
+ case BI_CALL:
+ /* another activ has been pushed onto activ_stack */
+ RUN_ACTIV.bi_func_id = func_id;
+ RUN_ACTIV.bi_func_data = p.u.call.data;
+ RUN_ACTIV.bi_func_pc = p.u.call.pc;
+ break;
+ case BI_SUSPEND:
+ {
+ enum error e = suspend_task(p);
+
+ if (e == E_NONE)
+ return OUTCOME_BLOCKED;
+ else
+ PUSH_ERROR(e);
+ }
+ break;
+ }
+ }
+ }
+ break;
+
+ case OP_EXTENDED:
+ {
+ register enum Extended_Opcode eop = *bv;
+ bv++;
+ if (COUNT_EOP_TICK(eop))
+ ticks_remaining--;
+ switch (eop) {
+ case EOP_RANGESET:
+ {
+ Var base, from, to, value;
+
+ value = POP(); /* rhs value (list or string) */
+ to = POP(); /* end of range (integer) */
+ from = POP(); /* start of range (integer) */
+ base = POP(); /* lhs (list or string) */
+ /* base[from..to] = value */
+ if (to.type != TYPE_INT || from.type != TYPE_INT
+ || (base.type != TYPE_LIST && base.type != TYPE_STR)
+ || (value.type != TYPE_LIST && value.type != TYPE_STR)
+ || (base.type != value.type)) {
+ free_var(base);
+ free_var(to);
+ free_var(from);
+ free_var(value);
+ PUSH_ERROR(E_TYPE);
+ } else if (rangeset_check(base.type == TYPE_STR
+ ? strlen(base.v.str)
+ : base.v.list[0].v.num,
+ from.v.num, to.v.num)) {
+ free_var(base);
+ free_var(to);
+ free_var(from);
+ free_var(value);
+ PUSH_ERROR(E_RANGE);
+ } else if (base.type == TYPE_LIST)
+ PUSH(listrangeset(base, from.v.num, to.v.num, value));
+ else /* TYPE_STR */
+ PUSH(strrangeset(base, from.v.num, to.v.num, value));
+ }
+ break;
+
+ case EOP_LENGTH:
+ {
+ unsigned i = READ_BYTES(bv, bc.numbytes_stack);
+ Var item, v;
+
+ v.type = TYPE_INT;
+ item = RUN_ACTIV.base_rt_stack[i];
+ if (item.type == TYPE_STR) {
+ v.v.num = strlen(item.v.str);
+ PUSH(v);
+ } else if (item.type == TYPE_LIST) {
+ v.v.num = item.v.list[0].v.num;
+ PUSH(v);
+ } else
+ PUSH_ERROR(E_TYPE);
+ }
+ break;
+
+ case EOP_EXP:
+ {
+ Var lhs, rhs, ans;
+
+ rhs = POP();
+ lhs = POP();
+ ans = do_power(lhs, rhs);
+ free_var(lhs);
+ free_var(rhs);
+ if (ans.type == TYPE_ERR)
+ PUSH_ERROR(ans.v.err);
+ else
+ PUSH(ans);
+ }
+ break;
+
+ case EOP_SCATTER:
+ {
+ int nargs = READ_BYTES(bv, 1);
+ int nreq = READ_BYTES(bv, 1);
+ int rest = READ_BYTES(bv, 1);
+ int have_rest = (rest > nargs ? 0 : 1);
+ Var list;
+ int len = 0, nopt_avail, nrest, i, offset;
+ int done, where = 0;
+ enum error e = E_NONE;
+
+ list = TOP_RT_VALUE;
+ if (list.type != TYPE_LIST)
+ e = E_TYPE;
+ else if ((len = list.v.list[0].v.num) < nreq
+ || (!have_rest && len > nargs))
+ e = E_ARGS;
+
+ if (e != E_NONE) { /* skip rest of operands */
+ free_var(POP()); /* replace list with error code */
+ PUSH_ERROR(e);
+ for (i = 1; i <= nargs; i++) {
+ READ_BYTES(bv, bc.numbytes_var_name);
+ READ_BYTES(bv, bc.numbytes_label);
+ }
+ } else {
+ nopt_avail = len - nreq;
+ nrest = (have_rest && len >= nargs ? len - nargs + 1
+ : 0);
+ for (offset = 0, i = 1; i <= nargs; i++) {
+ int id = READ_BYTES(bv, bc.numbytes_var_name);
+ int label = READ_BYTES(bv, bc.numbytes_label);
+
+ if (i == rest) { /* rest */
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = sublist(var_ref(list),
+ i,
+ i + nrest - 1);
+ offset += nrest - 1;
+ } else if (label == 0) { /* required */
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] =
+ var_ref(list.v.list[i + offset]);
+ } else { /* optional */
+ if (nopt_avail > 0) {
+ nopt_avail--;
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] =
+ var_ref(list.v.list[i + offset]);
+ } else {
+ offset--;
+ if (where == 0 && label != 1)
+ where = label;
+ }
+ }
+ }
+ }
+
+ done = READ_BYTES(bv, bc.numbytes_label);
+ if (where == 0)
+ JUMP(done);
+ else
+ JUMP(where);
+ }
+ break;
+
+ case EOP_PUSH_LABEL:
+ case EOP_TRY_FINALLY:
+ {
+ Var v;
+
+ v.type = (eop == EOP_PUSH_LABEL ? TYPE_INT : TYPE_FINALLY);
+ v.v.num = READ_BYTES(bv, bc.numbytes_label);
+ PUSH(v);
+ }
+ break;
+
+ case EOP_CATCH:
+ case EOP_TRY_EXCEPT:
+ {
+ Var v;
+
+ v.type = TYPE_CATCH;
+ v.v.num = (eop == EOP_CATCH ? 1 : READ_BYTES(bv, 1));
+ PUSH(v);
+ }
+ break;
+
+ case EOP_END_CATCH:
+ case EOP_END_EXCEPT:
+ {
+ Var v, marker;
+ int i;
+
+ if (eop == EOP_END_CATCH)
+ v = POP();
+
+ marker = POP();
+ if (marker.type != TYPE_CATCH)
+ panic("Stack marker is not TYPE_CATCH!");
+ for (i = 0; i < marker.v.num; i++) {
+ (void) POP(); /* handler PC */
+ free_var(POP()); /* code list */
+ }
+
+ if (eop == EOP_END_CATCH)
+ PUSH(v);
+
+ JUMP(READ_BYTES(bv, bc.numbytes_label));
+ }
+ break;
+
+ case EOP_END_FINALLY:
+ {
+ Var v, why;
+
+ v = POP();
+ if (v.type != TYPE_FINALLY)
+ panic("Stack marker is not TYPE_FINALLY!");
+ why.type = TYPE_INT;
+ why.v.num = FIN_FALL_THRU;
+ PUSH(why);
+ PUSH(zero);
+ }
+ break;
+
+ case EOP_CONTINUE:
+ {
+ Var v, why;
+
+ v = POP();
+ why = POP();
+ switch (why.type == TYPE_INT ? why.v.num : -1) {
+ case FIN_FALL_THRU:
+ /* Do nothing; normal case. */
+ break;
+ case FIN_EXIT:
+ case FIN_RAISE:
+ case FIN_RETURN:
+ case FIN_UNCAUGHT:
+ STORE_STATE_VARIABLES();
+ if (unwind_stack(why.v.num, v, &outcome))
+ return outcome;
+ LOAD_STATE_VARIABLES();
+ break;
+ default:
+ panic("Unknown FINALLY reason!");
+ }
+ }
+ break;
+
+ case EOP_WHILE_ID:
+ {
+ unsigned id = READ_BYTES(bv, bc.numbytes_var_name);
+ free_var(RUN_ACTIV.rt_env[id]);
+ RUN_ACTIV.rt_env[id] = var_ref(TOP_RT_VALUE);
+ }
+ goto do_test;
+
+ case EOP_EXIT_ID:
+ READ_BYTES(bv, bc.numbytes_var_name); /* ignore id */
+ /* fall thru */
+ case EOP_EXIT:
+ {
+ Var v;
+
+ v = new_list(2);
+ v