diff options
-rw-r--r-- | AddingNewMOOTypes.txt | 38 | ||||
-rw-r--r-- | ChangeLog.txt | 2297 | ||||
-rw-r--r-- | MOOCodeSequences.txt | 347 | ||||
-rw-r--r-- | Makefile.in | 439 | ||||
-rw-r--r-- | Minimal.db | 76 | ||||
-rw-r--r-- | README | 211 | ||||
-rw-r--r-- | README.Minimal | 75 | ||||
-rw-r--r-- | aclocal.m4 | 32 | ||||
-rw-r--r-- | ast.c | 466 | ||||
-rw-r--r-- | ast.h | 258 | ||||
-rw-r--r-- | bf_register.h | 55 | ||||
-rw-r--r-- | client_bsd.c | 119 | ||||
-rw-r--r-- | client_sysv.c | 238 | ||||
-rw-r--r-- | code_gen.c | 1205 | ||||
-rw-r--r-- | code_gen.h | 37 | ||||
-rw-r--r-- | config.h.in | 319 | ||||
-rwxr-xr-x | configure | 2894 | ||||
-rw-r--r-- | configure.in | 574 | ||||
-rw-r--r-- | db.h | 547 | ||||
-rw-r--r-- | db_file.c | 757 | ||||
-rw-r--r-- | db_io.c | 438 | ||||
-rw-r--r-- | db_io.h | 115 | ||||
-rw-r--r-- | db_objects.c | 569 | ||||
-rw-r--r-- | db_private.h | 159 | ||||
-rw-r--r-- | db_properties.c | 700 | ||||
-rw-r--r-- | db_verbs.c | 573 | ||||
-rw-r--r-- | decompile.c | 1015 | ||||
-rw-r--r-- | decompile.h | 36 | ||||
-rw-r--r-- | disassemble.c | 496 | ||||
-rw-r--r-- | disassemble.h | 38 | ||||
-rw-r--r-- | eval_env.c | 131 | ||||
-rw-r--r-- | eval_env.h | 59 | ||||
-rw-r--r-- | eval_vm.c | 172 | ||||
-rw-r--r-- | eval_vm.h | 60 | ||||
-rw-r--r-- | exceptions.c | 87 | ||||
-rw-r--r-- | exceptions.h | 178 | ||||
-rw-r--r-- | execute.c | 2850 | ||||
-rw-r--r-- | execute.h | 185 | ||||
-rw-r--r-- | extensions.c | 167 | ||||
-rw-r--r-- | functions.c | 510 | ||||
-rw-r--r-- | functions.h | 132 | ||||
-rw-r--r-- | getpagesize.h | 59 | ||||
-rw-r--r-- | gnu-malloc.c | 923 | ||||
-rw-r--r-- | keywords.c | 189 | ||||
-rw-r--r-- | keywords.gperf | 91 | ||||
-rw-r--r-- | keywords.h | 49 | ||||
-rw-r--r-- | list.c | 1198 | ||||
-rw-r--r-- | list.h | 55 | ||||
-rw-r--r-- | log.c | 201 | ||||
-rw-r--r-- | log.h | 55 | ||||
-rw-r--r-- | malloc.c | 90 | ||||
-rw-r--r-- | match.c | 180 | ||||
-rw-r--r-- | match.h | 47 | ||||
-rw-r--r-- | md5.c | 325 | ||||
-rw-r--r-- | md5.h | 75 | ||||
-rw-r--r-- | my-ctype.h | 43 | ||||
-rw-r--r-- | my-fcntl.h | 61 | ||||
-rw-r--r-- | my-in.h | 75 | ||||
-rw-r--r-- | my-inet.h | 45 | ||||
-rw-r--r-- | my-ioctl.h | 51 | ||||
-rw-r--r-- | my-math.h | 43 | ||||
-rw-r--r-- | my-poll.h | 48 | ||||
-rw-r--r-- | my-signal.h | 80 | ||||
-rw-r--r-- | my-socket.h | 83 | ||||
-rw-r--r-- | my-stat.h | 60 | ||||
-rw-r--r-- | my-stdarg.h | 79 | ||||
-rw-r--r-- | my-stdio.h | 100 | ||||
-rw-r--r-- | my-stdlib.h | 114 | ||||
-rw-r--r-- | my-string.h | 124 | ||||
-rw-r--r-- | my-stropts.h | 45 | ||||
-rw-r--r-- | my-sys-time.h | 70 | ||||
-rw-r--r-- | my-time.h | 80 | ||||
-rw-r--r-- | my-tiuser.h | 63 | ||||
-rw-r--r-- | my-types.h | 80 | ||||
-rw-r--r-- | my-unistd.h | 88 | ||||
-rw-r--r-- | my-wait.h | 42 | ||||
-rw-r--r-- | name_lookup.c | 404 | ||||
-rw-r--r-- | name_lookup.h | 62 | ||||
-rw-r--r-- | net_bsd_lcl.c | 219 | ||||
-rw-r--r-- | net_bsd_tcp.c | 364 | ||||
-rw-r--r-- | net_mp_fake.c | 188 | ||||
-rw-r--r-- | net_mp_poll.c | 123 | ||||
-rw-r--r-- | net_mp_selct.c | 106 | ||||
-rw-r--r-- | net_mplex.c | 46 | ||||
-rw-r--r-- | net_mplex.h | 88 | ||||
-rw-r--r-- | net_multi.c | 828 | ||||
-rw-r--r-- | net_multi.h | 62 | ||||
-rw-r--r-- | net_proto.c | 52 | ||||
-rw-r--r-- | net_proto.h | 196 | ||||
-rw-r--r-- | net_single.c | 287 | ||||
-rw-r--r-- | net_sysv_lcl.c | 424 | ||||
-rw-r--r-- | net_sysv_tcp.c | 435 | ||||
-rw-r--r-- | network.c | 48 | ||||
-rw-r--r-- | network.h | 287 | ||||
-rw-r--r-- | numbers.c | 782 | ||||
-rw-r--r-- | numbers.h | 41 | ||||
-rw-r--r-- | objects.c | 683 | ||||
-rw-r--r-- | opcode.h | 144 | ||||
-rw-r--r-- | options.h | 358 | ||||
-rw-r--r-- | parse_cmd.c | 305 | ||||
-rw-r--r-- | parse_cmd.h | 75 | ||||
-rw-r--r-- | parser.h | 64 | ||||
-rw-r--r-- | parser.y | 1325 | ||||
-rw-r--r-- | pattern.c | 194 | ||||
-rw-r--r-- | pattern.h | 49 | ||||
-rw-r--r-- | pgperf/Makefile | 77 | ||||
-rw-r--r-- | pgperf/README | 10 | ||||
-rw-r--r-- | pgperf/boolarray.c | 90 | ||||
-rw-r--r-- | pgperf/boolarray.h | 48 | ||||
-rw-r--r-- | pgperf/getopt.c | 413 | ||||
-rw-r--r-- | pgperf/gperf-to-do | 22 | ||||
-rw-r--r-- | pgperf/hashtable.c | 132 | ||||
-rw-r--r-- | pgperf/hashtable.h | 37 | ||||
-rw-r--r-- | pgperf/iterator.c | 106 | ||||
-rw-r--r-- | pgperf/iterator.h | 47 | ||||
-rw-r--r-- | pgperf/keylist.c | 1085 | ||||
-rw-r--r-- | pgperf/keylist.h | 54 | ||||
-rw-r--r-- | pgperf/listnode.c | 116 | ||||
-rw-r--r-- | pgperf/listnode.h | 43 | ||||
-rw-r--r-- | pgperf/main.c | 96 | ||||
-rw-r--r-- | pgperf/mon.out | bin | 0 -> 85682 bytes | |||
-rw-r--r-- | pgperf/options.c | 451 | ||||
-rw-r--r-- | pgperf/options.h | 154 | ||||
-rw-r--r-- | pgperf/perfect.c | 353 | ||||
-rw-r--r-- | pgperf/perfect.h | 45 | ||||
-rw-r--r-- | pgperf/prototype.h | 15 | ||||
-rw-r--r-- | pgperf/readline.c | 87 | ||||
-rw-r--r-- | pgperf/readline.h | 31 | ||||
-rw-r--r-- | pgperf/stderr.c | 90 | ||||
-rw-r--r-- | pgperf/stderr.h | 29 | ||||
-rw-r--r-- | pgperf/version.c | 22 | ||||
-rw-r--r-- | pgperf/xmalloc.c | 78 | ||||
-rw-r--r-- | program.c | 151 | ||||
-rw-r--r-- | program.h | 84 | ||||
-rw-r--r-- | property.c | 385 | ||||
-rw-r--r-- | quota.c | 99 | ||||
-rw-r--r-- | quota.h | 38 | ||||
-rw-r--r-- | random.h | 68 | ||||
-rw-r--r-- | ref_count.c | 237 | ||||
-rw-r--r-- | ref_count.h | 33 | ||||
-rw-r--r-- | regexpr.c | 1727 | ||||
-rw-r--r-- | regexpr.h | 128 | ||||
-rwxr-xr-x | restart | 66 | ||||
-rwxr-xr-x | restart.sh | 59 | ||||
-rw-r--r-- | server.c | 1877 | ||||
-rw-r--r-- | server.h | 189 | ||||
-rw-r--r-- | storage.c | 247 | ||||
-rw-r--r-- | storage.h | 83 | ||||
-rw-r--r-- | streams.c | 263 | ||||
-rw-r--r-- | streams.h | 58 | ||||
-rw-r--r-- | structures.h | 131 | ||||
-rw-r--r-- | sym_table.c | 200 | ||||
-rw-r--r-- | sym_table.h | 86 | ||||
-rw-r--r-- | tasks.c | 2074 | ||||
-rw-r--r-- | tasks.h | 154 | ||||
-rw-r--r-- | timers.c | 347 | ||||
-rw-r--r-- | timers.h | 55 | ||||
-rw-r--r-- | tokens.h | 36 | ||||
-rw-r--r-- | unparse.c | 786 | ||||
-rw-r--r-- | unparse.h | 75 | ||||
-rw-r--r-- | utils.c | 530 | ||||
-rw-r--r-- | utils.h | 108 | ||||
-rw-r--r-- | verbs.c | 666 | ||||
-rw-r--r-- | verbs.h | 36 | ||||
-rw-r--r-- | version.c | 51 | ||||
-rw-r--r-- | version.h | 87 |
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 @@ -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 @@ -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. + */ @@ -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: @@ -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 + */ @@ -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 + */ @@ -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 = ¤t_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 = ≻ + 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 |