Compare commits

...
Author SHA1 Message Date
antirez 0011a55684 make clean now cleans Lua as well 2011-05-03 02:11:31 +02:00
antirez a048f12527 version bumped from 2.2.5 to 2.2.105 to distinguish 2.2 versions with scripting support. 2011-05-03 02:05:18 +02:00
antirez 4982fa1b9e Lua scripting backported from unstable to 2.2-scripting branch 2011-05-03 02:03:29 +02:00
antirez 9d665825d9 Fixed a bug with replication where SLAVEOF NO ONE caused a slave to close the connection with its slaves 2011-04-29 14:18:33 +02:00
antirez 19b55f8ebc Redis 2.2.5 2011-04-22 16:25:13 +02:00
Pieter Noordhuis bf3692797d Use correct argc/argv for cleanup when loading AOF 2011-04-22 09:41:07 +02:00
Pieter Noordhuis da06854477 Reformat AOF tests 2011-04-22 09:40:10 +02:00
antirez 38de9362b2 Revert "tests for variadic list push"
This reverts commit 1b905277bb.
Moving all the new stuff in 2.4 branch.
2011-04-15 17:41:32 +02:00
antirez 7600994937 Revert "variadic LPUSH/RPUSH backported to 2.2"
This reverts commit c138dc7da4.
Moving all the new stuff in 2.4 branch.
2011-04-15 17:41:07 +02:00
antirez 6a9764d183 Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-15 17:28:03 +02:00
antirez c138dc7da4 variadic LPUSH/RPUSH backported to 2.2 2011-04-15 17:27:20 +02:00
antirez 1b905277bb tests for variadic list push 2011-04-15 17:15:59 +02:00
antirez 2b886275e9 Redis 2.2.4 2011-04-06 15:39:54 +02:00
antirez 93db956e06 make sure that OBJECT ENCODING returns skiplist for sorted sets, and not raw, so that once we will merge specially encoded sorted sets everything will make sense. 2011-04-06 15:38:08 +02:00
antirez 27fc6199ca CHANGELOG updated 2011-04-06 12:46:39 +02:00
antirez 0b01578d2e Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-06 12:23:44 +02:00
antirez 920c45b818 version is now 2.2.3 2011-04-06 12:23:16 +02:00
antirez cf6c3f4b04 OBJECT command implemented 2011-04-06 12:22:09 +02:00
antirez fb90934c47 fixed memory leak introduced with the previous commit. Many thanks to Pieter Noordhuis for spotting it in no time 2011-03-31 19:54:08 +02:00
antirez 207ca3cedd Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-03-31 16:45:27 +02:00
antirez 97d3b7dc8d Fixed issue #503. MONITOR + QUIT could crash the server, there are actually other interactions that could have the same effect (for instance Pub/Sub). 2011-03-31 16:45:05 +02:00
antirez abdbfc14c0 Fixed sdssplitargs() handling of hex-style escapes. 2011-03-22 22:49:33 +01:00
antirez de5d4be994 Merge remote branch 'pietern/2.2-sigs' into 2.2 2011-03-07 12:21:41 +01:00
Pieter Noordhuis 69bfdcf7d4 Show database number in cli prompt when non-zero 2011-03-06 21:10:50 +01:00
Pieter Noordhuis 699465c31e Store SELECTed database for reconnect (issue #468) 2011-03-06 20:53:13 +01:00
Pieter Noordhuis 45332cc47b Minor memory leak in redis-cli (issue #464) 2011-03-06 20:53:12 +01:00
Pieter Noordhuis 2d2c17b075 Save RDB on SIGTERM (see issue #471) 2011-03-06 17:49:22 +01:00
antirez 048c0f0f37 RELEASE NOTES updated 2011-03-04 16:31:20 +01:00
antirez 3eb3cc9da4 version is now 2.2.2 2011-03-04 16:29:38 +01:00
antirez 74f6ee8dd5 Fixed return value of GETRANGE / SUBSTR 2011-03-04 16:23:18 +01:00
antirez 7a55d72f28 AOF file descriptor leak fixed 2011-03-04 16:14:09 +01:00
antirez 4e4b3b943c Redis 2.1.1, release notes updated 2011-02-23 15:48:24 +01:00
Pieter Noordhuis 61e2547675 Restore argc/argv in EXEC after command is executed 2011-02-23 09:39:29 +01:00
antirez 595b5974f8 fixed client-libraries/README to reflect the new site path instead of google code 2011-02-22 18:22:00 +01:00
antirez 58418d7c3e linenoise updated 2011-02-22 17:40:33 +01:00
antirez c02bda324f fixed color support for test suite. Now colors are shown as long as there is xterm somewhere inside the TERM env var 2011-02-22 16:40:24 +01:00
Paul Gideon Dann 1afef16d88 Allow port to be 0 in configuration, in order to allow disabling of TCP. 2011-02-22 12:12:30 +01:00
antirez 7377aacd84 seed the random number generator at startup 2011-02-22 00:01:48 +01:00
antirez eece2d528c suppress a Linux warning, for 2.2 sake 2011-02-21 17:50:54 +01:00
antirez eb5aeaf138 check return value of getcwd() 2011-02-21 17:41:25 +01:00
antirez 1a6281f52f version bumped to 2.2.0 and release notes added 2011-02-17 17:15:52 +01:00
antirez c2571b314a CONFIG SET/GET for all the special encoding parameters of sets, lists, hashes 2011-02-17 12:21:22 +01:00
Pieter Noordhuis 97aeda9828 Morph SPOP to SREM for correct behavior on log replay/replication 2011-02-16 00:24:54 +01:00
antirez ca9d961073 CONFIG GET/SET dir implemented 2011-02-14 02:51:49 +01:00
antirez 2446fbb3bb Merge remote branch 'pietern/2.2-brpoplpush' into 2.2 2011-02-07 15:27:45 +01:00
antirez 970e813be2 Fix for the previous port 0 patch, thanks to Pieter for noticing the error 2011-02-07 13:43:55 +01:00
antirez a36d524562 If port zero is specified, Redis will not listen for TCP connections 2011-02-07 12:52:40 +01:00
Pieter Noordhuis bbaf76ea2f Clarify comment 2011-01-31 16:50:17 +01:00
Pieter Noordhuis 669f302e16 Reply with single null bulk for unsuccesful BRPOPLPUSH 2011-01-31 16:50:00 +01:00
antirez 71791e7a8e propagate key eviction to slaves and AOF, like for expires, so that replicas and AOFs are always consistent even in maxmemory mode. 2011-01-27 16:53:23 +01:00
antirez 2f91d7ada4 add tarball script added 2011-01-25 10:45:53 +01:00
antirez 5ef8f9531d other minor README changes 2011-01-25 10:26:00 +01:00
antirez 3ce30a6906 README updated 2011-01-25 10:25:05 +01:00
antirez 65e79a8272 release noted updated, version bumped to 2.1.12 (2.2.0 RC4) 2011-01-25 10:21:13 +01:00
antirez ef8bc13d0d Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-01-24 10:07:18 +01:00
antirez 0ffeadd290 portability fix in redis benchmark, using zu format specified with size_t instead of lu 2011-01-24 10:06:09 +01:00
antirez db0e263b42 Fixed issue #435 and at the same time introduced explicit ping in the master-slave channel that will detect a blocked master or a broken even if apparently connected TCP link. 2011-01-20 18:02:51 +01:00
antirez d526d09e41 Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-01-20 16:50:02 +01:00
antirez 9738e587da Fixed compilation on FreeBSD 2011-01-20 10:59:25 +01:00
antirez da67b5f59a typo fixed in release notes for 2.2.0 RC3 2011-01-17 12:21:59 +01:00
antirez 825d2904d1 release notes updated for 2.2.0 RC3 2011-01-17 12:19:15 +01:00
Pieter Noordhuis 3becef9ee6 Reverse commits changing sorted set code for 2.2 2011-01-17 11:15:50 +01:00
antirez 2ccf6e81a6 version bumped to 2.1.11 2011-01-17 10:12:57 +01:00
Pieter Noordhuis e18b59ae7e Remove client from list of unblocked clients when it is free'd 2011-01-17 10:03:21 +01:00
Pieter Noordhuis 8ff1353766 Merge branch '2.2' of git://github.com/antirez/redis into 2.2 2011-01-17 09:44:12 +01:00
antirez 99677e89d0 default hash encoding settings also fixed in redis.conf 2011-01-15 22:32:34 +01:00
antirez f43502fb0e lame typo causing bad hashes memory characteristic 2011-01-15 22:24:48 +01:00
antirez ebb07fb4bb implemented two new INFO fields showing the size of clients max input and output buffers. 2011-01-14 10:20:35 +01:00
antirez c13c4080e5 useless expression removed in zmalloc.c 2011-01-14 09:57:07 +01:00
antirez 360664c9c8 master-slave replication fixed, it was not listing any key using KEYS command in the slave. 2011-01-14 09:54:41 +01:00
Pieter Noordhuis 7ac1b364fe Merge branch 'zset-range' into 2.2 2011-01-13 16:30:02 +01:00
Pieter Noordhuis 1b2b8cbbde Compiler should decide on inlining 2011-01-13 16:06:03 +01:00
antirez f487bbbfe2 allocation stats in INFO 2011-01-09 15:57:46 +01:00
Pieter Noordhuis 4b2499cf2f Remove newlines for networking errors 2011-01-05 10:50:47 +01:00
Pieter Noordhuis 01a0b09886 Remove glueoutputbuf from redis.conf 2011-01-05 10:44:54 +01:00
Pieter Noordhuis 90419b5681 Remove glueoutputbuf option and broken code 2011-01-05 10:37:23 +01:00
antirez ae7db3fe83 Merge remote branch 'pietern/bench-fix' into 2.2 2010-12-23 13:17:54 +01:00
Pieter Noordhuis 454f4bfd28 Fix compiler warnings on Solaris 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 1408d147e4 limits.h is already included from redis.h 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 78055bd4ca Solaris 10 doesn't know AF_LOCAL 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 736d447380 Solaris doesn't support -rdynamic 2010-12-23 13:02:22 +01:00
Pieter Noordhuis 71b6f64f2a Update hiredis to 0.9.2 2010-12-23 13:02:21 +01:00
Pieter Noordhuis 2380388974 Randomize keys and set start time when first write event fires 2010-12-23 11:22:40 +01:00
Pieter Noordhuis 3c49070b35 Find substrings to randomize when the client is created 2010-12-23 11:04:44 +01:00
Pieter Noordhuis d69a483556 Make the MSET benchmark *really* work with 10 keys 2010-12-22 18:39:52 +01:00
Pieter Noordhuis f2f2424e00 Remove code duplication 2010-12-22 18:31:33 +01:00
antirez c4e93cadfb Merge branch '2.2' of github.com:antirez/redis into 2.2 2010-12-20 13:35:13 +01:00
antirez 0540df22ba version bumped to 2.1.10 2010-12-20 13:34:07 +01:00
antirez 9ac5be2ebe Improved release notes. Added RC2 changes 2010-12-20 13:32:54 +01:00
antirez b902c1413e TODO file just pointing to the master branch TODO file 2010-12-20 13:16:38 +01:00
antirez 54e9f28922 Merge branch '2.2' of github.com:antirez/redis into 2.2 2010-12-20 13:14:36 +01:00
antirez dd889d884f removed some dead code, added evicted keys counter separated from expired keys. 2010-12-19 15:15:33 +01:00
antirez 1443a814d0 overflow detection in INCR family functions 2010-12-19 12:23:35 +01:00
Didier Spezia f474a5bd4e Add wait states to deal with many connections. 2010-12-18 12:11:26 +01:00
Pieter Noordhuis 1cd3c1e08c Use multi-bulk protocol by default in redis-benchmark 2010-12-18 12:10:24 +01:00
Pieter Noordhuis 174df6fe49 Re-use variable data in redis-benchmark 2010-12-16 23:41:58 +01:00
Pieter Noordhuis 53f1d81712 Fix NULL-termination of variable data in redis-benchmark 2010-12-16 23:35:56 +01:00
Pieter Noordhuis 021321e0ef Update hiredis to 0.9.2 2010-12-16 23:32:02 +01:00
antirez 076ff11c06 fixed typo in release notes 2010-12-15 18:10:15 +01:00
antirez 6418b4c790 help.h updated 2010-12-15 17:39:40 +01:00
antirez 59aee5513d added the mandatory Cheers in the release notes 2010-12-15 17:19:01 +01:00
antirez 9fecc4057a release notes, info on 2.0 compatibility 2010-12-15 17:18:15 +01:00
antirez 51716cbdb8 added more info in the release notes 2010-12-15 17:00:54 +01:00
antirez 9a7a07a466 removed a line from release notes that was not true (feature already backported into 2.0) 2010-12-15 16:54:08 +01:00
antirez 0898aa462e typo fixed 2010-12-15 16:51:01 +01:00
antirez 4c4dec7202 release notes added 2010-12-15 16:44:29 +01:00
antirez 401c3e213c bulk transfers limited to 512 MB as this is the new limit of all the redis strings 2010-12-15 16:07:49 +01:00
antirez beb1aab3fd Merge remote branch 'pietern/cli' 2010-12-15 16:03:43 +01:00
Pieter Noordhuis 28c07c7bf8 Specify multi-bulk delimiter via options 2010-12-15 16:02:07 +01:00
antirez 6674e3b910 added new HTML doc pages 2010-12-15 15:59:45 +01:00
Pieter Noordhuis 65add0a311 Improved raw output mode
Raw output mode is selected by default when STDOUT is not a tty. For
ttys, raw output can be forced using the option "--raw".
2010-12-15 15:59:06 +01:00
antirez b9f7e9e63c HTML doc updated 2010-12-15 15:49:29 +01:00
antirez 2136a880a7 version bumped to 2.1.8 2010-12-15 15:22:04 +01:00
Pieter Noordhuis f18e059e82 Make redis-cli help a little better 2010-12-15 15:00:47 +01:00
Pieter Noordhuis c392edf531 Show redis-cli version with repository information if present 2010-12-15 14:34:05 +01:00
Pieter Noordhuis c8061392e1 Minor changes to Makefile 2010-12-15 12:48:12 +01:00
antirez 53f0efe28c Merge remote branch 'pietern/string-patches' 2010-12-15 11:52:55 +01:00
Pieter Noordhuis 7d5f5712d9 Update tests for STRLEN 2010-12-15 11:49:39 +01:00
Pieter Noordhuis ad1b4f4f59 Use helper function for string object length 2010-12-15 11:49:04 +01:00
Pieter Noordhuis 1333f98dd2 Use helper functions in APPEND 2010-12-15 11:40:36 +01:00
Pieter Noordhuis 8f8eeffec1 Disable negative offsets for SETRANGE 2010-12-15 11:30:50 +01:00
Pieter Noordhuis e983cf34be Add fuzzy test for SETBIT 2010-12-15 11:20:54 +01:00
antirez 180e07b8c0 fixed test rendering when test failed in non verbose mode 2010-12-15 11:04:04 +01:00
antirez 7982173fef print test names inline in the non verbose output mode 2010-12-15 10:44:36 +01:00
antirez eae9cce166 colorized make test output when the verbose output is disabled (default) 2010-12-15 10:14:34 +01:00
Pieter Noordhuis 30407e1f4f Make SETBIT return original bit value 2010-12-15 00:42:32 +01:00
antirez 6a246b1e7e special encoding limits redefined, and all the config options for special encodings added in the example redis.conf file 2010-12-14 18:23:52 +01:00
antirez cc7c4158bc Merge remote branch 'jonahharris/syslog' 2010-12-14 17:53:28 +01:00
antirez 8e33831b2b Merge remote branch 'pietern/cli-monitor' 2010-12-14 17:48:52 +01:00
antirez 648e965460 removed a test that will never be true fixing the compilation on Linux 2010-12-14 17:46:20 +01:00
antirez 603e616bf4 Merge branch 'master' of github.com:antirez/redis 2010-12-14 17:42:46 +01:00
antirez 57997664ea Merge remote branch 'pietern/strrange' 2010-12-14 17:42:01 +01:00
Pieter Noordhuis 2b2eca1f56 Zero-pad timestamps in MONITOR output
Original report and fix:
http://code.google.com/p/redis/issues/detail?id=404
2010-12-14 17:39:34 +01:00
antirez 96b5d05fde initialized a few vars just to avoid warnings, not a real problem. 2010-12-14 16:39:33 +01:00
antirez f858c11d7d Merge remote branch 'pietern/brpoplpush' 2010-12-14 16:26:37 +01:00
Pieter Noordhuis d8f160a848 Add test cases for GETRANGE against integer-encoded strings 2010-12-14 15:35:35 +01:00
Pieter Noordhuis ef11bcccca Refactor and rename SUBSTR to GETRANGE
SUBSTR is renamed to GETRANGE to have better consistency between command
names (with SETRANGE as its dual). GETRANGE is still aliased as SUBSTR.
2010-12-14 15:16:29 +01:00
Pieter Noordhuis 9f9e1ceaa0 Add SETRANGE command implementation and tests 2010-12-14 14:20:51 +01:00
antirez 8c304be359 cow friendly HGETALL and variants 2010-12-14 12:10:51 +01:00
Pieter Noordhuis 7ecd4644e7 Don't decode object on STRLEN when not necessary 2010-12-14 10:42:41 +01:00
antirez 5ce3a24dba Linenoise updated to latest version 2010-12-10 19:22:13 +01:00
antirez afd438dfff previouse INCR implementation restored, was actually faster for some reson not fully clear at the moment 2010-12-10 19:15:00 +01:00
antirez d6107fd6f1 Merge remote branch 'pietern/testverbosity' 2010-12-10 17:24:03 +01:00
antirez a15742a41b dont take the fast path for INCR if the resulting integer will fit into a shared integer range 2010-12-10 17:23:38 +01:00
antirez b215a496a4 faster INCR doing far less allocation in common cases 2010-12-10 17:03:38 +01:00
Pieter Noordhuis 6f8a32d5c7 Be less verbose in testing; improve error handling 2010-12-10 16:13:21 +01:00
antirez dd48de748c fixed compilation on Linux 2010-12-10 15:47:01 +01:00
antirez 4b918769a1 command line option in test suite to activate valgrind mode 2010-12-10 15:40:48 +01:00
antirez e9bc56dbed valgrind suppression file added 2010-12-10 15:36:04 +01:00
antirez 3d24304ff9 HGET HMGET are now COW friendly, plus API refactoring and changes needed for the new implementation. 2010-12-10 15:17:55 +01:00
Pieter Noordhuis cc20906390 Change function name to match what it does 2010-12-10 12:16:16 +01:00
Pieter Noordhuis 586500c0ef Typo 2010-12-10 12:06:24 +01:00
Pieter Noordhuis eae33c1c81 Add generic function to grow an sds value
Move logic concerned with setting a bit in an sds to the SETBIT command
instead of keeping it in sds.c. The function to grow an sds can and will
be reused for a command to set a range within a string value.
2010-12-10 11:58:21 +01:00
antirez 1b508da7ca SINTER/MEMBERS are now COW friendly, also some refactoring around was needed to get this result. 2010-12-09 23:01:09 +01:00
Jonah H. Harris 8b5db0a8dc removed dup syslog-ident conditional 2010-12-09 11:26:33 -05:00
Pieter Noordhuis 076f88d657 Enforce maximum string value length of 512MB 2010-12-09 17:16:10 +01:00
Jonah H. Harris e1a586ee69 syslog support 2010-12-09 11:10:21 -05:00
Pieter Noordhuis 3c1bf4957e Add commands SETBIT/GETBIT 2010-12-09 16:39:33 +01:00
Pieter Noordhuis 4769dc7826 Undo rename of function names where something went wrong 2010-12-09 10:37:35 +01:00
antirez a5be65f71c COW friendly versions of SPOP and SRANDMEMBER commands, with some change to the set encoding-agnostic API. 2010-12-09 10:21:02 +01:00
Pieter Noordhuis 039357e471 Move logic concerned with zset ranges
This also optimizes ZREVRANGEBYSCORE for pathological cases where a
sorted set contains many elements with the same score. Previously,
it would traverse the list from back to front in such a case.
2010-12-07 23:21:07 +01:00
antirez d51ebef509 LRANGE converted into a COW friendly command. Some refactoring, comment, and new addReply*() family function added in the process. 2010-12-07 16:33:13 +01:00
Pieter Noordhuis a4ce758155 Don't execute commands for clients when they are unblocked 2010-12-06 16:39:39 +01:00
Pieter Noordhuis ecf9401415 Fix case and indent 2010-12-06 16:04:42 +01:00
Pieter Noordhuis 8a88c368ed Check other blocked clients when value could not be pushed 2010-12-06 16:04:10 +01:00
Pieter Noordhuis ac06fc011d Move code for pushing on a (blocking) RPOPLPUSH 2010-12-06 14:48:58 +01:00
Pieter Noordhuis 5fa95ad763 Rename blpop_blocked_clients to bpop_blocked_clients 2010-12-06 14:05:01 +01:00
Pieter Noordhuis c8a0070a61 Move timeout logic 2010-12-06 13:45:48 +01:00
Pieter Noordhuis bc8ffafeb9 Merge branch 'master' into brpoplpush 2010-12-06 12:31:56 +01:00
antirez bbac56c2f8 added support for ctrl-l and clear command into redis-cli. To clear the screen is a good idea from time to time :). Also linenoise updated to the current version to support this new feature. 2010-12-01 11:18:59 +01:00
antirez ce260f736e minor merge conflicts merging cli-help branch fixed 2010-11-30 11:39:55 +01:00
Michel Martens & Damian Janowski baa14ef913 Fix BRPOPLPUSH behavior for all use cases. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens 8987bf23bf Adhere to conventions. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens e3c51c4b1b Rename bstate to bpop. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens 59bd44d1c8 Remove warning. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens 7c25a43adc Handle BRPOPLPUSH inside a transaction. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens ba3b474111 Refactor code for BRPOPLPUSH. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens 357a841714 Move to struct. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens b2a7fd0cf7 BRPOPLPUSH. 2010-11-29 23:52:07 -03:00
Damian Janowski & Michel Martens 8a979f0390 Fix case in RPOPLPUSH. 2010-11-29 23:52:07 -03:00
Pieter Noordhuis c54afb6d0d Minor update to linenoise 2010-11-29 20:53:57 +01:00
Pieter Noordhuis b2cc45bfbc Refactor and support help for command names with spaces 2010-11-29 20:26:32 +01:00
Pieter Noordhuis 41945ba6ae Use linenoise completion API from redis-cli 2010-11-29 19:27:36 +01:00
Pieter Noordhuis ff4058183b Update linenoise 2010-11-29 19:27:06 +01:00
antirez d8d528e992 quick and dirty fix for hiredis bug creating problem with the new redis-cli connect commmand. Also change prompt when redis-cli is not connected 2010-11-29 12:20:17 +01:00
antirez efcf948c1a new redis-cli command connect 2010-11-29 12:17:55 +01:00
antirez 5b761a3387 gitignore now includes a few more files 2010-11-29 11:14:57 +01:00
Pieter Noordhuis a2a69d5803 Refactor help-related code into redis-cli.c 2010-11-28 21:37:19 +01:00
Pieter Noordhuis 50d0e82d54 Update help.h generator script to output man-style argument list 2010-11-28 17:45:58 +01:00
Pieter Noordhuis 2612e0521f Merge remote branch 'visionmedia/cli-help' into cli-help 2010-11-26 20:46:42 +01:00
Pieter Noordhuis a4e48b417d Don't hardcode make to "make" 2010-11-21 16:44:17 +01:00
Pieter Noordhuis bd70a5f588 Stop using /dev/null to find out the serialized object length
Now the rdbSave* functions return the number of bytes written (or
required to write) in serializing a Redis object, writing to /dev/null
and using ftell (which doesn't work on FreeBSD) isn't needed anymore.
2010-11-21 16:31:27 +01:00
Pieter Noordhuis 9a68cf91f0 Wrapper around fwrite to make API consistent 2010-11-21 16:12:25 +01:00
Pieter Noordhuis 8a623a98c3 Return number of bytes written from rdbSave* functions 2010-11-21 16:07:46 +01:00
Tj Holowaychuk 5397f2b596 Added redis-cli interactive help support
updated via commands.json in redis-doc repo. Currently
use `make src/help.h` to re-generate. The following
are valid from the REPL:

  help
  help [command]
  help [group]
  help groups

ex:

  help sort
  help hash
2010-11-16 05:50:26 -08:00
antirez c086b85afb added a few more files to gitignore 2010-11-15 15:50:41 +01:00
antirez 9fd01051bf Fix for bug 374, thanks to Jeremy Zawodny for reporting and tracing why it was crashing. 2010-11-12 20:02:20 +01:00
antirez 0c2f75c6d8 volatile-lru maxmemory policy segfault fixed, thanks to Anthony Lauzon for reporting the problem with the patch. Original patch modified a bit in order to avoid the double lookup if the policy is allkeys-lru 2010-11-11 13:19:17 +01:00
antirez 11fd0c422b now redis-cli is able to show the Git SHA1 in the version output 2010-11-08 16:26:02 +01:00
antirez d9d8ccab93 make sure to flush stdout every line read in monitor mode, to play well with redirection to file 2010-11-08 16:14:15 +01:00
antirez 5402c4262e added noeviction policy to redis maxmemory. ZSCORE removed from the list of commands that can't be called when we are low on memory, this command was added in the past for a stupid error. 2010-11-08 16:12:16 +01:00
antirez 240f8dbf3f build redis-server at the end so have a more pleasing to see Make output and the advice to run the test suite at the end. 2010-11-08 13:19:58 +01:00
antirez be98a33b51 fixed compilation with 32bit target 2010-11-08 12:53:36 +01:00
antirez 97e7f8aec3 non blocking loading of the DB / AOF with informations and ETA in INFO output 2010-11-08 11:52:03 +01:00
Pieter Noordhuis 57c9babd81 Update hiredis 2010-11-05 17:24:48 +01:00
Pieter Noordhuis abc3ff4d90 Only cascade clean target to deps 2010-11-05 17:22:16 +01:00
antirez 645e9962cb version bumped to 2.1.7 after merging with aaslave branch for non blocking slaves 2010-11-05 11:00:20 +01:00
antirez 3b5e72d402 Merge branch 'aaslave' 2010-11-05 10:59:49 +01:00
antirez ecc72ca17f version bumped to 2.1.6 2010-11-05 10:57:53 +01:00
antirez 56fce7ce7d Merge remote branch 'pietern/benchmark' 2010-11-05 10:29:59 +01:00
Pieter Noordhuis feecb608ed Use multi-bulk protocol in SET benchmark 2010-11-05 10:22:52 +01:00
antirez 4ebfc45528 config option to select if when replication link with master a slave should or not serve stale data 2010-11-04 19:59:21 +01:00
antirez 12ebe2ac17 replication asynchronous SYNC information in INFO output 2010-11-04 18:50:23 +01:00
antirez f6433915fe more replication info in logs 2010-11-04 18:14:20 +01:00
antirez 26b3366993 non blocking slave replication is now more non blocking than the first implementation... 2010-11-04 18:09:35 +01:00
antirez 62ec599c36 typos and minor stuff fixed in the new non blocking replication code 2010-11-04 17:35:03 +01:00
antirez f4aa600b99 first attempt to non blocking implementation of slave replication and SYNC bulk data download. Never compiled so far... 2010-11-04 17:29:53 +01:00
Pieter Noordhuis 8146e31677 Microsecond latency resolution in redis-benchmark 2010-11-04 16:15:35 +01:00
Pieter Noordhuis ec8f06675a Use hiredis from redis-benchmark 2010-11-04 13:37:05 +01:00
Pieter Noordhuis afc156c2d8 Update hiredis 2010-11-04 13:35:47 +01:00
antirez 4d7e125519 minor test suite bug fixed 2010-11-04 10:48:49 +01:00
Pieter Noordhuis a9b18e54d4 Stop using the freelist robj* cache 2010-11-04 10:09:30 +01:00
antirez 8df3dcada5 top level make clean also clean hiredis and linoise (deps) 2010-11-03 18:14:09 +01:00
Pieter Noordhuis e902b579b4 Import linenoise as dependency for redis-cli 2010-11-03 17:15:23 +01:00
Pieter Noordhuis 339b9dc2d2 Put duration in parenthesis 2010-11-03 17:07:10 +01:00
Pieter Noordhuis cfcd5d6d43 Add proper numbering for multi bulk replies in redis-cli 2010-11-03 17:03:54 +01:00
Pieter Noordhuis 7fc4ce13ed Use hiredis from redis-cli 2010-11-03 16:09:38 +01:00
Pieter Noordhuis 24f753a8b9 Add hiredis dependency for redis-cli, redis-benchmark, etc 2010-11-03 16:03:04 +01:00
antirez 8d3e063a0a added support for command renaming/suppression in redis.conf 2010-11-03 12:14:36 +01:00
antirez 1b1f47c915 command lookup process turned into a much more flexible and probably faster hash table 2010-11-03 11:23:59 +01:00
antirez 297e77c6ab Now it is possible to use quoted strings in the redis.conf file accordingly to the sdssplitargs() function capabilities. 2010-11-03 10:31:19 +01:00
antirez 0a546fc017 Merge remote branch 'pietern/unixsocket' 2010-11-02 23:47:52 +01:00
antirez 539fb43820 RSS information in INFO output 2010-11-02 22:47:10 +01:00
antirez 7d0966a6b7 Do not update the LRU info on key lookup when we have a saving child. With this trivial change the additional memory used while saving with a background child in presence of many read operations is zero. 2010-11-02 18:59:48 +01:00
antirez 3ce014c766 redis-cli in interactive mode now prints the time elapsed of the operation performed took more than half a second. 2010-11-02 18:08:30 +01:00
antirez b5b22da8e6 Revert "Function to compute RSS memory usage in a fast way, suitable to be called inside keys eviction loops"
This reverts commit 7d47ecd543.
2010-11-02 12:10:41 +01:00
antirez ca734d17ad Revert "Now maxmemory, VM, and everything else uses the fast RSS memory used estimation instead of raw memory reported by zmalloc(). This means that setting maxmemory to 2GB will really have the effect of using up to 2GB of memory."
This reverts commit a3e60027e7.
2010-11-02 12:09:59 +01:00
antirez a3e60027e7 Now maxmemory, VM, and everything else uses the fast RSS memory used estimation instead of raw memory reported by zmalloc(). This means that setting maxmemory to 2GB will really have the effect of using up to 2GB of memory. 2010-11-02 11:50:55 +01:00
antirez 7d47ecd543 Function to compute RSS memory usage in a fast way, suitable to be called inside keys eviction loops 2010-11-02 11:40:35 +01:00
antirez 10c12171cc removed a number of stupid compilation warnings on Linux 2010-11-02 11:15:09 +01:00
antirez 92e282288f zmalloc functions to get RSS and fragmentation refactored into two separated functions 2010-11-02 10:51:09 +01:00
antirez 21dbc6499a merge conflict resolved 2010-10-28 22:59:47 +02:00
Pieter Noordhuis 4794d88f15 Rewrite comment that was no longer valid 2010-10-28 16:59:05 +01:00
Pieter Noordhuis a3a323e0e5 When REDIS_CLOSE_AFTER_REPLY is set, there may never be new replies 2010-10-28 16:52:23 +01:00
Pieter Noordhuis 5e78edb350 Unify two client flags that mean the same 2010-10-28 15:07:45 +01:00
antirez 73abd0a9d2 Merge remote branch 'remotes/pietern/zrevrangebyscore' 2010-10-28 14:12:25 +02:00
antirez 1de98301f6 removed useless spaces from DEBUG OBJECT output 2010-10-27 17:11:17 +02:00
Pieter Noordhuis e584d82fec Return error to client on wrong type for HMGET 2010-10-26 12:33:17 +02:00
antirez 244201f6ba added a missing prototype from syncio.c in redis.h 2010-10-25 10:54:37 +02:00
antirez d08fac3eb9 more generally usable i/o functions moved to syncio.c 2010-10-25 10:53:28 +02:00
antirez 19e61097c5 synchronous I/O networking functions originally used just for replication refactored in a file as generally useful, they are used in the cluster branch for MIGRATE. 2010-10-24 16:22:52 +02:00
Pieter Noordhuis d94ac406ba Exclusively use either tcmalloc or OSX's native malloc_size() 2010-10-23 10:18:48 +02:00
Pieter Noordhuis 7cdc98b630 Don't use prefix when malloc_size() can be called
Also, use tcmalloc functions explicitly via macros to prevent symbol
lookups to resolve to native malloc/free on OSX.
2010-10-23 09:59:28 +02:00
antirez da47440d44 Make sure to reset the signal handler and deliver again the original crashing signal when dumping the stack trace. This will allow to dump the core if core dumping is enabled. 2010-10-22 23:30:48 +02:00
Robey Pointer d8a717fb1a if server.saveparamslen is not set, don't save the DB on exit. 2010-10-22 23:17:28 +02:00
antirez fc41345116 README more specific about tcmalloc space benefits 2010-10-22 00:18:26 +02:00
antirez 1a587ff843 Makefile typo fixed for tcmalloc option 2010-10-22 00:16:32 +02:00
antirez 13b3715925 reports if tcmalloc is in used in INFO output 2010-10-22 00:10:17 +02:00
antirez 0a802bd7a0 support for compiling with tcmalloc 2010-10-22 00:06:44 +02:00
antirez 75fcab8c23 version bumped to 2.1.5 2010-10-21 17:55:44 +02:00
Pieter Noordhuis a375b077cc Skip object encoding where it doesn't make sense 2010-10-17 18:13:31 +02:00
Pieter Noordhuis 19408d83a4 Object encoding in hash function is done by a more specific function 2010-10-17 17:46:55 +02:00
Pieter Noordhuis 33aba595b0 Removed unused command flags 2010-10-17 17:31:40 +02:00
Pieter Noordhuis 75b41de8ca Convert objects in the command procs instead of the protocol code 2010-10-17 17:21:41 +02:00
Pieter Noordhuis b19c33d48a Prevent clients from making too large multibulk requests 2010-10-15 19:15:38 +02:00
Pieter Noordhuis ea5b70924d Add benchmark for MSET 2010-10-15 18:17:06 +02:00
antirez 13a49af44b prevent small integer sharing when maxmemory is active. So every object will use a private LRU field and the LRU algorithm can work well 2010-10-15 18:04:05 +02:00
Pieter Noordhuis 1aa608fc68 Change protocol from bulk to inline in redis-benchmark 2010-10-15 17:34:20 +02:00
Pieter Noordhuis 9da6caac4e Don't reset the client when processCommand returns REDIS_ERR 2010-10-15 17:27:05 +02:00
Pieter Noordhuis dc11daf3b5 Change tests to use either the inline or the multibulk protocol 2010-10-15 17:25:20 +02:00
Pieter Noordhuis 5b12b47df9 Show output of leaks command on a leak 2010-10-15 15:56:16 +02:00
Pieter Noordhuis 00cf82c0bd Change tcl client to only use the multibulk protocol 2010-10-15 15:50:29 +02:00
Pieter Noordhuis cd8788f26d Refactor request parsing code for efficiency 2010-10-15 15:44:55 +02:00
Pieter Noordhuis 5a4f9f27e7 Add tests for OK on QUIT 2010-10-15 12:54:53 +02:00
antirez 9f8ded8ced CONFIG RESETSTAT no longer resets the server uptime. Now keyspace hits/misses are reset as well. 2010-10-15 12:29:05 +02:00
antirez 95506e4611 minor aesthetic change 2010-10-15 12:22:48 +02:00
antirez 53eeeaff08 added keyspace_hits and keyspace_misses fields in INFO output 2010-10-15 12:19:21 +02:00
antirez a36879293d maxmemory-samples implemented in CONFIG command and configuration file 2010-10-15 11:57:38 +02:00
antirez 670bf2fd36 Don't increment dirty on expireIfNeeded() as natural expires are not considered database changes. This will avoid useless read-only commands in the AOF file as a result of, for instance, a GET operation triggering an expirIfNeeded() call resulting in an expired key removed. 2010-10-15 11:29:03 +02:00
antirez b33ef40105 Merge remote branch 'pietern/ziplist-regression' 2010-10-14 21:23:01 +02:00
antirez 165346ca29 implemented different algorithms for maxmemory 2010-10-14 21:22:21 +02:00
Pieter Noordhuis 306c6a02e3 Replace ziplist stresser and fix regression 2010-10-14 21:11:42 +02:00
antirez ef59a8bc9e Object approximated LRU algorithm enhanced / fixed / refactored. This is used for the VM currently but will soon be used for maxmemory expiring. 2010-10-14 13:52:58 +02:00
Pieter Noordhuis 7236fdb22f Return error when min and/or max in the sorted set range spec is not a double 2010-10-13 21:59:24 +02:00
Pieter Noordhuis 91504b6cbe Make ZREMRANGEBYSCORE accept the same range spec as ZRANGEBYSCORE
This allows to use inclusive/exclusive bounds for min and max when
deleting a range of scores from a sorted set.
2010-10-13 21:43:58 +02:00
Pieter Noordhuis 26f3388d27 Merge branch 'master' into zrevrangebyscore 2010-10-13 20:29:50 +02:00
Pieter Noordhuis b04ce2a35c Merge master with resolved conflict in src/redis-cli.c 2010-10-13 18:55:46 +02:00
Pieter Noordhuis 4fe83b554a sockaddr_un.sun_path appears to never hold anything after accept() 2010-10-13 18:50:07 +02:00
Pieter Noordhuis ab17b909fe Use different accept handlers for TCP and unix socket connections 2010-10-13 18:34:24 +02:00
Pieter Noordhuis 893819801d Remove disabling TCP with port -1 2010-10-13 17:18:58 +02:00
Pieter Noordhuis 5d10923f7b Rename variable sockpath to unixsocket 2010-10-13 17:17:56 +02:00
Pieter Noordhuis 704bd093be Move creating socket/bind+listen on socket to separate functions
Thanks to tav (http://github.com/tav) for original code.
2010-10-13 16:47:22 +02:00
Pieter Noordhuis 941c9fa285 Return OK on QUIT 2010-10-13 11:25:40 +02:00
Pieter Noordhuis 9f1ae9abee Allow to specify which specific test files to run 2010-10-13 09:26:44 +02:00
antirez b4f2e412d0 free memory if the maxmemory parameter is reduced via CONFIG SET 2010-10-11 16:46:21 +02:00
antirez 1dd10ca233 maxmemory fixed, we now try to release memory just before we check for the memory limit. Before fixing there was code between the attempt to free memory and the check for memory limits, and this code could result into allocations going again after the memory limit. 2010-10-11 13:05:09 +02:00
antirez 144a5e72f2 fixed an alignment problem with time_t is 32 bit, long is 64 bit, and arch is sparc or any other where unaligned accesses will result to sigbus 2010-10-07 16:21:35 +02:00
antirez fdc0bde935 minor typo fixed, reported by Thomas Bassetto 2010-10-07 12:49:14 +02:00
antirez 4610b0332c intset stress testing added, ziplist stress testing relocated in a more appropriate place 2010-09-24 11:15:06 +02:00
antirez ef27ba988b explicit regression test for a ziplist bug added 2010-09-24 10:37:00 +02:00
antirez 1a06bf93c4 ziplist implementation fuzzy tests 2010-09-24 10:30:15 +02:00
antirez e43505d6e9 Merge remote branch 'pietern/ziplist-fix' 2010-09-24 01:15:16 +02:00
Pieter Noordhuis b0d605c1d6 Add regression test and fix for >255 byte string entries 2010-09-23 22:04:19 +02:00
antirez 30d31cc8bb Contributing file added 2010-09-23 18:24:47 +02:00
antirez 963238f713 more tests for sds.c 2010-09-23 16:39:02 +02:00
antirez 136cf53f22 minimal C test framework + a first example sds.c tests 2010-09-23 16:05:17 +02:00
Pieter Noordhuis 56e52b69fe Update rdb.c to properly work with new memory strategy for sorted sets 2010-09-22 18:07:52 +02:00
antirez 50a9fad5d5 two leaks fixed 2010-09-22 17:49:04 +02:00
antirez beb7756dcb error generation format reverted to the new style after merge 2010-09-22 16:10:13 +02:00
antirez b882056c93 Merge remote branch 'pietern/zset-mem' 2010-09-22 16:09:33 +02:00
antirez 5ca2f0c498 preventive conflict resolution to merge pietern/zset-mem 2010-09-22 16:09:30 +02:00
antirez 5171777bf1 Merge remote branch 'pietern/ziplist-eff' 2010-09-22 12:59:25 +02:00
antirez a4f3f93b90 new parsing code bugfixing 2010-09-17 16:05:01 +02:00
antirez 34a719d250 try to parse the request in a smarter way to gain speed... work in progress 2010-09-17 15:26:07 +02:00
Pieter Noordhuis d433ebc681 Finished code for sorted set memory efficiency 2010-09-16 15:42:36 +02:00
Pieter Noordhuis 25bb8a4452 Add ZREVRANGEBYSCORE and refactor Z*RANGEBYSCORE 2010-09-16 14:38:07 +02:00
Pieter Noordhuis 192fc3376a Merge branch 'zset-mem' into zrevrangebyscore 2010-09-16 14:32:30 +02:00
antirez 2b00385d51 Added used CPU statistics in INFO output, obtained via getrusage() 2010-09-16 13:28:58 +02:00
antirez 4c2e506a39 modified a bit addReply() to play better with copy on write now that we have a static buffer. Changed the name of a function from _ensureFileEvent() to _installWriteEvent(). 2010-09-16 13:08:40 +02:00
antirez 83f39c7ab2 Merge remote branch 'pietern/networking-perf' 2010-09-16 12:02:18 +02:00
Pieter Noordhuis f335779240 Static buffer in client struct has a constant size 2010-09-16 11:59:53 +02:00
antirez 89f9f83769 Merge remote branch 'pietern/networking-perf' 2010-09-16 11:38:40 +02:00
antirez 3856f14759 This should fix Issue 332: when there is a background process saving we still allow the hash tables to grow, but only when a critical treshold is reached. Formerly we prevented the resize at all triggering pathological O(N) behavior. Also there is a fix for the statistics in INFO about the number of keys expired 2010-09-15 14:09:41 +02:00
antirez 412e457c27 fixed typo in the latest commit 2010-09-14 15:18:18 +02:00
antirez 1d18f50458 Advertise the existence of redis-check dump --fix when logging an error about corrupted AOF file 2010-09-14 15:09:37 +02:00
Pedro Melo 0997b4119d Fixed missed use of INSTALL_TOP
Thanks to sylr@github

Signed-off-by: Pedro Melo <melo@simplicidade.org>
2010-09-13 16:50:57 +01:00
Pedro Melo e984050fb9 Make sure INSTALL_TOP exists before we install to it
Signed-off-by: Pedro Melo <melo@simplicidade.org>
2010-09-13 16:11:55 +01:00
Pedro Melo e13865033d Rename INSTALL_TOP to PREFIX; update documentation
Signed-off-by: Pedro Melo <melo@simplicidade.org>
2010-09-13 16:09:11 +01:00
Pieter Noordhuis 3c23ee1ba2 Fix another test that sometimes returned the swapped object instead of encoding 2010-09-13 16:59:46 +02:00
antirez bc63407be6 redis-cli does no longer try to auto detect if it is used inside a pipe. To read last argument from stdandard input there is to use the -x option. This will make it playing better inside cron scripts and in general when stdin is hacked. 2010-09-09 16:38:10 +02:00
antirez 7f9a4db3c0 Fix for the init script provided with Redis, thanks to Rowan. This fixes issue 316 2010-09-09 10:24:56 +02:00
antirez da14590bd9 Fix re-enabled again, I forgot to check if VM was enabled before calling handleClientsBlockedOnSwappedKey() 2010-09-08 13:47:28 +02:00
antirez 155fb4b45e latest fix reverted, there is some problem reported by the CI test 2010-09-08 13:45:51 +02:00
antirez 7f00cd2264 Fixed a race condition in VM happening when a key was deleted while there was a client waiting for this key to be resumed from swap to memory. The client would hang forever. 2010-09-08 13:26:16 +02:00
Pieter Noordhuis efc5d4cc0d Fix test that sometimes returned the swapped object instead of encoding 2010-09-07 11:49:33 +02:00
Pieter Noordhuis 106bd87a3c Fix bug where the client is not present in server.clients when free'ing it
When creating the readable event results in an error (this happens when
the server hits OS limits), the client was not added to the list of
clients when freeClient was called. This results in an assertion error.
It is better to check this condition first and free the client
immediately when this condition occurs. Port of 00a90feb.
2010-09-07 10:25:34 +02:00
Pieter Noordhuis 84403fe7c1 Allow a random seed argument for the ziplist test binary 2010-09-07 00:08:42 +02:00
Pieter Noordhuis 169d2ef1e0 Fix updating the prevlen field of consecutive entries
In the condition where the prevlen field of the next entry on insert
and delete operations needs more bytes to be properly encoded, the next
entry also needs to be updated with a new prevlen. This patch makes sure
that this effect cascades throughout the ziplist.
2010-09-07 00:04:57 +02:00
antirez abe18d0e00 Fix for solaris compilation bug Issue 325 2010-09-06 10:12:44 +02:00
Pieter Noordhuis 9e83ac06ef Merge branch 'master' into networking-perf
Resolved conflict in src/db.c and changed adding an error to the reply
in blockingPopGenericCommand to use the new API.
2010-09-03 16:44:50 +02:00
antirez 556bdfbab9 added some comment and changed coding style for fix for 237 2010-09-03 10:24:18 +02:00
Anko painting b435f64510 fix for issue 237 2010-09-02 21:13:27 -07:00
Pieter Noordhuis 49128f0b9d Fix bug in gluing a deferred multi bulk length to the next reply chunk 2010-09-02 23:34:41 +02:00
Pieter Noordhuis 3ab203762f Use specialized function to add status and error replies 2010-09-02 23:33:06 +02:00
Pieter Noordhuis 60361e5aac Add sds function that can be called with va_list 2010-09-02 21:00:15 +02:00
Pieter Noordhuis 36c19d03e0 Changed reply buildup internals 2010-09-02 19:52:04 +02:00
Pieter Noordhuis 4a7893ca9c Removed unneeded function 2010-09-02 19:52:04 +02:00
Pieter Noordhuis b70d355521 Use existing reply functions where possible 2010-09-02 19:52:04 +02:00
Pieter Noordhuis cd76bb651d Free the sds in addReplySds when it cannot be added to the reply 2010-09-02 19:52:04 +02:00
Pieter Noordhuis 2403fc9fde Intialize bufpos in the fake client 2010-09-02 19:52:04 +02:00
Pieter Noordhuis 0537e7bf80 Use specialized function to add multi bulk reply length 2010-09-02 12:51:14 +02:00
antirez 73db2acc37 memory fragmentation reporting in INFO also added for Mac OS X 2010-09-02 10:57:58 +02:00
antirez eddb388ef9 memory fragmentation ratio in INFO output 2010-09-02 10:34:39 +02:00
antirez a047bf52a4 fixed a few harmless warnings complining on Linux 2010-09-01 18:31:30 +02:00
antirez dbebd395eb Version is now 2.1.4 -- AKA 2.2-alpha1 2010-08-31 18:34:34 +02:00
antirez ec96ef47a6 Merge remote branch 'pietern/issue-300' 2010-08-31 17:39:06 +02:00
Pieter Noordhuis f85202c3dc Fix compilation errors and add warning for 32-bit platforms 2010-08-31 13:06:26 +02:00
antirez d320764706 We finally have an half decent README! (Issue 277) 2010-08-31 11:42:52 +02:00
antirez f7f12a606c resolved conflict merging pietern/bpop-timeout 2010-08-31 11:23:12 +02:00
antirez fc09b9f449 Merge remote branch 'pietern/issue-300' 2010-08-31 11:19:11 +02:00
antirez 08f55b786b faster server starting in Redis tests 2010-08-31 11:17:06 +02:00
Pieter Noordhuis 7b30cc3a7b Fix issue 300 by upgrading variable types to 64-bit 2010-08-31 10:21:35 +02:00
Pieter Noordhuis 1eb13e4913 Fix set tests to make sets have a deterministic encoding 2010-08-31 09:37:35 +02:00
Pieter Noordhuis 57b0738011 Don't build a reply when replaying the AOF 2010-08-30 16:51:39 +02:00
Pieter Noordhuis b301c1fc2b Wrapper for adding unknown multi bulk length to reply list 2010-08-30 16:39:14 +02:00
Pieter Noordhuis 834ef78e27 Refactor reply buildup for speed on large multi bulk replies 2010-08-30 16:39:08 +02:00
antirez fb92ecece7 BLPOP inside MULTI/EXEC block no longer crashes, instead if the list is empty the behavior is like if the timeout is reached. This fixes Issue 285 2010-08-30 16:31:03 +02:00
antirez 8079656a8e Now redis-cli replies to help showing some basic usage information (Issue 291) 2010-08-30 15:57:03 +02:00
antirez 93b2a7718e It is now possible to use authentication and DB selection options at the same time in redis-cli (Issue 298) 2010-08-30 15:36:13 +02:00
antirez e0e1c19520 Fixed MONITOR mode and Issue 296 2010-08-30 11:51:45 +02:00
antirez 8fedd04dcc Makefile deps updated 2010-08-30 11:37:17 +02:00
Pieter Noordhuis ed0dd55402 Show the current throughput while benchmarking 2010-08-30 11:25:02 +02:00
Pieter Noordhuis 36babc1e31 Refactor reply parsing code in redis-benchmark for efficiency 2010-08-30 11:14:54 +02:00
antirez 2f6b31c3bb Fix for a race in BGSAVE that may result in some data not being saved as soon as possible (when the configured saving triggers should fire). Also known as Issue 313, more details there in the google code issue. 2010-08-30 10:32:32 +02:00
antirez e5f257c2b2 fix for the prev fix 2010-08-27 17:06:36 +02:00
antirez c1ae36aea8 Fix for bug 312, yet to verify in a couple of minutes... 2010-08-27 17:04:26 +02:00
antirez 09252fc4f3 Fixed another instace of the Issue 173 2010-08-27 12:46:10 +02:00
antirez 357d36733d Fixed segfault in freeMemoryIfNeeded due to the fact that keys are now sds strings and not objects in the main hash table, thanks to Anthony Lauzon for spotting the bug and providing a patch. 2010-08-27 11:01:03 +02:00
antirez 2df84b7269 intset loading bug fixed 2010-08-26 19:10:40 +02:00
antirez ec7e138926 test for intset integer encodability test and some small refactoring 2010-08-26 18:47:03 +02:00
antirez 23c64fe50d translated a few long logn into int64_t for correctness and to avoid compilation warnings as well 2010-08-26 18:11:26 +02:00
antirez 588cd980e9 redis-cli tests commented out since there is a problem with the CI server and this tests, will be activated again once the problem is fixed 2010-08-26 17:52:02 +02:00
antirez 452ccf7a41 SORT stress testing against bigger aggregate values 2010-08-26 17:29:13 +02:00
antirez e4ecc93119 Version is now 2.1.3 2010-08-26 16:58:02 +02:00
antirez 4e0d34089a Merge remote branch 'pietern/redis-cli-fix' 2010-08-26 16:19:06 +02:00
Pieter Noordhuis 94364d53b4 Verify that the blocking pop timeout value is a non-negative integer 2010-08-26 14:05:14 +02:00
antirez e59a64b8d3 forgot the traceleaks var in tests set to 1, reverted to the default, 0 2010-08-26 13:18:56 +02:00
Pieter Noordhuis 2929ca9786 Fix parenthesis error on decrementing *argc 2010-08-26 13:18:44 +02:00
antirez bad7d097e9 fixed a memory leak in the new Set code 2010-08-26 13:18:24 +02:00
antirez 5f19e8a4a5 computeObjectSwappability is now able to compute swappability for intset encoded sets 2010-08-26 12:28:53 +02:00
Pieter Noordhuis 740eee1cc6 Fix type that was not renamed and compiler warning 2010-08-26 12:13:51 +02:00
antirez 674492bceb removed a duplicated ERRNO checking that is useless at all 2010-08-26 12:10:16 +02:00
antirez acc75bfd4f Merge remote branch 'pietern/intset-split' 2010-08-26 12:04:24 +02:00
Pieter Noordhuis f9d5c4e33c Make the function intsetUpgrade self-contained 2010-08-26 11:22:58 +02:00
Pieter Noordhuis 76864d5626 Expand macro's to functions for readability 2010-08-26 11:06:30 +02:00
Pieter Noordhuis b4b62c34db Use fstat to detect if stdin was redirected 2010-08-25 14:48:50 +02:00
Pieter Noordhuis f791d66e20 Make helper functions simpler 2010-08-25 14:15:41 +02:00
Pieter Noordhuis f9b252613b Comments in redis-cli tests 2010-08-25 14:08:32 +02:00
Pieter Noordhuis ae77016e57 Add a newline to tty output after every reply 2010-08-25 13:39:11 +02:00
Pieter Noordhuis 5d15b5207d Re-introduce the interactive field so we can reconnect in interactive mode 2010-08-25 13:09:22 +02:00
Pieter Noordhuis 4b93e5e267 Merge master and move argument splitting patch to sds.c 2010-08-25 13:08:43 +02:00
antirez c0b3d42372 redis-cli now supports automatically reconnection in interactive mode 2010-08-24 18:39:34 +02:00
antirez b37ca6edb1 Issue 179 fixed, now redis-cli is able to parse correctly multi bulk replies with elements that are errors 2010-08-24 18:08:09 +02:00
antirez 695fe87456 The pid file is now created only after the server is correctly initialied. It is also removed on sigterm and when the stack trace is produced after a sigbus or a sigsegv. This two changes should fix the Issue 175 2010-08-24 17:09:25 +02:00
antirez b91d605a35 slave now detect lost connection during SYNC, fixing Issue 173 2010-08-24 16:25:00 +02:00
antirez 778b2210a9 slave with attached slaves now close the conection to all the slaves when the connection to the master is lost. Now a slave without a connected link to the master will refuse SYNC from other slaves. Enhanced the replication error reporting. All this will fix Issue 156 2010-08-24 16:04:13 +02:00
antirez e452436a07 BLPOPping clients are no longer subject to connection timeouts, fixing issues 155 2010-08-24 12:10:59 +02:00
antirez 01daeecee7 added tests for invalid bulk argument 2010-08-24 11:49:05 +02:00
antirez a679185aa5 sanity check for the bulk argument in protocol parsing code, fixing issue 146 2010-08-24 11:45:05 +02:00
antirez e193873025 changed the comments on top of redis-copy.rb to reflect what the program really does 2010-08-24 10:10:01 +02:00
antirez c91abdcd07 Fixed overflow detection in argument to long convertion function in general, and in expire/ttl pairs specifically, addressing issue 54 2010-08-23 17:06:38 +02:00
Pieter Noordhuis cb72d0f155 Rename iterator to setTypeIterator for consistency 2010-08-21 11:38:24 +02:00
Pieter Noordhuis 029e5577ff Make SORT use the hybrid set accessors to allow sorting intsets 2010-08-21 11:20:41 +02:00
Pieter Noordhuis 2b9a59471f SORT tests with hash table encoded set as input 2010-08-21 11:03:56 +02:00
Pieter Noordhuis ced6709cb9 Make SORT tests use both ziplists and linked lists as input 2010-08-21 11:02:22 +02:00
Pieter Noordhuis 5d4f3a8c85 Move SORT tests around 2010-08-21 10:55:53 +02:00
Pieter Noordhuis a53ebb4c8e Don't abort test suite when the server block has a return value 2010-08-21 10:54:31 +02:00
Pieter Noordhuis 87c74dfaa8 Check if stroll return value was clamped 2010-08-20 13:42:42 +02:00
Pieter Noordhuis aaada3f962 Merge branch 'master' into intset-split
Conflicts:
	src/Makefile
	src/t_set.c
2010-08-20 12:40:55 +02:00
Pieter Noordhuis c470538142 Make ziplist schema more efficient for strings with length > 15 2010-08-13 19:29:22 +02:00
antirez cbce517145 redis cli argument splitting is general and is now moved into the sds.c lib 2010-08-05 11:36:39 +02:00
Pieter Noordhuis abb731e5b8 Deprecate starting interactive mode using the -i flag 2010-08-04 18:36:03 +02:00
Pieter Noordhuis cf0c6b78f1 Set tty before going into interactive mode to get non-pretty output when
the commands are read from stdin.
2010-08-04 18:16:39 +02:00
Pieter Noordhuis 3a51bff035 Change output format for non-tty redis-cli execution 2010-08-04 17:46:56 +02:00
Pieter Noordhuis 123a10f7a5 Let the output mode depend on having a tty or not 2010-08-04 17:16:05 +02:00
Pieter Noordhuis 07242c0ccf Tests for redis-cli in non-interactive mode
Minor change in redis-cli output for the (multi-)bulk response but this
will be fixed in the next commit.
2010-08-04 17:02:13 +02:00
Pieter Noordhuis 0439d792c4 Add tests for quotation in an interactive redis-cli session
Patched redis-cli to abort on unexpected quotation. This caused
redis-cli to get into an infinite, memory-consuming loop.
2010-08-04 16:15:33 +02:00
Pieter Noordhuis f2dd4769dd Tests for the interactive mode of redis-cli
Changed redis-cli to output the raw response for a bulk reply when it is
run in interactive mode instead of checking isatty.
2010-08-04 15:28:03 +02:00
Pieter Noordhuis 69ef89f2cf Reference zset score in zskiplistNode from dict entries
This avoids the extra allocation of sizeof(double) for storing the score
of a zset entry in the hash table. Saves sizeof(double) + malloc
overhead = approx. 16 bytes per zset entry.
2010-08-03 20:49:53 +02:00
Pieter Noordhuis 2159782b51 Use flexible array in zskiplistNode to reduce memory usage 2010-08-03 19:22:09 +02:00
antirez 1fb4e8def7 PERSIST: a fix and some basic test 2010-08-03 14:25:22 +02:00
antirez a539d29ac5 PERSIST command implemented 2010-08-03 14:19:20 +02:00
antirez 6146329f1f replication test with expires 2010-08-03 13:38:39 +02:00
Pieter Noordhuis a5639e7dd9 Change initialization to allow listening on both a port and socket 2010-08-03 13:33:12 +02:00
antirez 23d7298b15 Merge remote branch 'pietern/test-encoding-fix' into writeonexpire 2010-08-03 13:09:27 +02:00
antirez 2c572622fb no longer passing tests due to the new write-on-volatile semantics modified/removed 2010-08-03 13:08:32 +02:00
antirez 0cf5b7b57c allow to set a new EXPIRE of an existing volatile key 2010-08-03 12:26:30 +02:00
antirez c25a5d3b10 memory leak removed from expire propagation code 2010-08-02 21:37:39 +02:00
antirez bcf2995c98 support for write operations against expiring keys, by master-controlled expiring in replication and AOF synthesizing DEL operations 2010-08-02 18:13:39 +02:00
Pieter Noordhuis 7e91f971f7 Add support for domain sockets to redis-cli 2010-08-01 23:06:00 +02:00
Pieter Noordhuis c61e69257a Support for Redis to listen on a Unix socket 2010-08-01 22:55:24 +02:00
Pieter Noordhuis 6825491928 Fix assertion function on value encoding 2010-08-01 11:20:26 +02:00
Pieter Noordhuis 2767f1c0c6 fix aof and digest code to work with dual set encoding 2010-07-02 20:42:20 +02:00
Pieter Noordhuis 96ffb2fe97 merged intset code into the split files 2010-07-02 19:57:12 +02:00
Pieter Noordhuis 400aea2b13 expand the dictionary of the target set to the right size when converting from intset 2010-06-13 21:52:07 +02:00
Pieter Noordhuis 273f616930 make sure sets have the right encoding when loaded from rdb 2010-06-13 21:42:04 +02:00
Pieter Noordhuis ab37269c38 use max number of intset entries in tests and make SUNION/SINTER/SDIFF tests use less entries 2010-06-13 21:37:46 +02:00
Pieter Noordhuis 70ff3511bc configure maximum number of entries in an intset 2010-06-13 15:21:25 +02:00
Pieter Noordhuis b978abbf02 small refactor of SMOVE and tests for SMOVE on sets with different encoding 2010-06-13 11:16:18 +02:00
Pieter Noordhuis d0b58d5300 intset encoding for sets, refactored set tests to test both encodings 2010-06-12 22:25:22 +02:00
Pieter Noordhuis e24d93762f intset housekeeping 2010-06-11 19:22:27 +02:00
Pieter Noordhuis 35cabcb505 wrapper functions for the set type to support multiple encodings 2010-06-11 18:35:57 +02:00
Pieter Noordhuis 3ab98cef4e move logic for obvious corner cases to intsetSearch 2010-06-11 15:07:24 +02:00
Pieter Noordhuis 144b0094c3 initial implementation for the intset 2010-06-11 15:07:24 +02:00
249 changed files with 43027 additions and 3597 deletions
+4 -1
View File
@@ -1,4 +1,3 @@
00-RELEASENOTES
*.o
*.rdb
*.log
@@ -16,3 +15,7 @@ src/release.h
appendonly.aof
SHORT_TERM_TODO
redis.conf.*
release.h
src/transfer.sh
src/configs
src/redis-server.dSYM
+139
View File
@@ -0,0 +1,139 @@
Redis 2.2 release notes
Migrating from 2.0 to 2.2
=========================
Redis 2.0 is mostly a strict subset of 2.2. Some return value changed in edge
cases, basicaly it is very unlikely that you will experience any problem
upgrading your 2.0 instances to 2.2, as 2.2 can work as a drop in replacement
for 2.0.
---------
CHANGELOG
---------
What's new in Redis 2.2.5
=========================
* Fixed a crash occurring when loading an AOF containing the SPOP command.
What's new in Redis 2.2.4
=========================
* Return value of OBJECT DEBUG against sorted sets fixed, now is "skiplist".
What's new in Redis 2.2.3
=========================
* Fixed issue #503. MONITOR + QUIT (and other combinations) could crash
the server.
* OBJECT command implemented. See http://redis.io/commands/object
* Fixed a problem in redis-cli related to escapes in the form "\x..".
* Fixed a minor memory leak in redis-cli
* Saved RDB on SIGTERM on archs where it was not working properly.
What's new in Redis 2.2.2
=========================
Redis 2.2.2 is a bugfix release. Changelog:
* AOF file descriptor leak after the first rewrite fixed.
* Return value of GETRANGE / SUBSTR is now an empty string if the interval
specified is not valid, or the key does not exist. Was a null bulk reply
before, but this is against the Redis way.
What's new in Redis 2.2.1
=========================
Redis 2.2.1 is a bugfix release. Changelog:
* Fixed an SPOP crash. When using SPOP in a MULTI/EXEC block there was a problem
introduced in the latest release when fixing an SPOP replication/AOF related
bug.
What's new in Redis 2.2.0 final
===============================
Redis 2.2.0 final is both a bug fix and minor enhancement release:
* SPOP is now replicated correctly in AOF and across slaves.
* CONFIG SET/GET for all the special encoding parameters of sets, lists, hashes.
* Now BRPOPLPUSH will reply with single null bulk on timeout.
* Specifying port 0 in redis.conf will tell Redis to don't listen on TCP socket.
* Propagate key eviction to slaves and AOF, similarly to expires.
What's new in Redis 2.1.10 (2.2 Release Candidate 4)
====================================================
Redis 2.2 RC4 is a bug fix and minor enhancement release:
* Fixed timeout error in replication where master took a big time to BGSAVE.
* Introduced explicit PING between master and slave, to reliably detect when
the link is down, even if the socket remains apparently connected.
* Fixed compilation on FreeBSD.
* Removed a small portability issue in redis-benchmark.
What's new in Redis 2.1.10 (2.2 Release Candidate 3)
====================================================
Redis 2.2 RC3 is a bug fix and minor enhancements release:
* Solaris fixes
* Fixes and improvements for redis-benchmark
* New INFO field with memory allocation details
* New INFO fields with info about clients max input/output buffer
* Replication: KEYS fixed in slaves
* Different default thresholds for Hash type memory saving encodings
What's new in Redis 2.1.10 (2.2 Release Candidate 2)
====================================================
Redis 2.2 RC2 is exactly like RC1 with the following minor changes:
* Added evicted keys counter separated from expired keys.
* Overflow detection in INCR family functions.
Enjoy,
Salvatore
What's new in Redis 2.1.9 (2.2 Release Candidate 1)
===================================================
This is the first Release Candidate of Redis 2.2, in our experience the
server is very stable, but in the latest weeks we rewrote part of the internals
in order to use a lot less memory while the saving child process is performing
a BGREWRITEAOF or a BGSAVE, so handle with care for a couple of weeks.
Oh, and I've some very good news: the majority of apps can work if you simply replace 2.2 in your old 2.0 environment. I can't think of any breakage.
WHAT'S NEW IN REDIS 2.2 compared to the 2.0 version?
====================================================
* Specially encoded data types, small lists and sets can now use up to an order of magnitude less memory.
* VM partial rewrite for code cleaness and memory usage.
* Change to the implementation of the top level dictionary for better memory efficienty.
* redis-cli is hugely improved: tab completion, inline help (Thanks to TJ Holowaychuk), raw output, rewritten using the new hiredis C library.
* Networking internals rewritten for efficiency. You can expect LRANGE and similar commands to be at least 10 times faster.
* Most read only commands are now copy-on-write friendly, this means that Redis will use little memory when a saving child is active and the parent process is mostly stressed by read queries.
* Non blocking replication even from the point of view of the slave, with configurable behavior about what to do when the link is disconnected. You can select if serving old data or replying with an error.
* Check-and-set (CAS) transactions with the new WATCH command.
* Now write operations work against keys with an EXPIRE set! Imagine the possibilities.
* New maxmemory eviction policies. It is possible to select among LRU, farest TTL expire, and other algorithms, and if when the memory limit is reached only keys with an expire set or all the keys should be expired.
* SETBIT / GETBIT / SETRANGE / GETRANGE / STRLEN. Now your strings are your arrays!
* Syslog support (Thanks to Jonah H. Harris)
* Unix domain socket support.
* New List related functions LINSERT, LPUSHX, RPUSHX (Thanks to Robey Pointer)
* BRPOPLPUSH (Thanks to Michel Martens and Damian Janowski)
* Much more interesting informations in the INFO output.
* Sorted sets are now less memory hungry.
* Non blocking loading of .rdb / AOF file on startup, with progress information in the INFO output.
* Now Redis has a clean, powerful, supported C library: hiredis.
* Code layout completely new, the 2.0.x huge redis.c file is now splitted in many parts.
* Redis-benchmark rewritten to be faster and in order to use hiredis as well.
* Ability to rename or disable commands from the config file.
* Endless other CPU optimizations and bugs fixed.
Credits: Where not specified the implementation and design are done by Salvatore Sanfilippo and Pieter Noordhuis. Thanks to VMware for making all this possible. Also many thanks to all the other contributors and the amazing community we have.
Cheers,
Salvatore
+13
View File
@@ -0,0 +1,13 @@
1. Enter irc.freenode.org #redis and start talking with 'antirez' and/or 'pietern' to check if there is interest for such a feature and to understand the probability of it being merged. We'll try hard to keep Redis simple... so you'll likely encounter an high resistence.
2. Drop a message to the Redis Google Group with a proposal of semantics/API.
3. If steps 1 and 2 are ok, use the following procedure to submit a patch:
a. Fork Redis on github
b. Create a topic branch (git checkout -b my_branch)
c. Push to your branch (git push origin my_branch)
d. Create an issue in the Redis google code site with a link to your patch
e. Done :)
Thanks!
+12 -1
View File
@@ -3,7 +3,18 @@ To compile Redis, do the following:
cd src; make
The compilation will produce a redis-server binary.
Copy this file where you want.
To install Redis, use
make install
and all the binaries will be installed on /usr/local/bin.
Alternatively:
make PREFIX=/some/other/directory
to have the binaries in /some/other/directory/bin.
Run the server using the following command line:
+10 -1
View File
@@ -8,7 +8,16 @@ all:
install: dummy
cd src && $(MAKE) $@
$(TARGETS) clean:
clean:
cd src && $(MAKE) $@
cd deps/hiredis && $(MAKE) $@
cd deps/linenoise && $(MAKE) $@
cd deps/lua && $(MAKE) $@
$(TARGETS):
cd src && $(MAKE) $@
src/help.h:
@./utils/generate-command-help.rb > $@
dummy:
+83 -1
View File
@@ -1 +1,83 @@
Check the 'doc' directory. doc/README.html is a good starting point :)
Where to find complete Redis documentation?
-------------------------------------------
This README is just a fast "quick start" document. You can find more detailed
documentation at http://redis.io
Building Redis
--------------
It is as simple as:
% make
Redis is just a single binary, but if you want to install it you can use
the "make install" target that will copy the binary in /usr/local/bin
for default. You can also use "make PREFIX=/some/other/directory install"
if you wish to use a different destination.
You can run a 32 bit Redis binary using:
% make 32bit
After building Redis is a good idea to test it, using:
% make test
Buliding using tcmalloc
-----------------------
tcmalloc is a fast and space efficient implementation (for little objects)
of malloc(). Compiling Redis with it can improve performances and memeory
usage. You can read more about it here:
http://goog-perftools.sourceforge.net/doc/tcmalloc.html
In order to compile Redis with tcmalloc support install tcmalloc on your system
and then use:
% make USE_TCMALLOC=yes
Note that you can pass any other target to make, as long as you append
USE_TCMALLOC=yes at the end.
Running Redis
-------------
To run Redis with the default configuration just type:
% cd src
% ./redis-server
If you want to provide your redis.conf, you have to run it using an additional
parameter (the path of the configuration file):
% cd src
% ./redis-server /path/to/redis.conf
Playing with Redis
------------------
You can use redis-cli to play with Redis. Start a redis-server instance,
then in another terminal try the following:
% cd src
% ./redis-cli
redis> ping
PONG
redis> set foo bar
OK
redis> get foo
"bar"
redis> incr mycounter
(integer) 1
redis> incr mycounter
(integer) 2
redis>
You can find the list of all the available commands here:
http://redis.io/commands
Enjoy!
+3 -65
View File
@@ -1,66 +1,4 @@
Redis TODO and Roadmap
----------------------
This is a stable release! No TODO file here.
Please check the TODO file in the master branch on github.
VERSION 2.2 TODO (Optimizations and latency)
============================================
* Support for syslog(3).
* Change the implementation of ZCOUNT to use the augmented skiplist in order to be much faster.
* Add an explicit test for MULTI/EXEC reloaded in the AOF.
* Command table -> hash table, with support for command renaming
VM TODO
=======
* Use multiple open FDs against the VM file, one for thread.
* Check what happens performance-wise if instead of creating threads again and again the same threads are reused forever. Note: this requires a way to disable this clients in the child, but waiting for empty new jobs queue can be enough.
STRING COMMANDS
===============
* Implement STRLEN, PEEK, POKE, SETBIT, GETBIT
OTHER IMPORTANT THINGS THAT WILL BE ADDED BUT I'M NOT SURE WHEN
===============================================================
BIG ONES:
* BRPOPLPUSH
* Specially encoded memory-saving integer sets.
* A command to export a JSON dump (there should be mostly working patch needing major reworking).
* Specially encoded sets of integers (this includes a big refactoring providing an higher level layer for Sets manipulation)
SMALL ONES:
* If sizeof(double) == sizeof(void*) we could store the double value of sorted sets directly in place of the pointer instead of allocating it in the heap.
* Delete on writes against expire policy should only happen after argument parsing for commands doing their own arg parsing stuff.
* Give errors when incrementing a key that does not look like an integer, when providing as a sorted set score something can't be parsed as a double, and so forth.
* MSADD (n keys) (n values). See this thread in the Redis google group: http://groups.google.com/group/redis-db/browse_thread/thread/e766d84eb375cd41
* Don't save empty lists / sets / zsets on disk with snapshotting.
* Remove keys when a list / set / zset reaches length of 0.
* An option to exec a command slave-side if the master connection is lost: even cooler: if the script returns "0" the slave elects itself as master, otherwise continue trying to reconnect.
* PING the master from time to time to check if it's gone.
THE "MAYBE" TODO LIST: things that may or may not get implemented
=================================================================
Most of this can be seen just as proposals, the fact they are in this list
it's not a guarantee they'll ever get implemented ;)
* SORT: Don't copy the list into a vector when BY argument is constant.
* Write the hash table size of every db in the dump, so that Redis can resize the hash table just one time when loading a big DB.
* Byte Array type (BA prefixed commands): BASETBIT BAGETBIT BASETU8 U16 U32 U64 S8 S16 S32 S64, ability to atomically INCRBY all the base types. BARANGE to get a range of bytes as a bulk value, BASETRANGE to set a range of bytes.
* Read-only mode.
* Kill the delete-on-write behavior of expires, replicating DELs
* Multiple BY in SORT.
KNOWN BUGS
==========
* LRANGE and other commands are using 32 bit integers for ranges, and overflows are not detected. So LRANGE mylist 0 23498204823094823904823904 will have random effects.
REDIS CLI TODO
==============
* Computer parsable output generation
* Memoize return values so that they can be used later as arguments, like $1
https://github.com/antirez/redis/raw/master/TODO
+1 -1
View File
@@ -1,6 +1,6 @@
An updated list of client libraries for Redis can be found here:
http://code.google.com/p/redis
http://redis.io/clients
All the links are in the front page.
+6
View File
@@ -0,0 +1,6 @@
/hiredis-test
/hiredis-example*
/*.o
/*.so
/*.dylib
/*.a
+10
View File
@@ -0,0 +1,10 @@
Copyright (c) 2006-2009, Salvatore Sanfilippo
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+112
View File
@@ -0,0 +1,112 @@
# Hiredis Makefile
# Copyright (C) 2010 Salvatore Sanfilippo <antirez at gmail dot com>
# This file is released under the BSD license, see the COPYING file
OBJ = net.o hiredis.o sds.o async.o
BINS = hiredis-example hiredis-test
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
OPTIMIZATION?=-O3
ifeq ($(uname_S),SunOS)
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -D__EXTENSIONS__ -D_XPG6 $(ARCH) $(PROF)
CCLINK?=-ldl -lnsl -lsocket -lm -lpthread
LDFLAGS?=-L. -Wl,-R,.
DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=$(CC) -G -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
else ifeq ($(uname_S),Darwin)
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L. -Wl,-rpath,.
OBJARCH?=-arch i386 -arch x86_64
DYLIBNAME?=libhiredis.dylib
DYLIB_MAKE_CMD?=libtool -dynamic -o ${DYLIBNAME} -lm ${DEBUG} - ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=libtool -static -o ${STLIBNAME} - ${OBJ}
else
CFLAGS?=-std=c99 -pedantic $(OPTIMIZATION) -fPIC -Wall -W -Wwrite-strings $(ARCH) $(PROF)
CCLINK?=-lm -pthread
LDFLAGS?=-L. -Wl,-rpath,.
DYLIBNAME?=libhiredis.so
DYLIB_MAKE_CMD?=gcc -shared -Wl,-soname,${DYLIBNAME} -o ${DYLIBNAME} ${OBJ}
STLIBNAME?=libhiredis.a
STLIB_MAKE_CMD?=ar rcs ${STLIBNAME} ${OBJ}
endif
CCOPT= $(CFLAGS) $(CCLINK)
DEBUG?= -g -ggdb
PREFIX?= /usr/local
INSTALL_INC= $(PREFIX)/include/hiredis
INSTALL_LIB= $(PREFIX)/lib
INSTALL= cp -a
all: ${DYLIBNAME} ${BINS}
# Deps (use make dep to generate this)
net.o: net.c fmacros.h net.h
async.o: async.c async.h hiredis.h sds.h util.h
example.o: example.c hiredis.h
hiredis.o: hiredis.c hiredis.h net.h sds.h util.h
sds.o: sds.c sds.h
test.o: test.c hiredis.h
${DYLIBNAME}: ${OBJ}
${DYLIB_MAKE_CMD}
${STLIBNAME}: ${OBJ}
${STLIB_MAKE_CMD}
dynamic: ${DYLIBNAME}
static: ${STLIBNAME}
# Binaries:
hiredis-example-libevent: example-libevent.c adapters/libevent.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -levent example-libevent.c
hiredis-example-libev: example-libev.c adapters/libev.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis -lev example-libev.c
ifndef AE_DIR
hiredis-example-ae:
@echo "Please specify AE_DIR (e.g. <redis repository>/src)"
@false
else
hiredis-example-ae: example-ae.c adapters/ae.h ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) -I$(AE_DIR) $(LDFLAGS) -lhiredis example-ae.c $(AE_DIR)/ae.o $(AE_DIR)/zmalloc.o
endif
hiredis-%: %.o ${DYLIBNAME}
$(CC) -o $@ $(CCOPT) $(DEBUG) $(LDFLAGS) -lhiredis $<
test: hiredis-test
./hiredis-test
.c.o:
$(CC) -c $(CFLAGS) $(OBJARCH) $(DEBUG) $(COMPILE_TIME) $<
clean:
rm -rf ${DYLIBNAME} ${STLIBNAME} $(BINS) hiredis-example* *.o *.gcda *.gcno *.gcov
dep:
$(CC) -MM *.c
install: ${DYLIBNAME} ${STLIBNAME}
mkdir -p $(INSTALL_INC) $(INSTALL_LIB)
$(INSTALL) hiredis.h async.h adapters $(INSTALL_INC)
$(INSTALL) ${DYLIBNAME} ${STLIBNAME} $(INSTALL_LIB)
32bit:
@echo ""
@echo "WARNING: if it fails under Linux you probably need to install libc6-dev-i386"
@echo ""
$(MAKE) ARCH="-m32"
gprof:
$(MAKE) PROF="-pg"
gcov:
$(MAKE) PROF="-fprofile-arcs -ftest-coverage"
noopt:
$(MAKE) OPTIMIZATION=""
+311
View File
@@ -0,0 +1,311 @@
# HIREDIS
Hiredis is a minimalistic C client library for the [Redis](http://redis.io/) database.
It is minimalistic because it just adds minimal support for the protocol, but
at the same time it uses an high level printf-alike API in order to make it
much higher level than otherwise suggested by its minimal code base and the
lack of explicit bindings for every Redis command.
Apart from supporting sending commands and receiving replies, it comes with
a reply parser that is decoupled from the I/O layer. It
is a stream parser designed for easy reusability, which can for instance be used
in higher level language bindings for efficient reply parsing.
Hiredis only supports the binary-safe Redis protocol, so you can use it with any
Redis version >= 1.2.0.
The library comes with multiple APIs. There is the
*synchronous API*, the *asynchronous API* and the *reply parsing API*.
## UPGRADING
Version 0.9.0 is a major overhaul of hiredis in every aspect. However, upgrading existing
code using hiredis should not be a big pain. The key thing to keep in mind when
upgrading is that hiredis >= 0.9.0 uses a `redisContext*` to keep state, in contrast to
the stateless 0.0.1 that only has a file descriptor to work with.
## Synchronous API
To consume the synchronous API, there are only a few function calls that need to be introduced:
redisContext *redisConnect(const char *ip, int port);
void *redisCommand(redisContext *c, const char *format, ...);
void freeReplyObject(void *reply);
### Connecting
The function `redisConnect` is used to create a so-called `redisContext`. The
context is where Hiredis holds state for a connection. The `redisContext`
struct has an integer `err` field that is non-zero when an the connection is in
an error state. The field `errstr` will contain a string with a description of
the error. More information on errors can be found in the **Errors** section.
After trying to connect to Redis using `redisConnect` you should
check the `err` field to see if establishing the connection was successful:
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
### Sending commands
There are several ways to issue commands to Redis. The first that will be introduced is
`redisCommand`. This function takes a format similar to printf. In the simplest form,
it is used like this:
reply = redisCommand(context, "SET foo bar");
The specifier `%s` interpolates a string in the command, and uses `strlen` to
determine the length of the string:
reply = redisCommand(context, "SET foo %s", value);
When you need to pass binary safe strings in a command, the `%b` specifier can be
used. Together with a pointer to the string, it requires a `size_t` length argument
of the string:
reply = redisCommand(context, "SET foo %b", value, valuelen);
Internally, Hiredis splits the command in different arguments and will
convert it to the protocol used to communicate with Redis.
One or more spaces separates arguments, so you can use the specifiers
anywhere in an argument:
reply = redisCommand("SET key:%s %s", myid, value);
### Using replies
The return value of `redisCommand` holds a reply when the command was
successfully executed. When an error occurs, the return value is `NULL` and
the `err` field in the context will be set (see section on **Errors**).
Once an error is returned the context cannot be reused and you should set up
a new connection.
The standard replies that `redisCommand` are of the type `redisReply`. The
`type` field in the `redisReply` should be used to test what kind of reply
was received:
* **`REDIS_REPLY_STATUS`**:
* The command replied with a status reply. The status string can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`.
* **`REDIS_REPLY_ERROR`**:
* The command replied with an error. The error string can be accessed identical to `REDIS_REPLY_STATUS`.
* **`REDIS_REPLY_INTEGER`**:
* The command replied with an integer. The integer value can be accessed using the
`reply->integer` field of type `long long`.
* **`REDIS_REPLY_NIL`**:
* The command replied with a **nil** object. There is no data to access.
* **`REDIS_REPLY_STRING`**:
* A bulk (string) reply. The value of the reply can be accessed using `reply->str`.
The length of this string can be accessed using `reply->len`.
* **`REDIS_REPLY_ARRAY`**:
* A multi bulk reply. The number of elements in the multi bulk reply is stored in
`reply->elements`. Every element in the multi bulk reply is a `redisReply` object as well
and can be accessed via `reply->elements[..index..]`.
Redis may reply with nested arrays but this is fully supported.
Replies should be freed using the `freeReplyObject()` function.
Note that this function will take care of freeing sub-replies objects
contained in arrays and nested arrays, so there is no need for the user to
free the sub replies (it is actually harmful and will corrupt the memory).
### Cleaning up
To disconnect and free the context the following function can be used:
void redisFree(redisContext *c);
This function immediately closes the socket and then free's the allocations done in
creating the context.
### Sending commands (cont'd)
Together with `redisCommand`, the function `redisCommandArgv` can be used to issue commands.
It has the following prototype:
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
It takes the number of arguments `argc`, an array of strings `argv` and the lengths of the
arguments `argvlen`. For convenience, `argvlen` may be set to `NULL` and the function will
use `strlen(3)` on every argument to determine its length. Obviously, when any of the arguments
need to be binary safe, the entire array of lengths `argvlen` should be provided.
The return value has the same semantic as `redisCommand`.
### Pipelining
To explain how Hiredis supports pipelining in a blocking connection, there needs to be
understanding of the internal execution flow.
When any of the functions in the `redisCommand` family is called, Hiredis first formats the
command according to the Redis protocol. The formatted command is then put in the output buffer
of the context. This output buffer is dynamic, so it can hold any number of commands.
After the command is put in the output buffer, `redisGetReply` is called. This function has the
following two execution paths:
1. The input buffer is non-empty:
* Try to parse a single reply from the input buffer and return it
* If no reply could be parsed, continue at *2*
2. The input buffer is empty:
* Write the **entire** output buffer to the socket
* Read from the socket until a single reply could be parsed
The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply
is expected on the socket. To pipeline commands, the only things that needs to be done is
filling up the output buffer. For this cause, two commands can be used that are identical
to the `redisCommand` family, apart from not returning a reply:
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
After calling either function one or more times, `redisGetReply` can be used to receive the
subsequent replies. The return value for this function is either `REDIS_OK` or `REDIS_ERR`, where
the latter means an error occurred while reading a reply. Just as with the other commands,
the `err` field in the context can be used to find out what the cause of this error is.
The following examples shows a simple pipeline (resulting in only a single call to `write(2)` and
a single call to `write(2)`):
redisReply *reply;
redisAppendCommand(context,"SET foo bar");
redisAppendCommand(context,"GET foo");
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);
This API can also be used to implement a blocking subscriber:
reply = redisCommand(context,"SUBSCRIBE foo");
freeReplyObject(reply);
while(redisGetReply(context,&reply) == REDIS_OK) {
// consume message
freeReplyObject(reply);
}
### Errors
When a function call is not successful, depending on the function either `NULL` or `REDIS_ERR` is
returned. The `err` field inside the context will be non-zero and set to one of the
following constants:
* **`REDIS_ERR_IO`**:
There was an I/O error while creating the connection, trying to write
to the socket or read from the socket. If you included `errno.h` in your
application, you can use the global `errno` variable to find out what is
wrong.
* **`REDIS_ERR_EOF`**:
The server closed the connection which resulted in an empty read.
* **`REDIS_ERR_PROTOCOL`**:
There was an error while parsing the protocol.
* **`REDIS_ERR_OTHER`**:
Any other error. Currently, it is only used when a specified hostname to connect
to cannot be resolved.
In every case, the `errstr` field in the context will be set to hold a string representation
of the error.
## Asynchronous API
Hiredis comes with an asynchronous API that works easily with any event library.
Examples are bundled that show using Hiredis with [libev](http://software.schmorp.de/pkg/libev.html)
and [libevent](http://monkey.org/~provos/libevent/).
### Connecting
The function `redisAsyncConnect` can be used to establish a non-blocking connection to
Redis. It returns a pointer to the newly created `redisAsyncContext` struct. The `err` field
should be checked after creation to see if there were errors creating the connection.
Because the connection that will be created is non-blocking, the kernel is not able to
instantly return if the specified host and port is able to accept a connection.
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
// handle error
}
The asynchronous context can hold a disconnect callback function that is called when the
connection is disconnected (either because of an error or per user request). This function should
have the following prototype:
void(const redisAsyncContext *c, int status);
On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the
user, or `REDIS_ERR` when the disconnection was caused by an error. When it is `REDIS_ERR`, the `err`
field in the context can be accessed to find out the cause of the error.
The context object is always free'd after the disconnect callback fired. When a reconnect is needed,
the disconnect callback is a good point to do so.
Setting the disconnect callback can only be done once per context. For subsequent calls it will
return `REDIS_ERR`. The function to set the disconnect callback has the following prototype:
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
### Sending commands and their callbacks
In an asynchronous context, commands are automatically pipelined due to the nature of an event loop.
Therefore, unlike the synchronous API, there is only a single way to send commands.
Because commands are sent to Redis asynchronously, issuing a command requires a callback function
that is called when the reply is received. Reply callbacks should have the following prototype:
void(redisAsyncContext *c, void *reply, void *privdata);
The `privdata` argument can be used to curry arbitrary data to the callback from the point where
the command is initially queued for execution.
The functions that can be used to issue commands in an asynchronous context are:
int redisAsyncCommand(
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
const char *format, ...);
int redisAsyncCommandArgv(
redisAsyncContext *ac, redisCallbackFn *fn, void *privdata,
int argc, const char **argv, const size_t *argvlen);
Both functions work like their blocking counterparts. The return value is `REDIS_OK` when the command
was successfully added to the output buffer and `REDIS_ERR` otherwise. Example: when the connection
is being disconnected per user-request, no new commands may be added to the output buffer and `REDIS_ERR` is
returned on calls to the `redisAsyncCommand` family.
If the reply for a command with a `NULL` callback is read, it is immediately free'd. When the callback
for a command is non-`NULL`, it is responsible for cleaning up the reply.
All pending callbacks are called with a `NULL` reply when the context encountered an error.
### Disconnecting
An asynchronous connection can be terminated using:
void redisAsyncDisconnect(redisAsyncContext *ac);
When this function is called, the connection is **not** immediately terminated. Instead, new
commands are no longer accepted and the connection is only terminated when all pending commands
have been written to the socket, their respective replies have been read and their respective
callbacks have been executed. After this, the disconnection callback is executed with the
`REDIS_OK` status and the context object is free'd.
### Hooking it up to event library *X*
There are a few hooks that need to be set on the context object after it is created.
See the `adapters/` directory for bindings to *libev* and *libevent*.
## Reply parsing API
To be done.
## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
Pieter Noordhuis (pcnoordhuis at gmail) and is released under the BSD license.
+2
View File
@@ -0,0 +1,2 @@
- add redisCommandVector()
- add support for pipelining
+95
View File
@@ -0,0 +1,95 @@
#include <sys/types.h>
#include <ae.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisAeEvents {
redisAsyncContext *context;
aeEventLoop *loop;
int fd;
int reading, writing;
} redisAeEvents;
void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleRead(e->context);
}
void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
((void)el); ((void)fd); ((void)mask);
redisAeEvents *e = (redisAeEvents*)privdata;
redisAsyncHandleWrite(e->context);
}
void redisAeAddRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->reading) {
e->reading = 1;
aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
}
}
void redisAeDelRead(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->reading) {
e->reading = 0;
aeDeleteFileEvent(loop,e->fd,AE_READABLE);
}
}
void redisAeAddWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (!e->writing) {
e->writing = 1;
aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
}
}
void redisAeDelWrite(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
aeEventLoop *loop = e->loop;
if (e->writing) {
e->writing = 0;
aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
}
}
void redisAeCleanup(void *privdata) {
redisAeEvents *e = (redisAeEvents*)privdata;
redisAeDelRead(privdata);
redisAeDelWrite(privdata);
free(e);
}
int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisAeEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisAeEvents*)malloc(sizeof(*e));
e->context = ac;
e->loop = loop;
e->fd = c->fd;
e->reading = e->writing = 0;
/* Register functions to start/stop listening for events */
ac->evAddRead = redisAeAddRead;
ac->evDelRead = redisAeDelRead;
ac->evAddWrite = redisAeAddWrite;
ac->evDelWrite = redisAeDelWrite;
ac->evCleanup = redisAeCleanup;
ac->_adapter_data = e;
return REDIS_OK;
}
+113
View File
@@ -0,0 +1,113 @@
#include <sys/types.h>
#include <ev.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisLibevEvents {
redisAsyncContext *context;
struct ev_loop *loop;
int reading, writing;
ev_io rev, wev;
} redisLibevEvents;
void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleRead(e->context);
}
void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
#if EV_MULTIPLICITY
((void)loop);
#endif
((void)revents);
redisLibevEvents *e = (redisLibevEvents*)watcher->data;
redisAsyncHandleWrite(e->context);
}
void redisLibevAddRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->reading) {
e->reading = 1;
ev_io_start(EV_A_ &e->rev);
}
}
void redisLibevDelRead(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->reading) {
e->reading = 0;
ev_io_stop(EV_A_ &e->rev);
}
}
void redisLibevAddWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (!e->writing) {
e->writing = 1;
ev_io_start(EV_A_ &e->wev);
}
}
void redisLibevDelWrite(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
struct ev_loop *loop = e->loop;
((void)loop);
if (e->writing) {
e->writing = 0;
ev_io_stop(EV_A_ &e->wev);
}
}
void redisLibevCleanup(void *privdata) {
redisLibevEvents *e = (redisLibevEvents*)privdata;
redisLibevDelRead(privdata);
redisLibevDelWrite(privdata);
free(e);
}
int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisLibevEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibevEvents*)malloc(sizeof(*e));
e->context = ac;
#if EV_MULTIPLICITY
e->loop = loop;
#else
e->loop = NULL;
#endif
e->reading = e->writing = 0;
e->rev.data = e;
e->wev.data = e;
/* Register functions to start/stop listening for events */
ac->evAddRead = redisLibevAddRead;
ac->evDelRead = redisLibevDelRead;
ac->evAddWrite = redisLibevAddWrite;
ac->evDelWrite = redisLibevDelWrite;
ac->evCleanup = redisLibevCleanup;
ac->_adapter_data = e;
/* Initialize read/write events */
ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
return REDIS_OK;
}
+76
View File
@@ -0,0 +1,76 @@
#include <sys/types.h>
#include <event.h>
#include "../hiredis.h"
#include "../async.h"
typedef struct redisLibeventEvents {
redisAsyncContext *context;
struct event rev, wev;
} redisLibeventEvents;
void redisLibeventReadEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleRead(e->context);
}
void redisLibeventWriteEvent(int fd, short event, void *arg) {
((void)fd); ((void)event);
redisLibeventEvents *e = (redisLibeventEvents*)arg;
redisAsyncHandleWrite(e->context);
}
void redisLibeventAddRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->rev,NULL);
}
void redisLibeventDelRead(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
}
void redisLibeventAddWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_add(&e->wev,NULL);
}
void redisLibeventDelWrite(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->wev);
}
void redisLibeventCleanup(void *privdata) {
redisLibeventEvents *e = (redisLibeventEvents*)privdata;
event_del(&e->rev);
event_del(&e->wev);
free(e);
}
int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
redisContext *c = &(ac->c);
redisLibeventEvents *e;
/* Nothing should be attached when something is already attached */
if (ac->_adapter_data != NULL)
return REDIS_ERR;
/* Create container for context and r/w events */
e = (redisLibeventEvents*)malloc(sizeof(*e));
e->context = ac;
/* Register functions to start/stop listening for events */
ac->evAddRead = redisLibeventAddRead;
ac->evDelRead = redisLibeventDelRead;
ac->evAddWrite = redisLibeventAddWrite;
ac->evDelWrite = redisLibeventDelWrite;
ac->evCleanup = redisLibeventCleanup;
ac->_adapter_data = e;
/* Initialize and install read/write events */
event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
event_set(&e->wev,c->fd,EV_WRITE,redisLibeventWriteEvent,e);
event_base_set(base,&e->rev);
event_base_set(base,&e->wev);
return REDIS_OK;
}
+321
View File
@@ -0,0 +1,321 @@
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <string.h>
#include <assert.h>
#include "async.h"
#include "sds.h"
#include "util.h"
/* Forward declaration of function in hiredis.c */
void __redisAppendCommand(redisContext *c, char *cmd, size_t len);
static redisAsyncContext *redisAsyncInitialize(redisContext *c) {
redisAsyncContext *ac = realloc(c,sizeof(redisAsyncContext));
c = &(ac->c);
/* The regular connect functions will always set the flag REDIS_CONNECTED.
* For the async API, we want to wait until the first write event is
* received up before setting this flag, so reset it here. */
c->flags &= ~REDIS_CONNECTED;
ac->err = 0;
ac->errstr = NULL;
ac->data = NULL;
ac->_adapter_data = NULL;
ac->evAddRead = NULL;
ac->evDelRead = NULL;
ac->evAddWrite = NULL;
ac->evDelWrite = NULL;
ac->evCleanup = NULL;
ac->onConnect = NULL;
ac->onDisconnect = NULL;
ac->replies.head = NULL;
ac->replies.tail = NULL;
return ac;
}
/* We want the error field to be accessible directly instead of requiring
* an indirection to the redisContext struct. */
static void __redisAsyncCopyError(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
ac->err = c->err;
ac->errstr = c->errstr;
}
redisAsyncContext *redisAsyncConnect(const char *ip, int port) {
redisContext *c = redisConnectNonBlock(ip,port);
redisAsyncContext *ac = redisAsyncInitialize(c);
__redisAsyncCopyError(ac);
return ac;
}
redisAsyncContext *redisAsyncConnectUnix(const char *path) {
redisContext *c = redisConnectUnixNonBlock(path);
redisAsyncContext *ac = redisAsyncInitialize(c);
__redisAsyncCopyError(ac);
return ac;
}
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn) {
redisContext *c = &(ac->c);
return redisSetReplyObjectFunctions(c,fn);
}
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn) {
if (ac->onConnect == NULL) {
ac->onConnect = fn;
return REDIS_OK;
}
return REDIS_ERR;
}
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn) {
if (ac->onDisconnect == NULL) {
ac->onDisconnect = fn;
return REDIS_OK;
}
return REDIS_ERR;
}
/* Helper functions to push/shift callbacks */
static int __redisPushCallback(redisCallbackList *list, redisCallback *source) {
redisCallback *cb;
/* Copy callback from stack to heap */
cb = calloc(1,sizeof(*cb));
if (!cb) redisOOM();
if (source != NULL) {
cb->fn = source->fn;
cb->privdata = source->privdata;
}
/* Store callback in list */
if (list->head == NULL)
list->head = cb;
if (list->tail != NULL)
list->tail->next = cb;
list->tail = cb;
return REDIS_OK;
}
static int __redisShiftCallback(redisCallbackList *list, redisCallback *target) {
redisCallback *cb = list->head;
if (cb != NULL) {
list->head = cb->next;
if (cb == list->tail)
list->tail = NULL;
/* Copy callback from heap to stack */
if (target != NULL)
memcpy(target,cb,sizeof(*cb));
free(cb);
return REDIS_OK;
}
return REDIS_ERR;
}
/* Tries to do a clean disconnect from Redis, meaning it stops new commands
* from being issued, but tries to flush the output buffer and execute
* callbacks for all remaining replies.
*
* This functions is generally called from within a callback, so the
* processCallbacks function will pick up the flag when there are no
* more replies. */
void redisAsyncDisconnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
c->flags |= REDIS_DISCONNECTING;
}
/* Helper function to make the disconnect happen and clean up. */
static void __redisAsyncDisconnect(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
int status;
/* Make sure error is accessible if there is any */
__redisAsyncCopyError(ac);
status = (ac->err == 0) ? REDIS_OK : REDIS_ERR;
if (status == REDIS_OK) {
/* When the connection is cleanly disconnected, there should not
* be pending callbacks. */
assert(__redisShiftCallback(&ac->replies,NULL) == REDIS_ERR);
} else {
/* Callbacks should not be able to issue new commands. */
c->flags |= REDIS_DISCONNECTING;
/* Execute pending callbacks with NULL reply. */
while (__redisShiftCallback(&ac->replies,&cb) == REDIS_OK) {
if (cb.fn != NULL)
cb.fn(ac,NULL,cb.privdata);
}
}
/* Signal event lib to clean up */
if (ac->evCleanup) ac->evCleanup(ac->_adapter_data);
/* Execute callback with proper status */
if (ac->onDisconnect) ac->onDisconnect(ac,status);
/* Cleanup self */
redisFree(c);
}
void redisProcessCallbacks(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
redisCallback cb;
void *reply = NULL;
int status;
while((status = redisGetReply(c,&reply)) == REDIS_OK) {
if (reply == NULL) {
/* When the connection is being disconnected and there are
* no more replies, this is the cue to really disconnect. */
if (c->flags & REDIS_DISCONNECTING && sdslen(c->obuf) == 0) {
__redisAsyncDisconnect(ac);
return;
}
/* When the connection is not being disconnected, simply stop
* trying to get replies and wait for the next loop tick. */
break;
}
/* Shift callback and execute it */
assert(__redisShiftCallback(&ac->replies,&cb) == REDIS_OK);
if (cb.fn != NULL) {
cb.fn(ac,reply,cb.privdata);
} else {
c->fn->freeObject(reply);
}
}
/* Disconnect when there was an error reading the reply */
if (status != REDIS_OK)
__redisAsyncDisconnect(ac);
}
/* This function should be called when the socket is readable.
* It processes all replies that can be read and executes their callbacks.
*/
void redisAsyncHandleRead(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
if (redisBufferRead(c) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Always re-schedule reads */
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
redisProcessCallbacks(ac);
}
}
void redisAsyncHandleWrite(redisAsyncContext *ac) {
redisContext *c = &(ac->c);
int done = 0;
if (redisBufferWrite(c,&done) == REDIS_ERR) {
__redisAsyncDisconnect(ac);
} else {
/* Continue writing when not done, stop writing otherwise */
if (!done) {
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
} else {
if (ac->evDelWrite) ac->evDelWrite(ac->_adapter_data);
}
/* Always schedule reads after writes */
if (ac->evAddRead) ac->evAddRead(ac->_adapter_data);
/* Fire onConnect when this is the first write event. */
if (!(c->flags & REDIS_CONNECTED)) {
c->flags |= REDIS_CONNECTED;
if (ac->onConnect) ac->onConnect(ac);
}
}
}
/* Helper function for the redisAsyncCommand* family of functions.
*
* Write a formatted command to the output buffer and register the provided
* callback function with the context.
*/
static int __redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, char *cmd, size_t len) {
redisContext *c = &(ac->c);
redisCallback cb;
/* Don't accept new commands when the connection is lazily closed. */
if (c->flags & REDIS_DISCONNECTING) return REDIS_ERR;
__redisAppendCommand(c,cmd,len);
/* Store callback */
cb.fn = fn;
cb.privdata = privdata;
__redisPushCallback(&ac->replies,&cb);
/* Always schedule a write when the write buffer is non-empty */
if (ac->evAddWrite) ac->evAddWrite(ac->_adapter_data);
return REDIS_OK;
}
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap) {
char *cmd;
int len;
int status;
len = redisvFormatCommand(&cmd,format,ap);
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
free(cmd);
return status;
}
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...) {
va_list ap;
int status;
va_start(ap,format);
status = redisvAsyncCommand(ac,fn,privdata,format,ap);
va_end(ap);
return status;
}
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen) {
char *cmd;
int len;
int status;
len = redisFormatCommandArgv(&cmd,argc,argv,argvlen);
status = __redisAsyncCommand(ac,fn,privdata,cmd,len);
free(cmd);
return status;
}
+112
View File
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_ASYNC_H
#define __HIREDIS_ASYNC_H
#include "hiredis.h"
#ifdef __cplusplus
extern "C" {
#endif
struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
/* Reply callback prototype and container */
typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
typedef struct redisCallback {
struct redisCallback *next; /* simple singly linked list */
redisCallbackFn *fn;
void *privdata;
} redisCallback;
/* List of callbacks for either regular replies or pub/sub */
typedef struct redisCallbackList {
redisCallback *head, *tail;
} redisCallbackList;
/* Connection callback prototypes */
typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
typedef void (redisConnectCallback)(const struct redisAsyncContext*);
/* Context for an async connection to Redis */
typedef struct redisAsyncContext {
/* Hold the regular context, so it can be realloc'ed. */
redisContext c;
/* Setup error flags so they can be used directly. */
int err;
char *errstr;
/* Not used by hiredis */
void *data;
/* Used by the different event lib adapters to store their private data */
void *_adapter_data;
/* Called when the library expects to start reading/writing.
* The supplied functions should be idempotent. */
void (*evAddRead)(void *privdata);
void (*evDelRead)(void *privdata);
void (*evAddWrite)(void *privdata);
void (*evDelWrite)(void *privdata);
void (*evCleanup)(void *privdata);
/* Called when either the connection is terminated due to an error or per
* user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
redisDisconnectCallback *onDisconnect;
/* Called when the first write event was received. */
redisConnectCallback *onConnect;
/* Reply callbacks */
redisCallbackList replies;
} redisAsyncContext;
/* Functions that proxy to hiredis */
redisAsyncContext *redisAsyncConnect(const char *ip, int port);
int redisAsyncSetReplyObjectFunctions(redisAsyncContext *ac, redisReplyObjectFunctions *fn);
int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
void redisAsyncDisconnect(redisAsyncContext *ac);
/* Handle read/write events */
void redisAsyncHandleRead(redisAsyncContext *ac);
void redisAsyncHandleWrite(redisAsyncContext *ac);
/* Command functions for an async context. Write the command to the
* output buffer and register the provided callback. */
int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif
+53
View File
@@ -0,0 +1,53 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/ae.h"
/* Put event loop in the global scope, so it can be explicitly stopped */
static aeEventLoop *loop;
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c) {
((void)c);
printf("connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
}
printf("disconnected...\n");
aeStop(loop);
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
loop = aeCreateEventLoop();
redisAeAttach(loop, c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
aeMain(loop);
return 0;
}
+47
View File
@@ -0,0 +1,47 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/libev.h"
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c) {
((void)c);
printf("connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
}
printf("disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisLibevAttach(EV_DEFAULT_ c);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
ev_loop(EV_DEFAULT_ 0);
return 0;
}
+48
View File
@@ -0,0 +1,48 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "hiredis.h"
#include "async.h"
#include "adapters/libevent.h"
void getCallback(redisAsyncContext *c, void *r, void *privdata) {
redisReply *reply = r;
if (reply == NULL) return;
printf("argv[%s]: %s\n", (char*)privdata, reply->str);
/* Disconnect after receiving the reply to GET */
redisAsyncDisconnect(c);
}
void connectCallback(const redisAsyncContext *c) {
((void)c);
printf("connected...\n");
}
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Error: %s\n", c->errstr);
}
printf("disconnected...\n");
}
int main (int argc, char **argv) {
signal(SIGPIPE, SIG_IGN);
struct event_base *base = event_base_new();
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
/* Let *c leak for now... */
printf("Error: %s\n", c->errstr);
return 1;
}
redisLibeventAttach(c,base);
redisAsyncSetConnectCallback(c,connectCallback);
redisAsyncSetDisconnectCallback(c,disconnectCallback);
redisAsyncCommand(c, NULL, NULL, "SET key %b", argv[argc-1], strlen(argv[argc-1]));
redisAsyncCommand(c, getCallback, (char*)"end-1", "GET key");
event_base_dispatch(base);
return 0;
}
+67
View File
@@ -0,0 +1,67 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "hiredis.h"
int main(void) {
unsigned int j;
redisContext *c;
redisReply *reply;
c = redisConnect((char*)"127.0.0.1", 6379);
if (c->err) {
printf("Connection error: %s\n", c->errstr);
exit(1);
}
/* PING server */
reply = redisCommand(c,"PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key */
reply = redisCommand(c,"SET %s %s", "foo", "hello world");
printf("SET: %s\n", reply->str);
freeReplyObject(reply);
/* Set a key using binary safe API */
reply = redisCommand(c,"SET %b %b", "bar", 3, "hello", 5);
printf("SET (binary API): %s\n", reply->str);
freeReplyObject(reply);
/* Try a GET and two INCR */
reply = redisCommand(c,"GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* again ... */
reply = redisCommand(c,"INCR counter");
printf("INCR counter: %lld\n", reply->integer);
freeReplyObject(reply);
/* Create a list of numbers, from 0 to 9 */
reply = redisCommand(c,"DEL mylist");
freeReplyObject(reply);
for (j = 0; j < 10; j++) {
char buf[64];
snprintf(buf,64,"%d",j);
reply = redisCommand(c,"LPUSH mylist element-%s", buf);
freeReplyObject(reply);
}
/* Let's check what we have inside the list */
reply = redisCommand(c,"LRANGE mylist 0 -1");
if (reply->type == REDIS_REPLY_ARRAY) {
for (j = 0; j < reply->elements; j++) {
printf("%u) %s\n", j, reply->element[j]->str);
}
}
freeReplyObject(reply);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
#ifndef _REDIS_FMACRO_H
#define _REDIS_FMACRO_H
#define _BSD_SOURCE
#ifdef __linux__
#define _XOPEN_SOURCE 700
#else
#define _XOPEN_SOURCE
#endif
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
#endif
+1058
View File
File diff suppressed because it is too large Load Diff
+170
View File
@@ -0,0 +1,170 @@
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __HIREDIS_H
#define __HIREDIS_H
#include <stdio.h> /* for size_t */
#include <stdarg.h> /* for va_list */
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 9
#define HIREDIS_PATCH 2
#define REDIS_ERR -1
#define REDIS_OK 0
/* When an error occurs, the err flag in a context is set to hold the type of
* error that occured. REDIS_ERR_IO means there was an I/O error and you
* should use the "errno" variable to find out what is wrong.
* For other values, the "errstr" field will hold a description. */
#define REDIS_ERR_IO 1 /* error in read or write */
#define REDIS_ERR_EOF 3 /* eof */
#define REDIS_ERR_PROTOCOL 4 /* protocol error */
#define REDIS_ERR_OTHER 2 /* something else */
/* Connection type can be blocking or non-blocking and is set in the
* least significant bit of the flags field in redisContext. */
#define REDIS_BLOCK 0x1
/* Connection may be disconnected before being free'd. The second bit
* in the flags field is set when the context is connected. */
#define REDIS_CONNECTED 0x2
/* The async API might try to disconnect cleanly and flush the output
* buffer and read all subsequent replies before disconnecting.
* This flag means no new commands can come in and the connection
* should be terminated once all replies have been read. */
#define REDIS_DISCONNECTING 0x4
#define REDIS_REPLY_STRING 1
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5
#define REDIS_REPLY_ERROR 6
#ifdef __cplusplus
extern "C" {
#endif
/* This is the reply object returned by redisCommand() */
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int len; /* Length of string */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;
typedef struct redisReadTask {
int type;
int elements; /* number of elements in multibulk container */
int idx; /* index in parent (array) object */
void *obj; /* holds user-generated value for a read task */
struct redisReadTask *parent; /* parent task */
void *privdata; /* user-settable arbitrary field */
} redisReadTask;
typedef struct redisReplyObjectFunctions {
void *(*createString)(const redisReadTask*, char*, size_t);
void *(*createArray)(const redisReadTask*, int);
void *(*createInteger)(const redisReadTask*, long long);
void *(*createNil)(const redisReadTask*);
void (*freeObject)(void*);
} redisReplyObjectFunctions;
struct redisContext; /* need forward declaration of redisContext */
/* Context for a connection to Redis */
typedef struct redisContext {
int fd;
int flags;
char *obuf; /* Write buffer */
int err; /* Error flags, 0 when there is no error */
char *errstr; /* String representation of error when applicable */
/* Function set for reply buildup and reply reader */
redisReplyObjectFunctions *fn;
void *reader;
} redisContext;
void freeReplyObject(void *reply);
void *redisReplyReaderCreate();
int redisReplyReaderSetReplyObjectFunctions(void *reader, redisReplyObjectFunctions *fn);
int redisReplyReaderSetPrivdata(void *reader, void *privdata);
void *redisReplyReaderGetObject(void *reader);
char *redisReplyReaderGetError(void *reader);
void redisReplyReaderFree(void *ptr);
void redisReplyReaderFeed(void *reader, char *buf, size_t len);
int redisReplyReaderGetReply(void *reader, void **reply);
/* Functions to format a command according to the protocol. */
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixNonBlock(const char *path);
int redisSetReplyObjectFunctions(redisContext *c, redisReplyObjectFunctions *fn);
void redisFree(redisContext *c);
int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done);
/* In a blocking context, this function first checks if there are unconsumed
* replies to return and returns one if so. Otherwise, it flushes the output
* buffer to the socket and reads until it has a reply. In a non-blocking
* context, it will return unconsumed replies until there are no more. */
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);
/* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
void redisvAppendCommand(redisContext *c, const char *format, va_list ap);
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/* Issue a command to Redis. In a blocking context, it is identical to calling
* redisAppendCommand, followed by redisGetReply. The function will return
* NULL if there was an error in performing the request, otherwise it will
* return the reply. In a non-blocking context, it is identical to calling
* only redisAppendCommand and will always return NULL. */
void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif
+170
View File
@@ -0,0 +1,170 @@
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <netdb.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include "net.h"
#include "sds.h"
/* Forward declaration */
void __redisSetError(redisContext *c, int type, sds err);
static int redisCreateSocket(redisContext *c, int type) {
int s, on = 1;
if ((s = socket(type, SOCK_STREAM, 0)) == -1) {
__redisSetError(c,REDIS_ERR_IO,NULL);
return REDIS_ERR;
}
if (type == AF_INET) {
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
__redisSetError(c,REDIS_ERR_IO,NULL);
close(s);
return REDIS_ERR;
}
}
return s;
}
static int redisSetNonBlock(redisContext *c, int fd) {
int flags;
/* Set the socket nonblocking.
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
* interrupted by a signal. */
if ((flags = fcntl(fd, F_GETFL)) == -1) {
__redisSetError(c,REDIS_ERR_IO,
sdscatprintf(sdsempty(), "fcntl(F_GETFL): %s", strerror(errno)));
close(fd);
return REDIS_ERR;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
__redisSetError(c,REDIS_ERR_IO,
sdscatprintf(sdsempty(), "fcntl(F_SETFL,O_NONBLOCK): %s", strerror(errno)));
close(fd);
return REDIS_ERR;
}
return REDIS_OK;
}
static int redisSetTcpNoDelay(redisContext *c, int fd) {
int yes = 1;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1) {
__redisSetError(c,REDIS_ERR_IO,
sdscatprintf(sdsempty(), "setsockopt(TCP_NODELAY): %s", strerror(errno)));
return REDIS_ERR;
}
return REDIS_OK;
}
int redisContextConnectTcp(redisContext *c, const char *addr, int port) {
int s;
int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_in sa;
if ((s = redisCreateSocket(c,AF_INET)) == REDIS_ERR)
return REDIS_ERR;
if (!blocking && redisSetNonBlock(c,s) == REDIS_ERR)
return REDIS_ERR;
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
if (inet_aton(addr, &sa.sin_addr) == 0) {
struct hostent *he;
he = gethostbyname(addr);
if (he == NULL) {
__redisSetError(c,REDIS_ERR_OTHER,
sdscatprintf(sdsempty(),"Can't resolve: %s",addr));
close(s);
return REDIS_ERR;
}
memcpy(&sa.sin_addr, he->h_addr, sizeof(struct in_addr));
}
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
__redisSetError(c,REDIS_ERR_IO,NULL);
close(s);
return REDIS_ERR;
}
}
if (redisSetTcpNoDelay(c,s) != REDIS_OK) {
close(s);
return REDIS_ERR;
}
c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
}
int redisContextConnectUnix(redisContext *c, const char *path) {
int s;
int blocking = (c->flags & REDIS_BLOCK);
struct sockaddr_un sa;
if ((s = redisCreateSocket(c,AF_LOCAL)) == REDIS_ERR)
return REDIS_ERR;
if (!blocking && redisSetNonBlock(c,s) != REDIS_OK)
return REDIS_ERR;
sa.sun_family = AF_LOCAL;
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (connect(s, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
if (errno == EINPROGRESS && !blocking) {
/* This is ok. */
} else {
__redisSetError(c,REDIS_ERR_IO,NULL);
close(s);
return REDIS_ERR;
}
}
c->fd = s;
c->flags |= REDIS_CONNECTED;
return REDIS_OK;
}
+43
View File
@@ -0,0 +1,43 @@
/* Extracted from anet.c to work properly with Hiredis error reporting.
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __NET_H
#define __NET_H
#include "hiredis.h"
#if defined(__sun)
#define AF_LOCAL AF_UNIX
#endif
int redisContextConnectTcp(redisContext *c, const char *addr, int port);
int redisContextConnectUnix(redisContext *c, const char *path);
#endif
+479
View File
@@ -0,0 +1,479 @@
/* SDSLib, A C dynamic strings library
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#define SDS_ABORT_ON_OOM
#include "sds.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
static void sdsOomAbort(void) {
fprintf(stderr,"SDS: Out Of Memory (SDS_ABORT_ON_OOM defined)\n");
abort();
}
sds sdsnewlen(const void *init, size_t initlen) {
struct sdshdr *sh;
sh = malloc(sizeof(struct sdshdr)+initlen+1);
#ifdef SDS_ABORT_ON_OOM
if (sh == NULL) sdsOomAbort();
#else
if (sh == NULL) return NULL;
#endif
sh->len = initlen;
sh->free = 0;
if (initlen) {
if (init) memcpy(sh->buf, init, initlen);
else memset(sh->buf,0,initlen);
}
sh->buf[initlen] = '\0';
return (char*)sh->buf;
}
sds sdsempty(void) {
return sdsnewlen("",0);
}
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
}
size_t sdslen(const sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->len;
}
sds sdsdup(const sds s) {
return sdsnewlen(s, sdslen(s));
}
void sdsfree(sds s) {
if (s == NULL) return;
free(s-sizeof(struct sdshdr));
}
size_t sdsavail(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
return sh->free;
}
void sdsupdatelen(sds s) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
int reallen = strlen(s);
sh->free += (sh->len-reallen);
sh->len = reallen;
}
static sds sdsMakeRoomFor(sds s, size_t addlen) {
struct sdshdr *sh, *newsh;
size_t free = sdsavail(s);
size_t len, newlen;
if (free >= addlen) return s;
len = sdslen(s);
sh = (void*) (s-(sizeof(struct sdshdr)));
newlen = (len+addlen)*2;
newsh = realloc(sh, sizeof(struct sdshdr)+newlen+1);
#ifdef SDS_ABORT_ON_OOM
if (newsh == NULL) sdsOomAbort();
#else
if (newsh == NULL) return NULL;
#endif
newsh->free = newlen - len;
return newsh->buf;
}
sds sdscatlen(sds s, const void *t, size_t len) {
struct sdshdr *sh;
size_t curlen = sdslen(s);
s = sdsMakeRoomFor(s,len);
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
memcpy(s+curlen, t, len);
sh->len = curlen+len;
sh->free = sh->free-len;
s[curlen+len] = '\0';
return s;
}
sds sdscat(sds s, const char *t) {
return sdscatlen(s, t, strlen(t));
}
sds sdscpylen(sds s, char *t, size_t len) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t totlen = sh->free+sh->len;
if (totlen < len) {
s = sdsMakeRoomFor(s,len-sh->len);
if (s == NULL) return NULL;
sh = (void*) (s-(sizeof(struct sdshdr)));
totlen = sh->free+sh->len;
}
memcpy(s, t, len);
s[len] = '\0';
sh->len = len;
sh->free = totlen-len;
return s;
}
sds sdscpy(sds s, char *t) {
return sdscpylen(s, t, strlen(t));
}
sds sdscatvprintf(sds s, const char *fmt, va_list ap) {
va_list cpy;
char *buf, *t;
size_t buflen = 16;
while(1) {
buf = malloc(buflen);
#ifdef SDS_ABORT_ON_OOM
if (buf == NULL) sdsOomAbort();
#else
if (buf == NULL) return NULL;
#endif
buf[buflen-2] = '\0';
va_copy(cpy,ap);
vsnprintf(buf, buflen, fmt, cpy);
if (buf[buflen-2] != '\0') {
free(buf);
buflen *= 2;
continue;
}
break;
}
t = sdscat(s, buf);
free(buf);
return t;
}
sds sdscatprintf(sds s, const char *fmt, ...) {
va_list ap;
char *t;
va_start(ap, fmt);
t = sdscatvprintf(s,fmt,ap);
va_end(ap);
return t;
}
sds sdstrim(sds s, const char *cset) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
char *start, *end, *sp, *ep;
size_t len;
sp = start = s;
ep = end = s+sdslen(s)-1;
while(sp <= end && strchr(cset, *sp)) sp++;
while(ep > start && strchr(cset, *ep)) ep--;
len = (sp > ep) ? 0 : ((ep-sp)+1);
if (sh->buf != sp) memmove(sh->buf, sp, len);
sh->buf[len] = '\0';
sh->free = sh->free+(sh->len-len);
sh->len = len;
return s;
}
sds sdsrange(sds s, int start, int end) {
struct sdshdr *sh = (void*) (s-(sizeof(struct sdshdr)));
size_t newlen, len = sdslen(s);
if (len == 0) return s;
if (start < 0) {
start = len+start;
if (start < 0) start = 0;
}
if (end < 0) {
end = len+end;
if (end < 0) end = 0;
}
newlen = (start > end) ? 0 : (end-start)+1;
if (newlen != 0) {
if (start >= (signed)len) start = len-1;
if (end >= (signed)len) end = len-1;
newlen = (start > end) ? 0 : (end-start)+1;
} else {
start = 0;
}
if (start != 0) memmove(sh->buf, sh->buf+start, newlen);
sh->buf[newlen] = 0;
sh->free = sh->free+(sh->len-newlen);
sh->len = newlen;
return s;
}
void sdstolower(sds s) {
int len = sdslen(s), j;
for (j = 0; j < len; j++) s[j] = tolower(s[j]);
}
void sdstoupper(sds s) {
int len = sdslen(s), j;
for (j = 0; j < len; j++) s[j] = toupper(s[j]);
}
int sdscmp(sds s1, sds s2) {
size_t l1, l2, minlen;
int cmp;
l1 = sdslen(s1);
l2 = sdslen(s2);
minlen = (l1 < l2) ? l1 : l2;
cmp = memcmp(s1,s2,minlen);
if (cmp == 0) return l1-l2;
return cmp;
}
/* Split 's' with separator in 'sep'. An array
* of sds strings is returned. *count will be set
* by reference to the number of tokens returned.
*
* On out of memory, zero length string, zero length
* separator, NULL is returned.
*
* Note that 'sep' is able to split a string using
* a multi-character separator. For example
* sdssplit("foo_-_bar","_-_"); will return two
* elements "foo" and "bar".
*
* This version of the function is binary-safe but
* requires length arguments. sdssplit() is just the
* same function but for zero-terminated strings.
*/
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count) {
int elements = 0, slots = 5, start = 0, j;
sds *tokens = malloc(sizeof(sds)*slots);
#ifdef SDS_ABORT_ON_OOM
if (tokens == NULL) sdsOomAbort();
#endif
if (seplen < 1 || len < 0 || tokens == NULL) return NULL;
if (len == 0) {
*count = 0;
return tokens;
}
for (j = 0; j < (len-(seplen-1)); j++) {
/* make sure there is room for the next element and the final one */
if (slots < elements+2) {
sds *newtokens;
slots *= 2;
newtokens = realloc(tokens,sizeof(sds)*slots);
if (newtokens == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
tokens = newtokens;
}
/* search the separator */
if ((seplen == 1 && *(s+j) == sep[0]) || (memcmp(s+j,sep,seplen) == 0)) {
tokens[elements] = sdsnewlen(s+start,j-start);
if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
elements++;
start = j+seplen;
j = j+seplen-1; /* skip the separator */
}
}
/* Add the final element. We are sure there is room in the tokens array. */
tokens[elements] = sdsnewlen(s+start,len-start);
if (tokens[elements] == NULL) {
#ifdef SDS_ABORT_ON_OOM
sdsOomAbort();
#else
goto cleanup;
#endif
}
elements++;
*count = elements;
return tokens;
#ifndef SDS_ABORT_ON_OOM
cleanup:
{
int i;
for (i = 0; i < elements; i++) sdsfree(tokens[i]);
free(tokens);
return NULL;
}
#endif
}
void sdsfreesplitres(sds *tokens, int count) {
if (!tokens) return;
while(count--)
sdsfree(tokens[count]);
free(tokens);
}
sds sdsfromlonglong(long long value) {
char buf[32], *p;
unsigned long long v;
v = (value < 0) ? -value : value;
p = buf+31; /* point to the last character */
do {
*p-- = '0'+(v%10);
v /= 10;
} while(v);
if (value < 0) *p-- = '-';
p++;
return sdsnewlen(p,32-(p-buf));
}
sds sdscatrepr(sds s, char *p, size_t len) {
s = sdscatlen(s,"\"",1);
while(len--) {
switch(*p) {
case '\\':
case '"':
s = sdscatprintf(s,"\\%c",*p);
break;
case '\n': s = sdscatlen(s,"\\n",1); break;
case '\r': s = sdscatlen(s,"\\r",1); break;
case '\t': s = sdscatlen(s,"\\t",1); break;
case '\a': s = sdscatlen(s,"\\a",1); break;
case '\b': s = sdscatlen(s,"\\b",1); break;
default:
if (isprint(*p))
s = sdscatprintf(s,"%c",*p);
else
s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
break;
}
p++;
}
return sdscatlen(s,"\"",1);
}
/* Split a line into arguments, where every argument can be in the
* following programming-language REPL-alike form:
*
* foo bar "newline are supported\n" and "\xff\x00otherstuff"
*
* The number of arguments is stored into *argc, and an array
* of sds is returned. The caller should sdsfree() all the returned
* strings and finally free() the array itself.
*
* Note that sdscatrepr() is able to convert back a string into
* a quoted string in the same format sdssplitargs() is able to parse.
*/
sds *sdssplitargs(char *line, int *argc) {
char *p = line;
char *current = NULL;
char **vector = NULL;
*argc = 0;
while(1) {
/* skip blanks */
while(*p && isspace(*p)) p++;
if (*p) {
/* get a token */
int inq=0; /* set to 1 if we are in "quotes" */
int done=0;
if (current == NULL) current = sdsempty();
while(!done) {
if (inq) {
if (*p == '\\' && *(p+1)) {
char c;
p++;
switch(*p) {
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'b': c = '\b'; break;
case 'a': c = '\a'; break;
default: c = *p; break;
}
current = sdscatlen(current,&c,1);
} else if (*p == '"') {
/* closing quote must be followed by a space */
if (*(p+1) && !isspace(*(p+1))) goto err;
done=1;
} else if (!*p) {
/* unterminated quotes */
goto err;
} else {
current = sdscatlen(current,p,1);
}
} else {
switch(*p) {
case ' ':
case '\n':
case '\r':
case '\t':
case '\0':
done=1;
break;
case '"':
inq=1;
break;
default:
current = sdscatlen(current,p,1);
break;
}
}
if (*p) p++;
}
/* add the token to the vector */
vector = realloc(vector,((*argc)+1)*sizeof(char*));
vector[*argc] = current;
(*argc)++;
current = NULL;
} else {
return vector;
}
}
err:
while((*argc)--)
sdsfree(vector[*argc]);
free(vector);
if (current) sdsfree(current);
return NULL;
}
+77
View File
@@ -0,0 +1,77 @@
/* SDSLib, A C dynamic strings library
*
* Copyright (c) 2006-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SDS_H
#define __SDS_H
#include <sys/types.h>
#include <stdarg.h>
typedef char *sds;
struct sdshdr {
int len;
int free;
char buf[];
};
sds sdsnewlen(const void *init, size_t initlen);
sds sdsnew(const char *init);
sds sdsempty();
size_t sdslen(const sds s);
sds sdsdup(const sds s);
void sdsfree(sds s);
size_t sdsavail(sds s);
sds sdscatlen(sds s, const void *t, size_t len);
sds sdscat(sds s, const char *t);
sds sdscpylen(sds s, char *t, size_t len);
sds sdscpy(sds s, char *t);
sds sdscatvprintf(sds s, const char *fmt, va_list ap);
#ifdef __GNUC__
sds sdscatprintf(sds s, const char *fmt, ...)
__attribute__((format(printf, 2, 3)));
#else
sds sdscatprintf(sds s, const char *fmt, ...);
#endif
sds sdstrim(sds s, const char *cset);
sds sdsrange(sds s, int start, int end);
void sdsupdatelen(sds s);
int sdscmp(sds s1, sds s2);
sds *sdssplitlen(char *s, int len, char *sep, int seplen, int *count);
void sdsfreesplitres(sds *tokens, int count);
void sdstolower(sds s);
void sdstoupper(sds s);
sds sdsfromlonglong(long long value);
sds sdscatrepr(sds s, char *p, size_t len);
sds *sdssplitargs(char *line, int *argc);
#endif
+479
View File
@@ -0,0 +1,479 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/time.h>
#include <assert.h>
#include <unistd.h>
#include <signal.h>
#include "hiredis.h"
/* The following lines make up our testing "framework" :) */
static int tests = 0, fails = 0;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test_cond(_c) if(_c) printf("PASSED\n"); else {printf("FAILED\n"); fails++;}
static long long usec(void) {
struct timeval tv;
gettimeofday(&tv,NULL);
return (((long long)tv.tv_sec)*1000000)+tv.tv_usec;
}
static int use_unix = 0;
static redisContext *blocking_context = NULL;
static void __connect(redisContext **target) {
*target = blocking_context = (use_unix ?
redisConnectUnix("/tmp/redis.sock") : redisConnect((char*)"127.0.0.1", 6379));
if (blocking_context->err) {
printf("Connection error: %s\n", blocking_context->errstr);
exit(1);
}
}
static void test_format_commands() {
char *cmd;
int len;
test("Format command without interpolation: ");
len = redisFormatCommand(&cmd,"SET foo bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%s string interpolation: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","bar");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%s and an empty string: ");
len = redisFormatCommand(&cmd,"SET %s %s","foo","");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with %%b string interpolation: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"b\0r",3);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nb\0r\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command with %%b and an empty string: ");
len = redisFormatCommand(&cmd,"SET %b %b","foo",3,"",0);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$0\r\n\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(0+2));
free(cmd);
test("Format command with literal %%: ");
len = redisFormatCommand(&cmd,"SET %% %%");
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$1\r\n%\r\n$1\r\n%\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(1+2)+4+(1+2));
free(cmd);
test("Format command with printf-delegation (long long): ");
len = redisFormatCommand(&cmd,"key:%08lld",1234ll);
test_cond(strncmp(cmd,"*1\r\n$12\r\nkey:00001234\r\n",len) == 0 &&
len == 4+5+(12+2));
free(cmd);
test("Format command with printf-delegation (float): ");
len = redisFormatCommand(&cmd,"v:%06.1f",12.34f);
test_cond(strncmp(cmd,"*1\r\n$8\r\nv:0012.3\r\n",len) == 0 &&
len == 4+4+(8+2));
free(cmd);
test("Format command with printf-delegation and extra interpolation: ");
len = redisFormatCommand(&cmd,"key:%d %b",1234,"foo",3);
test_cond(strncmp(cmd,"*2\r\n$8\r\nkey:1234\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(8+2)+4+(3+2));
free(cmd);
test("Format command with wrong printf format and extra interpolation: ");
len = redisFormatCommand(&cmd,"key:%08p %b",1234,"foo",3);
test_cond(strncmp(cmd,"*2\r\n$6\r\nkey:8p\r\n$3\r\nfoo\r\n",len) == 0 &&
len == 4+4+(6+2)+4+(3+2));
free(cmd);
const char *argv[3];
argv[0] = "SET";
argv[1] = "foo\0xxx";
argv[2] = "bar";
size_t lens[3] = { 3, 7, 3 };
int argc = 3;
test("Format command by passing argc/argv without lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,NULL);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(3+2)+4+(3+2));
free(cmd);
test("Format command by passing argc/argv with lengths: ");
len = redisFormatCommandArgv(&cmd,argc,argv,lens);
test_cond(strncmp(cmd,"*3\r\n$3\r\nSET\r\n$7\r\nfoo\0xxx\r\n$3\r\nbar\r\n",len) == 0 &&
len == 4+4+(3+2)+4+(7+2)+4+(3+2));
free(cmd);
}
static void test_blocking_connection() {
redisContext *c;
redisReply *reply;
int major, minor;
test("Returns error when host cannot be resolved: ");
c = redisConnect((char*)"idontexist.local", 6379);
test_cond(c->err == REDIS_ERR_OTHER &&
strcmp(c->errstr,"Can't resolve: idontexist.local") == 0);
redisFree(c);
test("Returns error when the port is not open: ");
c = redisConnect((char*)"localhost", 56380);
test_cond(c->err == REDIS_ERR_IO &&
strcmp(c->errstr,"Connection refused") == 0);
redisFree(c);
__connect(&c);
test("Is able to deliver commands: ");
reply = redisCommand(c,"PING");
test_cond(reply->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->str,"pong") == 0)
freeReplyObject(reply);
/* Switch to DB 9 for testing, now that we know we can chat. */
reply = redisCommand(c,"SELECT 9");
freeReplyObject(reply);
/* Make sure the DB is emtpy */
reply = redisCommand(c,"DBSIZE");
if (reply->type != REDIS_REPLY_INTEGER || reply->integer != 0) {
printf("Database #9 is not empty, test can not continue\n");
exit(1);
}
freeReplyObject(reply);
test("Is a able to send commands verbatim: ");
reply = redisCommand(c,"SET foo bar");
test_cond (reply->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->str,"ok") == 0)
freeReplyObject(reply);
test("%%s String interpolation works: ");
reply = redisCommand(c,"SET %s %s","foo","hello world");
freeReplyObject(reply);
reply = redisCommand(c,"GET foo");
test_cond(reply->type == REDIS_REPLY_STRING &&
strcmp(reply->str,"hello world") == 0);
freeReplyObject(reply);
test("%%b String interpolation works: ");
reply = redisCommand(c,"SET %b %b","foo",3,"hello\x00world",11);
freeReplyObject(reply);
reply = redisCommand(c,"GET foo");
test_cond(reply->type == REDIS_REPLY_STRING &&
memcmp(reply->str,"hello\x00world",11) == 0)
test("Binary reply length is correct: ");
test_cond(reply->len == 11)
freeReplyObject(reply);
test("Can parse nil replies: ");
reply = redisCommand(c,"GET nokey");
test_cond(reply->type == REDIS_REPLY_NIL)
freeReplyObject(reply);
/* test 7 */
test("Can parse integer replies: ");
reply = redisCommand(c,"INCR mycounter");
test_cond(reply->type == REDIS_REPLY_INTEGER && reply->integer == 1)
freeReplyObject(reply);
test("Can parse multi bulk replies: ");
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
freeReplyObject(redisCommand(c,"LPUSH mylist bar"));
reply = redisCommand(c,"LRANGE mylist 0 -1");
test_cond(reply->type == REDIS_REPLY_ARRAY &&
reply->elements == 2 &&
!memcmp(reply->element[0]->str,"bar",3) &&
!memcmp(reply->element[1]->str,"foo",3))
freeReplyObject(reply);
/* m/e with multi bulk reply *before* other reply.
* specifically test ordering of reply items to parse. */
test("Can handle nested multi bulk replies: ");
freeReplyObject(redisCommand(c,"MULTI"));
freeReplyObject(redisCommand(c,"LRANGE mylist 0 -1"));
freeReplyObject(redisCommand(c,"PING"));
reply = (redisCommand(c,"EXEC"));
test_cond(reply->type == REDIS_REPLY_ARRAY &&
reply->elements == 2 &&
reply->element[0]->type == REDIS_REPLY_ARRAY &&
reply->element[0]->elements == 2 &&
!memcmp(reply->element[0]->element[0]->str,"bar",3) &&
!memcmp(reply->element[0]->element[1]->str,"foo",3) &&
reply->element[1]->type == REDIS_REPLY_STATUS &&
strcasecmp(reply->element[1]->str,"pong") == 0);
freeReplyObject(reply);
{
/* Find out Redis version to determine the path for the next test */
const char *field = "redis_version:";
char *p, *eptr;
reply = redisCommand(c,"INFO");
p = strstr(reply->str,field);
major = strtol(p+strlen(field),&eptr,10);
p = eptr+1; /* char next to the first "." */
minor = strtol(p,&eptr,10);
freeReplyObject(reply);
}
test("Returns I/O error when the connection is lost: ");
reply = redisCommand(c,"QUIT");
if (major >= 2 && minor > 0) {
/* > 2.0 returns OK on QUIT and read() should be issued once more
* to know the descriptor is at EOF. */
test_cond(strcasecmp(reply->str,"OK") == 0 &&
redisGetReply(c,(void**)&reply) == REDIS_ERR);
freeReplyObject(reply);
} else {
test_cond(reply == NULL);
}
/* On 2.0, QUIT will cause the connection to be closed immediately and
* the read(2) for the reply on QUIT will set the error to EOF.
* On >2.0, QUIT will return with OK and another read(2) needed to be
* issued to find out the socket was closed by the server. In both
* conditions, the error will be set to EOF. */
assert(c->err == REDIS_ERR_EOF &&
strcmp(c->errstr,"Server closed the connection") == 0);
/* Clean up context and reconnect again */
redisFree(c);
__connect(&c);
}
static void test_reply_reader() {
void *reader;
void *reply;
char *err;
int ret;
test("Error handling in reply parser: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
redisReplyReaderFree(reader);
/* when the reply already contains multiple items, they must be free'd
* on an error. valgrind will bark when this doesn't happen. */
test("Memory cleanup in reply parser: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"*2\r\n",4);
redisReplyReaderFeed(reader,(char*)"$5\r\nhello\r\n",11);
redisReplyReaderFeed(reader,(char*)"@foo\r\n",6);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strcasecmp(err,"Protocol error, got \"@\" as reply type byte") == 0);
redisReplyReaderFree(reader);
test("Set error on nested multi bulks with depth > 1: ");
reader = redisReplyReaderCreate();
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
redisReplyReaderFeed(reader,(char*)"*1\r\n",4);
ret = redisReplyReaderGetReply(reader,NULL);
err = redisReplyReaderGetError(reader);
test_cond(ret == REDIS_ERR &&
strncasecmp(err,"No support for",14) == 0);
redisReplyReaderFree(reader);
test("Works with NULL functions for reply: ");
reader = redisReplyReaderCreate();
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
redisReplyReaderFeed(reader,(char*)"+OK\r\n",5);
ret = redisReplyReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReplyReaderFree(reader);
test("Works when a single newline (\\r\\n) covers two calls to feed: ");
reader = redisReplyReaderCreate();
redisReplyReaderSetReplyObjectFunctions(reader,NULL);
redisReplyReaderFeed(reader,(char*)"+OK\r",4);
ret = redisReplyReaderGetReply(reader,&reply);
assert(ret == REDIS_OK && reply == NULL);
redisReplyReaderFeed(reader,(char*)"\n",1);
ret = redisReplyReaderGetReply(reader,&reply);
test_cond(ret == REDIS_OK && reply == (void*)REDIS_REPLY_STATUS);
redisReplyReaderFree(reader);
}
static void test_throughput() {
int i;
long long t1, t2;
redisContext *c = blocking_context;
redisReply **replies;
test("Throughput:\n");
for (i = 0; i < 500; i++)
freeReplyObject(redisCommand(c,"LPUSH mylist foo"));
replies = malloc(sizeof(redisReply*)*1000);
t1 = usec();
for (i = 0; i < 1000; i++) {
replies[i] = redisCommand(c,"PING");
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_STATUS);
}
t2 = usec();
for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(1000x PING: %.2fs)\n", (t2-t1)/1000000.0);
replies = malloc(sizeof(redisReply*)*1000);
t1 = usec();
for (i = 0; i < 1000; i++) {
replies[i] = redisCommand(c,"LRANGE mylist 0 499");
assert(replies[i] != NULL && replies[i]->type == REDIS_REPLY_ARRAY);
assert(replies[i] != NULL && replies[i]->elements == 500);
}
t2 = usec();
for (i = 0; i < 1000; i++) freeReplyObject(replies[i]);
free(replies);
printf("\t(1000x LRANGE with 500 elements: %.2fs)\n", (t2-t1)/1000000.0);
}
static void cleanup() {
redisContext *c = blocking_context;
redisReply *reply;
/* Make sure we're on DB 9 */
reply = redisCommand(c,"SELECT 9");
assert(reply != NULL); freeReplyObject(reply);
reply = redisCommand(c,"FLUSHDB");
assert(reply != NULL); freeReplyObject(reply);
redisFree(c);
}
// static long __test_callback_flags = 0;
// static void __test_callback(redisContext *c, void *privdata) {
// ((void)c);
// /* Shift to detect execution order */
// __test_callback_flags <<= 8;
// __test_callback_flags |= (long)privdata;
// }
//
// static void __test_reply_callback(redisContext *c, redisReply *reply, void *privdata) {
// ((void)c);
// /* Shift to detect execution order */
// __test_callback_flags <<= 8;
// __test_callback_flags |= (long)privdata;
// if (reply) freeReplyObject(reply);
// }
//
// static redisContext *__connect_nonblock() {
// /* Reset callback flags */
// __test_callback_flags = 0;
// return redisConnectNonBlock("127.0.0.1", 6379, NULL);
// }
//
// static void test_nonblocking_connection() {
// redisContext *c;
// int wdone = 0;
//
// test("Calls command callback when command is issued: ");
// c = __connect_nonblock();
// redisSetCommandCallback(c,__test_callback,(void*)1);
// redisCommand(c,"PING");
// test_cond(__test_callback_flags == 1);
// redisFree(c);
//
// test("Calls disconnect callback on redisDisconnect: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)2);
// redisDisconnect(c);
// test_cond(__test_callback_flags == 2);
// redisFree(c);
//
// test("Calls disconnect callback and free callback on redisFree: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)2);
// redisSetFreeCallback(c,__test_callback,(void*)4);
// redisFree(c);
// test_cond(__test_callback_flags == ((2 << 8) | 4));
//
// test("redisBufferWrite against empty write buffer: ");
// c = __connect_nonblock();
// test_cond(redisBufferWrite(c,&wdone) == REDIS_OK && wdone == 1);
// redisFree(c);
//
// test("redisBufferWrite against not yet connected fd: ");
// c = __connect_nonblock();
// redisCommand(c,"PING");
// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
// strncmp(c->error,"write:",6) == 0);
// redisFree(c);
//
// test("redisBufferWrite against closed fd: ");
// c = __connect_nonblock();
// redisCommand(c,"PING");
// redisDisconnect(c);
// test_cond(redisBufferWrite(c,NULL) == REDIS_ERR &&
// strncmp(c->error,"write:",6) == 0);
// redisFree(c);
//
// test("Process callbacks in the right sequence: ");
// c = __connect_nonblock();
// redisCommandWithCallback(c,__test_reply_callback,(void*)1,"PING");
// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
// redisCommandWithCallback(c,__test_reply_callback,(void*)3,"PING");
//
// /* Write output buffer */
// wdone = 0;
// while(!wdone) {
// usleep(500);
// redisBufferWrite(c,&wdone);
// }
//
// /* Read until at least one callback is executed (the 3 replies will
// * arrive in a single packet, causing all callbacks to be executed in
// * a single pass). */
// while(__test_callback_flags == 0) {
// assert(redisBufferRead(c) == REDIS_OK);
// redisProcessCallbacks(c);
// }
// test_cond(__test_callback_flags == 0x010203);
// redisFree(c);
//
// test("redisDisconnect executes pending callbacks with NULL reply: ");
// c = __connect_nonblock();
// redisSetDisconnectCallback(c,__test_callback,(void*)1);
// redisCommandWithCallback(c,__test_reply_callback,(void*)2,"PING");
// redisDisconnect(c);
// test_cond(__test_callback_flags == 0x0201);
// redisFree(c);
// }
int main(int argc, char **argv) {
if (argc > 1) {
if (strcmp(argv[1],"-s") == 0)
use_unix = 1;
}
signal(SIGPIPE, SIG_IGN);
test_format_commands();
test_blocking_connection();
test_reply_reader();
// test_nonblocking_connection();
test_throughput();
cleanup();
if (fails == 0) {
printf("ALL TESTS PASSED\n");
} else {
printf("*** %d TESTS FAILED ***\n", fails);
}
return 0;
}
+40
View File
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __UTIL_H
#define __UTIL_H
#include <stdlib.h>
/* Abort on out of memory */
static void redisOOM(void) {
fprintf(stderr,"Out of memory in hiredis");
exit(1);
}
#endif
+1
View File
@@ -0,0 +1 @@
linenoise_example*
+10
View File
@@ -0,0 +1,10 @@
linenoise_example: linenoise.h linenoise.c
linenoise_example: linenoise.o example.o
$(CC) $(ARCH) -Wall -W -Os -g -o linenoise_example linenoise.o example.o
.c.o:
$(CC) $(ARCH) -c -Wall -W -Os -g $<
clean:
rm -f linenoise_example *.o
+45
View File
@@ -0,0 +1,45 @@
# Linenoise
A minimal, zero-config, BSD licensed, readline replacement.
News: linenoise is now part of [Android](http://android.git.kernel.org/?p=platform/system/core.git;a=tree;f=liblinenoise;h=56450eaed7f783760e5e6a5993ef75cde2e29dea;hb=HEAD Android)!
## Can a line editing library be 20k lines of code?
Line editing with some support for history is a really important feature for command line utilities. Instead of retyping almost the same stuff again and again it's just much better to hit the up arrow and edit on syntax errors, or in order to try a slightly different command. But apparently code dealing with terminals is some sort of Black Magic: readline is 30k lines of code, libedit 20k. Is it reasonable to link small utilities to huge libraries just to get a minimal support for line editing?
So what usually happens is either:
* Large programs with configure scripts disabling line editing if readline is not present in the system, or not supporting it at all since readline is GPL licensed and libedit (the BSD clone) is not as known and available as readline is (Readl world example of this problem: Tclsh).
* Smaller programs not using a configure script not supporting line editing at all (A problem we had with Redis-cli for instance).
The result is a pollution of binaries without line editing support.
So I spent more or less two hours doing a reality check resulting in this little library: is it *really* needed for a line editing library to be 20k lines of code? Apparently not, it is possibe to get a very small, zero configuration, trivial to embed library, that solves the problem. Smaller programs will just include this, supporing line editing out of the box. Larger programs may use this little library or just checking with configure if readline/libedit is available and resorting to linenoise if not.
## Terminals, in 2010.
Apparently almost every terminal you can happen to use today has some kind of support for VT100 alike escape sequences. So I tried to write a lib using just very basic VT100 features. The resulting library appears to work everywhere I tried to use it.
Since it's so young I guess there are a few bugs, or the lib may not compile or work with some operating system, but it's a matter of a few weeks and eventually we'll get it right, and there will be no excuses for not shipping command line tools without built-in line editing support.
The library is currently less than 400 lines of code. In order to use it in your project just look at the *example.c* file in the source distribution, it is trivial. Linenoise is BSD code, so you can use both in free software and commercial software.
## Tested with...
* Linux text only console ($TERM = linux)
* Linux KDE terminal application ($TERM = xterm)
* Linux xterm ($TERM = xterm)
* Mac OS X iTerm ($TERM = xterm)
* Mac OS X default Terminal.app ($TERM = xterm)
* OpenBSD 4.5 through an OSX Terminal.app ($TERM = screen)
* IBM AIX 6.1
* FreeBSD xterm ($TERM = xterm)
Please test it everywhere you can and report back!
## Let's push this forward!
Please fork it and add something interesting and send me a pull request. What's especially interesting are fixes, new key bindings, completion.
Send feedbacks to antirez at gmail
+27
View File
@@ -0,0 +1,27 @@
#include <stdio.h>
#include <stdlib.h>
#include "linenoise.h"
void completion(const char *buf, linenoiseCompletions *lc) {
if (buf[0] == 'h') {
linenoiseAddCompletion(lc,"hello");
linenoiseAddCompletion(lc,"hello there");
}
}
int main(void) {
char *line;
linenoiseSetCompletionCallback(completion);
linenoiseHistoryLoad("history.txt"); /* Load the history at startup */
while((line = linenoise("hello> ")) != NULL) {
if (line[0] != '\0') {
printf("echo: '%s'\n", line);
linenoiseHistoryAdd(line);
linenoiseHistorySave("history.txt"); /* Save every new entry */
}
free(line);
}
return 0;
}
+143 -5
View File
@@ -9,6 +9,8 @@
* the 2010 UNIX computers around.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -66,11 +68,20 @@
* CUF (CUrsor Forward)
* Sequence: ESC [ n C
* Effect: moves cursor forward of n chars
*
* The following are used to clear the screen: ESC [ H ESC [ 2 J
* This is actually composed of two sequences:
*
* cursorhome
* Sequence: ESC [ H
* Effect: moves the cursor to upper left corner
*
* ED2 (Clear entire screen)
* Sequence: ESC [ 2 J
* Effect: clear the whole screen
*
*/
#include "fmacros.h"
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
@@ -81,10 +92,12 @@
#include <sys/types.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "linenoise.h"
#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
#define LINENOISE_MAX_LINE 4096
static char *unsupported_term[] = {"dumb","cons25",NULL};
static linenoiseCompletionCallback *completionCallback = NULL;
static struct termios orig_termios; /* in order to restore at exit */
static int rawmode = 0; /* for atexit() function to check if restore is needed*/
@@ -197,6 +210,80 @@ static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_
if (write(fd,seq,strlen(seq)) == -1) return;
}
static void beep() {
fprintf(stderr, "\x7");
fflush(stderr);
}
static void freeCompletions(linenoiseCompletions *lc) {
size_t i;
for (i = 0; i < lc->len; i++)
free(lc->cvec[i]);
if (lc->cvec != NULL)
free(lc->cvec);
}
static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
linenoiseCompletions lc = { 0, NULL };
int nread, nwritten;
char c = 0;
completionCallback(buf,&lc);
if (lc.len == 0) {
beep();
} else {
size_t stop = 0, i = 0;
size_t clen;
while(!stop) {
/* Show completion or original buffer */
if (i < lc.len) {
clen = strlen(lc.cvec[i]);
refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
} else {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
nread = read(fd,&c,1);
if (nread <= 0) {
freeCompletions(&lc);
return -1;
}
switch(c) {
case 9: /* tab */
i = (i+1) % (lc.len+1);
if (i == lc.len) beep();
break;
case 27: /* escape */
/* Re-show original buffer */
if (i < lc.len) {
refreshLine(fd,prompt,buf,*len,*pos,cols);
}
stop = 1;
break;
default:
/* Update buffer and return */
if (i < lc.len) {
nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
*len = *pos = nwritten;
}
stop = 1;
break;
}
}
}
freeCompletions(&lc);
return c; /* Return last read character */
}
void linenoiseClearScreen(void) {
if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
/* nothing to do, just to avoid warning. */
}
}
static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
size_t plen = strlen(prompt);
size_t pos = 0;
@@ -215,16 +302,27 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
while(1) {
char c;
int nread;
char seq[2];
char seq[2], seq2[2];
nread = read(fd,&c,1);
if (nread <= 0) return len;
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == 9 && completionCallback != NULL) {
c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
/* Return on errors */
if (c < 0) return len;
/* Read next character when 0 */
if (c == 0) continue;
}
switch(c) {
case 13: /* enter */
case 4: /* ctrl-d */
history_len--;
free(history[history_len]);
return (len == 0 && c == 4) ? -1 : (int)len;
return (int)len;
case 3: /* ctrl-c */
errno = EAGAIN;
return -1;
@@ -238,6 +336,18 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
refreshLine(fd,prompt,buf,len,pos,cols);
}
break;
case 4: /* ctrl-d, remove char at right of cursor */
if (len > 1 && pos < (len-1)) {
memmove(buf+pos,buf+pos+1,len-pos);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
} else if (len == 0) {
history_len--;
free(history[history_len]);
return -1;
}
break;
case 20: /* ctrl-t */
if (pos > 0 && pos < len) {
int aux = buf[pos-1];
@@ -296,6 +406,18 @@ up_down_arrow:
len = pos = strlen(buf);
refreshLine(fd,prompt,buf,len,pos,cols);
}
} else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
/* extended escape */
if (read(fd,seq2,2) == -1) break;
if (seq[1] == 51 && seq2[0] == 126) {
/* delete */
if (len > 0 && pos < len) {
memmove(buf+pos,buf+pos+1,len-pos-1);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
}
}
}
break;
default:
@@ -340,6 +462,9 @@ up_down_arrow:
pos = len;
refreshLine(fd,prompt,buf,len,pos,cols);
break;
case 12: /* ctrl+l, clear screen */
linenoiseClearScreen();
refreshLine(fd,prompt,buf,len,pos,cols);
}
}
return len;
@@ -392,6 +517,19 @@ char *linenoise(const char *prompt) {
}
}
/* Register a callback function to be called for tab-completion. */
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
completionCallback = fn;
}
void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
size_t len = strlen(str);
char *copy = malloc(len+1);
memcpy(copy,str,len+1);
lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
lc->cvec[lc->len++] = copy;
}
/* Using a circular buffer is smarter, but a bit more complex to handle. */
int linenoiseHistoryAdd(const char *line) {
char *linecopy;
+12
View File
@@ -4,6 +4,8 @@
* See linenoise.c for more information.
*
* Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -34,10 +36,20 @@
#ifndef __LINENOISE_H
#define __LINENOISE_H
typedef struct linenoiseCompletions {
size_t len;
char **cvec;
} linenoiseCompletions;
typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
void linenoiseAddCompletion(linenoiseCompletions *, char *);
char *linenoise(const char *prompt);
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(char *filename);
int linenoiseHistoryLoad(char *filename);
void linenoiseClearScreen(void);
#endif /* __LINENOISE_H */
+34
View File
@@ -0,0 +1,34 @@
Lua License
-----------
Lua is licensed under the terms of the MIT license reproduced below.
This means that Lua is free software and can be used for both academic
and commercial purposes at absolutely no cost.
For details and rationale, see http://www.lua.org/license.html .
===============================================================================
Copyright (C) 1994-2008 Lua.org, PUC-Rio.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
===============================================================================
(end of COPYRIGHT)
+183
View File
@@ -0,0 +1,183 @@
HISTORY for Lua 5.1
* Changes from version 5.0 to 5.1
-------------------------------
Language:
+ new module system.
+ new semantics for control variables of fors.
+ new semantics for setn/getn.
+ new syntax/semantics for varargs.
+ new long strings and comments.
+ new `mod' operator (`%')
+ new length operator #t
+ metatables for all types
API:
+ new functions: lua_createtable, lua_get(set)field, lua_push(to)integer.
+ user supplies memory allocator (lua_open becomes lua_newstate).
+ luaopen_* functions must be called through Lua.
Implementation:
+ new configuration scheme via luaconf.h.
+ incremental garbage collection.
+ better handling of end-of-line in the lexer.
+ fully reentrant parser (new Lua function `load')
+ better support for 64-bit machines.
+ native loadlib support for Mac OS X.
+ standard distribution in only one library (lualib.a merged into lua.a)
* Changes from version 4.0 to 5.0
-------------------------------
Language:
+ lexical scoping.
+ Lua coroutines.
+ standard libraries now packaged in tables.
+ tags replaced by metatables and tag methods replaced by metamethods,
stored in metatables.
+ proper tail calls.
+ each function can have its own global table, which can be shared.
+ new __newindex metamethod, called when we insert a new key into a table.
+ new block comments: --[[ ... ]].
+ new generic for.
+ new weak tables.
+ new boolean type.
+ new syntax "local function".
+ (f()) returns the first value returned by f.
+ {f()} fills a table with all values returned by f.
+ \n ignored in [[\n .
+ fixed and-or priorities.
+ more general syntax for function definition (e.g. function a.x.y:f()...end).
+ more general syntax for function calls (e.g. (print or write)(9)).
+ new functions (time/date, tmpfile, unpack, require, load*, etc.).
API:
+ chunks are loaded by using lua_load; new luaL_loadfile and luaL_loadbuffer.
+ introduced lightweight userdata, a simple "void*" without a metatable.
+ new error handling protocol: the core no longer prints error messages;
all errors are reported to the caller on the stack.
+ new lua_atpanic for host cleanup.
+ new, signal-safe, hook scheme.
Implementation:
+ new license: MIT.
+ new, faster, register-based virtual machine.
+ support for external multithreading and coroutines.
+ new and consistent error message format.
+ the core no longer needs "stdio.h" for anything (except for a single
use of sprintf to convert numbers to strings).
+ lua.c now runs the environment variable LUA_INIT, if present. It can
be "@filename", to run a file, or the chunk itself.
+ support for user extensions in lua.c.
sample implementation given for command line editing.
+ new dynamic loading library, active by default on several platforms.
+ safe garbage-collector metamethods.
+ precompiled bytecodes checked for integrity (secure binary dostring).
+ strings are fully aligned.
+ position capture in string.find.
+ read('*l') can read lines with embedded zeros.
* Changes from version 3.2 to 4.0
-------------------------------
Language:
+ new "break" and "for" statements (both numerical and for tables).
+ uniform treatment of globals: globals are now stored in a Lua table.
+ improved error messages.
+ no more '$debug': full speed *and* full debug information.
+ new read form: read(N) for next N bytes.
+ general read patterns now deprecated.
(still available with -DCOMPAT_READPATTERNS.)
+ all return values are passed as arguments for the last function
(old semantics still available with -DLUA_COMPAT_ARGRET)
+ garbage collection tag methods for tables now deprecated.
+ there is now only one tag method for order.
API:
+ New API: fully re-entrant, simpler, and more efficient.
+ New debug API.
Implementation:
+ faster than ever: cleaner virtual machine and new hashing algorithm.
+ non-recursive garbage-collector algorithm.
+ reduced memory usage for programs with many strings.
+ improved treatment for memory allocation errors.
+ improved support for 16-bit machines (we hope).
+ code now compiles unmodified as both ANSI C and C++.
+ numbers in bases other than 10 are converted using strtoul.
+ new -f option in Lua to support #! scripts.
+ luac can now combine text and binaries.
* Changes from version 3.1 to 3.2
-------------------------------
+ redirected all output in Lua's core to _ERRORMESSAGE and _ALERT.
+ increased limit on the number of constants and globals per function
(from 2^16 to 2^24).
+ debugging info (lua_debug and hooks) moved into lua_state and new API
functions provided to get and set this info.
+ new debug lib gives full debugging access within Lua.
+ new table functions "foreachi", "sort", "tinsert", "tremove", "getn".
+ new io functions "flush", "seek".
* Changes from version 3.0 to 3.1
-------------------------------
+ NEW FEATURE: anonymous functions with closures (via "upvalues").
+ new syntax:
- local variables in chunks.
- better scope control with DO block END.
- constructors can now be also written: { record-part; list-part }.
- more general syntax for function calls and lvalues, e.g.:
f(x).y=1
o:f(x,y):g(z)
f"string" is sugar for f("string")
+ strings may now contain arbitrary binary data (e.g., embedded zeros).
+ major code re-organization and clean-up; reduced module interdependecies.
+ no arbitrary limits on the total number of constants and globals.
+ support for multiple global contexts.
+ better syntax error messages.
+ new traversal functions "foreach" and "foreachvar".
+ the default for numbers is now double.
changing it to use floats or longs is easy.
+ complete debug information stored in pre-compiled chunks.
+ sample interpreter now prompts user when run interactively, and also
handles control-C interruptions gracefully.
* Changes from version 2.5 to 3.0
-------------------------------
+ NEW CONCEPT: "tag methods".
Tag methods replace fallbacks as the meta-mechanism for extending the
semantics of Lua. Whereas fallbacks had a global nature, tag methods
work on objects having the same tag (e.g., groups of tables).
Existing code that uses fallbacks should work without change.
+ new, general syntax for constructors {[exp] = exp, ... }.
+ support for handling variable number of arguments in functions (varargs).
+ support for conditional compilation ($if ... $else ... $end).
+ cleaner semantics in API simplifies host code.
+ better support for writing libraries (auxlib.h).
+ better type checking and error messages in the standard library.
+ luac can now also undump.
* Changes from version 2.4 to 2.5
-------------------------------
+ io and string libraries are now based on pattern matching;
the old libraries are still available for compatibility
+ dofile and dostring can now return values (via return statement)
+ better support for 16- and 64-bit machines
+ expanded documentation, with more examples
* Changes from version 2.2 to 2.4
-------------------------------
+ external compiler creates portable binary files that can be loaded faster
+ interface for debugging and profiling
+ new "getglobal" fallback
+ new functions for handling references to Lua objects
+ new functions in standard lib
+ only one copy of each string is stored
+ expanded documentation, with more examples
* Changes from version 2.1 to 2.2
-------------------------------
+ functions now may be declared with any "lvalue" as a name
+ garbage collection of functions
+ support for pipes
* Changes from version 1.1 to 2.1
-------------------------------
+ object-oriented support
+ fallbacks
+ simplified syntax for tables
+ many internal improvements
(end of HISTORY)
+99
View File
@@ -0,0 +1,99 @@
INSTALL for Lua 5.1
* Building Lua
------------
Lua is built in the src directory, but the build process can be
controlled from the top-level Makefile.
Building Lua on Unix systems should be very easy. First do "make" and
see if your platform is listed. If so, just do "make xxx", where xxx
is your platform name. The platforms currently supported are:
aix ansi bsd freebsd generic linux macosx mingw posix solaris
If your platform is not listed, try the closest one or posix, generic,
ansi, in this order.
See below for customization instructions and for instructions on how
to build with other Windows compilers.
If you want to check that Lua has been built correctly, do "make test"
after building Lua. Also, have a look at the example programs in test.
* Installing Lua
--------------
Once you have built Lua, you may want to install it in an official
place in your system. In this case, do "make install". The official
place and the way to install files are defined in Makefile. You must
have the right permissions to install files.
If you want to build and install Lua in one step, do "make xxx install",
where xxx is your platform name.
If you want to install Lua locally, then do "make local". This will
create directories bin, include, lib, man, and install Lua there as
follows:
bin: lua luac
include: lua.h luaconf.h lualib.h lauxlib.h lua.hpp
lib: liblua.a
man/man1: lua.1 luac.1
These are the only directories you need for development.
There are man pages for lua and luac, in both nroff and html, and a
reference manual in html in doc, some sample code in test, and some
useful stuff in etc. You don't need these directories for development.
If you want to install Lua locally, but in some other directory, do
"make install INSTALL_TOP=xxx", where xxx is your chosen directory.
See below for instructions for Windows and other systems.
* Customization
-------------
Three things can be customized by editing a file:
- Where and how to install Lua -- edit Makefile.
- How to build Lua -- edit src/Makefile.
- Lua features -- edit src/luaconf.h.
You don't actually need to edit the Makefiles because you may set the
relevant variables when invoking make.
On the other hand, if you need to select some Lua features, you'll need
to edit src/luaconf.h. The edited file will be the one installed, and
it will be used by any Lua clients that you build, to ensure consistency.
We strongly recommend that you enable dynamic loading. This is done
automatically for all platforms listed above that have this feature
(and also Windows). See src/luaconf.h and also src/Makefile.
* Building Lua on Windows and other systems
-----------------------------------------
If you're not using the usual Unix tools, then the instructions for
building Lua depend on the compiler you use. You'll need to create
projects (or whatever your compiler uses) for building the library,
the interpreter, and the compiler, as follows:
library: lapi.c lcode.c ldebug.c ldo.c ldump.c lfunc.c lgc.c llex.c
lmem.c lobject.c lopcodes.c lparser.c lstate.c lstring.c
ltable.c ltm.c lundump.c lvm.c lzio.c
lauxlib.c lbaselib.c ldblib.c liolib.c lmathlib.c loslib.c
ltablib.c lstrlib.c loadlib.c linit.c
interpreter: library, lua.c
compiler: library, luac.c print.c
If you use Visual Studio .NET, you can use etc/luavs.bat in its
"Command Prompt".
If all you want is to build the Lua interpreter, you may put all .c files
in a single project, except for luac.c and print.c. Or just use etc/all.c.
To use Lua as a library in your own programs, you'll need to know how to
create and use libraries with your compiler.
As mentioned above, you may edit luaconf.h to select some features before
building Lua.
(end of INSTALL)
+128
View File
@@ -0,0 +1,128 @@
# makefile for installing Lua
# see INSTALL for installation instructions
# see src/Makefile and src/luaconf.h for further customization
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= none
# Where to install. The installation starts in the src and doc directories,
# so take care if INSTALL_TOP is not an absolute path.
INSTALL_TOP= /usr/local
INSTALL_BIN= $(INSTALL_TOP)/bin
INSTALL_INC= $(INSTALL_TOP)/include
INSTALL_LIB= $(INSTALL_TOP)/lib
INSTALL_MAN= $(INSTALL_TOP)/man/man1
#
# You probably want to make INSTALL_LMOD and INSTALL_CMOD consistent with
# LUA_ROOT, LUA_LDIR, and LUA_CDIR in luaconf.h (and also with etc/lua.pc).
INSTALL_LMOD= $(INSTALL_TOP)/share/lua/$V
INSTALL_CMOD= $(INSTALL_TOP)/lib/lua/$V
# How to install. If your install program does not support "-p", then you
# may have to run ranlib on the installed liblua.a (do "make ranlib").
INSTALL= install -p
INSTALL_EXEC= $(INSTALL) -m 0755
INSTALL_DATA= $(INSTALL) -m 0644
#
# If you don't have install you can use cp instead.
# INSTALL= cp -p
# INSTALL_EXEC= $(INSTALL)
# INSTALL_DATA= $(INSTALL)
# Utilities.
MKDIR= mkdir -p
RANLIB= ranlib
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
# Convenience platforms targets.
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
# What to install.
TO_BIN= lua luac
TO_INC= lua.h luaconf.h lualib.h lauxlib.h ../etc/lua.hpp
TO_LIB= liblua.a
TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.1
R= 5.1.4
all: $(PLAT)
$(PLATS) clean:
cd src && $(MAKE) ARCH="$(ARCH)" $@
test: dummy
src/lua test/hello.lua
install: dummy
cd src && $(MKDIR) $(INSTALL_BIN) $(INSTALL_INC) $(INSTALL_LIB) $(INSTALL_MAN) $(INSTALL_LMOD) $(INSTALL_CMOD)
cd src && $(INSTALL_EXEC) $(TO_BIN) $(INSTALL_BIN)
cd src && $(INSTALL_DATA) $(TO_INC) $(INSTALL_INC)
cd src && $(INSTALL_DATA) $(TO_LIB) $(INSTALL_LIB)
cd doc && $(INSTALL_DATA) $(TO_MAN) $(INSTALL_MAN)
ranlib:
cd src && cd $(INSTALL_LIB) && $(RANLIB) $(TO_LIB)
local:
$(MAKE) install INSTALL_TOP=..
none:
@echo "Please do"
@echo " make PLATFORM"
@echo "where PLATFORM is one of these:"
@echo " $(PLATS)"
@echo "See INSTALL for complete instructions."
# make may get confused with test/ and INSTALL in a case-insensitive OS
dummy:
# echo config parameters
echo:
@echo ""
@echo "These are the parameters currently set in src/Makefile to build Lua $R:"
@echo ""
@cd src && $(MAKE) -s echo
@echo ""
@echo "These are the parameters currently set in Makefile to install Lua $R:"
@echo ""
@echo "PLAT = $(PLAT)"
@echo "INSTALL_TOP = $(INSTALL_TOP)"
@echo "INSTALL_BIN = $(INSTALL_BIN)"
@echo "INSTALL_INC = $(INSTALL_INC)"
@echo "INSTALL_LIB = $(INSTALL_LIB)"
@echo "INSTALL_MAN = $(INSTALL_MAN)"
@echo "INSTALL_LMOD = $(INSTALL_LMOD)"
@echo "INSTALL_CMOD = $(INSTALL_CMOD)"
@echo "INSTALL_EXEC = $(INSTALL_EXEC)"
@echo "INSTALL_DATA = $(INSTALL_DATA)"
@echo ""
@echo "See also src/luaconf.h ."
@echo ""
# echo private config parameters
pecho:
@echo "V = $(V)"
@echo "R = $(R)"
@echo "TO_BIN = $(TO_BIN)"
@echo "TO_INC = $(TO_INC)"
@echo "TO_LIB = $(TO_LIB)"
@echo "TO_MAN = $(TO_MAN)"
# echo config parameters as Lua code
# uncomment the last sed expression if you want nil instead of empty strings
lecho:
@echo "-- installation parameters for Lua $R"
@echo "VERSION = '$V'"
@echo "RELEASE = '$R'"
@$(MAKE) echo | grep = | sed -e 's/= /= "/' -e 's/$$/"/' #-e 's/""/nil/'
@echo "-- EOF"
# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) clean test install local none dummy echo pecho lecho
# (end of Makefile)
+37
View File
@@ -0,0 +1,37 @@
README for Lua 5.1
See INSTALL for installation instructions.
See HISTORY for a summary of changes since the last released version.
* What is Lua?
------------
Lua is a powerful, light-weight programming language designed for extending
applications. Lua is also frequently used as a general-purpose, stand-alone
language. Lua is free software.
For complete information, visit Lua's web site at http://www.lua.org/ .
For an executive summary, see http://www.lua.org/about.html .
Lua has been used in many different projects around the world.
For a short list, see http://www.lua.org/uses.html .
* Availability
------------
Lua is freely available for both academic and commercial purposes.
See COPYRIGHT and http://www.lua.org/license.html for details.
Lua can be downloaded at http://www.lua.org/download.html .
* Installation
------------
Lua is implemented in pure ANSI C, and compiles unmodified in all known
platforms that have an ANSI C compiler. In most Unix-like platforms, simply
do "make" with a suitable target. See INSTALL for detailed instructions.
* Origin
------
Lua is developed at Lua.org, a laboratory of the Department of Computer
Science of PUC-Rio (the Pontifical Catholic University of Rio de Janeiro
in Brazil).
For more information about the authors, see http://www.lua.org/authors.html .
(end of README)
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

+499
View File
@@ -0,0 +1,499 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Lua 5.1 Reference Manual - contents</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
<META HTTP-EQUIV="content-type" CONTENT="text/html; charset=iso-8859-1">
<STYLE TYPE="text/css">
ul {
list-style-type: none ;
list-style-position: outside ;
}
</STYLE>
</HEAD>
<BODY>
<HR>
<H1>
<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="" BORDER=0></A>
Lua 5.1 Reference Manual
</H1>
This is an online version of
<BLOCKQUOTE>
<A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
<IMG SRC="cover.png" ALT="" TITLE="buy from Amazon" BORDER=1 ALIGN="left" HSPACE=12>
</A>
<B>Lua 5.1 Reference Manual</B>
<BR>by R. Ierusalimschy, L. H. de Figueiredo, W. Celes
<BR>Lua.org, August 2006
<BR>ISBN 85-903798-3-3
<BR><A HREF="http://www.amazon.com/exec/obidos/ASIN/8590379833/lua-indexmanual-20">
<IMG SRC="amazon.gif" ALT="[Buy from Amazon]" BORDER=0></A>
<BR CLEAR="all">
</BLOCKQUOTE>
<P>
Buy a copy of this book and
<A HREF="http://www.lua.org/donations.html">help to support</A>
the Lua project.
<P>
The reference manual is the official definition of the Lua language.
For a complete introduction to Lua programming, see the book
<A HREF="http://www.lua.org/docs.html#books">Programming in Lua</A>.
<P>
<A HREF="manual.html">start</A>
&middot;
<A HREF="#contents">contents</A>
&middot;
<A HREF="#index">index</A>
&middot;
<A HREF="http://www.lua.org/manual/5.1/pt/">português</A>
&middot;
<A HREF="http://www.lua.org/manual/5.1/es/">español</A>
<HR>
<SMALL>
Copyright &copy; 2006-2008 Lua.org, PUC-Rio.
Freely available under the terms of the
<a href="http://www.lua.org/license.html#5">Lua license</a>.
</SMALL>
<P>
<H2><A NAME="contents">Contents</A></H2>
<UL style="padding: 0">
<LI><A HREF="manual.html">1 - Introduction</A>
<P>
<LI><A HREF="manual.html#2">2 - The Language</A>
<UL>
<LI><A HREF="manual.html#2.1">2.1 - Lexical Conventions</A>
<LI><A HREF="manual.html#2.2">2.2 - Values and Types</A>
<UL>
<LI><A HREF="manual.html#2.2.1">2.2.1 - Coercion</A>
</UL>
<LI><A HREF="manual.html#2.3">2.3 - Variables</A>
<LI><A HREF="manual.html#2.4">2.4 - Statements</A>
<UL>
<LI><A HREF="manual.html#2.4.1">2.4.1 - Chunks</A>
<LI><A HREF="manual.html#2.4.2">2.4.2 - Blocks</A>
<LI><A HREF="manual.html#2.4.3">2.4.3 - Assignment</A>
<LI><A HREF="manual.html#2.4.4">2.4.4 - Control Structures</A>
<LI><A HREF="manual.html#2.4.5">2.4.5 - For Statement</A>
<LI><A HREF="manual.html#2.4.6">2.4.6 - Function Calls as Statements</A>
<LI><A HREF="manual.html#2.4.7">2.4.7 - Local Declarations</A>
</UL>
<LI><A HREF="manual.html#2.5">2.5 - Expressions</A>
<UL>
<LI><A HREF="manual.html#2.5.1">2.5.1 - Arithmetic Operators</A>
<LI><A HREF="manual.html#2.5.2">2.5.2 - Relational Operators</A>
<LI><A HREF="manual.html#2.5.3">2.5.3 - Logical Operators</A>
<LI><A HREF="manual.html#2.5.4">2.5.4 - Concatenation</A>
<LI><A HREF="manual.html#2.5.5">2.5.5 - The Length Operator</A>
<LI><A HREF="manual.html#2.5.6">2.5.6 - Precedence</A>
<LI><A HREF="manual.html#2.5.7">2.5.7 - Table Constructors</A>
<LI><A HREF="manual.html#2.5.8">2.5.8 - Function Calls</A>
<LI><A HREF="manual.html#2.5.9">2.5.9 - Function Definitions</A>
</UL>
<LI><A HREF="manual.html#2.6">2.6 - Visibility Rules</A>
<LI><A HREF="manual.html#2.7">2.7 - Error Handling</A>
<LI><A HREF="manual.html#2.8">2.8 - Metatables</A>
<LI><A HREF="manual.html#2.9">2.9 - Environments</A>
<LI><A HREF="manual.html#2.10">2.10 - Garbage Collection</A>
<UL>
<LI><A HREF="manual.html#2.10.1">2.10.1 - Garbage-Collection Metamethods</A>
<LI><A HREF="manual.html#2.10.2">2.10.2 - Weak Tables</A>
</UL>
<LI><A HREF="manual.html#2.11">2.11 - Coroutines</A>
</UL>
<P>
<LI><A HREF="manual.html#3">3 - The Application Program Interface</A>
<UL>
<LI><A HREF="manual.html#3.1">3.1 - The Stack</A>
<LI><A HREF="manual.html#3.2">3.2 - Stack Size</A>
<LI><A HREF="manual.html#3.3">3.3 - Pseudo-Indices</A>
<LI><A HREF="manual.html#3.4">3.4 - C Closures</A>
<LI><A HREF="manual.html#3.5">3.5 - Registry</A>
<LI><A HREF="manual.html#3.6">3.6 - Error Handling in C</A>
<LI><A HREF="manual.html#3.7">3.7 - Functions and Types</A>
<LI><A HREF="manual.html#3.8">3.8 - The Debug Interface</A>
</UL>
<P>
<LI><A HREF="manual.html#4">4 - The Auxiliary Library</A>
<UL>
<LI><A HREF="manual.html#4.1">4.1 - Functions and Types</A>
</UL>
<P>
<LI><A HREF="manual.html#5">5 - Standard Libraries</A>
<UL>
<LI><A HREF="manual.html#5.1">5.1 - Basic Functions</A>
<LI><A HREF="manual.html#5.2">5.2 - Coroutine Manipulation</A>
<LI><A HREF="manual.html#5.3">5.3 - Modules</A>
<LI><A HREF="manual.html#5.4">5.4 - String Manipulation</A>
<UL>
<LI><A HREF="manual.html#5.4.1">5.4.1 - Patterns</A>
</UL>
<LI><A HREF="manual.html#5.5">5.5 - Table Manipulation</A>
<LI><A HREF="manual.html#5.6">5.6 - Mathematical Functions</A>
<LI><A HREF="manual.html#5.7">5.7 - Input and Output Facilities</A>
<LI><A HREF="manual.html#5.8">5.8 - Operating System Facilities</A>
<LI><A HREF="manual.html#5.9">5.9 - The Debug Library</A>
</UL>
<P>
<LI><A HREF="manual.html#6">6 - Lua Stand-alone</A>
<P>
<LI><A HREF="manual.html#7">7 - Incompatibilities with the Previous Version</A>
<UL>
<LI><A HREF="manual.html#7.1">7.1 - Changes in the Language</A>
<LI><A HREF="manual.html#7.2">7.2 - Changes in the Libraries</A>
<LI><A HREF="manual.html#7.3">7.3 - Changes in the API</A>
</UL>
<P>
<LI><A HREF="manual.html#8">8 - The Complete Syntax of Lua</A>
</UL>
<H2><A NAME="index">Index</A></H2>
<TABLE WIDTH="100%">
<TR VALIGN="top">
<TD>
<H3><A NAME="functions">Lua functions</A></H3>
<A HREF="manual.html#pdf-_G">_G</A><BR>
<A HREF="manual.html#pdf-_VERSION">_VERSION</A><BR>
<A HREF="manual.html#pdf-assert">assert</A><BR>
<A HREF="manual.html#pdf-collectgarbage">collectgarbage</A><BR>
<A HREF="manual.html#pdf-dofile">dofile</A><BR>
<A HREF="manual.html#pdf-error">error</A><BR>
<A HREF="manual.html#pdf-getfenv">getfenv</A><BR>
<A HREF="manual.html#pdf-getmetatable">getmetatable</A><BR>
<A HREF="manual.html#pdf-ipairs">ipairs</A><BR>
<A HREF="manual.html#pdf-load">load</A><BR>
<A HREF="manual.html#pdf-loadfile">loadfile</A><BR>
<A HREF="manual.html#pdf-loadstring">loadstring</A><BR>
<A HREF="manual.html#pdf-module">module</A><BR>
<A HREF="manual.html#pdf-next">next</A><BR>
<A HREF="manual.html#pdf-pairs">pairs</A><BR>
<A HREF="manual.html#pdf-pcall">pcall</A><BR>
<A HREF="manual.html#pdf-print">print</A><BR>
<A HREF="manual.html#pdf-rawequal">rawequal</A><BR>
<A HREF="manual.html#pdf-rawget">rawget</A><BR>
<A HREF="manual.html#pdf-rawset">rawset</A><BR>
<A HREF="manual.html#pdf-require">require</A><BR>
<A HREF="manual.html#pdf-select">select</A><BR>
<A HREF="manual.html#pdf-setfenv">setfenv</A><BR>
<A HREF="manual.html#pdf-setmetatable">setmetatable</A><BR>
<A HREF="manual.html#pdf-tonumber">tonumber</A><BR>
<A HREF="manual.html#pdf-tostring">tostring</A><BR>
<A HREF="manual.html#pdf-type">type</A><BR>
<A HREF="manual.html#pdf-unpack">unpack</A><BR>
<A HREF="manual.html#pdf-xpcall">xpcall</A><BR>
<P>
<A HREF="manual.html#pdf-coroutine.create">coroutine.create</A><BR>
<A HREF="manual.html#pdf-coroutine.resume">coroutine.resume</A><BR>
<A HREF="manual.html#pdf-coroutine.running">coroutine.running</A><BR>
<A HREF="manual.html#pdf-coroutine.status">coroutine.status</A><BR>
<A HREF="manual.html#pdf-coroutine.wrap">coroutine.wrap</A><BR>
<A HREF="manual.html#pdf-coroutine.yield">coroutine.yield</A><BR>
<P>
<A HREF="manual.html#pdf-debug.debug">debug.debug</A><BR>
<A HREF="manual.html#pdf-debug.getfenv">debug.getfenv</A><BR>
<A HREF="manual.html#pdf-debug.gethook">debug.gethook</A><BR>
<A HREF="manual.html#pdf-debug.getinfo">debug.getinfo</A><BR>
<A HREF="manual.html#pdf-debug.getlocal">debug.getlocal</A><BR>
<A HREF="manual.html#pdf-debug.getmetatable">debug.getmetatable</A><BR>
<A HREF="manual.html#pdf-debug.getregistry">debug.getregistry</A><BR>
<A HREF="manual.html#pdf-debug.getupvalue">debug.getupvalue</A><BR>
<A HREF="manual.html#pdf-debug.setfenv">debug.setfenv</A><BR>
<A HREF="manual.html#pdf-debug.sethook">debug.sethook</A><BR>
<A HREF="manual.html#pdf-debug.setlocal">debug.setlocal</A><BR>
<A HREF="manual.html#pdf-debug.setmetatable">debug.setmetatable</A><BR>
<A HREF="manual.html#pdf-debug.setupvalue">debug.setupvalue</A><BR>
<A HREF="manual.html#pdf-debug.traceback">debug.traceback</A><BR>
</TD>
<TD>
<H3>&nbsp;</H3>
<A HREF="manual.html#pdf-file:close">file:close</A><BR>
<A HREF="manual.html#pdf-file:flush">file:flush</A><BR>
<A HREF="manual.html#pdf-file:lines">file:lines</A><BR>
<A HREF="manual.html#pdf-file:read">file:read</A><BR>
<A HREF="manual.html#pdf-file:seek">file:seek</A><BR>
<A HREF="manual.html#pdf-file:setvbuf">file:setvbuf</A><BR>
<A HREF="manual.html#pdf-file:write">file:write</A><BR>
<P>
<A HREF="manual.html#pdf-io.close">io.close</A><BR>
<A HREF="manual.html#pdf-io.flush">io.flush</A><BR>
<A HREF="manual.html#pdf-io.input">io.input</A><BR>
<A HREF="manual.html#pdf-io.lines">io.lines</A><BR>
<A HREF="manual.html#pdf-io.open">io.open</A><BR>
<A HREF="manual.html#pdf-io.output">io.output</A><BR>
<A HREF="manual.html#pdf-io.popen">io.popen</A><BR>
<A HREF="manual.html#pdf-io.read">io.read</A><BR>
<A HREF="manual.html#pdf-io.stderr">io.stderr</A><BR>
<A HREF="manual.html#pdf-io.stdin">io.stdin</A><BR>
<A HREF="manual.html#pdf-io.stdout">io.stdout</A><BR>
<A HREF="manual.html#pdf-io.tmpfile">io.tmpfile</A><BR>
<A HREF="manual.html#pdf-io.type">io.type</A><BR>
<A HREF="manual.html#pdf-io.write">io.write</A><BR>
<P>
<A HREF="manual.html#pdf-math.abs">math.abs</A><BR>
<A HREF="manual.html#pdf-math.acos">math.acos</A><BR>
<A HREF="manual.html#pdf-math.asin">math.asin</A><BR>
<A HREF="manual.html#pdf-math.atan">math.atan</A><BR>
<A HREF="manual.html#pdf-math.atan2">math.atan2</A><BR>
<A HREF="manual.html#pdf-math.ceil">math.ceil</A><BR>
<A HREF="manual.html#pdf-math.cos">math.cos</A><BR>
<A HREF="manual.html#pdf-math.cosh">math.cosh</A><BR>
<A HREF="manual.html#pdf-math.deg">math.deg</A><BR>
<A HREF="manual.html#pdf-math.exp">math.exp</A><BR>
<A HREF="manual.html#pdf-math.floor">math.floor</A><BR>
<A HREF="manual.html#pdf-math.fmod">math.fmod</A><BR>
<A HREF="manual.html#pdf-math.frexp">math.frexp</A><BR>
<A HREF="manual.html#pdf-math.huge">math.huge</A><BR>
<A HREF="manual.html#pdf-math.ldexp">math.ldexp</A><BR>
<A HREF="manual.html#pdf-math.log">math.log</A><BR>
<A HREF="manual.html#pdf-math.log10">math.log10</A><BR>
<A HREF="manual.html#pdf-math.max">math.max</A><BR>
<A HREF="manual.html#pdf-math.min">math.min</A><BR>
<A HREF="manual.html#pdf-math.modf">math.modf</A><BR>
<A HREF="manual.html#pdf-math.pi">math.pi</A><BR>
<A HREF="manual.html#pdf-math.pow">math.pow</A><BR>
<A HREF="manual.html#pdf-math.rad">math.rad</A><BR>
<A HREF="manual.html#pdf-math.random">math.random</A><BR>
<A HREF="manual.html#pdf-math.randomseed">math.randomseed</A><BR>
<A HREF="manual.html#pdf-math.sin">math.sin</A><BR>
<A HREF="manual.html#pdf-math.sinh">math.sinh</A><BR>
<A HREF="manual.html#pdf-math.sqrt">math.sqrt</A><BR>
<A HREF="manual.html#pdf-math.tan">math.tan</A><BR>
<A HREF="manual.html#pdf-math.tanh">math.tanh</A><BR>
<P>
<A HREF="manual.html#pdf-os.clock">os.clock</A><BR>
<A HREF="manual.html#pdf-os.date">os.date</A><BR>
<A HREF="manual.html#pdf-os.difftime">os.difftime</A><BR>
<A HREF="manual.html#pdf-os.execute">os.execute</A><BR>
<A HREF="manual.html#pdf-os.exit">os.exit</A><BR>
<A HREF="manual.html#pdf-os.getenv">os.getenv</A><BR>
<A HREF="manual.html#pdf-os.remove">os.remove</A><BR>
<A HREF="manual.html#pdf-os.rename">os.rename</A><BR>
<A HREF="manual.html#pdf-os.setlocale">os.setlocale</A><BR>
<A HREF="manual.html#pdf-os.time">os.time</A><BR>
<A HREF="manual.html#pdf-os.tmpname">os.tmpname</A><BR>
<P>
<A HREF="manual.html#pdf-package.cpath">package.cpath</A><BR>
<A HREF="manual.html#pdf-package.loaded">package.loaded</A><BR>
<A HREF="manual.html#pdf-package.loaders">package.loaders</A><BR>
<A HREF="manual.html#pdf-package.loadlib">package.loadlib</A><BR>
<A HREF="manual.html#pdf-package.path">package.path</A><BR>
<A HREF="manual.html#pdf-package.preload">package.preload</A><BR>
<A HREF="manual.html#pdf-package.seeall">package.seeall</A><BR>
<P>
<A HREF="manual.html#pdf-string.byte">string.byte</A><BR>
<A HREF="manual.html#pdf-string.char">string.char</A><BR>
<A HREF="manual.html#pdf-string.dump">string.dump</A><BR>
<A HREF="manual.html#pdf-string.find">string.find</A><BR>
<A HREF="manual.html#pdf-string.format">string.format</A><BR>
<A HREF="manual.html#pdf-string.gmatch">string.gmatch</A><BR>
<A HREF="manual.html#pdf-string.gsub">string.gsub</A><BR>
<A HREF="manual.html#pdf-string.len">string.len</A><BR>
<A HREF="manual.html#pdf-string.lower">string.lower</A><BR>
<A HREF="manual.html#pdf-string.match">string.match</A><BR>
<A HREF="manual.html#pdf-string.rep">string.rep</A><BR>
<A HREF="manual.html#pdf-string.reverse">string.reverse</A><BR>
<A HREF="manual.html#pdf-string.sub">string.sub</A><BR>
<A HREF="manual.html#pdf-string.upper">string.upper</A><BR>
<P>
<A HREF="manual.html#pdf-table.concat">table.concat</A><BR>
<A HREF="manual.html#pdf-table.insert">table.insert</A><BR>
<A HREF="manual.html#pdf-table.maxn">table.maxn</A><BR>
<A HREF="manual.html#pdf-table.remove">table.remove</A><BR>
<A HREF="manual.html#pdf-table.sort">table.sort</A><BR>
</TD>
<TD>
<H3>C API</H3>
<A HREF="manual.html#lua_Alloc">lua_Alloc</A><BR>
<A HREF="manual.html#lua_CFunction">lua_CFunction</A><BR>
<A HREF="manual.html#lua_Debug">lua_Debug</A><BR>
<A HREF="manual.html#lua_Hook">lua_Hook</A><BR>
<A HREF="manual.html#lua_Integer">lua_Integer</A><BR>
<A HREF="manual.html#lua_Number">lua_Number</A><BR>
<A HREF="manual.html#lua_Reader">lua_Reader</A><BR>
<A HREF="manual.html#lua_State">lua_State</A><BR>
<A HREF="manual.html#lua_Writer">lua_Writer</A><BR>
<P>
<A HREF="manual.html#lua_atpanic">lua_atpanic</A><BR>
<A HREF="manual.html#lua_call">lua_call</A><BR>
<A HREF="manual.html#lua_checkstack">lua_checkstack</A><BR>
<A HREF="manual.html#lua_close">lua_close</A><BR>
<A HREF="manual.html#lua_concat">lua_concat</A><BR>
<A HREF="manual.html#lua_cpcall">lua_cpcall</A><BR>
<A HREF="manual.html#lua_createtable">lua_createtable</A><BR>
<A HREF="manual.html#lua_dump">lua_dump</A><BR>
<A HREF="manual.html#lua_equal">lua_equal</A><BR>
<A HREF="manual.html#lua_error">lua_error</A><BR>
<A HREF="manual.html#lua_gc">lua_gc</A><BR>
<A HREF="manual.html#lua_getallocf">lua_getallocf</A><BR>
<A HREF="manual.html#lua_getfenv">lua_getfenv</A><BR>
<A HREF="manual.html#lua_getfield">lua_getfield</A><BR>
<A HREF="manual.html#lua_getglobal">lua_getglobal</A><BR>
<A HREF="manual.html#lua_gethook">lua_gethook</A><BR>
<A HREF="manual.html#lua_gethookcount">lua_gethookcount</A><BR>
<A HREF="manual.html#lua_gethookmask">lua_gethookmask</A><BR>
<A HREF="manual.html#lua_getinfo">lua_getinfo</A><BR>
<A HREF="manual.html#lua_getlocal">lua_getlocal</A><BR>
<A HREF="manual.html#lua_getmetatable">lua_getmetatable</A><BR>
<A HREF="manual.html#lua_getstack">lua_getstack</A><BR>
<A HREF="manual.html#lua_gettable">lua_gettable</A><BR>
<A HREF="manual.html#lua_gettop">lua_gettop</A><BR>
<A HREF="manual.html#lua_getupvalue">lua_getupvalue</A><BR>
<A HREF="manual.html#lua_insert">lua_insert</A><BR>
<A HREF="manual.html#lua_isboolean">lua_isboolean</A><BR>
<A HREF="manual.html#lua_iscfunction">lua_iscfunction</A><BR>
<A HREF="manual.html#lua_isfunction">lua_isfunction</A><BR>
<A HREF="manual.html#lua_islightuserdata">lua_islightuserdata</A><BR>
<A HREF="manual.html#lua_isnil">lua_isnil</A><BR>
<A HREF="manual.html#lua_isnone">lua_isnone</A><BR>
<A HREF="manual.html#lua_isnoneornil">lua_isnoneornil</A><BR>
<A HREF="manual.html#lua_isnumber">lua_isnumber</A><BR>
<A HREF="manual.html#lua_isstring">lua_isstring</A><BR>
<A HREF="manual.html#lua_istable">lua_istable</A><BR>
<A HREF="manual.html#lua_isthread">lua_isthread</A><BR>
<A HREF="manual.html#lua_isuserdata">lua_isuserdata</A><BR>
<A HREF="manual.html#lua_lessthan">lua_lessthan</A><BR>
<A HREF="manual.html#lua_load">lua_load</A><BR>
<A HREF="manual.html#lua_newstate">lua_newstate</A><BR>
<A HREF="manual.html#lua_newtable">lua_newtable</A><BR>
<A HREF="manual.html#lua_newthread">lua_newthread</A><BR>
<A HREF="manual.html#lua_newuserdata">lua_newuserdata</A><BR>
<A HREF="manual.html#lua_next">lua_next</A><BR>
<A HREF="manual.html#lua_objlen">lua_objlen</A><BR>
<A HREF="manual.html#lua_pcall">lua_pcall</A><BR>
<A HREF="manual.html#lua_pop">lua_pop</A><BR>
<A HREF="manual.html#lua_pushboolean">lua_pushboolean</A><BR>
<A HREF="manual.html#lua_pushcclosure">lua_pushcclosure</A><BR>
<A HREF="manual.html#lua_pushcfunction">lua_pushcfunction</A><BR>
<A HREF="manual.html#lua_pushfstring">lua_pushfstring</A><BR>
<A HREF="manual.html#lua_pushinteger">lua_pushinteger</A><BR>
<A HREF="manual.html#lua_pushlightuserdata">lua_pushlightuserdata</A><BR>
<A HREF="manual.html#lua_pushliteral">lua_pushliteral</A><BR>
<A HREF="manual.html#lua_pushlstring">lua_pushlstring</A><BR>
<A HREF="manual.html#lua_pushnil">lua_pushnil</A><BR>
<A HREF="manual.html#lua_pushnumber">lua_pushnumber</A><BR>
<A HREF="manual.html#lua_pushstring">lua_pushstring</A><BR>
<A HREF="manual.html#lua_pushthread">lua_pushthread</A><BR>
<A HREF="manual.html#lua_pushvalue">lua_pushvalue</A><BR>
<A HREF="manual.html#lua_pushvfstring">lua_pushvfstring</A><BR>
<A HREF="manual.html#lua_rawequal">lua_rawequal</A><BR>
<A HREF="manual.html#lua_rawget">lua_rawget</A><BR>
<A HREF="manual.html#lua_rawgeti">lua_rawgeti</A><BR>
<A HREF="manual.html#lua_rawset">lua_rawset</A><BR>
<A HREF="manual.html#lua_rawseti">lua_rawseti</A><BR>
<A HREF="manual.html#lua_register">lua_register</A><BR>
<A HREF="manual.html#lua_remove">lua_remove</A><BR>
<A HREF="manual.html#lua_replace">lua_replace</A><BR>
<A HREF="manual.html#lua_resume">lua_resume</A><BR>
<A HREF="manual.html#lua_setallocf">lua_setallocf</A><BR>
<A HREF="manual.html#lua_setfenv">lua_setfenv</A><BR>
<A HREF="manual.html#lua_setfield">lua_setfield</A><BR>
<A HREF="manual.html#lua_setglobal">lua_setglobal</A><BR>
<A HREF="manual.html#lua_sethook">lua_sethook</A><BR>
<A HREF="manual.html#lua_setlocal">lua_setlocal</A><BR>
<A HREF="manual.html#lua_setmetatable">lua_setmetatable</A><BR>
<A HREF="manual.html#lua_settable">lua_settable</A><BR>
<A HREF="manual.html#lua_settop">lua_settop</A><BR>
<A HREF="manual.html#lua_setupvalue">lua_setupvalue</A><BR>
<A HREF="manual.html#lua_status">lua_status</A><BR>
<A HREF="manual.html#lua_toboolean">lua_toboolean</A><BR>
<A HREF="manual.html#lua_tocfunction">lua_tocfunction</A><BR>
<A HREF="manual.html#lua_tointeger">lua_tointeger</A><BR>
<A HREF="manual.html#lua_tolstring">lua_tolstring</A><BR>
<A HREF="manual.html#lua_tonumber">lua_tonumber</A><BR>
<A HREF="manual.html#lua_topointer">lua_topointer</A><BR>
<A HREF="manual.html#lua_tostring">lua_tostring</A><BR>
<A HREF="manual.html#lua_tothread">lua_tothread</A><BR>
<A HREF="manual.html#lua_touserdata">lua_touserdata</A><BR>
<A HREF="manual.html#lua_type">lua_type</A><BR>
<A HREF="manual.html#lua_typename">lua_typename</A><BR>
<A HREF="manual.html#lua_upvalueindex">lua_upvalueindex</A><BR>
<A HREF="manual.html#lua_xmove">lua_xmove</A><BR>
<A HREF="manual.html#lua_yield">lua_yield</A><BR>
</TD>
<TD>
<H3>auxiliary library</H3>
<A HREF="manual.html#luaL_Buffer">luaL_Buffer</A><BR>
<A HREF="manual.html#luaL_Reg">luaL_Reg</A><BR>
<P>
<A HREF="manual.html#luaL_addchar">luaL_addchar</A><BR>
<A HREF="manual.html#luaL_addlstring">luaL_addlstring</A><BR>
<A HREF="manual.html#luaL_addsize">luaL_addsize</A><BR>
<A HREF="manual.html#luaL_addstring">luaL_addstring</A><BR>
<A HREF="manual.html#luaL_addvalue">luaL_addvalue</A><BR>
<A HREF="manual.html#luaL_argcheck">luaL_argcheck</A><BR>
<A HREF="manual.html#luaL_argerror">luaL_argerror</A><BR>
<A HREF="manual.html#luaL_buffinit">luaL_buffinit</A><BR>
<A HREF="manual.html#luaL_callmeta">luaL_callmeta</A><BR>
<A HREF="manual.html#luaL_checkany">luaL_checkany</A><BR>
<A HREF="manual.html#luaL_checkint">luaL_checkint</A><BR>
<A HREF="manual.html#luaL_checkinteger">luaL_checkinteger</A><BR>
<A HREF="manual.html#luaL_checklong">luaL_checklong</A><BR>
<A HREF="manual.html#luaL_checklstring">luaL_checklstring</A><BR>
<A HREF="manual.html#luaL_checknumber">luaL_checknumber</A><BR>
<A HREF="manual.html#luaL_checkoption">luaL_checkoption</A><BR>
<A HREF="manual.html#luaL_checkstack">luaL_checkstack</A><BR>
<A HREF="manual.html#luaL_checkstring">luaL_checkstring</A><BR>
<A HREF="manual.html#luaL_checktype">luaL_checktype</A><BR>
<A HREF="manual.html#luaL_checkudata">luaL_checkudata</A><BR>
<A HREF="manual.html#luaL_dofile">luaL_dofile</A><BR>
<A HREF="manual.html#luaL_dostring">luaL_dostring</A><BR>
<A HREF="manual.html#luaL_error">luaL_error</A><BR>
<A HREF="manual.html#luaL_getmetafield">luaL_getmetafield</A><BR>
<A HREF="manual.html#luaL_getmetatable">luaL_getmetatable</A><BR>
<A HREF="manual.html#luaL_gsub">luaL_gsub</A><BR>
<A HREF="manual.html#luaL_loadbuffer">luaL_loadbuffer</A><BR>
<A HREF="manual.html#luaL_loadfile">luaL_loadfile</A><BR>
<A HREF="manual.html#luaL_loadstring">luaL_loadstring</A><BR>
<A HREF="manual.html#luaL_newmetatable">luaL_newmetatable</A><BR>
<A HREF="manual.html#luaL_newstate">luaL_newstate</A><BR>
<A HREF="manual.html#luaL_openlibs">luaL_openlibs</A><BR>
<A HREF="manual.html#luaL_optint">luaL_optint</A><BR>
<A HREF="manual.html#luaL_optinteger">luaL_optinteger</A><BR>
<A HREF="manual.html#luaL_optlong">luaL_optlong</A><BR>
<A HREF="manual.html#luaL_optlstring">luaL_optlstring</A><BR>
<A HREF="manual.html#luaL_optnumber">luaL_optnumber</A><BR>
<A HREF="manual.html#luaL_optstring">luaL_optstring</A><BR>
<A HREF="manual.html#luaL_prepbuffer">luaL_prepbuffer</A><BR>
<A HREF="manual.html#luaL_pushresult">luaL_pushresult</A><BR>
<A HREF="manual.html#luaL_ref">luaL_ref</A><BR>
<A HREF="manual.html#luaL_register">luaL_register</A><BR>
<A HREF="manual.html#luaL_typename">luaL_typename</A><BR>
<A HREF="manual.html#luaL_typerror">luaL_typerror</A><BR>
<A HREF="manual.html#luaL_unref">luaL_unref</A><BR>
<A HREF="manual.html#luaL_where">luaL_where</A><BR>
</TD>
</TR>
</TABLE>
<P>
<HR>
<SMALL>
Last update:
Sat Jan 19 13:24:29 BRST 2008
</SMALL>
<!--
Last change: revised for Lua 5.1.3
-->
</BODY>
</HTML>
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

+163
View File
@@ -0,0 +1,163 @@
.\" $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $
.TH LUA 1 "$Date: 2006/01/06 16:03:34 $"
.SH NAME
lua \- Lua interpreter
.SH SYNOPSIS
.B lua
[
.I options
]
[
.I script
[
.I args
]
]
.SH DESCRIPTION
.B lua
is the stand-alone Lua interpreter.
It loads and executes Lua programs,
either in textual source form or
in precompiled binary form.
(Precompiled binaries are output by
.BR luac ,
the Lua compiler.)
.B lua
can be used as a batch interpreter and also interactively.
.LP
The given
.I options
(see below)
are executed and then
the Lua program in file
.I script
is loaded and executed.
The given
.I args
are available to
.I script
as strings in a global table named
.BR arg .
If these arguments contain spaces or other characters special to the shell,
then they should be quoted
(but note that the quotes will be removed by the shell).
The arguments in
.B arg
start at 0,
which contains the string
.RI ' script '.
The index of the last argument is stored in
.BR arg.n .
The arguments given in the command line before
.IR script ,
including the name of the interpreter,
are available in negative indices in
.BR arg .
.LP
At the very start,
before even handling the command line,
.B lua
executes the contents of the environment variable
.BR LUA_INIT ,
if it is defined.
If the value of
.B LUA_INIT
is of the form
.RI '@ filename ',
then
.I filename
is executed.
Otherwise, the string is assumed to be a Lua statement and is executed.
.LP
Options start with
.B '\-'
and are described below.
You can use
.B "'\--'"
to signal the end of options.
.LP
If no arguments are given,
then
.B "\-v \-i"
is assumed when the standard input is a terminal;
otherwise,
.B "\-"
is assumed.
.LP
In interactive mode,
.B lua
prompts the user,
reads lines from the standard input,
and executes them as they are read.
If a line does not contain a complete statement,
then a secondary prompt is displayed and
lines are read until a complete statement is formed or
a syntax error is found.
So, one way to interrupt the reading of an incomplete statement is
to force a syntax error:
adding a
.B ';'
in the middle of a statement is a sure way of forcing a syntax error
(except inside multiline strings and comments; these must be closed explicitly).
If a line starts with
.BR '=' ,
then
.B lua
displays the values of all the expressions in the remainder of the
line. The expressions must be separated by commas.
The primary prompt is the value of the global variable
.BR _PROMPT ,
if this value is a string;
otherwise, the default prompt is used.
Similarly, the secondary prompt is the value of the global variable
.BR _PROMPT2 .
So,
to change the prompts,
set the corresponding variable to a string of your choice.
You can do that after calling the interpreter
or on the command line
(but in this case you have to be careful with quotes
if the prompt string contains a space; otherwise you may confuse the shell.)
The default prompts are "> " and ">> ".
.SH OPTIONS
.TP
.B \-
load and execute the standard input as a file,
that is,
not interactively,
even when the standard input is a terminal.
.TP
.BI \-e " stat"
execute statement
.IR stat .
You need to quote
.I stat
if it contains spaces, quotes,
or other characters special to the shell.
.TP
.B \-i
enter interactive mode after
.I script
is executed.
.TP
.BI \-l " name"
call
.BI require(' name ')
before executing
.IR script .
Typically used to load libraries.
.TP
.B \-v
show version information.
.SH "SEE ALSO"
.BR luac (1)
.br
http://www.lua.org/
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH AUTHORS
R. Ierusalimschy,
L. H. de Figueiredo,
and
W. Celes
.\" EOF
+41
View File
@@ -0,0 +1,41 @@
body {
color: #000000 ;
background-color: #FFFFFF ;
font-family: sans-serif ;
text-align: justify ;
margin-right: 20px ;
margin-left: 20px ;
}
h1, h2, h3, h4 {
font-weight: normal ;
font-style: italic ;
}
a:link {
color: #000080 ;
background-color: inherit ;
text-decoration: none ;
}
a:visited {
background-color: inherit ;
text-decoration: none ;
}
a:link:hover, a:visited:hover {
color: #000080 ;
background-color: #E0E0FF ;
}
a:link:active, a:visited:active {
color: #FF0000 ;
}
hr {
border: 0 ;
height: 1px ;
color: #a0a0a0 ;
background-color: #a0a0a0 ;
}
+172
View File
@@ -0,0 +1,172 @@
<!-- $Id: lua.man,v 1.11 2006/01/06 16:03:34 lhf Exp $ -->
<HTML>
<HEAD>
<TITLE>LUA man page</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H2>NAME</H2>
lua - Lua interpreter
<H2>SYNOPSIS</H2>
<B>lua</B>
[
<I>options</I>
]
[
<I>script</I>
[
<I>args</I>
]
]
<H2>DESCRIPTION</H2>
<B>lua</B>
is the stand-alone Lua interpreter.
It loads and executes Lua programs,
either in textual source form or
in precompiled binary form.
(Precompiled binaries are output by
<B>luac</B>,
the Lua compiler.)
<B>lua</B>
can be used as a batch interpreter and also interactively.
<P>
The given
<I>options</I>
(see below)
are executed and then
the Lua program in file
<I>script</I>
is loaded and executed.
The given
<I>args</I>
are available to
<I>script</I>
as strings in a global table named
<B>arg</B>.
If these arguments contain spaces or other characters special to the shell,
then they should be quoted
(but note that the quotes will be removed by the shell).
The arguments in
<B>arg</B>
start at 0,
which contains the string
'<I>script</I>'.
The index of the last argument is stored in
<B>arg.n</B>.
The arguments given in the command line before
<I>script</I>,
including the name of the interpreter,
are available in negative indices in
<B>arg</B>.
<P>
At the very start,
before even handling the command line,
<B>lua</B>
executes the contents of the environment variable
<B>LUA_INIT</B>,
if it is defined.
If the value of
<B>LUA_INIT</B>
is of the form
'@<I>filename</I>',
then
<I>filename</I>
is executed.
Otherwise, the string is assumed to be a Lua statement and is executed.
<P>
Options start with
<B>'-'</B>
and are described below.
You can use
<B>'--'</B>
to signal the end of options.
<P>
If no arguments are given,
then
<B>"-v -i"</B>
is assumed when the standard input is a terminal;
otherwise,
<B>"-"</B>
is assumed.
<P>
In interactive mode,
<B>lua</B>
prompts the user,
reads lines from the standard input,
and executes them as they are read.
If a line does not contain a complete statement,
then a secondary prompt is displayed and
lines are read until a complete statement is formed or
a syntax error is found.
So, one way to interrupt the reading of an incomplete statement is
to force a syntax error:
adding a
<B>';'</B>
in the middle of a statement is a sure way of forcing a syntax error
(except inside multiline strings and comments; these must be closed explicitly).
If a line starts with
<B>'='</B>,
then
<B>lua</B>
displays the values of all the expressions in the remainder of the
line. The expressions must be separated by commas.
The primary prompt is the value of the global variable
<B>_PROMPT</B>,
if this value is a string;
otherwise, the default prompt is used.
Similarly, the secondary prompt is the value of the global variable
<B>_PROMPT2</B>.
So,
to change the prompts,
set the corresponding variable to a string of your choice.
You can do that after calling the interpreter
or on the command line
(but in this case you have to be careful with quotes
if the prompt string contains a space; otherwise you may confuse the shell.)
The default prompts are "&gt; " and "&gt;&gt; ".
<H2>OPTIONS</H2>
<P>
<B>-</B>
load and execute the standard input as a file,
that is,
not interactively,
even when the standard input is a terminal.
<P>
<B>-e </B><I>stat</I>
execute statement
<I>stat</I>.
You need to quote
<I>stat </I>
if it contains spaces, quotes,
or other characters special to the shell.
<P>
<B>-i</B>
enter interactive mode after
<I>script</I>
is executed.
<P>
<B>-l </B><I>name</I>
call
<B>require</B>('<I>name</I>')
before executing
<I>script</I>.
Typically used to load libraries.
<P>
<B>-v</B>
show version information.
<H2>SEE ALSO</H2>
<B>luac</B>(1)
<BR>
<A HREF="http://www.lua.org/">http://www.lua.org/</A>
<H2>DIAGNOSTICS</H2>
Error messages should be self explanatory.
<H2>AUTHORS</H2>
R. Ierusalimschy,
L. H. de Figueiredo,
and
W. Celes
<!-- EOF -->
</BODY>
</HTML>
+136
View File
@@ -0,0 +1,136 @@
.\" $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $
.TH LUAC 1 "$Date: 2006/01/06 16:03:34 $"
.SH NAME
luac \- Lua compiler
.SH SYNOPSIS
.B luac
[
.I options
] [
.I filenames
]
.SH DESCRIPTION
.B luac
is the Lua compiler.
It translates programs written in the Lua programming language
into binary files that can be later loaded and executed.
.LP
The main advantages of precompiling chunks are:
faster loading,
protecting source code from accidental user changes,
and
off-line syntax checking.
.LP
Pre-compiling does not imply faster execution
because in Lua chunks are always compiled into bytecodes before being executed.
.B luac
simply allows those bytecodes to be saved in a file for later execution.
.LP
Pre-compiled chunks are not necessarily smaller than the corresponding source.
The main goal in pre-compiling is faster loading.
.LP
The binary files created by
.B luac
are portable only among architectures with the same word size and byte order.
.LP
.B luac
produces a single output file containing the bytecodes
for all source files given.
By default,
the output file is named
.BR luac.out ,
but you can change this with the
.B \-o
option.
.LP
In the command line,
you can mix
text files containing Lua source and
binary files containing precompiled chunks.
This is useful to combine several precompiled chunks,
even from different (but compatible) platforms,
into a single precompiled chunk.
.LP
You can use
.B "'\-'"
to indicate the standard input as a source file
and
.B "'\--'"
to signal the end of options
(that is,
all remaining arguments will be treated as files even if they start with
.BR "'\-'" ).
.LP
The internal format of the binary files produced by
.B luac
is likely to change when a new version of Lua is released.
So,
save the source files of all Lua programs that you precompile.
.LP
.SH OPTIONS
Options must be separate.
.TP
.B \-l
produce a listing of the compiled bytecode for Lua's virtual machine.
Listing bytecodes is useful to learn about Lua's virtual machine.
If no files are given, then
.B luac
loads
.B luac.out
and lists its contents.
.TP
.BI \-o " file"
output to
.IR file ,
instead of the default
.BR luac.out .
(You can use
.B "'\-'"
for standard output,
but not on platforms that open standard output in text mode.)
The output file may be a source file because
all files are loaded before the output file is written.
Be careful not to overwrite precious files.
.TP
.B \-p
load files but do not generate any output file.
Used mainly for syntax checking and for testing precompiled chunks:
corrupted files will probably generate errors when loaded.
Lua always performs a thorough integrity test on precompiled chunks.
Bytecode that passes this test is completely safe,
in the sense that it will not break the interpreter.
However,
there is no guarantee that such code does anything sensible.
(None can be given, because the halting problem is unsolvable.)
If no files are given, then
.B luac
loads
.B luac.out
and tests its contents.
No messages are displayed if the file passes the integrity test.
.TP
.B \-s
strip debug information before writing the output file.
This saves some space in very large chunks,
but if errors occur when running a stripped chunk,
then the error messages may not contain the full information they usually do.
For instance,
line numbers and names of local variables are lost.
.TP
.B \-v
show version information.
.SH FILES
.TP 15
.B luac.out
default output file
.SH "SEE ALSO"
.BR lua (1)
.br
http://www.lua.org/
.SH DIAGNOSTICS
Error messages should be self explanatory.
.SH AUTHORS
L. H. de Figueiredo,
R. Ierusalimschy and
W. Celes
.\" EOF
+145
View File
@@ -0,0 +1,145 @@
<!-- $Id: luac.man,v 1.28 2006/01/06 16:03:34 lhf Exp $ -->
<HTML>
<HEAD>
<TITLE>LUAC man page</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H2>NAME</H2>
luac - Lua compiler
<H2>SYNOPSIS</H2>
<B>luac</B>
[
<I>options</I>
] [
<I>filenames</I>
]
<H2>DESCRIPTION</H2>
<B>luac</B>
is the Lua compiler.
It translates programs written in the Lua programming language
into binary files that can be later loaded and executed.
<P>
The main advantages of precompiling chunks are:
faster loading,
protecting source code from accidental user changes,
and
off-line syntax checking.
<P>
Precompiling does not imply faster execution
because in Lua chunks are always compiled into bytecodes before being executed.
<B>luac</B>
simply allows those bytecodes to be saved in a file for later execution.
<P>
Precompiled chunks are not necessarily smaller than the corresponding source.
The main goal in precompiling is faster loading.
<P>
The binary files created by
<B>luac</B>
are portable only among architectures with the same word size and byte order.
<P>
<B>luac</B>
produces a single output file containing the bytecodes
for all source files given.
By default,
the output file is named
<B>luac.out</B>,
but you can change this with the
<B>-o</B>
option.
<P>
In the command line,
you can mix
text files containing Lua source and
binary files containing precompiled chunks.
This is useful because several precompiled chunks,
even from different (but compatible) platforms,
can be combined into a single precompiled chunk.
<P>
You can use
<B>'-'</B>
to indicate the standard input as a source file
and
<B>'--'</B>
to signal the end of options
(that is,
all remaining arguments will be treated as files even if they start with
<B>'-'</B>).
<P>
The internal format of the binary files produced by
<B>luac</B>
is likely to change when a new version of Lua is released.
So,
save the source files of all Lua programs that you precompile.
<P>
<H2>OPTIONS</H2>
Options must be separate.
<P>
<B>-l</B>
produce a listing of the compiled bytecode for Lua's virtual machine.
Listing bytecodes is useful to learn about Lua's virtual machine.
If no files are given, then
<B>luac</B>
loads
<B>luac.out</B>
and lists its contents.
<P>
<B>-o </B><I>file</I>
output to
<I>file</I>,
instead of the default
<B>luac.out</B>.
(You can use
<B>'-'</B>
for standard output,
but not on platforms that open standard output in text mode.)
The output file may be a source file because
all files are loaded before the output file is written.
Be careful not to overwrite precious files.
<P>
<B>-p</B>
load files but do not generate any output file.
Used mainly for syntax checking and for testing precompiled chunks:
corrupted files will probably generate errors when loaded.
Lua always performs a thorough integrity test on precompiled chunks.
Bytecode that passes this test is completely safe,
in the sense that it will not break the interpreter.
However,
there is no guarantee that such code does anything sensible.
(None can be given, because the halting problem is unsolvable.)
If no files are given, then
<B>luac</B>
loads
<B>luac.out</B>
and tests its contents.
No messages are displayed if the file passes the integrity test.
<P>
<B>-s</B>
strip debug information before writing the output file.
This saves some space in very large chunks,
but if errors occur when running a stripped chunk,
then the error messages may not contain the full information they usually do.
For instance,
line numbers and names of local variables are lost.
<P>
<B>-v</B>
show version information.
<H2>FILES</H2>
<P>
<B>luac.out</B>
default output file
<H2>SEE ALSO</H2>
<B>lua</B>(1)
<BR>
<A HREF="http://www.lua.org/">http://www.lua.org/</A>
<H2>DIAGNOSTICS</H2>
Error messages should be self explanatory.
<H2>AUTHORS</H2>
L. H. de Figueiredo,
R. Ierusalimschy and
W. Celes
<!-- EOF -->
</BODY>
</HTML>
+13
View File
@@ -0,0 +1,13 @@
h3 code {
font-family: inherit ;
}
pre {
font-size: 105% ;
}
span.apii {
float: right ;
font-family: inherit ;
}
+8801
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
<HTML>
<HEAD>
<TITLE>Lua documentation</TITLE>
<LINK REL="stylesheet" TYPE="text/css" HREF="lua.css">
</HEAD>
<BODY>
<HR>
<H1>
<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua" BORDER=0></A>
Documentation
</H1>
This is the documentation included in the source distribution of Lua 5.1.4.
<UL>
<LI><A HREF="contents.html">Reference manual</A>
<LI><A HREF="lua.html">lua man page</A>
<LI><A HREF="luac.html">luac man page</A>
<LI><A HREF="../README">lua/README</A>
<LI><A HREF="../etc/README">lua/etc/README</A>
<LI><A HREF="../test/README">lua/test/README</A>
</UL>
Lua's
<A HREF="http://www.lua.org/">official web site</A>
contains updated documentation,
especially the
<A HREF="http://www.lua.org/manual/5.1/">reference manual</A>.
<P>
<HR>
<SMALL>
Last update:
Tue Aug 12 14:46:07 BRT 2008
</SMALL>
</BODY>
</HTML>
+44
View File
@@ -0,0 +1,44 @@
# makefile for Lua etc
TOP= ..
LIB= $(TOP)/src
INC= $(TOP)/src
BIN= $(TOP)/src
SRC= $(TOP)/src
TST= $(TOP)/test
CC= gcc
CFLAGS= -O2 -Wall -I$(INC) $(MYCFLAGS)
MYCFLAGS=
MYLDFLAGS= -Wl,-E
MYLIBS= -lm
#MYLIBS= -lm -Wl,-E -ldl -lreadline -lhistory -lncurses
RM= rm -f
default:
@echo 'Please choose a target: min noparser one strict clean'
min: min.c
$(CC) $(CFLAGS) $@.c -L$(LIB) -llua $(MYLIBS)
echo 'print"Hello there!"' | ./a.out
noparser: noparser.o
$(CC) noparser.o $(SRC)/lua.o -L$(LIB) -llua $(MYLIBS)
$(BIN)/luac $(TST)/hello.lua
-./a.out luac.out
-./a.out -e'a=1'
one:
$(CC) $(CFLAGS) all.c $(MYLIBS)
./a.out $(TST)/hello.lua
strict:
-$(BIN)/lua -e 'print(a);b=2'
-$(BIN)/lua -lstrict -e 'print(a)'
-$(BIN)/lua -e 'function f() b=2 end f()'
-$(BIN)/lua -lstrict -e 'function f() b=2 end f()'
clean:
$(RM) a.out core core.* *.o luac.out
.PHONY: default min noparser one strict clean
+37
View File
@@ -0,0 +1,37 @@
This directory contains some useful files and code.
Unlike the code in ../src, everything here is in the public domain.
If any of the makes fail, you're probably not using the same libraries
used to build Lua. Set MYLIBS in Makefile accordingly.
all.c
Full Lua interpreter in a single file.
Do "make one" for a demo.
lua.hpp
Lua header files for C++ using 'extern "C"'.
lua.ico
A Lua icon for Windows (and web sites: save as favicon.ico).
Drawn by hand by Markus Gritsch <gritsch@iue.tuwien.ac.at>.
lua.pc
pkg-config data for Lua
luavs.bat
Script to build Lua under "Visual Studio .NET Command Prompt".
Run it from the toplevel as etc\luavs.bat.
min.c
A minimal Lua interpreter.
Good for learning and for starting your own.
Do "make min" for a demo.
noparser.c
Linking with noparser.o avoids loading the parsing modules in lualib.a.
Do "make noparser" for a demo.
strict.lua
Traps uses of undeclared global variables.
Do "make strict" for a demo.
+38
View File
@@ -0,0 +1,38 @@
/*
* all.c -- Lua core, libraries and interpreter in a single file
*/
#define luaall_c
#include "lapi.c"
#include "lcode.c"
#include "ldebug.c"
#include "ldo.c"
#include "ldump.c"
#include "lfunc.c"
#include "lgc.c"
#include "llex.c"
#include "lmem.c"
#include "lobject.c"
#include "lopcodes.c"
#include "lparser.c"
#include "lstate.c"
#include "lstring.c"
#include "ltable.c"
#include "ltm.c"
#include "lundump.c"
#include "lvm.c"
#include "lzio.c"
#include "lauxlib.c"
#include "lbaselib.c"
#include "ldblib.c"
#include "liolib.c"
#include "linit.c"
#include "lmathlib.c"
#include "loadlib.c"
#include "loslib.c"
#include "lstrlib.c"
#include "ltablib.c"
#include "lua.c"
+9
View File
@@ -0,0 +1,9 @@
// lua.hpp
// Lua header files for C++
// <<extern "C">> not supplied automatically because Lua also compiles as C++
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

+31
View File
@@ -0,0 +1,31 @@
# lua.pc -- pkg-config data for Lua
# vars from install Makefile
# grep '^V=' ../Makefile
V= 5.1
# grep '^R=' ../Makefile
R= 5.1.4
# grep '^INSTALL_.*=' ../Makefile | sed 's/INSTALL_TOP/prefix/'
prefix= /usr/local
INSTALL_BIN= ${prefix}/bin
INSTALL_INC= ${prefix}/include
INSTALL_LIB= ${prefix}/lib
INSTALL_MAN= ${prefix}/man/man1
INSTALL_LMOD= ${prefix}/share/lua/${V}
INSTALL_CMOD= ${prefix}/lib/lua/${V}
# canonical vars
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: Lua
Description: An Extensible Extension Language
Version: ${R}
Requires:
Libs: -L${libdir} -llua -lm
Cflags: -I${includedir}
# (end of lua.pc)
+28
View File
@@ -0,0 +1,28 @@
@rem Script to build Lua under "Visual Studio .NET Command Prompt".
@rem Do not run from this directory; run it from the toplevel: etc\luavs.bat .
@rem It creates lua51.dll, lua51.lib, lua.exe, and luac.exe in src.
@rem (contributed by David Manura and Mike Pall)
@setlocal
@set MYCOMPILE=cl /nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE
@set MYLINK=link /nologo
@set MYMT=mt /nologo
cd src
%MYCOMPILE% /DLUA_BUILD_AS_DLL l*.c
del lua.obj luac.obj
%MYLINK% /DLL /out:lua51.dll l*.obj
if exist lua51.dll.manifest^
%MYMT% -manifest lua51.dll.manifest -outputresource:lua51.dll;2
%MYCOMPILE% /DLUA_BUILD_AS_DLL lua.c
%MYLINK% /out:lua.exe lua.obj lua51.lib
if exist lua.exe.manifest^
%MYMT% -manifest lua.exe.manifest -outputresource:lua.exe
%MYCOMPILE% l*.c print.c
del lua.obj linit.obj lbaselib.obj ldblib.obj liolib.obj lmathlib.obj^
loslib.obj ltablib.obj lstrlib.obj loadlib.obj
%MYLINK% /out:luac.exe *.obj
if exist luac.exe.manifest^
%MYMT% -manifest luac.exe.manifest -outputresource:luac.exe
del *.obj *.manifest
cd ..
+39
View File
@@ -0,0 +1,39 @@
/*
* min.c -- a minimal Lua interpreter
* loads stdin only with minimal error handling.
* no interaction, and no standard library, only a "print" function.
*/
#include <stdio.h>
#include "lua.h"
#include "lauxlib.h"
static int print(lua_State *L)
{
int n=lua_gettop(L);
int i;
for (i=1; i<=n; i++)
{
if (i>1) printf("\t");
if (lua_isstring(L,i))
printf("%s",lua_tostring(L,i));
else if (lua_isnil(L,i))
printf("%s","nil");
else if (lua_isboolean(L,i))
printf("%s",lua_toboolean(L,i) ? "true" : "false");
else
printf("%s:%p",luaL_typename(L,i),lua_topointer(L,i));
}
printf("\n");
return 0;
}
int main(void)
{
lua_State *L=lua_open();
lua_register(L,"print",print);
if (luaL_dofile(L,NULL)!=0) fprintf(stderr,"%s\n",lua_tostring(L,-1));
lua_close(L);
return 0;
}
+50
View File
@@ -0,0 +1,50 @@
/*
* The code below can be used to make a Lua core that does not contain the
* parsing modules (lcode, llex, lparser), which represent 35% of the total core.
* You'll only be able to load binary files and strings, precompiled with luac.
* (Of course, you'll have to build luac with the original parsing modules!)
*
* To use this module, simply compile it ("make noparser" does that) and list
* its object file before the Lua libraries. The linker should then not load
* the parsing modules. To try it, do "make luab".
*
* If you also want to avoid the dump module (ldump.o), define NODUMP.
* #define NODUMP
*/
#define LUA_CORE
#include "llex.h"
#include "lparser.h"
#include "lzio.h"
LUAI_FUNC void luaX_init (lua_State *L) {
UNUSED(L);
}
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) {
UNUSED(z);
UNUSED(buff);
UNUSED(name);
lua_pushliteral(L,"parser not loaded");
lua_error(L);
return NULL;
}
#ifdef NODUMP
#include "lundump.h"
LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) {
UNUSED(f);
UNUSED(w);
UNUSED(data);
UNUSED(strip);
#if 1
UNUSED(L);
return 0;
#else
lua_pushliteral(L,"dumper not loaded");
lua_error(L);
#endif
}
#endif
+41
View File
@@ -0,0 +1,41 @@
--
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
--
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end
+182
View File
@@ -0,0 +1,182 @@
# makefile for building Lua
# see ../INSTALL for installation instructions
# see ../Makefile and luaconf.h for further customization
# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT =======================
# Your platform. See PLATS for possible values.
PLAT= none
CC= gcc
CFLAGS= -O2 -Wall $(MYCFLAGS) $(ARCH)
AR= ar rcu
RANLIB= ranlib
RM= rm -f
LIBS= -lm $(MYLIBS)
MYCFLAGS=
MYLDFLAGS=
MYLIBS=
# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE =========
PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris
LUA_A= liblua.a
CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \
lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \
lundump.o lvm.o lzio.o
LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \
lstrlib.o loadlib.o linit.o
LUA_T= lua
LUA_O= lua.o
LUAC_T= luac
LUAC_O= luac.o print.o
ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O)
ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T)
ALL_A= $(LUA_A)
default: $(PLAT)
all: $(ALL_T)
o: $(ALL_O)
a: $(ALL_A)
$(LUA_A): $(CORE_O) $(LIB_O)
$(AR) $@ $?
$(RANLIB) $@
$(LUA_T): $(LUA_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) $(ARCH)
$(LUAC_T): $(LUAC_O) $(LUA_A)
$(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) $(ARCH)
clean:
$(RM) $(ALL_T) $(ALL_O)
depend:
@$(CC) $(CFLAGS) -MM l*.c print.c
echo:
@echo "PLAT = $(PLAT)"
@echo "CC = $(CC)"
@echo "CFLAGS = $(CFLAGS)"
@echo "AR = $(AR)"
@echo "RANLIB = $(RANLIB)"
@echo "RM = $(RM)"
@echo "MYCFLAGS = $(MYCFLAGS)"
@echo "MYLDFLAGS = $(MYLDFLAGS)"
@echo "MYLIBS = $(MYLIBS)"
# convenience targets for popular platforms
none:
@echo "Please choose a platform:"
@echo " $(PLATS)"
aix:
$(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall"
ansi:
$(MAKE) all MYCFLAGS=-DLUA_ANSI ARCH="$(ARCH)"
bsd:
$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E"
freebsd:
$(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline"
generic:
$(MAKE) all MYCFLAGS=
linux:
$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses"
macosx:
$(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline"
# use this on Mac OS X 10.3-
# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX
mingw:
$(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \
"AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \
"MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe
$(MAKE) "LUAC_T=luac.exe" luac.exe
posix:
$(MAKE) all MYCFLAGS=-DLUA_USE_POSIX
solaris:
$(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl"
# list targets that do not create files (but not all makes understand .PHONY)
.PHONY: all $(PLATS) default o a clean depend echo none
# DO NOT DELETE
lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \
lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \
lundump.h lvm.h
lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h
lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h
lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \
ltable.h
ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h
ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \
llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
lfunc.h lstring.h lgc.h ltable.h lvm.h
ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \
ltable.h lundump.h lvm.h
ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \
lzio.h lmem.h lundump.h
lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \
lstate.h ltm.h lzio.h
lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h
linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h
liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h
llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \
lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h
lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h
lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h
loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h
lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \
ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h
lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h
loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h
lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \
lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \
lfunc.h lstring.h lgc.h ltable.h
lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h
lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \
ltm.h lzio.h lstring.h lgc.h
lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h
ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \
ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h
ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h
ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \
lmem.h lstring.h lgc.h ltable.h
lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h
luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \
lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \
lundump.h
lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h
lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \
lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h
lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \
lzio.h
print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \
ltm.h lzio.h lmem.h lopcodes.h lundump.h
# (end of Makefile)
+1087
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
/*
** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions from Lua API
** See Copyright Notice in lua.h
*/
#ifndef lapi_h
#define lapi_h
#include "lobject.h"
LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o);
#endif
+652
View File
@@ -0,0 +1,652 @@
/*
** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* This file uses only the official API of Lua.
** Any function declared here could be written as an application function.
*/
#define lauxlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#define FREELIST_REF 0 /* free list of references */
/* convert a stack index to positive */
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
lua_gettop(L) + (i) + 1)
/*
** {======================================================
** Error-report functions
** =======================================================
*/
LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {
lua_Debug ar;
if (!lua_getstack(L, 0, &ar)) /* no stack frame? */
return luaL_error(L, "bad argument #%d (%s)", narg, extramsg);
lua_getinfo(L, "n", &ar);
if (strcmp(ar.namewhat, "method") == 0) {
narg--; /* do not count `self' */
if (narg == 0) /* error is in the self argument itself? */
return luaL_error(L, "calling " LUA_QS " on bad self (%s)",
ar.name, extramsg);
}
if (ar.name == NULL)
ar.name = "?";
return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)",
narg, ar.name, extramsg);
}
LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {
const char *msg = lua_pushfstring(L, "%s expected, got %s",
tname, luaL_typename(L, narg));
return luaL_argerror(L, narg, msg);
}
static void tag_error (lua_State *L, int narg, int tag) {
luaL_typerror(L, narg, lua_typename(L, tag));
}
LUALIB_API void luaL_where (lua_State *L, int level) {
lua_Debug ar;
if (lua_getstack(L, level, &ar)) { /* check function at level */
lua_getinfo(L, "Sl", &ar); /* get info about it */
if (ar.currentline > 0) { /* is there info? */
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
return;
}
}
lua_pushliteral(L, ""); /* else, no information available... */
}
LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
luaL_where(L, 1);
lua_pushvfstring(L, fmt, argp);
va_end(argp);
lua_concat(L, 2);
return lua_error(L);
}
/* }====================================================== */
LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def,
const char *const lst[]) {
const char *name = (def) ? luaL_optstring(L, narg, def) :
luaL_checkstring(L, narg);
int i;
for (i=0; lst[i]; i++)
if (strcmp(lst[i], name) == 0)
return i;
return luaL_argerror(L, narg,
lua_pushfstring(L, "invalid option " LUA_QS, name));
}
LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
if (!lua_isnil(L, -1)) /* name already in use? */
return 0; /* leave previous value on top, but return 0 */
lua_pop(L, 1);
lua_newtable(L); /* create metatable */
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */
return 1;
}
LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) {
void *p = lua_touserdata(L, ud);
if (p != NULL) { /* value is a userdata? */
if (lua_getmetatable(L, ud)) { /* does it have a metatable? */
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */
if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */
lua_pop(L, 2); /* remove both metatables */
return p;
}
}
}
luaL_typerror(L, ud, tname); /* else error */
return NULL; /* to avoid warnings */
}
LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) {
if (!lua_checkstack(L, space))
luaL_error(L, "stack overflow (%s)", mes);
}
LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {
if (lua_type(L, narg) != t)
tag_error(L, narg, t);
}
LUALIB_API void luaL_checkany (lua_State *L, int narg) {
if (lua_type(L, narg) == LUA_TNONE)
luaL_argerror(L, narg, "value expected");
}
LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) {
const char *s = lua_tolstring(L, narg, len);
if (!s) tag_error(L, narg, LUA_TSTRING);
return s;
}
LUALIB_API const char *luaL_optlstring (lua_State *L, int narg,
const char *def, size_t *len) {
if (lua_isnoneornil(L, narg)) {
if (len)
*len = (def ? strlen(def) : 0);
return def;
}
else return luaL_checklstring(L, narg, len);
}
LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) {
lua_Number d = lua_tonumber(L, narg);
if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
tag_error(L, narg, LUA_TNUMBER);
return d;
}
LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) {
return luaL_opt(L, luaL_checknumber, narg, def);
}
LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) {
lua_Integer d = lua_tointeger(L, narg);
if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */
tag_error(L, narg, LUA_TNUMBER);
return d;
}
LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg,
lua_Integer def) {
return luaL_opt(L, luaL_checkinteger, narg, def);
}
LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) {
if (!lua_getmetatable(L, obj)) /* no metatable? */
return 0;
lua_pushstring(L, event);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) {
lua_pop(L, 2); /* remove metatable and metafield */
return 0;
}
else {
lua_remove(L, -2); /* remove only metatable */
return 1;
}
}
LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) {
obj = abs_index(L, obj);
if (!luaL_getmetafield(L, obj, event)) /* no metafield? */
return 0;
lua_pushvalue(L, obj);
lua_call(L, 1, 1);
return 1;
}
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l) {
luaI_openlib(L, libname, l, 0);
}
static int libsize (const luaL_Reg *l) {
int size = 0;
for (; l->name; l++) size++;
return size;
}
LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
const luaL_Reg *l, int nup) {
if (libname) {
int size = libsize(l);
/* check whether lib already exists */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
luaL_error(L, "name conflict for module " LUA_QS, libname);
lua_pushvalue(L, -1);
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */
}
lua_remove(L, -2); /* remove _LOADED table */
lua_insert(L, -(nup+1)); /* move library table to below upvalues */
}
for (; l->name; l++) {
int i;
for (i=0; i<nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
lua_pushcclosure(L, l->func, nup);
lua_setfield(L, -(nup+2), l->name);
}
lua_pop(L, nup); /* remove upvalues */
}
/*
** {======================================================
** getn-setn: size for arrays
** =======================================================
*/
#if defined(LUA_COMPAT_GETN)
static int checkint (lua_State *L, int topop) {
int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1;
lua_pop(L, topop);
return n;
}
static void getsizes (lua_State *L) {
lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES");
if (lua_isnil(L, -1)) { /* no `size' table? */
lua_pop(L, 1); /* remove nil */
lua_newtable(L); /* create it */
lua_pushvalue(L, -1); /* `size' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushliteral(L, "kv");
lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */
}
}
LUALIB_API void luaL_setn (lua_State *L, int t, int n) {
t = abs_index(L, t);
lua_pushliteral(L, "n");
lua_rawget(L, t);
if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */
lua_pushliteral(L, "n"); /* use it */
lua_pushinteger(L, n);
lua_rawset(L, t);
}
else { /* use `sizes' */
getsizes(L);
lua_pushvalue(L, t);
lua_pushinteger(L, n);
lua_rawset(L, -3); /* sizes[t] = n */
lua_pop(L, 1); /* remove `sizes' */
}
}
LUALIB_API int luaL_getn (lua_State *L, int t) {
int n;
t = abs_index(L, t);
lua_pushliteral(L, "n"); /* try t.n */
lua_rawget(L, t);
if ((n = checkint(L, 1)) >= 0) return n;
getsizes(L); /* else try sizes[t] */
lua_pushvalue(L, t);
lua_rawget(L, -2);
if ((n = checkint(L, 2)) >= 0) return n;
return (int)lua_objlen(L, t);
}
#endif
/* }====================================================== */
LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p,
const char *r) {
const char *wild;
size_t l = strlen(p);
luaL_Buffer b;
luaL_buffinit(L, &b);
while ((wild = strstr(s, p)) != NULL) {
luaL_addlstring(&b, s, wild - s); /* push prefix */
luaL_addstring(&b, r); /* push replacement in place of pattern */
s = wild + l; /* continue after `p' */
}
luaL_addstring(&b, s); /* push last suffix */
luaL_pushresult(&b);
return lua_tostring(L, -1);
}
LUALIB_API const char *luaL_findtable (lua_State *L, int idx,
const char *fname, int szhint) {
const char *e;
lua_pushvalue(L, idx);
do {
e = strchr(fname, '.');
if (e == NULL) e = fname + strlen(fname);
lua_pushlstring(L, fname, e - fname);
lua_rawget(L, -2);
if (lua_isnil(L, -1)) { /* no such field? */
lua_pop(L, 1); /* remove this nil */
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */
lua_pushlstring(L, fname, e - fname);
lua_pushvalue(L, -2);
lua_settable(L, -4); /* set new table into field */
}
else if (!lua_istable(L, -1)) { /* field has a non-table value? */
lua_pop(L, 2); /* remove table and value */
return fname; /* return problematic part of the name */
}
lua_remove(L, -2); /* remove previous table */
fname = e + 1;
} while (*e == '.');
return NULL;
}
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
#define bufflen(B) ((B)->p - (B)->buffer)
#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B)))
#define LIMIT (LUA_MINSTACK/2)
static int emptybuffer (luaL_Buffer *B) {
size_t l = bufflen(B);
if (l == 0) return 0; /* put nothing on stack */
else {
lua_pushlstring(B->L, B->buffer, l);
B->p = B->buffer;
B->lvl++;
return 1;
}
}
static void adjuststack (luaL_Buffer *B) {
if (B->lvl > 1) {
lua_State *L = B->L;
int toget = 1; /* number of levels to concat */
size_t toplen = lua_strlen(L, -1);
do {
size_t l = lua_strlen(L, -(toget+1));
if (B->lvl - toget + 1 >= LIMIT || toplen > l) {
toplen += l;
toget++;
}
else break;
} while (toget < B->lvl);
lua_concat(L, toget);
B->lvl = B->lvl - toget + 1;
}
}
LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) {
if (emptybuffer(B))
adjuststack(B);
return B->buffer;
}
LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) {
while (l--)
luaL_addchar(B, *s++);
}
LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) {
luaL_addlstring(B, s, strlen(s));
}
LUALIB_API void luaL_pushresult (luaL_Buffer *B) {
emptybuffer(B);
lua_concat(B->L, B->lvl);
B->lvl = 1;
}
LUALIB_API void luaL_addvalue (luaL_Buffer *B) {
lua_State *L = B->L;
size_t vl;
const char *s = lua_tolstring(L, -1, &vl);
if (vl <= bufffree(B)) { /* fit into buffer? */
memcpy(B->p, s, vl); /* put it there */
B->p += vl;
lua_pop(L, 1); /* remove from stack */
}
else {
if (emptybuffer(B))
lua_insert(L, -2); /* put buffer before new value */
B->lvl++; /* add new value into B stack */
adjuststack(B);
}
}
LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) {
B->L = L;
B->p = B->buffer;
B->lvl = 0;
}
/* }====================================================== */
LUALIB_API int luaL_ref (lua_State *L, int t) {
int ref;
t = abs_index(L, t);
if (lua_isnil(L, -1)) {
lua_pop(L, 1); /* remove from stack */
return LUA_REFNIL; /* `nil' has a unique fixed reference */
}
lua_rawgeti(L, t, FREELIST_REF); /* get first free element */
ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */
lua_pop(L, 1); /* remove it from stack */
if (ref != 0) { /* any free element? */
lua_rawgeti(L, t, ref); /* remove it from list */
lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */
}
else { /* no free elements */
ref = (int)lua_objlen(L, t);
ref++; /* create new reference */
}
lua_rawseti(L, t, ref);
return ref;
}
LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
if (ref >= 0) {
t = abs_index(L, t);
lua_rawgeti(L, t, FREELIST_REF);
lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */
lua_pushinteger(L, ref);
lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */
}
}
/*
** {======================================================
** Load functions
** =======================================================
*/
typedef struct LoadF {
int extraline;
FILE *f;
char buff[LUAL_BUFFERSIZE];
} LoadF;
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
(void)L;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
if (feof(lf->f)) return NULL;
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
return (*size > 0) ? lf->buff : NULL;
}
static int errfile (lua_State *L, const char *what, int fnameindex) {
const char *serr = strerror(errno);
const char *filename = lua_tostring(L, fnameindex) + 1;
lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
lua_remove(L, fnameindex);
return LUA_ERRFILE;
}
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
LoadF lf;
int status, readstatus;
int c;
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
lf.extraline = 0;
if (filename == NULL) {
lua_pushliteral(L, "=stdin");
lf.f = stdin;
}
else {
lua_pushfstring(L, "@%s", filename);
lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex);
}
c = getc(lf.f);
if (c == '#') { /* Unix exec. file? */
lf.extraline = 1;
while ((c = getc(lf.f)) != EOF && c != '\n') ; /* skip first line */
if (c == '\n') c = getc(lf.f);
}
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ;
lf.extraline = 0;
}
ungetc(c, lf.f);
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */
if (readstatus) {
lua_settop(L, fnameindex); /* ignore results from `lua_load' */
return errfile(L, "read", fnameindex);
}
lua_remove(L, fnameindex);
return status;
}
typedef struct LoadS {
const char *s;
size_t size;
} LoadS;
static const char *getS (lua_State *L, void *ud, size_t *size) {
LoadS *ls = (LoadS *)ud;
(void)L;
if (ls->size == 0) return NULL;
*size = ls->size;
ls->size = 0;
return ls->s;
}
LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size,
const char *name) {
LoadS ls;
ls.s = buff;
ls.size = size;
return lua_load(L, getS, &ls, name);
}
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) {
return luaL_loadbuffer(L, s, strlen(s), s);
}
/* }====================================================== */
static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
(void)ud;
(void)osize;
if (nsize == 0) {
free(ptr);
return NULL;
}
else
return realloc(ptr, nsize);
}
static int panic (lua_State *L) {
(void)L; /* to avoid warnings */
fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
return 0;
}
LUALIB_API lua_State *luaL_newstate (void) {
lua_State *L = lua_newstate(l_alloc, NULL);
if (L) lua_atpanic(L, &panic);
return L;
}
+174
View File
@@ -0,0 +1,174 @@
/*
** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions for building Lua libraries
** See Copyright Notice in lua.h
*/
#ifndef lauxlib_h
#define lauxlib_h
#include <stddef.h>
#include <stdio.h>
#include "lua.h"
#if defined(LUA_COMPAT_GETN)
LUALIB_API int (luaL_getn) (lua_State *L, int t);
LUALIB_API void (luaL_setn) (lua_State *L, int t, int n);
#else
#define luaL_getn(L,i) ((int)lua_objlen(L, i))
#define luaL_setn(L,i,j) ((void)0) /* no op! */
#endif
#if defined(LUA_COMPAT_OPENLIB)
#define luaI_openlib luaL_openlib
#endif
/* extra error code for `luaL_load' */
#define LUA_ERRFILE (LUA_ERRERR+1)
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname,
const luaL_Reg *l, int nup);
LUALIB_API void (luaL_register) (lua_State *L, const char *libname,
const luaL_Reg *l);
LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e);
LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg);
LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg,
size_t *l);
LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg,
const char *def, size_t *l);
LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg);
LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def);
LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg);
LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
lua_Integer def);
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...);
LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
const char *const lst[]);
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
const char *name);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
LUALIB_API lua_State *(luaL_newstate) (void);
LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p,
const char *r);
LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx,
const char *fname, int szhint);
/*
** ===============================================================
** some useful macros
** ===============================================================
*/
#define luaL_argcheck(L, cond,numarg,extramsg) \
((void)((cond) || luaL_argerror(L, (numarg), (extramsg))))
#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL))
#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL))
#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n)))
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
#define luaL_dofile(L, fn) \
(luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_dostring(L, s) \
(luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0))
#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n)))
/*
** {======================================================
** Generic Buffer manipulation
** =======================================================
*/
typedef struct luaL_Buffer {
char *p; /* current position in buffer */
int lvl; /* number of strings in the stack (level) */
lua_State *L;
char buffer[LUAL_BUFFERSIZE];
} luaL_Buffer;
#define luaL_addchar(B,c) \
((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \
(*(B)->p++ = (char)(c)))
/* compatibility only */
#define luaL_putchar(B,c) luaL_addchar(B,c)
#define luaL_addsize(B,n) ((B)->p += (n))
LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B);
LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B);
LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l);
LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s);
LUALIB_API void (luaL_addvalue) (luaL_Buffer *B);
LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */
/* compatibility with ref system */
/* pre-defined references */
#define LUA_NOREF (-2)
#define LUA_REFNIL (-1)
#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \
(lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0))
#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref))
#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref))
#define luaL_reg luaL_Reg
#endif
+653
View File
@@ -0,0 +1,653 @@
/*
** $Id: lbaselib.c,v 1.191.1.6 2008/02/14 16:46:22 roberto Exp $
** Basic library
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lbaselib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** If your system does not support `stdout', you can just remove this function.
** If you need, you can define your own `print' function, following this
** model but changing `fputs' to put the strings at a proper place
** (a console window or a log file, for instance).
*/
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
lua_getglobal(L, "tostring");
for (i=1; i<=n; i++) {
const char *s;
lua_pushvalue(L, -1); /* function to be called */
lua_pushvalue(L, i); /* value to print */
lua_call(L, 1, 1);
s = lua_tostring(L, -1); /* get result */
if (s == NULL)
return luaL_error(L, LUA_QL("tostring") " must return a string to "
LUA_QL("print"));
if (i>1) fputs("\t", stdout);
fputs(s, stdout);
lua_pop(L, 1); /* pop result */
}
fputs("\n", stdout);
return 0;
}
static int luaB_tonumber (lua_State *L) {
int base = luaL_optint(L, 2, 10);
if (base == 10) { /* standard conversion */
luaL_checkany(L, 1);
if (lua_isnumber(L, 1)) {
lua_pushnumber(L, lua_tonumber(L, 1));
return 1;
}
}
else {
const char *s1 = luaL_checkstring(L, 1);
char *s2;
unsigned long n;
luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range");
n = strtoul(s1, &s2, base);
if (s1 != s2) { /* at least one valid digit? */
while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */
if (*s2 == '\0') { /* no invalid trailing characters? */
lua_pushnumber(L, (lua_Number)n);
return 1;
}
}
}
lua_pushnil(L); /* else not a number */
return 1;
}
static int luaB_error (lua_State *L) {
int level = luaL_optint(L, 2, 1);
lua_settop(L, 1);
if (lua_isstring(L, 1) && level > 0) { /* add extra information? */
luaL_where(L, level);
lua_pushvalue(L, 1);
lua_concat(L, 2);
}
return lua_error(L);
}
static int luaB_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L);
return 1; /* no metatable */
}
luaL_getmetafield(L, 1, "__metatable");
return 1; /* returns either __metatable field (if present) or metatable */
}
static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
if (luaL_getmetafield(L, 1, "__metatable"))
luaL_error(L, "cannot change a protected metatable");
lua_settop(L, 2);
lua_setmetatable(L, 1);
return 1;
}
static void getfunc (lua_State *L, int opt) {
if (lua_isfunction(L, 1)) lua_pushvalue(L, 1);
else {
lua_Debug ar;
int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1);
luaL_argcheck(L, level >= 0, 1, "level must be non-negative");
if (lua_getstack(L, level, &ar) == 0)
luaL_argerror(L, 1, "invalid level");
lua_getinfo(L, "f", &ar);
if (lua_isnil(L, -1))
luaL_error(L, "no function environment for tail call at level %d",
level);
}
}
static int luaB_getfenv (lua_State *L) {
getfunc(L, 1);
if (lua_iscfunction(L, -1)) /* is a C function? */
lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */
else
lua_getfenv(L, -1);
return 1;
}
static int luaB_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
getfunc(L, 0);
lua_pushvalue(L, 2);
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) {
/* change environment of current thread */
lua_pushthread(L);
lua_insert(L, -2);
lua_setfenv(L, -2);
return 0;
}
else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0)
luaL_error(L,
LUA_QL("setfenv") " cannot change environment of given object");
return 1;
}
static int luaB_rawequal (lua_State *L) {
luaL_checkany(L, 1);
luaL_checkany(L, 2);
lua_pushboolean(L, lua_rawequal(L, 1, 2));
return 1;
}
static int luaB_rawget (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_rawget(L, 1);
return 1;
}
static int luaB_rawset (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkany(L, 2);
luaL_checkany(L, 3);
lua_settop(L, 3);
lua_rawset(L, 1);
return 1;
}
static int luaB_gcinfo (lua_State *L) {
lua_pushinteger(L, lua_getgccount(L));
return 1;
}
static int luaB_collectgarbage (lua_State *L) {
static const char *const opts[] = {"stop", "restart", "collect",
"count", "step", "setpause", "setstepmul", NULL};
static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL};
int o = luaL_checkoption(L, 1, "collect", opts);
int ex = luaL_optint(L, 2, 0);
int res = lua_gc(L, optsnum[o], ex);
switch (optsnum[o]) {
case LUA_GCCOUNT: {
int b = lua_gc(L, LUA_GCCOUNTB, 0);
lua_pushnumber(L, res + ((lua_Number)b/1024));
return 1;
}
case LUA_GCSTEP: {
lua_pushboolean(L, res);
return 1;
}
default: {
lua_pushnumber(L, res);
return 1;
}
}
}
static int luaB_type (lua_State *L) {
luaL_checkany(L, 1);
lua_pushstring(L, luaL_typename(L, 1));
return 1;
}
static int luaB_next (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
else {
lua_pushnil(L);
return 1;
}
}
static int luaB_pairs (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
return 3;
}
static int ipairsaux (lua_State *L) {
int i = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
i++; /* next value */
lua_pushinteger(L, i);
lua_rawgeti(L, 1, i);
return (lua_isnil(L, -1)) ? 0 : 2;
}
static int luaB_ipairs (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushinteger(L, 0); /* and initial value */
return 3;
}
static int load_aux (lua_State *L, int status) {
if (status == 0) /* OK? */
return 1;
else {
lua_pushnil(L);
lua_insert(L, -2); /* put before error message */
return 2; /* return nil plus error message */
}
}
static int luaB_loadstring (lua_State *L) {
size_t l;
const char *s = luaL_checklstring(L, 1, &l);
const char *chunkname = luaL_optstring(L, 2, s);
return load_aux(L, luaL_loadbuffer(L, s, l, chunkname));
}
static int luaB_loadfile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
return load_aux(L, luaL_loadfile(L, fname));
}
/*
** Reader for generic `load' function: `lua_load' uses the
** stack for internal stuff, so the reader cannot change the
** stack top. Instead, it keeps its resulting string in a
** reserved slot inside the stack.
*/
static const char *generic_reader (lua_State *L, void *ud, size_t *size) {
(void)ud; /* to avoid warnings */
luaL_checkstack(L, 2, "too many nested functions");
lua_pushvalue(L, 1); /* get function */
lua_call(L, 0, 1); /* call it */
if (lua_isnil(L, -1)) {
*size = 0;
return NULL;
}
else if (lua_isstring(L, -1)) {
lua_replace(L, 3); /* save string in a reserved stack slot */
return lua_tolstring(L, 3, size);
}
else luaL_error(L, "reader function must return a string");
return NULL; /* to avoid warnings */
}
static int luaB_load (lua_State *L) {
int status;
const char *cname = luaL_optstring(L, 2, "=(load)");
luaL_checktype(L, 1, LUA_TFUNCTION);
lua_settop(L, 3); /* function, eventual name, plus one reserved slot */
status = lua_load(L, generic_reader, NULL, cname);
return load_aux(L, status);
}
static int luaB_dofile (lua_State *L) {
const char *fname = luaL_optstring(L, 1, NULL);
int n = lua_gettop(L);
if (luaL_loadfile(L, fname) != 0) lua_error(L);
lua_call(L, 0, LUA_MULTRET);
return lua_gettop(L) - n;
}
static int luaB_assert (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_toboolean(L, 1))
return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!"));
return lua_gettop(L);
}
static int luaB_unpack (lua_State *L) {
int i, e, n;
luaL_checktype(L, 1, LUA_TTABLE);
i = luaL_optint(L, 2, 1);
e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1));
if (i > e) return 0; /* empty range */
n = e - i + 1; /* number of elements */
if (n <= 0 || !lua_checkstack(L, n)) /* n <= 0 means arith. overflow */
return luaL_error(L, "too many results to unpack");
lua_rawgeti(L, 1, i); /* push arg[i] (avoiding overflow problems) */
while (i++ < e) /* push arg[i + 1...e] */
lua_rawgeti(L, 1, i);
return n;
}
static int luaB_select (lua_State *L) {
int n = lua_gettop(L);
if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') {
lua_pushinteger(L, n-1);
return 1;
}
else {
int i = luaL_checkint(L, 1);
if (i < 0) i = n + i;
else if (i > n) i = n;
luaL_argcheck(L, 1 <= i, 1, "index out of range");
return n - i;
}
}
static int luaB_pcall (lua_State *L) {
int status;
luaL_checkany(L, 1);
status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0);
lua_pushboolean(L, (status == 0));
lua_insert(L, 1);
return lua_gettop(L); /* return status + all results */
}
static int luaB_xpcall (lua_State *L) {
int status;
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_insert(L, 1); /* put error function under function to be called */
status = lua_pcall(L, 0, LUA_MULTRET, 1);
lua_pushboolean(L, (status == 0));
lua_replace(L, 1);
return lua_gettop(L); /* return status + all results */
}
static int luaB_tostring (lua_State *L) {
luaL_checkany(L, 1);
if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */
return 1; /* use its value */
switch (lua_type(L, 1)) {
case LUA_TNUMBER:
lua_pushstring(L, lua_tostring(L, 1));
break;
case LUA_TSTRING:
lua_pushvalue(L, 1);
break;
case LUA_TBOOLEAN:
lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false"));
break;
case LUA_TNIL:
lua_pushliteral(L, "nil");
break;
default:
lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1));
break;
}
return 1;
}
static int luaB_newproxy (lua_State *L) {
lua_settop(L, 1);
lua_newuserdata(L, 0); /* create proxy */
if (lua_toboolean(L, 1) == 0)
return 1; /* no metatable */
else if (lua_isboolean(L, 1)) {
lua_newtable(L); /* create a new metatable `m' ... */
lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */
lua_pushboolean(L, 1);
lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */
}
else {
int validproxy = 0; /* to check if weaktable[metatable(u)] == true */
if (lua_getmetatable(L, 1)) {
lua_rawget(L, lua_upvalueindex(1));
validproxy = lua_toboolean(L, -1);
lua_pop(L, 1); /* remove value */
}
luaL_argcheck(L, validproxy, 1, "boolean or proxy expected");
lua_getmetatable(L, 1); /* metatable is valid; get it */
}
lua_setmetatable(L, 2);
return 1;
}
static const luaL_Reg base_funcs[] = {
{"assert", luaB_assert},
{"collectgarbage", luaB_collectgarbage},
{"dofile", luaB_dofile},
{"error", luaB_error},
{"gcinfo", luaB_gcinfo},
{"getfenv", luaB_getfenv},
{"getmetatable", luaB_getmetatable},
{"loadfile", luaB_loadfile},
{"load", luaB_load},
{"loadstring", luaB_loadstring},
{"next", luaB_next},
{"pcall", luaB_pcall},
{"print", luaB_print},
{"rawequal", luaB_rawequal},
{"rawget", luaB_rawget},
{"rawset", luaB_rawset},
{"select", luaB_select},
{"setfenv", luaB_setfenv},
{"setmetatable", luaB_setmetatable},
{"tonumber", luaB_tonumber},
{"tostring", luaB_tostring},
{"type", luaB_type},
{"unpack", luaB_unpack},
{"xpcall", luaB_xpcall},
{NULL, NULL}
};
/*
** {======================================================
** Coroutine library
** =======================================================
*/
#define CO_RUN 0 /* running */
#define CO_SUS 1 /* suspended */
#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */
#define CO_DEAD 3
static const char *const statnames[] =
{"running", "suspended", "normal", "dead"};
static int costatus (lua_State *L, lua_State *co) {
if (L == co) return CO_RUN;
switch (lua_status(co)) {
case LUA_YIELD:
return CO_SUS;
case 0: {
lua_Debug ar;
if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */
return CO_NOR; /* it is running */
else if (lua_gettop(co) == 0)
return CO_DEAD;
else
return CO_SUS; /* initial state */
}
default: /* some error occured */
return CO_DEAD;
}
}
static int luaB_costatus (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
luaL_argcheck(L, co, 1, "coroutine expected");
lua_pushstring(L, statnames[costatus(L, co)]);
return 1;
}
static int auxresume (lua_State *L, lua_State *co, int narg) {
int status = costatus(L, co);
if (!lua_checkstack(co, narg))
luaL_error(L, "too many arguments to resume");
if (status != CO_SUS) {
lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]);
return -1; /* error flag */
}
lua_xmove(L, co, narg);
lua_setlevel(L, co);
status = lua_resume(co, narg);
if (status == 0 || status == LUA_YIELD) {
int nres = lua_gettop(co);
if (!lua_checkstack(L, nres + 1))
luaL_error(L, "too many results to resume");
lua_xmove(co, L, nres); /* move yielded values */
return nres;
}
else {
lua_xmove(co, L, 1); /* move error message */
return -1; /* error flag */
}
}
static int luaB_coresume (lua_State *L) {
lua_State *co = lua_tothread(L, 1);
int r;
luaL_argcheck(L, co, 1, "coroutine expected");
r = auxresume(L, co, lua_gettop(L) - 1);
if (r < 0) {
lua_pushboolean(L, 0);
lua_insert(L, -2);
return 2; /* return false + error message */
}
else {
lua_pushboolean(L, 1);
lua_insert(L, -(r + 1));
return r + 1; /* return true + `resume' returns */
}
}
static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
if (lua_isstring(L, -1)) { /* error object is a string? */
luaL_where(L, 1); /* add extra info */
lua_insert(L, -2);
lua_concat(L, 2);
}
lua_error(L); /* propagate error */
}
return r;
}
static int luaB_cocreate (lua_State *L) {
lua_State *NL = lua_newthread(L);
luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1,
"Lua function expected");
lua_pushvalue(L, 1); /* move function to top */
lua_xmove(L, NL, 1); /* move function from L to NL */
return 1;
}
static int luaB_cowrap (lua_State *L) {
luaB_cocreate(L);
lua_pushcclosure(L, luaB_auxwrap, 1);
return 1;
}
static int luaB_yield (lua_State *L) {
return lua_yield(L, lua_gettop(L));
}
static int luaB_corunning (lua_State *L) {
if (lua_pushthread(L))
lua_pushnil(L); /* main thread is not a coroutine */
return 1;
}
static const luaL_Reg co_funcs[] = {
{"create", luaB_cocreate},
{"resume", luaB_coresume},
{"running", luaB_corunning},
{"status", luaB_costatus},
{"wrap", luaB_cowrap},
{"yield", luaB_yield},
{NULL, NULL}
};
/* }====================================================== */
static void auxopen (lua_State *L, const char *name,
lua_CFunction f, lua_CFunction u) {
lua_pushcfunction(L, u);
lua_pushcclosure(L, f, 1);
lua_setfield(L, -2, name);
}
static void base_open (lua_State *L) {
/* set global _G */
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
/* open lib into global table */
luaL_register(L, "_G", base_funcs);
lua_pushliteral(L, LUA_VERSION);
lua_setglobal(L, "_VERSION"); /* set global _VERSION */
/* `ipairs' and `pairs' need auxliliary functions as upvalues */
auxopen(L, "ipairs", luaB_ipairs, ipairsaux);
auxopen(L, "pairs", luaB_pairs, luaB_next);
/* `newproxy' needs a weaktable as upvalue */
lua_createtable(L, 0, 1); /* new table `w' */
lua_pushvalue(L, -1); /* `w' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushliteral(L, "kv");
lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
lua_pushcclosure(L, luaB_newproxy, 1);
lua_setglobal(L, "newproxy"); /* set global `newproxy' */
}
LUALIB_API int luaopen_base (lua_State *L) {
base_open(L);
luaL_register(L, LUA_COLIBNAME, co_funcs);
return 2;
}
+839
View File
@@ -0,0 +1,839 @@
/*
** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#include <stdlib.h>
#define lcode_c
#define LUA_CORE
#include "lua.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "ltable.h"
#define hasjumps(e) ((e)->t != (e)->f)
static int isnumeral(expdesc *e) {
return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP);
}
void luaK_nil (FuncState *fs, int from, int n) {
Instruction *previous;
if (fs->pc > fs->lasttarget) { /* no jumps to current position? */
if (fs->pc == 0) { /* function start? */
if (from >= fs->nactvar)
return; /* positions are already clean */
}
else {
previous = &fs->f->code[fs->pc-1];
if (GET_OPCODE(*previous) == OP_LOADNIL) {
int pfrom = GETARG_A(*previous);
int pto = GETARG_B(*previous);
if (pfrom <= from && from <= pto+1) { /* can connect both? */
if (from+n-1 > pto)
SETARG_B(*previous, from+n-1);
return;
}
}
}
}
luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */
}
int luaK_jump (FuncState *fs) {
int jpc = fs->jpc; /* save list of jumps to here */
int j;
fs->jpc = NO_JUMP;
j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP);
luaK_concat(fs, &j, jpc); /* keep them on hold */
return j;
}
void luaK_ret (FuncState *fs, int first, int nret) {
luaK_codeABC(fs, OP_RETURN, first, nret+1, 0);
}
static int condjump (FuncState *fs, OpCode op, int A, int B, int C) {
luaK_codeABC(fs, op, A, B, C);
return luaK_jump(fs);
}
static void fixjump (FuncState *fs, int pc, int dest) {
Instruction *jmp = &fs->f->code[pc];
int offset = dest-(pc+1);
lua_assert(dest != NO_JUMP);
if (abs(offset) > MAXARG_sBx)
luaX_syntaxerror(fs->ls, "control structure too long");
SETARG_sBx(*jmp, offset);
}
/*
** returns current `pc' and marks it as a jump target (to avoid wrong
** optimizations with consecutive instructions not in the same basic block).
*/
int luaK_getlabel (FuncState *fs) {
fs->lasttarget = fs->pc;
return fs->pc;
}
static int getjump (FuncState *fs, int pc) {
int offset = GETARG_sBx(fs->f->code[pc]);
if (offset == NO_JUMP) /* point to itself represents end of list */
return NO_JUMP; /* end of list */
else
return (pc+1)+offset; /* turn offset into absolute position */
}
static Instruction *getjumpcontrol (FuncState *fs, int pc) {
Instruction *pi = &fs->f->code[pc];
if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1))))
return pi-1;
else
return pi;
}
/*
** check whether list has any jump that do not produce a value
** (or produce an inverted value)
*/
static int need_value (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list)) {
Instruction i = *getjumpcontrol(fs, list);
if (GET_OPCODE(i) != OP_TESTSET) return 1;
}
return 0; /* not found */
}
static int patchtestreg (FuncState *fs, int node, int reg) {
Instruction *i = getjumpcontrol(fs, node);
if (GET_OPCODE(*i) != OP_TESTSET)
return 0; /* cannot patch other instructions */
if (reg != NO_REG && reg != GETARG_B(*i))
SETARG_A(*i, reg);
else /* no register to put value or register already has the value */
*i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i));
return 1;
}
static void removevalues (FuncState *fs, int list) {
for (; list != NO_JUMP; list = getjump(fs, list))
patchtestreg(fs, list, NO_REG);
}
static void patchlistaux (FuncState *fs, int list, int vtarget, int reg,
int dtarget) {
while (list != NO_JUMP) {
int next = getjump(fs, list);
if (patchtestreg(fs, list, reg))
fixjump(fs, list, vtarget);
else
fixjump(fs, list, dtarget); /* jump to default target */
list = next;
}
}
static void dischargejpc (FuncState *fs) {
patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc);
fs->jpc = NO_JUMP;
}
void luaK_patchlist (FuncState *fs, int list, int target) {
if (target == fs->pc)
luaK_patchtohere(fs, list);
else {
lua_assert(target < fs->pc);
patchlistaux(fs, list, target, NO_REG, target);
}
}
void luaK_patchtohere (FuncState *fs, int list) {
luaK_getlabel(fs);
luaK_concat(fs, &fs->jpc, list);
}
void luaK_concat (FuncState *fs, int *l1, int l2) {
if (l2 == NO_JUMP) return;
else if (*l1 == NO_JUMP)
*l1 = l2;
else {
int list = *l1;
int next;
while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */
list = next;
fixjump(fs, list, l2);
}
}
void luaK_checkstack (FuncState *fs, int n) {
int newstack = fs->freereg + n;
if (newstack > fs->f->maxstacksize) {
if (newstack >= MAXSTACK)
luaX_syntaxerror(fs->ls, "function or expression too complex");
fs->f->maxstacksize = cast_byte(newstack);
}
}
void luaK_reserveregs (FuncState *fs, int n) {
luaK_checkstack(fs, n);
fs->freereg += n;
}
static void freereg (FuncState *fs, int reg) {
if (!ISK(reg) && reg >= fs->nactvar) {
fs->freereg--;
lua_assert(reg == fs->freereg);
}
}
static void freeexp (FuncState *fs, expdesc *e) {
if (e->k == VNONRELOC)
freereg(fs, e->u.s.info);
}
static int addk (FuncState *fs, TValue *k, TValue *v) {
lua_State *L = fs->L;
TValue *idx = luaH_set(L, fs->h, k);
Proto *f = fs->f;
int oldsize = f->sizek;
if (ttisnumber(idx)) {
lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v));
return cast_int(nvalue(idx));
}
else { /* constant not found; create a new entry */
setnvalue(idx, cast_num(fs->nk));
luaM_growvector(L, f->k, fs->nk, f->sizek, TValue,
MAXARG_Bx, "constant table overflow");
while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]);
setobj(L, &f->k[fs->nk], v);
luaC_barrier(L, f, v);
return fs->nk++;
}
}
int luaK_stringK (FuncState *fs, TString *s) {
TValue o;
setsvalue(fs->L, &o, s);
return addk(fs, &o, &o);
}
int luaK_numberK (FuncState *fs, lua_Number r) {
TValue o;
setnvalue(&o, r);
return addk(fs, &o, &o);
}
static int boolK (FuncState *fs, int b) {
TValue o;
setbvalue(&o, b);
return addk(fs, &o, &o);
}
static int nilK (FuncState *fs) {
TValue k, v;
setnilvalue(&v);
/* cannot use nil as key; instead use table itself to represent nil */
sethvalue(fs->L, &k, fs->h);
return addk(fs, &k, &v);
}
void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) {
if (e->k == VCALL) { /* expression is an open function call? */
SETARG_C(getcode(fs, e), nresults+1);
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), nresults+1);
SETARG_A(getcode(fs, e), fs->freereg);
luaK_reserveregs(fs, 1);
}
}
void luaK_setoneret (FuncState *fs, expdesc *e) {
if (e->k == VCALL) { /* expression is an open function call? */
e->k = VNONRELOC;
e->u.s.info = GETARG_A(getcode(fs, e));
}
else if (e->k == VVARARG) {
SETARG_B(getcode(fs, e), 2);
e->k = VRELOCABLE; /* can relocate its simple result */
}
}
void luaK_dischargevars (FuncState *fs, expdesc *e) {
switch (e->k) {
case VLOCAL: {
e->k = VNONRELOC;
break;
}
case VUPVAL: {
e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
case VGLOBAL: {
e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info);
e->k = VRELOCABLE;
break;
}
case VINDEXED: {
freereg(fs, e->u.s.aux);
freereg(fs, e->u.s.info);
e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux);
e->k = VRELOCABLE;
break;
}
case VVARARG:
case VCALL: {
luaK_setoneret(fs, e);
break;
}
default: break; /* there is one value available (somewhere) */
}
}
static int code_label (FuncState *fs, int A, int b, int jump) {
luaK_getlabel(fs); /* those instructions may be jump targets */
return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump);
}
static void discharge2reg (FuncState *fs, expdesc *e, int reg) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: {
luaK_nil(fs, reg, 1);
break;
}
case VFALSE: case VTRUE: {
luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0);
break;
}
case VK: {
luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info);
break;
}
case VKNUM: {
luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval));
break;
}
case VRELOCABLE: {
Instruction *pc = &getcode(fs, e);
SETARG_A(*pc, reg);
break;
}
case VNONRELOC: {
if (reg != e->u.s.info)
luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0);
break;
}
default: {
lua_assert(e->k == VVOID || e->k == VJMP);
return; /* nothing to do... */
}
}
e->u.s.info = reg;
e->k = VNONRELOC;
}
static void discharge2anyreg (FuncState *fs, expdesc *e) {
if (e->k != VNONRELOC) {
luaK_reserveregs(fs, 1);
discharge2reg(fs, e, fs->freereg-1);
}
}
static void exp2reg (FuncState *fs, expdesc *e, int reg) {
discharge2reg(fs, e, reg);
if (e->k == VJMP)
luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */
if (hasjumps(e)) {
int final; /* position after whole expression */
int p_f = NO_JUMP; /* position of an eventual LOAD false */
int p_t = NO_JUMP; /* position of an eventual LOAD true */
if (need_value(fs, e->t) || need_value(fs, e->f)) {
int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs);
p_f = code_label(fs, reg, 0, 1);
p_t = code_label(fs, reg, 1, 0);
luaK_patchtohere(fs, fj);
}
final = luaK_getlabel(fs);
patchlistaux(fs, e->f, final, reg, p_f);
patchlistaux(fs, e->t, final, reg, p_t);
}
e->f = e->t = NO_JUMP;
e->u.s.info = reg;
e->k = VNONRELOC;
}
void luaK_exp2nextreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
freeexp(fs, e);
luaK_reserveregs(fs, 1);
exp2reg(fs, e, fs->freereg - 1);
}
int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
if (e->k == VNONRELOC) {
if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */
if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */
exp2reg(fs, e, e->u.s.info); /* put value on it */
return e->u.s.info;
}
}
luaK_exp2nextreg(fs, e); /* default */
return e->u.s.info;
}
void luaK_exp2val (FuncState *fs, expdesc *e) {
if (hasjumps(e))
luaK_exp2anyreg(fs, e);
else
luaK_dischargevars(fs, e);
}
int luaK_exp2RK (FuncState *fs, expdesc *e) {
luaK_exp2val(fs, e);
switch (e->k) {
case VKNUM:
case VTRUE:
case VFALSE:
case VNIL: {
if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */
e->u.s.info = (e->k == VNIL) ? nilK(fs) :
(e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) :
boolK(fs, (e->k == VTRUE));
e->k = VK;
return RKASK(e->u.s.info);
}
else break;
}
case VK: {
if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */
return RKASK(e->u.s.info);
else break;
}
default: break;
}
/* not a constant in the right range: put it in a register */
return luaK_exp2anyreg(fs, e);
}
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) {
switch (var->k) {
case VLOCAL: {
freeexp(fs, ex);
exp2reg(fs, ex, var->u.s.info);
return;
}
case VUPVAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0);
break;
}
case VGLOBAL: {
int e = luaK_exp2anyreg(fs, ex);
luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);
break;
}
case VINDEXED: {
int e = luaK_exp2RK(fs, ex);
luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e);
break;
}
default: {
lua_assert(0); /* invalid var kind to store */
break;
}
}
freeexp(fs, ex);
}
void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
int func;
luaK_exp2anyreg(fs, e);
freeexp(fs, e);
func = fs->freereg;
luaK_reserveregs(fs, 2);
luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
freeexp(fs, key);
e->u.s.info = func;
e->k = VNONRELOC;
}
static void invertjump (FuncState *fs, expdesc *e) {
Instruction *pc = getjumpcontrol(fs, e->u.s.info);
lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET &&
GET_OPCODE(*pc) != OP_TEST);
SETARG_A(*pc, !(GETARG_A(*pc)));
}
static int jumponcond (FuncState *fs, expdesc *e, int cond) {
if (e->k == VRELOCABLE) {
Instruction ie = getcode(fs, e);
if (GET_OPCODE(ie) == OP_NOT) {
fs->pc--; /* remove previous OP_NOT */
return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond);
}
/* else go through */
}
discharge2anyreg(fs, e);
freeexp(fs, e);
return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond);
}
void luaK_goiftrue (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VK: case VKNUM: case VTRUE: {
pc = NO_JUMP; /* always true; do nothing */
break;
}
case VFALSE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
invertjump(fs, e);
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 0);
break;
}
}
luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */
luaK_patchtohere(fs, e->t);
e->t = NO_JUMP;
}
static void luaK_goiffalse (FuncState *fs, expdesc *e) {
int pc; /* pc of last jump */
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
pc = NO_JUMP; /* always false; do nothing */
break;
}
case VTRUE: {
pc = luaK_jump(fs); /* always jump */
break;
}
case VJMP: {
pc = e->u.s.info;
break;
}
default: {
pc = jumponcond(fs, e, 1);
break;
}
}
luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */
luaK_patchtohere(fs, e->f);
e->f = NO_JUMP;
}
static void codenot (FuncState *fs, expdesc *e) {
luaK_dischargevars(fs, e);
switch (e->k) {
case VNIL: case VFALSE: {
e->k = VTRUE;
break;
}
case VK: case VKNUM: case VTRUE: {
e->k = VFALSE;
break;
}
case VJMP: {
invertjump(fs, e);
break;
}
case VRELOCABLE:
case VNONRELOC: {
discharge2anyreg(fs, e);
freeexp(fs, e);
e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0);
e->k = VRELOCABLE;
break;
}
default: {
lua_assert(0); /* cannot happen */
break;
}
}
/* interchange true and false lists */
{ int temp = e->f; e->f = e->t; e->t = temp; }
removevalues(fs, e->f);
removevalues(fs, e->t);
}
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
t->u.s.aux = luaK_exp2RK(fs, k);
t->k = VINDEXED;
}
static int constfolding (OpCode op, expdesc *e1, expdesc *e2) {
lua_Number v1, v2, r;
if (!isnumeral(e1) || !isnumeral(e2)) return 0;
v1 = e1->u.nval;
v2 = e2->u.nval;
switch (op) {
case OP_ADD: r = luai_numadd(v1, v2); break;
case OP_SUB: r = luai_numsub(v1, v2); break;
case OP_MUL: r = luai_nummul(v1, v2); break;
case OP_DIV:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_numdiv(v1, v2); break;
case OP_MOD:
if (v2 == 0) return 0; /* do not attempt to divide by 0 */
r = luai_nummod(v1, v2); break;
case OP_POW: r = luai_numpow(v1, v2); break;
case OP_UNM: r = luai_numunm(v1); break;
case OP_LEN: return 0; /* no constant folding for 'len' */
default: lua_assert(0); r = 0; break;
}
if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */
e1->u.nval = r;
return 1;
}
static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) {
if (constfolding(op, e1, e2))
return;
else {
int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0;
int o1 = luaK_exp2RK(fs, e1);
if (o1 > o2) {
freeexp(fs, e1);
freeexp(fs, e2);
}
else {
freeexp(fs, e2);
freeexp(fs, e1);
}
e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2);
e1->k = VRELOCABLE;
}
}
static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1,
expdesc *e2) {
int o1 = luaK_exp2RK(fs, e1);
int o2 = luaK_exp2RK(fs, e2);
freeexp(fs, e2);
freeexp(fs, e1);
if (cond == 0 && op != OP_EQ) {
int temp; /* exchange args to replace by `<' or `<=' */
temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */
cond = 1;
}
e1->u.s.info = condjump(fs, op, cond, o1, o2);
e1->k = VJMP;
}
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) {
expdesc e2;
e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0;
switch (op) {
case OPR_MINUS: {
if (!isnumeral(e))
luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */
codearith(fs, OP_UNM, e, &e2);
break;
}
case OPR_NOT: codenot(fs, e); break;
case OPR_LEN: {
luaK_exp2anyreg(fs, e); /* cannot operate on constants */
codearith(fs, OP_LEN, e, &e2);
break;
}
default: lua_assert(0);
}
}
void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
switch (op) {
case OPR_AND: {
luaK_goiftrue(fs, v);
break;
}
case OPR_OR: {
luaK_goiffalse(fs, v);
break;
}
case OPR_CONCAT: {
luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */
break;
}
case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV:
case OPR_MOD: case OPR_POW: {
if (!isnumeral(v)) luaK_exp2RK(fs, v);
break;
}
default: {
luaK_exp2RK(fs, v);
break;
}
}
}
void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) {
switch (op) {
case OPR_AND: {
lua_assert(e1->t == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->f, e1->f);
*e1 = *e2;
break;
}
case OPR_OR: {
lua_assert(e1->f == NO_JUMP); /* list must be closed */
luaK_dischargevars(fs, e2);
luaK_concat(fs, &e2->t, e1->t);
*e1 = *e2;
break;
}
case OPR_CONCAT: {
luaK_exp2val(fs, e2);
if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) {
lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1);
freeexp(fs, e1);
SETARG_B(getcode(fs, e2), e1->u.s.info);
e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info;
}
else {
luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */
codearith(fs, OP_CONCAT, e1, e2);
}
break;
}
case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break;
case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break;
case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break;
case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break;
case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break;
case OPR_POW: codearith(fs, OP_POW, e1, e2); break;
case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break;
case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break;
case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break;
case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break;
case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break;
case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break;
default: lua_assert(0);
}
}
void luaK_fixline (FuncState *fs, int line) {
fs->f->lineinfo[fs->pc - 1] = line;
}
static int luaK_code (FuncState *fs, Instruction i, int line) {
Proto *f = fs->f;
dischargejpc(fs); /* `pc' will change */
/* put new instruction in code array */
luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction,
MAX_INT, "code size overflow");
f->code[fs->pc] = i;
/* save corresponding line information */
luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int,
MAX_INT, "code size overflow");
f->lineinfo[fs->pc] = line;
return fs->pc++;
}
int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) {
lua_assert(getOpMode(o) == iABC);
lua_assert(getBMode(o) != OpArgN || b == 0);
lua_assert(getCMode(o) != OpArgN || c == 0);
return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline);
}
int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx);
lua_assert(getCMode(o) == OpArgN);
return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline);
}
void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) {
int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1;
int b = (tostore == LUA_MULTRET) ? 0 : tostore;
lua_assert(tostore != 0);
if (c <= MAXARG_C)
luaK_codeABC(fs, OP_SETLIST, base, b, c);
else {
luaK_codeABC(fs, OP_SETLIST, base, b, 0);
luaK_code(fs, cast(Instruction, c), fs->ls->lastline);
}
fs->freereg = base + 1; /* free registers with list values */
}
+76
View File
@@ -0,0 +1,76 @@
/*
** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $
** Code generator for Lua
** See Copyright Notice in lua.h
*/
#ifndef lcode_h
#define lcode_h
#include "llex.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
/*
** Marks the end of a patch list. It is an invalid value both as an absolute
** address, and as a list link (would link an element to itself).
*/
#define NO_JUMP (-1)
/*
** grep "ORDER OPR" if you change these enums
*/
typedef enum BinOpr {
OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW,
OPR_CONCAT,
OPR_NE, OPR_EQ,
OPR_LT, OPR_LE, OPR_GT, OPR_GE,
OPR_AND, OPR_OR,
OPR_NOBINOPR
} BinOpr;
typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info])
#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx)
#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET)
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n);
LUAI_FUNC void luaK_checkstack (FuncState *fs, int n);
LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s);
LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r);
LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e);
LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults);
LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e);
LUAI_FUNC int luaK_jump (FuncState *fs);
LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret);
LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target);
LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list);
LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2);
LUAI_FUNC int luaK_getlabel (FuncState *fs);
LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v);
LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v);
LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2);
LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore);
#endif
+397
View File
@@ -0,0 +1,397 @@
/*
** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $
** Interface from Lua to its debug API
** See Copyright Notice in lua.h
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ldblib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int db_getregistry (lua_State *L) {
lua_pushvalue(L, LUA_REGISTRYINDEX);
return 1;
}
static int db_getmetatable (lua_State *L) {
luaL_checkany(L, 1);
if (!lua_getmetatable(L, 1)) {
lua_pushnil(L); /* no metatable */
}
return 1;
}
static int db_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
lua_settop(L, 2);
lua_pushboolean(L, lua_setmetatable(L, 1));
return 1;
}
static int db_getfenv (lua_State *L) {
lua_getfenv(L, 1);
return 1;
}
static int db_setfenv (lua_State *L) {
luaL_checktype(L, 2, LUA_TTABLE);
lua_settop(L, 2);
if (lua_setfenv(L, 1) == 0)
luaL_error(L, LUA_QL("setfenv")
" cannot change environment of given object");
return 1;
}
static void settabss (lua_State *L, const char *i, const char *v) {
lua_pushstring(L, v);
lua_setfield(L, -2, i);
}
static void settabsi (lua_State *L, const char *i, int v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, i);
}
static lua_State *getthread (lua_State *L, int *arg) {
if (lua_isthread(L, 1)) {
*arg = 1;
return lua_tothread(L, 1);
}
else {
*arg = 0;
return L;
}
}
static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) {
if (L == L1) {
lua_pushvalue(L, -2);
lua_remove(L, -3);
}
else
lua_xmove(L1, L, 1);
lua_setfield(L, -2, fname);
}
static int db_getinfo (lua_State *L) {
lua_Debug ar;
int arg;
lua_State *L1 = getthread(L, &arg);
const char *options = luaL_optstring(L, arg+2, "flnSu");
if (lua_isnumber(L, arg+1)) {
if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) {
lua_pushnil(L); /* level out of range */
return 1;
}
}
else if (lua_isfunction(L, arg+1)) {
lua_pushfstring(L, ">%s", options);
options = lua_tostring(L, -1);
lua_pushvalue(L, arg+1);
lua_xmove(L, L1, 1);
}
else
return luaL_argerror(L, arg+1, "function or level expected");
if (!lua_getinfo(L1, options, &ar))
return luaL_argerror(L, arg+2, "invalid option");
lua_createtable(L, 0, 2);
if (strchr(options, 'S')) {
settabss(L, "source", ar.source);
settabss(L, "short_src", ar.short_src);
settabsi(L, "linedefined", ar.linedefined);
settabsi(L, "lastlinedefined", ar.lastlinedefined);
settabss(L, "what", ar.what);
}
if (strchr(options, 'l'))
settabsi(L, "currentline", ar.currentline);
if (strchr(options, 'u'))
settabsi(L, "nups", ar.nups);
if (strchr(options, 'n')) {
settabss(L, "name", ar.name);
settabss(L, "namewhat", ar.namewhat);
}
if (strchr(options, 'L'))
treatstackoption(L, L1, "activelines");
if (strchr(options, 'f'))
treatstackoption(L, L1, "func");
return 1; /* return table */
}
static int db_getlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
const char *name;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2));
if (name) {
lua_xmove(L1, L, 1);
lua_pushstring(L, name);
lua_pushvalue(L, -2);
return 2;
}
else {
lua_pushnil(L);
return 1;
}
}
static int db_setlocal (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */
return luaL_argerror(L, arg+1, "level out of range");
luaL_checkany(L, arg+3);
lua_settop(L, arg+3);
lua_xmove(L, L1, 1);
lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2)));
return 1;
}
static int auxupvalue (lua_State *L, int get) {
const char *name;
int n = luaL_checkint(L, 2);
luaL_checktype(L, 1, LUA_TFUNCTION);
if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */
name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n);
if (name == NULL) return 0;
lua_pushstring(L, name);
lua_insert(L, -(get+1));
return get + 1;
}
static int db_getupvalue (lua_State *L) {
return auxupvalue(L, 1);
}
static int db_setupvalue (lua_State *L) {
luaL_checkany(L, 3);
return auxupvalue(L, 0);
}
static const char KEY_HOOK = 'h';
static void hookf (lua_State *L, lua_Debug *ar) {
static const char *const hooknames[] =
{"call", "return", "line", "count", "tail return"};
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
lua_pushlightuserdata(L, L);
lua_rawget(L, -2);
if (lua_isfunction(L, -1)) {
lua_pushstring(L, hooknames[(int)ar->event]);
if (ar->currentline >= 0)
lua_pushinteger(L, ar->currentline);
else lua_pushnil(L);
lua_assert(lua_getinfo(L, "lS", ar));
lua_call(L, 2, 0);
}
}
static int makemask (const char *smask, int count) {
int mask = 0;
if (strchr(smask, 'c')) mask |= LUA_MASKCALL;
if (strchr(smask, 'r')) mask |= LUA_MASKRET;
if (strchr(smask, 'l')) mask |= LUA_MASKLINE;
if (count > 0) mask |= LUA_MASKCOUNT;
return mask;
}
static char *unmakemask (int mask, char *smask) {
int i = 0;
if (mask & LUA_MASKCALL) smask[i++] = 'c';
if (mask & LUA_MASKRET) smask[i++] = 'r';
if (mask & LUA_MASKLINE) smask[i++] = 'l';
smask[i] = '\0';
return smask;
}
static void gethooktable (lua_State *L) {
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_rawget(L, LUA_REGISTRYINDEX);
if (!lua_istable(L, -1)) {
lua_pop(L, 1);
lua_createtable(L, 0, 1);
lua_pushlightuserdata(L, (void *)&KEY_HOOK);
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
}
}
static int db_sethook (lua_State *L) {
int arg, mask, count;
lua_Hook func;
lua_State *L1 = getthread(L, &arg);
if (lua_isnoneornil(L, arg+1)) {
lua_settop(L, arg+1);
func = NULL; mask = 0; count = 0; /* turn off hooks */
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checktype(L, arg+1, LUA_TFUNCTION);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_pushvalue(L, arg+1);
lua_rawset(L, -3); /* set new hook */
lua_pop(L, 1); /* remove hook table */
lua_sethook(L1, func, mask, count); /* set hooks */
return 0;
}
static int db_gethook (lua_State *L) {
int arg;
lua_State *L1 = getthread(L, &arg);
char buff[5];
int mask = lua_gethookmask(L1);
lua_Hook hook = lua_gethook(L1);
if (hook != NULL && hook != hookf) /* external hook? */
lua_pushliteral(L, "external hook");
else {
gethooktable(L);
lua_pushlightuserdata(L, L1);
lua_rawget(L, -2); /* get hook */
lua_remove(L, -2); /* remove hook table */
}
lua_pushstring(L, unmakemask(mask, buff));
lua_pushinteger(L, lua_gethookcount(L1));
return 3;
}
static int db_debug (lua_State *L) {
for (;;) {
char buffer[250];
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
strcmp(buffer, "cont\n") == 0)
return 0;
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
}
lua_settop(L, 0); /* remove eventual returns */
}
}
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
static int db_errorfb (lua_State *L) {
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
lua_State *L1 = getthread(L, &arg);
lua_Debug ar;
if (lua_isnumber(L, arg+2)) {
level = (int)lua_tointeger(L, arg+2);
lua_pop(L, 1);
}
else
level = (L == L1) ? 1 : 0; /* level 0 may be this own function */
if (lua_gettop(L) == arg)
lua_pushliteral(L, "");
else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */
else lua_pushliteral(L, "\n");
lua_pushliteral(L, "stack traceback:");
while (lua_getstack(L1, level++, &ar)) {
if (level > LEVELS1 && firstpart) {
/* no more than `LEVELS2' more levels? */
if (!lua_getstack(L1, level+LEVELS2, &ar))
level--; /* keep going */
else {
lua_pushliteral(L, "\n\t..."); /* too many levels */
while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */
level++;
}
firstpart = 0;
continue;
}
lua_pushliteral(L, "\n\t");
lua_getinfo(L1, "Snl", &ar);
lua_pushfstring(L, "%s:", ar.short_src);
if (ar.currentline > 0)
lua_pushfstring(L, "%d:", ar.currentline);
if (*ar.namewhat != '\0') /* is there a name? */
lua_pushfstring(L, " in function " LUA_QS, ar.name);
else {
if (*ar.what == 'm') /* main? */
lua_pushfstring(L, " in main chunk");
else if (*ar.what == 'C' || *ar.what == 't')
lua_pushliteral(L, " ?"); /* C function or tail call */
else
lua_pushfstring(L, " in function <%s:%d>",
ar.short_src, ar.linedefined);
}
lua_concat(L, lua_gettop(L) - arg);
}
lua_concat(L, lua_gettop(L) - arg);
return 1;
}
static const luaL_Reg dblib[] = {
{"debug", db_debug},
{"getfenv", db_getfenv},
{"gethook", db_gethook},
{"getinfo", db_getinfo},
{"getlocal", db_getlocal},
{"getregistry", db_getregistry},
{"getmetatable", db_getmetatable},
{"getupvalue", db_getupvalue},
{"setfenv", db_setfenv},
{"sethook", db_sethook},
{"setlocal", db_setlocal},
{"setmetatable", db_setmetatable},
{"setupvalue", db_setupvalue},
{"traceback", db_errorfb},
{NULL, NULL}
};
LUALIB_API int luaopen_debug (lua_State *L) {
luaL_register(L, LUA_DBLIBNAME, dblib);
return 1;
}
+638
View File
@@ -0,0 +1,638 @@
/*
** $Id: ldebug.c,v 2.29.1.6 2008/05/08 16:56:26 roberto Exp $
** Debug Interface
** See Copyright Notice in lua.h
*/
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#define ldebug_c
#define LUA_CORE
#include "lua.h"
#include "lapi.h"
#include "lcode.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
static int currentpc (lua_State *L, CallInfo *ci) {
if (!isLua(ci)) return -1; /* function is not a Lua function? */
if (ci == L->ci)
ci->savedpc = L->savedpc;
return pcRel(ci->savedpc, ci_func(ci)->l.p);
}
static int currentline (lua_State *L, CallInfo *ci) {
int pc = currentpc(L, ci);
if (pc < 0)
return -1; /* only active lua functions have current-line information */
else
return getline(ci_func(ci)->l.p, pc);
}
/*
** this function can be called asynchronous (e.g. during a signal)
*/
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
if (func == NULL || mask == 0) { /* turn off hooks? */
mask = 0;
func = NULL;
}
L->hook = func;
L->basehookcount = count;
resethookcount(L);
L->hookmask = cast_byte(mask);
return 1;
}
LUA_API lua_Hook lua_gethook (lua_State *L) {
return L->hook;
}
LUA_API int lua_gethookmask (lua_State *L) {
return L->hookmask;
}
LUA_API int lua_gethookcount (lua_State *L) {
return L->basehookcount;
}
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
int status;
CallInfo *ci;
lua_lock(L);
for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) {
level--;
if (f_isLua(ci)) /* Lua function? */
level -= ci->tailcalls; /* skip lost tail calls */
}
if (level == 0 && ci > L->base_ci) { /* level found? */
status = 1;
ar->i_ci = cast_int(ci - L->base_ci);
}
else if (level < 0) { /* level is of a lost tail call? */
status = 1;
ar->i_ci = 0;
}
else status = 0; /* no such level */
lua_unlock(L);
return status;
}
static Proto *getluaproto (CallInfo *ci) {
return (isLua(ci) ? ci_func(ci)->l.p : NULL);
}
static const char *findlocal (lua_State *L, CallInfo *ci, int n) {
const char *name;
Proto *fp = getluaproto(ci);
if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL)
return name; /* is a local variable in a Lua function */
else {
StkId limit = (ci == L->ci) ? L->top : (ci+1)->func;
if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */
return "(*temporary)";
else
return NULL;
}
}
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
CallInfo *ci = L->base_ci + ar->i_ci;
const char *name = findlocal(L, ci, n);
lua_lock(L);
if (name)
luaA_pushobject(L, ci->base + (n - 1));
lua_unlock(L);
return name;
}
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
CallInfo *ci = L->base_ci + ar->i_ci;
const char *name = findlocal(L, ci, n);
lua_lock(L);
if (name)
setobjs2s(L, ci->base + (n - 1), L->top - 1);
L->top--; /* pop value */
lua_unlock(L);
return name;
}
static void funcinfo (lua_Debug *ar, Closure *cl) {
if (cl->c.isC) {
ar->source = "=[C]";
ar->linedefined = -1;
ar->lastlinedefined = -1;
ar->what = "C";
}
else {
ar->source = getstr(cl->l.p->source);
ar->linedefined = cl->l.p->linedefined;
ar->lastlinedefined = cl->l.p->lastlinedefined;
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
}
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
}
static void info_tailcall (lua_Debug *ar) {
ar->name = ar->namewhat = "";
ar->what = "tail";
ar->lastlinedefined = ar->linedefined = ar->currentline = -1;
ar->source = "=(tail call)";
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
ar->nups = 0;
}
static void collectvalidlines (lua_State *L, Closure *f) {
if (f == NULL || f->c.isC) {
setnilvalue(L->top);
}
else {
Table *t = luaH_new(L, 0, 0);
int *lineinfo = f->l.p->lineinfo;
int i;
for (i=0; i<f->l.p->sizelineinfo; i++)
setbvalue(luaH_setnum(L, t, lineinfo[i]), 1);
sethvalue(L, L->top, t);
}
incr_top(L);
}
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
Closure *f, CallInfo *ci) {
int status = 1;
if (f == NULL) {
info_tailcall(ar);
return status;
}
for (; *what; what++) {
switch (*what) {
case 'S': {
funcinfo(ar, f);
break;
}
case 'l': {
ar->currentline = (ci) ? currentline(L, ci) : -1;
break;
}
case 'u': {
ar->nups = f->c.nupvalues;
break;
}
case 'n': {
ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL;
if (ar->namewhat == NULL) {
ar->namewhat = ""; /* not found */
ar->name = NULL;
}
break;
}
case 'L':
case 'f': /* handled by lua_getinfo */
break;
default: status = 0; /* invalid option */
}
}
return status;
}
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
int status;
Closure *f = NULL;
CallInfo *ci = NULL;
lua_lock(L);
if (*what == '>') {
StkId func = L->top - 1;
luai_apicheck(L, ttisfunction(func));
what++; /* skip the '>' */
f = clvalue(func);
L->top--; /* pop function */
}
else if (ar->i_ci != 0) { /* no tail call? */
ci = L->base_ci + ar->i_ci;
lua_assert(ttisfunction(ci->func));
f = clvalue(ci->func);
}
status = auxgetinfo(L, what, ar, f, ci);
if (strchr(what, 'f')) {
if (f == NULL) setnilvalue(L->top);
else setclvalue(L, L->top, f);
incr_top(L);
}
if (strchr(what, 'L'))
collectvalidlines(L, f);
lua_unlock(L);
return status;
}
/*
** {======================================================
** Symbolic Execution and code checker
** =======================================================
*/
#define check(x) if (!(x)) return 0;
#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode)
#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize)
static int precheck (const Proto *pt) {
check(pt->maxstacksize <= MAXSTACK);
check(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize);
check(!(pt->is_vararg & VARARG_NEEDSARG) ||
(pt->is_vararg & VARARG_HASARG));
check(pt->sizeupvalues <= pt->nups);
check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0);
check(pt->sizecode > 0 && GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN);
return 1;
}
#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1])
int luaG_checkopenop (Instruction i) {
switch (GET_OPCODE(i)) {
case OP_CALL:
case OP_TAILCALL:
case OP_RETURN:
case OP_SETLIST: {
check(GETARG_B(i) == 0);
return 1;
}
default: return 0; /* invalid instruction after an open call */
}
}
static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) {
switch (mode) {
case OpArgN: check(r == 0); break;
case OpArgU: break;
case OpArgR: checkreg(pt, r); break;
case OpArgK:
check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize);
break;
}
return 1;
}
static Instruction symbexec (const Proto *pt, int lastpc, int reg) {
int pc;
int last; /* stores position of last instruction that changed `reg' */
last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */
check(precheck(pt));
for (pc = 0; pc < lastpc; pc++) {
Instruction i = pt->code[pc];
OpCode op = GET_OPCODE(i);
int a = GETARG_A(i);
int b = 0;
int c = 0;
check(op < NUM_OPCODES);
checkreg(pt, a);
switch (getOpMode(op)) {
case iABC: {
b = GETARG_B(i);
c = GETARG_C(i);
check(checkArgMode(pt, b, getBMode(op)));
check(checkArgMode(pt, c, getCMode(op)));
break;
}
case iABx: {
b = GETARG_Bx(i);
if (getBMode(op) == OpArgK) check(b < pt->sizek);
break;
}
case iAsBx: {
b = GETARG_sBx(i);
if (getBMode(op) == OpArgR) {
int dest = pc+1+b;
check(0 <= dest && dest < pt->sizecode);
if (dest > 0) {
int j;
/* check that it does not jump to a setlist count; this
is tricky, because the count from a previous setlist may
have the same value of an invalid setlist; so, we must
go all the way back to the first of them (if any) */
for (j = 0; j < dest; j++) {
Instruction d = pt->code[dest-1-j];
if (!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)) break;
}
/* if 'j' is even, previous value is not a setlist (even if
it looks like one) */
check((j&1) == 0);
}
}
break;
}
}
if (testAMode(op)) {
if (a == reg) last = pc; /* change register `a' */
}
if (testTMode(op)) {
check(pc+2 < pt->sizecode); /* check skip */
check(GET_OPCODE(pt->code[pc+1]) == OP_JMP);
}
switch (op) {
case OP_LOADBOOL: {
if (c == 1) { /* does it jump? */
check(pc+2 < pt->sizecode); /* check its jump */
check(GET_OPCODE(pt->code[pc+1]) != OP_SETLIST ||
GETARG_C(pt->code[pc+1]) != 0);
}
break;
}
case OP_LOADNIL: {
if (a <= reg && reg <= b)
last = pc; /* set registers from `a' to `b' */
break;
}
case OP_GETUPVAL:
case OP_SETUPVAL: {
check(b < pt->nups);
break;
}
case OP_GETGLOBAL:
case OP_SETGLOBAL: {
check(ttisstring(&pt->k[b]));
break;
}
case OP_SELF: {
checkreg(pt, a+1);
if (reg == a+1) last = pc;
break;
}
case OP_CONCAT: {
check(b < c); /* at least two operands */
break;
}
case OP_TFORLOOP: {
check(c >= 1); /* at least one result (control variable) */
checkreg(pt, a+2+c); /* space for results */
if (reg >= a+2) last = pc; /* affect all regs above its base */
break;
}
case OP_FORLOOP:
case OP_FORPREP:
checkreg(pt, a+3);
/* go through */
case OP_JMP: {
int dest = pc+1+b;
/* not full check and jump is forward and do not skip `lastpc'? */
if (reg != NO_REG && pc < dest && dest <= lastpc)
pc += b; /* do the jump */
break;
}
case OP_CALL:
case OP_TAILCALL: {
if (b != 0) {
checkreg(pt, a+b-1);
}
c--; /* c = num. returns */
if (c == LUA_MULTRET) {
check(checkopenop(pt, pc));
}
else if (c != 0)
checkreg(pt, a+c-1);
if (reg >= a) last = pc; /* affect all registers above base */
break;
}
case OP_RETURN: {
b--; /* b = num. returns */
if (b > 0) checkreg(pt, a+b-1);
break;
}
case OP_SETLIST: {
if (b > 0) checkreg(pt, a + b);
if (c == 0) {
pc++;
check(pc < pt->sizecode - 1);
}
break;
}
case OP_CLOSURE: {
int nup, j;
check(b < pt->sizep);
nup = pt->p[b]->nups;
check(pc + nup < pt->sizecode);
for (j = 1; j <= nup; j++) {
OpCode op1 = GET_OPCODE(pt->code[pc + j]);
check(op1 == OP_GETUPVAL || op1 == OP_MOVE);
}
if (reg != NO_REG) /* tracing? */
pc += nup; /* do not 'execute' these pseudo-instructions */
break;
}
case OP_VARARG: {
check((pt->is_vararg & VARARG_ISVARARG) &&
!(pt->is_vararg & VARARG_NEEDSARG));
b--;
if (b == LUA_MULTRET) check(checkopenop(pt, pc));
checkreg(pt, a+b-1);
break;
}
default: break;
}
}
return pt->code[last];
}
#undef check
#undef checkjump
#undef checkreg
/* }====================================================== */
int luaG_checkcode (const Proto *pt) {
return (symbexec(pt, pt->sizecode, NO_REG) != 0);
}
static const char *kname (Proto *p, int c) {
if (ISK(c) && ttisstring(&p->k[INDEXK(c)]))
return svalue(&p->k[INDEXK(c)]);
else
return "?";
}
static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos,
const char **name) {
if (isLua(ci)) { /* a Lua function? */
Proto *p = ci_func(ci)->l.p;
int pc = currentpc(L, ci);
Instruction i;
*name = luaF_getlocalname(p, stackpos+1, pc);
if (*name) /* is a local? */
return "local";
i = symbexec(p, pc, stackpos); /* try symbolic execution */
lua_assert(pc != -1);
switch (GET_OPCODE(i)) {
case OP_GETGLOBAL: {
int g = GETARG_Bx(i); /* global index */
lua_assert(ttisstring(&p->k[g]));
*name = svalue(&p->k[g]);
return "global";
}
case OP_MOVE: {
int a = GETARG_A(i);
int b = GETARG_B(i); /* move from `b' to `a' */
if (b < a)
return getobjname(L, ci, b, name); /* get name for `b' */
break;
}
case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */
*name = kname(p, k);
return "field";
}
case OP_GETUPVAL: {
int u = GETARG_B(i); /* upvalue index */
*name = p->upvalues ? getstr(p->upvalues[u]) : "?";
return "upvalue";
}
case OP_SELF: {
int k = GETARG_C(i); /* key index */
*name = kname(p, k);
return "method";
}
default: break;
}
}
return NULL; /* no useful name found */
}
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
Instruction i;
if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1))
return NULL; /* calling function is not Lua (or is unknown) */
ci--; /* calling function */
i = ci_func(ci)->l.p->code[currentpc(L, ci)];
if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL ||
GET_OPCODE(i) == OP_TFORLOOP)
return getobjname(L, ci, GETARG_A(i), name);
else
return NULL; /* no useful name can be found */
}
/* only ANSI way to check whether a pointer points to an array */
static int isinstack (CallInfo *ci, const TValue *o) {
StkId p;
for (p = ci->base; p < ci->top; p++)
if (o == p) return 1;
return 0;
}
void luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
const char *name = NULL;
const char *t = luaT_typenames[ttype(o)];
const char *kind = (isinstack(L->ci, o)) ?
getobjname(L, L->ci, cast_int(o - L->base), &name) :
NULL;
if (kind)
luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)",
op, kind, name, t);
else
luaG_runerror(L, "attempt to %s a %s value", op, t);
}
void luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
lua_assert(!ttisstring(p1) && !ttisnumber(p1));
luaG_typeerror(L, p1, "concatenate");
}
void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
TValue temp;
if (luaV_tonumber(p1, &temp) == NULL)
p2 = p1; /* first operand is wrong */
luaG_typeerror(L, p2, "perform arithmetic on");
}
int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
const char *t1 = luaT_typenames[ttype(p1)];
const char *t2 = luaT_typenames[ttype(p2)];
if (t1[2] == t2[2])
luaG_runerror(L, "attempt to compare two %s values", t1);
else
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
return 0;
}
static void addinfo (lua_State *L, const char *msg) {
CallInfo *ci = L->ci;
if (isLua(ci)) { /* is Lua code? */
char buff[LUA_IDSIZE]; /* add file:line information */
int line = currentline(L, ci);
luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);
luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
}
}
void luaG_errormsg (lua_State *L) {
if (L->errfunc != 0) { /* is there an error handling function? */
StkId errfunc = restorestack(L, L->errfunc);
if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
setobjs2s(L, L->top, L->top - 1); /* move argument */
setobjs2s(L, L->top - 1, errfunc); /* push function */
incr_top(L);
luaD_call(L, L->top - 2, 1); /* call it */
}
luaD_throw(L, LUA_ERRRUN);
}
void luaG_runerror (lua_State *L, const char *fmt, ...) {
va_list argp;
va_start(argp, fmt);
addinfo(L, luaO_pushvfstring(L, fmt, argp));
va_end(argp);
luaG_errormsg(L);
}
+33
View File
@@ -0,0 +1,33 @@
/*
** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions from Debug Interface module
** See Copyright Notice in lua.h
*/
#ifndef ldebug_h
#define ldebug_h
#include "lstate.h"
#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1)
#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0)
#define resethookcount(L) (L->hookcount = L->basehookcount)
LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o,
const char *opname);
LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2);
LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1,
const TValue *p2);
LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...);
LUAI_FUNC void luaG_errormsg (lua_State *L);
LUAI_FUNC int luaG_checkcode (const Proto *pt);
LUAI_FUNC int luaG_checkopenop (Instruction i);
#endif
+518
View File
@@ -0,0 +1,518 @@
/*
** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#include <setjmp.h>
#include <stdlib.h>
#include <string.h>
#define ldo_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"
#include "lzio.h"
/*
** {======================================================
** Error-recovery functions
** =======================================================
*/
/* chain list of long jump buffers */
struct lua_longjmp {
struct lua_longjmp *previous;
luai_jmpbuf b;
volatile int status; /* error code */
};
void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) {
case LUA_ERRMEM: {
setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG));
break;
}
case LUA_ERRERR: {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
case LUA_ERRSYNTAX:
case LUA_ERRRUN: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
}
}
L->top = oldtop + 1;
}
static void restore_stack_limit (lua_State *L) {
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
int inuse = cast_int(L->ci - L->base_ci);
if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
luaD_reallocCI(L, LUAI_MAXCALLS);
}
}
static void resetstack (lua_State *L, int status) {
L->ci = L->base_ci;
L->base = L->ci->base;
luaF_close(L, L->base); /* close eventual pending closures */
luaD_seterrorobj(L, status, L->base);
L->nCcalls = L->baseCcalls;
L->allowhook = 1;
restore_stack_limit(L);
L->errfunc = 0;
L->errorJmp = NULL;
}
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
}
else {
L->status = cast_byte(errcode);
if (G(L)->panic) {
resetstack(L, errcode);
lua_unlock(L);
G(L)->panic(L);
}
exit(EXIT_FAILURE);
}
}
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
struct lua_longjmp lj;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
return lj.status;
}
/* }====================================================== */
static void correctstack (lua_State *L, TValue *oldstack) {
CallInfo *ci;
GCObject *up;
L->top = (L->top - oldstack) + L->stack;
for (up = L->openupval; up != NULL; up = up->gch.next)
gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack;
for (ci = L->base_ci; ci <= L->ci; ci++) {
ci->top = (ci->top - oldstack) + L->stack;
ci->base = (ci->base - oldstack) + L->stack;
ci->func = (ci->func - oldstack) + L->stack;
}
L->base = (L->base - oldstack) + L->stack;
}
void luaD_reallocstack (lua_State *L, int newsize) {
TValue *oldstack = L->stack;
int realsize = newsize + 1 + EXTRA_STACK;
lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1);
luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue);
L->stacksize = realsize;
L->stack_last = L->stack+newsize;
correctstack(L, oldstack);
}
void luaD_reallocCI (lua_State *L, int newsize) {
CallInfo *oldci = L->base_ci;
luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo);
L->size_ci = newsize;
L->ci = (L->ci - oldci) + L->base_ci;
L->end_ci = L->base_ci + L->size_ci - 1;
}
void luaD_growstack (lua_State *L, int n) {
if (n <= L->stacksize) /* double size is enough? */
luaD_reallocstack(L, 2*L->stacksize);
else
luaD_reallocstack(L, L->stacksize + n);
}
static CallInfo *growCI (lua_State *L) {
if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */
luaD_throw(L, LUA_ERRERR);
else {
luaD_reallocCI(L, 2*L->size_ci);
if (L->size_ci > LUAI_MAXCALLS)
luaG_runerror(L, "stack overflow");
}
return ++L->ci;
}
void luaD_callhook (lua_State *L, int event, int line) {
lua_Hook hook = L->hook;
if (hook && L->allowhook) {
ptrdiff_t top = savestack(L, L->top);
ptrdiff_t ci_top = savestack(L, L->ci->top);
lua_Debug ar;
ar.event = event;
ar.currentline = line;
if (event == LUA_HOOKTAILRET)
ar.i_ci = 0; /* tail call; no debug information about it */
else
ar.i_ci = cast_int(L->ci - L->base_ci);
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
L->ci->top = L->top + LUA_MINSTACK;
lua_assert(L->ci->top <= L->stack_last);
L->allowhook = 0; /* cannot call hooks inside a hook */
lua_unlock(L);
(*hook)(L, &ar);
lua_lock(L);
lua_assert(!L->allowhook);
L->allowhook = 1;
L->ci->top = restorestack(L, ci_top);
L->top = restorestack(L, top);
}
}
static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
int i;
int nfixargs = p->numparams;
Table *htab = NULL;
StkId base, fixed;
for (; actual < nfixargs; ++actual)
setnilvalue(L->top++);
#if defined(LUA_COMPAT_VARARG)
if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */
int nvar = actual - nfixargs; /* number of extra arguments */
lua_assert(p->is_vararg & VARARG_HASARG);
luaC_checkGC(L);
htab = luaH_new(L, nvar, 1); /* create `arg' table */
for (i=0; i<nvar; i++) /* put extra arguments into `arg' table */
setobj2n(L, luaH_setnum(L, htab, i+1), L->top - nvar + i);
/* store counter in field `n' */
setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar));
}
#endif
/* move fixed parameters to final position */
fixed = L->top - actual; /* first fixed argument */
base = L->top; /* final position of first argument */
for (i=0; i<nfixargs; i++) {
setobjs2s(L, L->top++, fixed+i);
setnilvalue(fixed+i);
}
/* add `arg' parameter */
if (htab) {
sethvalue(L, L->top++, htab);
lua_assert(iswhite(obj2gco(htab)));
}
return base;
}
static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
StkId p;
ptrdiff_t funcr = savestack(L, func);
if (!ttisfunction(tm))
luaG_typeerror(L, func, "call");
/* Open a hole inside the stack at `func' */
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
incr_top(L);
func = restorestack(L, funcr); /* previous call may change stack */
setobj2s(L, func, tm); /* tag method is the new function to be called */
return func;
}
#define inc_ci(L) \
((L->ci == L->end_ci) ? growCI(L) : \
(condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci))
int luaD_precall (lua_State *L, StkId func, int nresults) {
LClosure *cl;
ptrdiff_t funcr;
if (!ttisfunction(func)) /* `func' is not a function? */
func = tryfuncTM(L, func); /* check the `function' tag method */
funcr = savestack(L, func);
cl = &clvalue(func)->l;
L->ci->savedpc = L->savedpc;
if (!cl->isC) { /* Lua function? prepare its call */
CallInfo *ci;
StkId st, base;
Proto *p = cl->p;
luaD_checkstack(L, p->maxstacksize);
func = restorestack(L, funcr);
if (!p->is_vararg) { /* no varargs? */
base = func + 1;
if (L->top > base + p->numparams)
L->top = base + p->numparams;
}
else { /* vararg function */
int nargs = cast_int(L->top - func) - 1;
base = adjust_varargs(L, p, nargs);
func = restorestack(L, funcr); /* previous call may change the stack */
}
ci = inc_ci(L); /* now `enter' new function */
ci->func = func;
L->base = ci->base = base;
ci->top = L->base + p->maxstacksize;
lua_assert(ci->top <= L->stack_last);
L->savedpc = p->code; /* starting point */
ci->tailcalls = 0;
ci->nresults = nresults;
for (st = L->top; st < ci->top; st++)
setnilvalue(st);
L->top = ci->top;
if (L->hookmask & LUA_MASKCALL) {
L->savedpc++; /* hooks assume 'pc' is already incremented */
luaD_callhook(L, LUA_HOOKCALL, -1);
L->savedpc--; /* correct 'pc' */
}
return PCRLUA;
}
else { /* if is a C function, call it */
CallInfo *ci;
int n;
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */
ci = inc_ci(L); /* now `enter' new function */
ci->func = restorestack(L, funcr);
L->base = ci->base = ci->func + 1;
ci->top = L->top + LUA_MINSTACK;
lua_assert(ci->top <= L->stack_last);
ci->nresults = nresults;
if (L->hookmask & LUA_MASKCALL)
luaD_callhook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
n = (*curr_func(L)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;
else {
luaD_poscall(L, L->top - n);
return PCRC;
}
}
}
static StkId callrethooks (lua_State *L, StkId firstResult) {
ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */
luaD_callhook(L, LUA_HOOKRET, -1);
if (f_isLua(L->ci)) { /* Lua function? */
while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */
luaD_callhook(L, LUA_HOOKTAILRET, -1);
}
return restorestack(L, fr);
}
int luaD_poscall (lua_State *L, StkId firstResult) {
StkId res;
int wanted, i;
CallInfo *ci;
if (L->hookmask & LUA_MASKRET)
firstResult = callrethooks(L, firstResult);
ci = L->ci--;
res = ci->func; /* res == final position of 1st result */
wanted = ci->nresults;
L->base = (ci - 1)->base; /* restore base */
L->savedpc = (ci - 1)->savedpc; /* restore savedpc */
/* move results to correct place */
for (i = wanted; i != 0 && firstResult < L->top; i--)
setobjs2s(L, res++, firstResult++);
while (i-- > 0)
setnilvalue(res++);
L->top = res;
return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */
}
/*
** Call a function (C or Lua). The function to be called is at *func.
** The arguments are on the stack, right after the function.
** When returns, all the results are on the stack, starting at the original
** function position.
*/
void luaD_call (lua_State *L, StkId func, int nResults) {
if (++L->nCcalls >= LUAI_MAXCCALLS) {
if (L->nCcalls == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3)))
luaD_throw(L, LUA_ERRERR); /* error while handing stack error */
}
if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */
luaV_execute(L, 1); /* call it */
L->nCcalls--;
luaC_checkGC(L);
}
static void resume (lua_State *L, void *ud) {
StkId firstArg = cast(StkId, ud);
CallInfo *ci = L->ci;
if (L->status == 0) { /* start coroutine? */
lua_assert(ci == L->base_ci && firstArg > L->base);
if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA)
return;
}
else { /* resuming from previous yield */
lua_assert(L->status == LUA_YIELD);
L->status = 0;
if (!f_isLua(ci)) { /* `common' yield? */
/* finish interrupted execution of `OP_CALL' */
lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL ||
GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL);
if (luaD_poscall(L, firstArg)) /* complete it... */
L->top = L->ci->top; /* and correct top if not multiple results */
}
else /* yielded inside a hook: just continue its execution */
L->base = L->ci->base;
}
luaV_execute(L, cast_int(L->ci - L->base_ci));
}
static int resume_error (lua_State *L, const char *msg) {
L->top = L->ci->base;
setsvalue2s(L, L->top, luaS_new(L, msg));
incr_top(L);
lua_unlock(L);
return LUA_ERRRUN;
}
LUA_API int lua_resume (lua_State *L, int nargs) {
int status;
lua_lock(L);
if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci))
return resume_error(L, "cannot resume non-suspended coroutine");
if (L->nCcalls >= LUAI_MAXCCALLS)
return resume_error(L, "C stack overflow");
luai_userstateresume(L, nargs);
lua_assert(L->errfunc == 0);
L->baseCcalls = ++L->nCcalls;
status = luaD_rawrunprotected(L, resume, L->top - nargs);
if (status != 0) { /* error? */
L->status = cast_byte(status); /* mark thread as `dead' */
luaD_seterrorobj(L, status, L->top);
L->ci->top = L->top;
}
else {
lua_assert(L->nCcalls == L->baseCcalls);
status = L->status;
}
--L->nCcalls;
lua_unlock(L);
return status;
}
LUA_API int lua_yield (lua_State *L, int nresults) {
luai_userstateyield(L, nresults);
lua_lock(L);
if (L->nCcalls > L->baseCcalls)
luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
L->base = L->top - nresults; /* protect stack slots below */
L->status = LUA_YIELD;
lua_unlock(L);
return -1;
}
int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t old_top, ptrdiff_t ef) {
int status;
unsigned short oldnCcalls = L->nCcalls;
ptrdiff_t old_ci = saveci(L, L->ci);
lu_byte old_allowhooks = L->allowhook;
ptrdiff_t old_errfunc = L->errfunc;
L->errfunc = ef;
status = luaD_rawrunprotected(L, func, u);
if (status != 0) { /* an error occurred? */
StkId oldtop = restorestack(L, old_top);
luaF_close(L, oldtop); /* close eventual pending closures */
luaD_seterrorobj(L, status, oldtop);
L->nCcalls = oldnCcalls;
L->ci = restoreci(L, old_ci);
L->base = L->ci->base;
L->savedpc = L->ci->savedpc;
L->allowhook = old_allowhooks;
restore_stack_limit(L);
}
L->errfunc = old_errfunc;
return status;
}
/*
** Execute a protected parser.
*/
struct SParser { /* data to `f_parser' */
ZIO *z;
Mbuffer buff; /* buffer to be used by the scanner */
const char *name;
};
static void f_parser (lua_State *L, void *ud) {
int i;
Proto *tf;
Closure *cl;
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */
cl->l.upvals[i] = luaF_newupval(L);
setclvalue(L, L->top, cl);
incr_top(L);
}
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) {
struct SParser p;
int status;
p.z = z; p.name = name;
luaZ_initbuffer(L, &p.buff);
status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc);
luaZ_freebuffer(L, &p.buff);
return status;
}
+57
View File
@@ -0,0 +1,57 @@
/*
** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $
** Stack and Call structure of Lua
** See Copyright Notice in lua.h
*/
#ifndef ldo_h
#define ldo_h
#include "lobject.h"
#include "lstate.h"
#include "lzio.h"
#define luaD_checkstack(L,n) \
if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \
luaD_growstack(L, n); \
else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1));
#define incr_top(L) {luaD_checkstack(L,1); L->top++;}
#define savestack(L,p) ((char *)(p) - (char *)L->stack)
#define restorestack(L,n) ((TValue *)((char *)L->stack + (n)))
#define saveci(L,p) ((char *)(p) - (char *)L->base_ci)
#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n)))
/* results from luaD_precall */
#define PCRLUA 0 /* initiated a call to a Lua function */
#define PCRC 1 /* did a call to a C function */
#define PCRYIELD 2 /* C funtion yielded */
/* type of protected functions, to be ran by `runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name);
LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line);
LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult);
LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
LUAI_FUNC void luaD_growstack (lua_State *L, int n);
LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
#endif
+164
View File
@@ -0,0 +1,164 @@
/*
** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
** save precompiled Lua chunks
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define ldump_c
#define LUA_CORE
#include "lua.h"
#include "lobject.h"
#include "lstate.h"
#include "lundump.h"
typedef struct {
lua_State* L;
lua_Writer writer;
void* data;
int strip;
int status;
} DumpState;
#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D)
#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D)
static void DumpBlock(const void* b, size_t size, DumpState* D)
{
if (D->status==0)
{
lua_unlock(D->L);
D->status=(*D->writer)(D->L,b,size,D->data);
lua_lock(D->L);
}
}
static void DumpChar(int y, DumpState* D)
{
char x=(char)y;
DumpVar(x,D);
}
static void DumpInt(int x, DumpState* D)
{
DumpVar(x,D);
}
static void DumpNumber(lua_Number x, DumpState* D)
{
DumpVar(x,D);
}
static void DumpVector(const void* b, int n, size_t size, DumpState* D)
{
DumpInt(n,D);
DumpMem(b,n,size,D);
}
static void DumpString(const TString* s, DumpState* D)
{
if (s==NULL || getstr(s)==NULL)
{
size_t size=0;
DumpVar(size,D);
}
else
{
size_t size=s->tsv.len+1; /* include trailing '\0' */
DumpVar(size,D);
DumpBlock(getstr(s),size,D);
}
}
#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D)
static void DumpFunction(const Proto* f, const TString* p, DumpState* D);
static void DumpConstants(const Proto* f, DumpState* D)
{
int i,n=f->sizek;
DumpInt(n,D);
for (i=0; i<n; i++)
{
const TValue* o=&f->k[i];
DumpChar(ttype(o),D);
switch (ttype(o))
{
case LUA_TNIL:
break;
case LUA_TBOOLEAN:
DumpChar(bvalue(o),D);
break;
case LUA_TNUMBER:
DumpNumber(nvalue(o),D);
break;
case LUA_TSTRING:
DumpString(rawtsvalue(o),D);
break;
default:
lua_assert(0); /* cannot happen */
break;
}
}
n=f->sizep;
DumpInt(n,D);
for (i=0; i<n; i++) DumpFunction(f->p[i],f->source,D);
}
static void DumpDebug(const Proto* f, DumpState* D)
{
int i,n;
n= (D->strip) ? 0 : f->sizelineinfo;
DumpVector(f->lineinfo,n,sizeof(int),D);
n= (D->strip) ? 0 : f->sizelocvars;
DumpInt(n,D);
for (i=0; i<n; i++)
{
DumpString(f->locvars[i].varname,D);
DumpInt(f->locvars[i].startpc,D);
DumpInt(f->locvars[i].endpc,D);
}
n= (D->strip) ? 0 : f->sizeupvalues;
DumpInt(n,D);
for (i=0; i<n; i++) DumpString(f->upvalues[i],D);
}
static void DumpFunction(const Proto* f, const TString* p, DumpState* D)
{
DumpString((f->source==p || D->strip) ? NULL : f->source,D);
DumpInt(f->linedefined,D);
DumpInt(f->lastlinedefined,D);
DumpChar(f->nups,D);
DumpChar(f->numparams,D);
DumpChar(f->is_vararg,D);
DumpChar(f->maxstacksize,D);
DumpCode(f,D);
DumpConstants(f,D);
DumpDebug(f,D);
}
static void DumpHeader(DumpState* D)
{
char h[LUAC_HEADERSIZE];
luaU_header(h);
DumpBlock(h,LUAC_HEADERSIZE,D);
}
/*
** dump Lua function as precompiled chunk
*/
int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip)
{
DumpState D;
D.L=L;
D.writer=w;
D.data=data;
D.strip=strip;
D.status=0;
DumpHeader(&D);
DumpFunction(f,NULL,&D);
return D.status;
}
+174
View File
@@ -0,0 +1,174 @@
/*
** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define lfunc_c
#define LUA_CORE
#include "lua.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) {
Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems)));
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
c->c.isC = 1;
c->c.env = e;
c->c.nupvalues = cast_byte(nelems);
return c;
}
Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) {
Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems)));
luaC_link(L, obj2gco(c), LUA_TFUNCTION);
c->l.isC = 0;
c->l.env = e;
c->l.nupvalues = cast_byte(nelems);
while (nelems--) c->l.upvals[nelems] = NULL;
return c;
}
UpVal *luaF_newupval (lua_State *L) {
UpVal *uv = luaM_new(L, UpVal);
luaC_link(L, obj2gco(uv), LUA_TUPVAL);
uv->v = &uv->u.value;
setnilvalue(uv->v);
return uv;
}
UpVal *luaF_findupval (lua_State *L, StkId level) {
global_State *g = G(L);
GCObject **pp = &L->openupval;
UpVal *p;
UpVal *uv;
while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {
lua_assert(p->v != &p->u.value);
if (p->v == level) { /* found a corresponding upvalue? */
if (isdead(g, obj2gco(p))) /* is it dead? */
changewhite(obj2gco(p)); /* ressurect it */
return p;
}
pp = &p->next;
}
uv = luaM_new(L, UpVal); /* not found: create a new one */
uv->tt = LUA_TUPVAL;
uv->marked = luaC_white(g);
uv->v = level; /* current value lives in the stack */
uv->next = *pp; /* chain it in the proper position */
*pp = obj2gco(uv);
uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
uv->u.l.next = g->uvhead.u.l.next;
uv->u.l.next->u.l.prev = uv;
g->uvhead.u.l.next = uv;
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
return uv;
}
static void unlinkupval (UpVal *uv) {
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */
uv->u.l.prev->u.l.next = uv->u.l.next;
}
void luaF_freeupval (lua_State *L, UpVal *uv) {
if (uv->v != &uv->u.value) /* is it open? */
unlinkupval(uv); /* remove from open list */
luaM_free(L, uv); /* free upvalue */
}
void luaF_close (lua_State *L, StkId level) {
UpVal *uv;
global_State *g = G(L);
while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {
GCObject *o = obj2gco(uv);
lua_assert(!isblack(o) && uv->v != &uv->u.value);
L->openupval = uv->next; /* remove from `open' list */
if (isdead(g, o))
luaF_freeupval(L, uv); /* free upvalue */
else {
unlinkupval(uv);
setobj(L, &uv->u.value, uv->v);
uv->v = &uv->u.value; /* now current value lives here */
luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
}
}
}
Proto *luaF_newproto (lua_State *L) {
Proto *f = luaM_new(L, Proto);
luaC_link(L, obj2gco(f), LUA_TPROTO);
f->k = NULL;
f->sizek = 0;
f->p = NULL;
f->sizep = 0;
f->code = NULL;
f->sizecode = 0;
f->sizelineinfo = 0;
f->sizeupvalues = 0;
f->nups = 0;
f->upvalues = NULL;
f->numparams = 0;
f->is_vararg = 0;
f->maxstacksize = 0;
f->lineinfo = NULL;
f->sizelocvars = 0;
f->locvars = NULL;
f->linedefined = 0;
f->lastlinedefined = 0;
f->source = NULL;
return f;
}
void luaF_freeproto (lua_State *L, Proto *f) {
luaM_freearray(L, f->code, f->sizecode, Instruction);
luaM_freearray(L, f->p, f->sizep, Proto *);
luaM_freearray(L, f->k, f->sizek, TValue);
luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
luaM_free(L, f);
}
void luaF_freeclosure (lua_State *L, Closure *c) {
int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) :
sizeLclosure(c->l.nupvalues);
luaM_freemem(L, c, size);
}
/*
** Look for n-th local variable at line `line' in function `func'.
** Returns NULL if not found.
*/
const char *luaF_getlocalname (const Proto *f, int local_number, int pc) {
int i;
for (i = 0; i<f->sizelocvars && f->locvars[i].startpc <= pc; i++) {
if (pc < f->locvars[i].endpc) { /* is variable active? */
local_number--;
if (local_number == 0)
return getstr(f->locvars[i].varname);
}
}
return NULL; /* not found */
}
+34
View File
@@ -0,0 +1,34 @@
/*
** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $
** Auxiliary functions to manipulate prototypes and closures
** See Copyright Notice in lua.h
*/
#ifndef lfunc_h
#define lfunc_h
#include "lobject.h"
#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \
cast(int, sizeof(TValue)*((n)-1)))
#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \
cast(int, sizeof(TValue *)*((n)-1)))
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e);
LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e);
LUAI_FUNC UpVal *luaF_newupval (lua_State *L);
LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
LUAI_FUNC void luaF_close (lua_State *L, StkId level);
LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c);
LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv);
LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
int pc);
#endif
+711
View File
@@ -0,0 +1,711 @@
/*
** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
#include <string.h>
#define lgc_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#define GCSTEPSIZE 1024u
#define GCSWEEPMAX 40
#define GCSWEEPCOST 10
#define GCFINALIZECOST 100
#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS))
#define makewhite(g,x) \
((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g)))
#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT)
#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT)
#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT)
#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT)
#define KEYWEAK bitmask(KEYWEAKBIT)
#define VALUEWEAK bitmask(VALUEWEAKBIT)
#define markvalue(g,o) { checkconsistency(o); \
if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); }
#define markobject(g,t) { if (iswhite(obj2gco(t))) \
reallymarkobject(g, obj2gco(t)); }
#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause)
static void removeentry (Node *n) {
lua_assert(ttisnil(gval(n)));
if (iscollectable(gkey(n)))
setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */
}
static void reallymarkobject (global_State *g, GCObject *o) {
lua_assert(iswhite(o) && !isdead(g, o));
white2gray(o);
switch (o->gch.tt) {
case LUA_TSTRING: {
return;
}
case LUA_TUSERDATA: {
Table *mt = gco2u(o)->metatable;
gray2black(o); /* udata are never gray */
if (mt) markobject(g, mt);
markobject(g, gco2u(o)->env);
return;
}
case LUA_TUPVAL: {
UpVal *uv = gco2uv(o);
markvalue(g, uv->v);
if (uv->v == &uv->u.value) /* closed? */
gray2black(o); /* open upvalues are never black */
return;
}
case LUA_TFUNCTION: {
gco2cl(o)->c.gclist = g->gray;
g->gray = o;
break;
}
case LUA_TTABLE: {
gco2h(o)->gclist = g->gray;
g->gray = o;
break;
}
case LUA_TTHREAD: {
gco2th(o)->gclist = g->gray;
g->gray = o;
break;
}
case LUA_TPROTO: {
gco2p(o)->gclist = g->gray;
g->gray = o;
break;
}
default: lua_assert(0);
}
}
static void marktmu (global_State *g) {
GCObject *u = g->tmudata;
if (u) {
do {
u = u->gch.next;
makewhite(g, u); /* may be marked, if left from previous GC */
reallymarkobject(g, u);
} while (u != g->tmudata);
}
}
/* move `dead' udata that need finalization to list `tmudata' */
size_t luaC_separateudata (lua_State *L, int all) {
global_State *g = G(L);
size_t deadmem = 0;
GCObject **p = &g->mainthread->next;
GCObject *curr;
while ((curr = *p) != NULL) {
if (!(iswhite(curr) || all) || isfinalized(gco2u(curr)))
p = &curr->gch.next; /* don't bother with them */
else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) {
markfinalized(gco2u(curr)); /* don't need finalization */
p = &curr->gch.next;
}
else { /* must call its gc method */
deadmem += sizeudata(gco2u(curr));
markfinalized(gco2u(curr));
*p = curr->gch.next;
/* link `curr' at the end of `tmudata' list */
if (g->tmudata == NULL) /* list is empty? */
g->tmudata = curr->gch.next = curr; /* creates a circular list */
else {
curr->gch.next = g->tmudata->gch.next;
g->tmudata->gch.next = curr;
g->tmudata = curr;
}
}
}
return deadmem;
}
static int traversetable (global_State *g, Table *h) {
int i;
int weakkey = 0;
int weakvalue = 0;
const TValue *mode;
if (h->metatable)
markobject(g, h->metatable);
mode = gfasttm(g, h->metatable, TM_MODE);
if (mode && ttisstring(mode)) { /* is there a weak mode? */
weakkey = (strchr(svalue(mode), 'k') != NULL);
weakvalue = (strchr(svalue(mode), 'v') != NULL);
if (weakkey || weakvalue) { /* is really weak? */
h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */
h->marked |= cast_byte((weakkey << KEYWEAKBIT) |
(weakvalue << VALUEWEAKBIT));
h->gclist = g->weak; /* must be cleared after GC, ... */
g->weak = obj2gco(h); /* ... so put in the appropriate list */
}
}
if (weakkey && weakvalue) return 1;
if (!weakvalue) {
i = h->sizearray;
while (i--)
markvalue(g, &h->array[i]);
}
i = sizenode(h);
while (i--) {
Node *n = gnode(h, i);
lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
if (ttisnil(gval(n)))
removeentry(n); /* remove empty entries */
else {
lua_assert(!ttisnil(gkey(n)));
if (!weakkey) markvalue(g, gkey(n));
if (!weakvalue) markvalue(g, gval(n));
}
}
return weakkey || weakvalue;
}
/*
** All marks are conditional because a GC may happen while the
** prototype is still being created
*/
static void traverseproto (global_State *g, Proto *f) {
int i;
if (f->source) stringmark(f->source);
for (i=0; i<f->sizek; i++) /* mark literals */
markvalue(g, &f->k[i]);
for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */
if (f->upvalues[i])
stringmark(f->upvalues[i]);
}
for (i=0; i<f->sizep; i++) { /* mark nested protos */
if (f->p[i])
markobject(g, f->p[i]);
}
for (i=0; i<f->sizelocvars; i++) { /* mark local-variable names */
if (f->locvars[i].varname)
stringmark(f->locvars[i].varname);
}
}
static void traverseclosure (global_State *g, Closure *cl) {
markobject(g, cl->c.env);
if (cl->c.isC) {
int i;
for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */
markvalue(g, &cl->c.upvalue[i]);
}
else {
int i;
lua_assert(cl->l.nupvalues == cl->l.p->nups);
markobject(g, cl->l.p);
for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */
markobject(g, cl->l.upvals[i]);
}
}
static void checkstacksizes (lua_State *L, StkId max) {
int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */
int s_used = cast_int(max - L->stack); /* part of stack in use */
if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */
return; /* do not touch the stacks */
if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci)
luaD_reallocCI(L, L->size_ci/2); /* still big enough... */
condhardstacktests(luaD_reallocCI(L, ci_used + 1));
if (4*s_used < L->stacksize &&
2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize)
luaD_reallocstack(L, L->stacksize/2); /* still big enough... */
condhardstacktests(luaD_reallocstack(L, s_used));
}
static void traversestack (global_State *g, lua_State *l) {
StkId o, lim;
CallInfo *ci;
markvalue(g, gt(l));
lim = l->top;
for (ci = l->base_ci; ci <= l->ci; ci++) {
lua_assert(ci->top <= l->stack_last);
if (lim < ci->top) lim = ci->top;
}
for (o = l->stack; o < l->top; o++)
markvalue(g, o);
for (; o <= lim; o++)
setnilvalue(o);
checkstacksizes(l, lim);
}
/*
** traverse one gray object, turning it to black.
** Returns `quantity' traversed.
*/
static l_mem propagatemark (global_State *g) {
GCObject *o = g->gray;
lua_assert(isgray(o));
gray2black(o);
switch (o->gch.tt) {
case LUA_TTABLE: {
Table *h = gco2h(o);
g->gray = h->gclist;
if (traversetable(g, h)) /* table is weak? */
black2gray(o); /* keep it gray */
return sizeof(Table) + sizeof(TValue) * h->sizearray +
sizeof(Node) * sizenode(h);
}
case LUA_TFUNCTION: {
Closure *cl = gco2cl(o);
g->gray = cl->c.gclist;
traverseclosure(g, cl);
return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) :
sizeLclosure(cl->l.nupvalues);
}
case LUA_TTHREAD: {
lua_State *th = gco2th(o);
g->gray = th->gclist;
th->gclist = g->grayagain;
g->grayagain = o;
black2gray(o);
traversestack(g, th);
return sizeof(lua_State) + sizeof(TValue) * th->stacksize +
sizeof(CallInfo) * th->size_ci;
}
case LUA_TPROTO: {
Proto *p = gco2p(o);
g->gray = p->gclist;
traverseproto(g, p);
return sizeof(Proto) + sizeof(Instruction) * p->sizecode +
sizeof(Proto *) * p->sizep +
sizeof(TValue) * p->sizek +
sizeof(int) * p->sizelineinfo +
sizeof(LocVar) * p->sizelocvars +
sizeof(TString *) * p->sizeupvalues;
}
default: lua_assert(0); return 0;
}
}
static size_t propagateall (global_State *g) {
size_t m = 0;
while (g->gray) m += propagatemark(g);
return m;
}
/*
** The next function tells whether a key or value can be cleared from
** a weak table. Non-collectable objects are never removed from weak
** tables. Strings behave as `values', so are never removed too. for
** other objects: if really collected, cannot keep them; for userdata
** being finalized, keep them in keys, but not in values
*/
static int iscleared (const TValue *o, int iskey) {
if (!iscollectable(o)) return 0;
if (ttisstring(o)) {
stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */
return 0;
}
return iswhite(gcvalue(o)) ||
(ttisuserdata(o) && (!iskey && isfinalized(uvalue(o))));
}
/*
** clear collected entries from weaktables
*/
static void cleartable (GCObject *l) {
while (l) {
Table *h = gco2h(l);
int i = h->sizearray;
lua_assert(testbit(h->marked, VALUEWEAKBIT) ||
testbit(h->marked, KEYWEAKBIT));
if (testbit(h->marked, VALUEWEAKBIT)) {
while (i--) {
TValue *o = &h->array[i];
if (iscleared(o, 0)) /* value was collected? */
setnilvalue(o); /* remove value */
}
}
i = sizenode(h);
while (i--) {
Node *n = gnode(h, i);
if (!ttisnil(gval(n)) && /* non-empty entry? */
(iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) {
setnilvalue(gval(n)); /* remove value ... */
removeentry(n); /* remove entry from table */
}
}
l = h->gclist;
}
}
static void freeobj (lua_State *L, GCObject *o) {
switch (o->gch.tt) {
case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break;
case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break;
case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break;
case LUA_TTABLE: luaH_free(L, gco2h(o)); break;
case LUA_TTHREAD: {
lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread);
luaE_freethread(L, gco2th(o));
break;
}
case LUA_TSTRING: {
G(L)->strt.nuse--;
luaM_freemem(L, o, sizestring(gco2ts(o)));
break;
}
case LUA_TUSERDATA: {
luaM_freemem(L, o, sizeudata(gco2u(o)));
break;
}
default: lua_assert(0);
}
}
#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM)
static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) {
GCObject *curr;
global_State *g = G(L);
int deadmask = otherwhite(g);
while ((curr = *p) != NULL && count-- > 0) {
if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */
sweepwholelist(L, &gco2th(curr)->openupval);
if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */
lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT));
makewhite(g, curr); /* make it white (for next cycle) */
p = &curr->gch.next;
}
else { /* must erase `curr' */
lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT));
*p = curr->gch.next;
if (curr == g->rootgc) /* is the first element of the list? */
g->rootgc = curr->gch.next; /* adjust first */
freeobj(L, curr);
}
}
return p;
}
static void checkSizes (lua_State *L) {
global_State *g = G(L);
/* check size of string hash */
if (g->strt.nuse < cast(lu_int32, g->strt.size/4) &&
g->strt.size > MINSTRTABSIZE*2)
luaS_resize(L, g->strt.size/2); /* table is too big */
/* check size of buffer */
if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */
size_t newsize = luaZ_sizebuffer(&g->buff) / 2;
luaZ_resizebuffer(L, &g->buff, newsize);
}
}
static void GCTM (lua_State *L) {
global_State *g = G(L);
GCObject *o = g->tmudata->gch.next; /* get first element */
Udata *udata = rawgco2u(o);
const TValue *tm;
/* remove udata from `tmudata' */
if (o == g->tmudata) /* last element? */
g->tmudata = NULL;
else
g->tmudata->gch.next = udata->uv.next;
udata->uv.next = g->mainthread->next; /* return it to `root' list */
g->mainthread->next = o;
makewhite(g, o);
tm = fasttm(L, udata->uv.metatable, TM_GC);
if (tm != NULL) {
lu_byte oldah = L->allowhook;
lu_mem oldt = g->GCthreshold;
L->allowhook = 0; /* stop debug hooks during GC tag method */
g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */
setobj2s(L, L->top, tm);
setuvalue(L, L->top+1, udata);
L->top += 2;
luaD_call(L, L->top - 2, 0);
L->allowhook = oldah; /* restore hooks */
g->GCthreshold = oldt; /* restore threshold */
}
}
/*
** Call all GC tag methods
*/
void luaC_callGCTM (lua_State *L) {
while (G(L)->tmudata)
GCTM(L);
}
void luaC_freeall (lua_State *L) {
global_State *g = G(L);
int i;
g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */
sweepwholelist(L, &g->rootgc);
for (i = 0; i < g->strt.size; i++) /* free all string lists */
sweepwholelist(L, &g->strt.hash[i]);
}
static void markmt (global_State *g) {
int i;
for (i=0; i<NUM_TAGS; i++)
if (g->mt[i]) markobject(g, g->mt[i]);
}
/* mark root set */
static void markroot (lua_State *L) {
global_State *g = G(L);
g->gray = NULL;
g->grayagain = NULL;
g->weak = NULL;
markobject(g, g->mainthread);
/* make global table be traversed before main stack */
markvalue(g, gt(g->mainthread));
markvalue(g, registry(L));
markmt(g);
g->gcstate = GCSpropagate;
}
static void remarkupvals (global_State *g) {
UpVal *uv;
for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) {
lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
if (isgray(obj2gco(uv)))
markvalue(g, uv->v);
}
}
static void atomic (lua_State *L) {
global_State *g = G(L);
size_t udsize; /* total size of userdata to be finalized */
/* remark occasional upvalues of (maybe) dead threads */
remarkupvals(g);
/* traverse objects cautch by write barrier and by 'remarkupvals' */
propagateall(g);
/* remark weak tables */
g->gray = g->weak;
g->weak = NULL;
lua_assert(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */
markmt(g); /* mark basic metatables (again) */
propagateall(g);
/* remark gray again */
g->gray = g->grayagain;
g->grayagain = NULL;
propagateall(g);
udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */
marktmu(g); /* mark `preserved' userdata */
udsize += propagateall(g); /* remark, to propagate `preserveness' */
cleartable(g->weak); /* remove collected objects from weak tables */
/* flip current white */
g->currentwhite = cast_byte(otherwhite(g));
g->sweepstrgc = 0;
g->sweepgc = &g->rootgc;
g->gcstate = GCSsweepstring;
g->estimate = g->totalbytes - udsize; /* first estimate */
}
static l_mem singlestep (lua_State *L) {
global_State *g = G(L);
/*lua_checkmemory(L);*/
switch (g->gcstate) {
case GCSpause: {
markroot(L); /* start a new collection */
return 0;
}
case GCSpropagate: {
if (g->gray)
return propagatemark(g);
else { /* no more `gray' objects */
atomic(L); /* finish mark phase */
return 0;
}
}
case GCSsweepstring: {
lu_mem old = g->totalbytes;
sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]);
if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */
g->gcstate = GCSsweep; /* end sweep-string phase */
lua_assert(old >= g->totalbytes);
g->estimate -= old - g->totalbytes;
return GCSWEEPCOST;
}
case GCSsweep: {
lu_mem old = g->totalbytes;
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
if (*g->sweepgc == NULL) { /* nothing more to sweep? */
checkSizes(L);
g->gcstate = GCSfinalize; /* end sweep phase */
}
lua_assert(old >= g->totalbytes);
g->estimate -= old - g->totalbytes;
return GCSWEEPMAX*GCSWEEPCOST;
}
case GCSfinalize: {
if (g->tmudata) {
GCTM(L);
if (g->estimate > GCFINALIZECOST)
g->estimate -= GCFINALIZECOST;
return GCFINALIZECOST;
}
else {
g->gcstate = GCSpause; /* end collection */
g->gcdept = 0;
return 0;
}
}
default: lua_assert(0); return 0;
}
}
void luaC_step (lua_State *L) {
global_State *g = G(L);
l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul;
if (lim == 0)
lim = (MAX_LUMEM-1)/2; /* no limit */
g->gcdept += g->totalbytes - g->GCthreshold;
do {
lim -= singlestep(L);
if (g->gcstate == GCSpause)
break;
} while (lim > 0);
if (g->gcstate != GCSpause) {
if (g->gcdept < GCSTEPSIZE)
g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/
else {
g->gcdept -= GCSTEPSIZE;
g->GCthreshold = g->totalbytes;
}
}
else {
lua_assert(g->totalbytes >= g->estimate);
setthreshold(g);
}
}
void luaC_fullgc (lua_State *L) {
global_State *g = G(L);
if (g->gcstate <= GCSpropagate) {
/* reset sweep marks to sweep all elements (returning them to white) */
g->sweepstrgc = 0;
g->sweepgc = &g->rootgc;
/* reset other collector lists */
g->gray = NULL;
g->grayagain = NULL;
g->weak = NULL;
g->gcstate = GCSsweepstring;
}
lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate);
/* finish any pending sweep phase */
while (g->gcstate != GCSfinalize) {
lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep);
singlestep(L);
}
markroot(L);
while (g->gcstate != GCSpause) {
singlestep(L);
}
setthreshold(g);
}
void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L);
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
lua_assert(ttype(&o->gch) != LUA_TTABLE);
/* must keep invariant? */
if (g->gcstate == GCSpropagate)
reallymarkobject(g, v); /* restore invariant */
else /* don't mind */
makewhite(g, o); /* mark as white just to avoid other barriers */
}
void luaC_barrierback (lua_State *L, Table *t) {
global_State *g = G(L);
GCObject *o = obj2gco(t);
lua_assert(isblack(o) && !isdead(g, o));
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
black2gray(o); /* make table gray (again) */
t->gclist = g->grayagain;
g->grayagain = o;
}
void luaC_link (lua_State *L, GCObject *o, lu_byte tt) {
global_State *g = G(L);
o->gch.next = g->rootgc;
g->rootgc = o;
o->gch.marked = luaC_white(g);
o->gch.tt = tt;
}
void luaC_linkupval (lua_State *L, UpVal *uv) {
global_State *g = G(L);
GCObject *o = obj2gco(uv);
o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */
g->rootgc = o;
if (isgray(o)) {
if (g->gcstate == GCSpropagate) {
gray2black(o); /* closed upvalues need barrier */
luaC_barrier(L, uv, uv->v);
}
else { /* sweep phase: sweep it (turning it into white) */
makewhite(g, o);
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
}
}
}
+110
View File
@@ -0,0 +1,110 @@
/*
** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $
** Garbage Collector
** See Copyright Notice in lua.h
*/
#ifndef lgc_h
#define lgc_h
#include "lobject.h"
/*
** Possible states of the Garbage Collector
*/
#define GCSpause 0
#define GCSpropagate 1
#define GCSsweepstring 2
#define GCSsweep 3
#define GCSfinalize 4
/*
** some userful bit tricks
*/
#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m)))
#define setbits(x,m) ((x) |= (m))
#define testbits(x,m) ((x) & (m))
#define bitmask(b) (1<<(b))
#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2))
#define l_setbit(x,b) setbits(x, bitmask(b))
#define resetbit(x,b) resetbits(x, bitmask(b))
#define testbit(x,b) testbits(x, bitmask(b))
#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2)))
#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2)))
#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2)))
/*
** Layout for bit use in `marked' field:
** bit 0 - object is white (type 0)
** bit 1 - object is white (type 1)
** bit 2 - object is black
** bit 3 - for userdata: has been finalized
** bit 3 - for tables: has weak keys
** bit 4 - for tables: has weak values
** bit 5 - object is fixed (should not be collected)
** bit 6 - object is "super" fixed (only the main thread)
*/
#define WHITE0BIT 0
#define WHITE1BIT 1
#define BLACKBIT 2
#define FINALIZEDBIT 3
#define KEYWEAKBIT 3
#define VALUEWEAKBIT 4
#define FIXEDBIT 5
#define SFIXEDBIT 6
#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT)
#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT)
#define isblack(x) testbit((x)->gch.marked, BLACKBIT)
#define isgray(x) (!isblack(x) && !iswhite(x))
#define otherwhite(g) (g->currentwhite ^ WHITEBITS)
#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
#define changewhite(x) ((x)->gch.marked ^= WHITEBITS)
#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT)
#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x)))
#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS)
#define luaC_checkGC(L) { \
condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \
if (G(L)->totalbytes >= G(L)->GCthreshold) \
luaC_step(L); }
#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \
luaC_barrierf(L,obj2gco(p),gcvalue(v)); }
#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \
luaC_barrierback(L,t); }
#define luaC_objbarrier(L,p,o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \
luaC_barrierf(L,obj2gco(p),obj2gco(o)); }
#define luaC_objbarriert(L,t,o) \
{ if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); }
LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all);
LUAI_FUNC void luaC_callGCTM (lua_State *L);
LUAI_FUNC void luaC_freeall (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);
LUAI_FUNC void luaC_fullgc (lua_State *L);
LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt);
LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv);
LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v);
LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t);
#endif
+38
View File
@@ -0,0 +1,38 @@
/*
** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $
** Initialization of libraries for lua.c
** See Copyright Notice in lua.h
*/
#define linit_c
#define LUA_LIB
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static const luaL_Reg lualibs[] = {
{"", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{NULL, NULL}
};
LUALIB_API void luaL_openlibs (lua_State *L) {
const luaL_Reg *lib = lualibs;
for (; lib->func; lib++) {
lua_pushcfunction(L, lib->func);
lua_pushstring(L, lib->name);
lua_call(L, 1, 0);
}
}
+553
View File
@@ -0,0 +1,553 @@
/*
** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define liolib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
static const char *const fnames[] = {"input", "output"};
static int pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
if (filename)
lua_pushfstring(L, "%s: %s", filename, strerror(en));
else
lua_pushfstring(L, "%s", strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static void fileerror (lua_State *L, int arg, const char *filename) {
lua_pushfstring(L, "%s: %s", filename, strerror(errno));
luaL_argerror(L, arg, lua_tostring(L, -1));
}
#define tofilep(L) ((FILE **)luaL_checkudata(L, 1, LUA_FILEHANDLE))
static int io_type (lua_State *L) {
void *ud;
luaL_checkany(L, 1);
ud = lua_touserdata(L, 1);
lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE);
if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1))
lua_pushnil(L); /* not a file */
else if (*((FILE **)ud) == NULL)
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static FILE *tofile (lua_State *L) {
FILE **f = tofilep(L);
if (*f == NULL)
luaL_error(L, "attempt to use a closed file");
return *f;
}
/*
** When creating file handles, always creates a `closed' file handle
** before opening the actual file; so, if there is a memory error, the
** file is not left opened.
*/
static FILE **newfile (lua_State *L) {
FILE **pf = (FILE **)lua_newuserdata(L, sizeof(FILE *));
*pf = NULL; /* file handle is currently `closed' */
luaL_getmetatable(L, LUA_FILEHANDLE);
lua_setmetatable(L, -2);
return pf;
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
/*
** function to close 'popen' files
*/
static int io_pclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = lua_pclose(L, *p);
*p = NULL;
return pushresult(L, ok, NULL);
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
FILE **p = tofilep(L);
int ok = (fclose(*p) == 0);
*p = NULL;
return pushresult(L, ok, NULL);
}
static int aux_close (lua_State *L) {
lua_getfenv(L, 1);
lua_getfield(L, -1, "__close");
return (lua_tocfunction(L, -1))(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1))
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT);
tofile(L); /* make sure argument is a file */
return aux_close(L);
}
static int io_gc (lua_State *L) {
FILE *f = *tofilep(L);
/* ignore closed files */
if (f != NULL)
aux_close(L);
return 0;
}
static int io_tostring (lua_State *L) {
FILE *f = *tofilep(L);
if (f == NULL)
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", f);
return 1;
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
/*
** this function has a separated environment, which defines the
** correct __close for 'popen' files
*/
static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
FILE **pf = newfile(L);
*pf = lua_popen(L, filename, mode);
return (*pf == NULL) ? pushresult(L, 0, filename) : 1;
}
static int io_tmpfile (lua_State *L) {
FILE **pf = newfile(L);
*pf = tmpfile();
return (*pf == NULL) ? pushresult(L, 0, NULL) : 1;
}
static FILE *getiofile (lua_State *L, int findex) {
FILE *f;
lua_rawgeti(L, LUA_ENVIRONINDEX, findex);
f = *(FILE **)lua_touserdata(L, -1);
if (f == NULL)
luaL_error(L, "standard %s file is closed", fnames[findex - 1]);
return f;
}
static int g_iofile (lua_State *L, int f, const char *mode) {
if (!lua_isnoneornil(L, 1)) {
const char *filename = lua_tostring(L, 1);
if (filename) {
FILE **pf = newfile(L);
*pf = fopen(filename, mode);
if (*pf == NULL)
fileerror(L, 1, filename);
}
else {
tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_rawseti(L, LUA_ENVIRONINDEX, f);
}
/* return current value */
lua_rawgeti(L, LUA_ENVIRONINDEX, f);
return 1;
}
static int io_input (lua_State *L) {
return g_iofile(L, IO_INPUT, "r");
}
static int io_output (lua_State *L) {
return g_iofile(L, IO_OUTPUT, "w");
}
static int io_readline (lua_State *L);
static void aux_lines (lua_State *L, int idx, int toclose) {
lua_pushvalue(L, idx);
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_pushcclosure(L, io_readline, 2);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 1, 0);
return 1;
}
static int io_lines (lua_State *L) {
if (lua_isnoneornil(L, 1)) { /* no arguments? */
/* will iterate over default input */
lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT);
return f_lines(L);
}
else {
const char *filename = luaL_checkstring(L, 1);
FILE **pf = newfile(L);
*pf = fopen(filename, "r");
if (*pf == NULL)
fileerror(L, 1, filename);
aux_lines(L, lua_gettop(L), 1);
return 1;
}
}
/*
** {======================================================
** READ
** =======================================================
*/
static int read_number (lua_State *L, FILE *f) {
lua_Number d;
if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) {
lua_pushnumber(L, d);
return 1;
}
else return 0; /* read fails */
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f);
lua_pushlstring(L, NULL, 0);
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f) {
luaL_Buffer b;
luaL_buffinit(L, &b);
for (;;) {
size_t l;
char *p = luaL_prepbuffer(&b);
if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */
luaL_pushresult(&b); /* close buffer */
return (lua_objlen(L, -1) > 0); /* check whether read something */
}
l = strlen(p);
if (l == 0 || p[l-1] != '\n')
luaL_addsize(&b, l);
else {
luaL_addsize(&b, l - 1); /* do not include `eol' */
luaL_pushresult(&b); /* close buffer */
return 1; /* read at least an `eol' */
}
}
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t rlen; /* how much to read */
size_t nr; /* number of chars actually read */
luaL_Buffer b;
luaL_buffinit(L, &b);
rlen = LUAL_BUFFERSIZE; /* try to read that much each time */
do {
char *p = luaL_prepbuffer(&b);
if (rlen > n) rlen = n; /* cannot read more than asked */
nr = fread(p, sizeof(char), rlen, f);
luaL_addsize(&b, nr);
n -= nr; /* still have to read `n' chars */
} while (n > 0 && nr == rlen); /* until end of count or eof */
luaL_pushresult(&b); /* close buffer */
return (n == 0 || lua_objlen(L, -1) > 0);
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)lua_tointeger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = lua_tostring(L, n);
luaL_argcheck(L, p && p[0] == '*', n, "invalid option");
switch (p[1]) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f);
break;
case 'a': /* file */
read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return pushresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
FILE *f = *(FILE **)lua_touserdata(L, lua_upvalueindex(1));
int sucess;
if (f == NULL) /* file is already closed? */
luaL_error(L, "file is already closed");
sucess = read_line(L, f);
if (ferror(f))
return luaL_error(L, "%s", strerror(errno));
if (sucess) return 1;
else { /* EOF */
if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - 1;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
status = status &&
fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0;
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
return pushresult(L, status, NULL);
}
static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
return g_write(L, tofile(L), 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
long offset = luaL_optlong(L, 3, 0);
op = fseek(f, offset, mode[op]);
if (op)
return pushresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], sz);
return pushresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
return pushresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
}
static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
static const luaL_Reg flib[] = {
{"close", io_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", io_gc},
{"__tostring", io_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, flib); /* file methods */
}
static void createstdfile (lua_State *L, FILE *f, int k, const char *fname) {
*newfile(L) = f;
if (k > 0) {
lua_pushvalue(L, -1);
lua_rawseti(L, LUA_ENVIRONINDEX, k);
}
lua_pushvalue(L, -2); /* copy environment */
lua_setfenv(L, -2); /* set it */
lua_setfield(L, -3, fname);
}
static void newfenv (lua_State *L, lua_CFunction cls) {
lua_createtable(L, 0, 1);
lua_pushcfunction(L, cls);
lua_setfield(L, -2, "__close");
}
LUALIB_API int luaopen_io (lua_State *L) {
createmeta(L);
/* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */
newfenv(L, io_fclose);
lua_replace(L, LUA_ENVIRONINDEX);
/* open library */
luaL_register(L, LUA_IOLIBNAME, iolib);
/* create (and set) default files */
newfenv(L, io_noclose); /* close function for default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, 0, "stderr");
lua_pop(L, 1); /* pop environment for default files */
lua_getfield(L, -1, "popen");
newfenv(L, io_pclose); /* create environment for 'popen' */
lua_setfenv(L, -2); /* set fenv for 'popen' */
lua_pop(L, 1); /* pop 'popen' */
return 1;
}
+461
View File
@@ -0,0 +1,461 @@
/*
** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <locale.h>
#include <string.h>
#define llex_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "llex.h"
#include "lobject.h"
#include "lparser.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "lzio.h"
#define next(ls) (ls->current = zgetc(ls->z))
#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r')
/* ORDER RESERVED */
const char *const luaX_tokens [] = {
"and", "break", "do", "else", "elseif",
"end", "false", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
"return", "then", "true", "until", "while",
"..", "...", "==", ">=", "<=", "~=",
"<number>", "<name>", "<string>", "<eof>",
NULL
};
#define save_and_next(ls) (save(ls, ls->current), next(ls))
static void save (LexState *ls, int c) {
Mbuffer *b = ls->buff;
if (b->n + 1 > b->buffsize) {
size_t newsize;
if (b->buffsize >= MAX_SIZET/2)
luaX_lexerror(ls, "lexical element too long", 0);
newsize = b->buffsize * 2;
luaZ_resizebuffer(ls->L, b, newsize);
}
b->buffer[b->n++] = cast(char, c);
}
void luaX_init (lua_State *L) {
int i;
for (i=0; i<NUM_RESERVED; i++) {
TString *ts = luaS_new(L, luaX_tokens[i]);
luaS_fix(ts); /* reserved words are never collected */
lua_assert(strlen(luaX_tokens[i])+1 <= TOKEN_LEN);
ts->tsv.reserved = cast_byte(i+1); /* reserved word */
}
}
#define MAXSRC 80
const char *luaX_token2str (LexState *ls, int token) {
if (token < FIRST_RESERVED) {
lua_assert(token == cast(unsigned char, token));
return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) :
luaO_pushfstring(ls->L, "%c", token);
}
else
return luaX_tokens[token-FIRST_RESERVED];
}
static const char *txtToken (LexState *ls, int token) {
switch (token) {
case TK_NAME:
case TK_STRING:
case TK_NUMBER:
save(ls, '\0');
return luaZ_buffer(ls->buff);
default:
return luaX_token2str(ls, token);
}
}
void luaX_lexerror (LexState *ls, const char *msg, int token) {
char buff[MAXSRC];
luaO_chunkid(buff, getstr(ls->source), MAXSRC);
msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg);
if (token)
luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token));
luaD_throw(ls->L, LUA_ERRSYNTAX);
}
void luaX_syntaxerror (LexState *ls, const char *msg) {
luaX_lexerror(ls, msg, ls->t.token);
}
TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
lua_State *L = ls->L;
TString *ts = luaS_newlstr(L, str, l);
TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */
if (ttisnil(o))
setbvalue(o, 1); /* make sure `str' will not be collected */
return ts;
}
static void inclinenumber (LexState *ls) {
int old = ls->current;
lua_assert(currIsNewline(ls));
next(ls); /* skip `\n' or `\r' */
if (currIsNewline(ls) && ls->current != old)
next(ls); /* skip `\n\r' or `\r\n' */
if (++ls->linenumber >= MAX_INT)
luaX_syntaxerror(ls, "chunk has too many lines");
}
void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) {
ls->decpoint = '.';
ls->L = L;
ls->lookahead.token = TK_EOS; /* no look-ahead token */
ls->z = z;
ls->fs = NULL;
ls->linenumber = 1;
ls->lastline = 1;
ls->source = source;
luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */
next(ls); /* read first char */
}
/*
** =======================================================
** LEXICAL ANALYZER
** =======================================================
*/
static int check_next (LexState *ls, const char *set) {
if (!strchr(set, ls->current))
return 0;
save_and_next(ls);
return 1;
}
static void buffreplace (LexState *ls, char from, char to) {
size_t n = luaZ_bufflen(ls->buff);
char *p = luaZ_buffer(ls->buff);
while (n--)
if (p[n] == from) p[n] = to;
}
static void trydecpoint (LexState *ls, SemInfo *seminfo) {
/* format error: try to update decimal point separator */
struct lconv *cv = localeconv();
char old = ls->decpoint;
ls->decpoint = (cv ? cv->decimal_point[0] : '.');
buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) {
/* format error with correct decimal point: no more options */
buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */
luaX_lexerror(ls, "malformed number", TK_NUMBER);
}
}
/* LUA_NUMBER */
static void read_numeral (LexState *ls, SemInfo *seminfo) {
lua_assert(isdigit(ls->current));
do {
save_and_next(ls);
} while (isdigit(ls->current) || ls->current == '.');
if (check_next(ls, "Ee")) /* `E'? */
check_next(ls, "+-"); /* optional exponent sign */
while (isalnum(ls->current) || ls->current == '_')
save_and_next(ls);
save(ls, '\0');
buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */
if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */
trydecpoint(ls, seminfo); /* try to update decimal point separator */
}
static int skip_sep (LexState *ls) {
int count = 0;
int s = ls->current;
lua_assert(s == '[' || s == ']');
save_and_next(ls);
while (ls->current == '=') {
save_and_next(ls);
count++;
}
return (ls->current == s) ? count : (-count) - 1;
}
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
int cont = 0;
(void)(cont); /* avoid warnings when `cont' is not used */
save_and_next(ls); /* skip 2nd `[' */
if (currIsNewline(ls)) /* string starts with a newline? */
inclinenumber(ls); /* skip it */
for (;;) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, (seminfo) ? "unfinished long string" :
"unfinished long comment", TK_EOS);
break; /* to avoid warnings */
#if defined(LUA_COMPAT_LSTR)
case '[': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `[' */
cont++;
#if LUA_COMPAT_LSTR == 1
if (sep == 0)
luaX_lexerror(ls, "nesting of [[...]] is deprecated", '[');
#endif
}
break;
}
#endif
case ']': {
if (skip_sep(ls) == sep) {
save_and_next(ls); /* skip 2nd `]' */
#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2
cont--;
if (sep == 0 && cont >= 0) break;
#endif
goto endloop;
}
break;
}
case '\n':
case '\r': {
save(ls, '\n');
inclinenumber(ls);
if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */
break;
}
default: {
if (seminfo) save_and_next(ls);
else next(ls);
}
}
} endloop:
if (seminfo)
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
luaZ_bufflen(ls->buff) - 2*(2 + sep));
}
static void read_string (LexState *ls, int del, SemInfo *seminfo) {
save_and_next(ls);
while (ls->current != del) {
switch (ls->current) {
case EOZ:
luaX_lexerror(ls, "unfinished string", TK_EOS);
continue; /* to avoid warnings */
case '\n':
case '\r':
luaX_lexerror(ls, "unfinished string", TK_STRING);
continue; /* to avoid warnings */
case '\\': {
int c;
next(ls); /* do not save the `\' */
switch (ls->current) {
case 'a': c = '\a'; break;
case 'b': c = '\b'; break;
case 'f': c = '\f'; break;
case 'n': c = '\n'; break;
case 'r': c = '\r'; break;
case 't': c = '\t'; break;
case 'v': c = '\v'; break;
case '\n': /* go through */
case '\r': save(ls, '\n'); inclinenumber(ls); continue;
case EOZ: continue; /* will raise an error next loop */
default: {
if (!isdigit(ls->current))
save_and_next(ls); /* handles \\, \", \', and \? */
else { /* \xxx */
int i = 0;
c = 0;
do {
c = 10*c + (ls->current-'0');
next(ls);
} while (++i<3 && isdigit(ls->current));
if (c > UCHAR_MAX)
luaX_lexerror(ls, "escape sequence too large", TK_STRING);
save(ls, c);
}
continue;
}
}
save(ls, c);
next(ls);
continue;
}
default:
save_and_next(ls);
}
}
save_and_next(ls); /* skip delimiter */
seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1,
luaZ_bufflen(ls->buff) - 2);
}
static int llex (LexState *ls, SemInfo *seminfo) {
luaZ_resetbuffer(ls->buff);
for (;;) {
switch (ls->current) {
case '\n':
case '\r': {
inclinenumber(ls);
continue;
}
case '-': {
next(ls);
if (ls->current != '-') return '-';
/* else is a comment */
next(ls);
if (ls->current == '[') {
int sep = skip_sep(ls);
luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */
if (sep >= 0) {
read_long_string(ls, NULL, sep); /* long comment */
luaZ_resetbuffer(ls->buff);
continue;
}
}
/* else short comment */
while (!currIsNewline(ls) && ls->current != EOZ)
next(ls);
continue;
}
case '[': {
int sep = skip_sep(ls);
if (sep >= 0) {
read_long_string(ls, seminfo, sep);
return TK_STRING;
}
else if (sep == -1) return '[';
else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING);
}
case '=': {
next(ls);
if (ls->current != '=') return '=';
else { next(ls); return TK_EQ; }
}
case '<': {
next(ls);
if (ls->current != '=') return '<';
else { next(ls); return TK_LE; }
}
case '>': {
next(ls);
if (ls->current != '=') return '>';
else { next(ls); return TK_GE; }
}
case '~': {
next(ls);
if (ls->current != '=') return '~';
else { next(ls); return TK_NE; }
}
case '"':
case '\'': {
read_string(ls, ls->current, seminfo);
return TK_STRING;
}
case '.': {
save_and_next(ls);
if (check_next(ls, ".")) {
if (check_next(ls, "."))
return TK_DOTS; /* ... */
else return TK_CONCAT; /* .. */
}
else if (!isdigit(ls->current)) return '.';
else {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
}
case EOZ: {
return TK_EOS;
}
default: {
if (isspace(ls->current)) {
lua_assert(!currIsNewline(ls));
next(ls);
continue;
}
else if (isdigit(ls->current)) {
read_numeral(ls, seminfo);
return TK_NUMBER;
}
else if (isalpha(ls->current) || ls->current == '_') {
/* identifier or reserved word */
TString *ts;
do {
save_and_next(ls);
} while (isalnum(ls->current) || ls->current == '_');
ts = luaX_newstring(ls, luaZ_buffer(ls->buff),
luaZ_bufflen(ls->buff));
if (ts->tsv.reserved > 0) /* reserved word? */
return ts->tsv.reserved - 1 + FIRST_RESERVED;
else {
seminfo->ts = ts;
return TK_NAME;
}
}
else {
int c = ls->current;
next(ls);
return c; /* single-char tokens (+ - / ...) */
}
}
}
}
}
void luaX_next (LexState *ls) {
ls->lastline = ls->linenumber;
if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */
ls->t = ls->lookahead; /* use this one */
ls->lookahead.token = TK_EOS; /* and discharge it */
}
else
ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */
}
void luaX_lookahead (LexState *ls) {
lua_assert(ls->lookahead.token == TK_EOS);
ls->lookahead.token = llex(ls, &ls->lookahead.seminfo);
}
+81
View File
@@ -0,0 +1,81 @@
/*
** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $
** Lexical Analyzer
** See Copyright Notice in lua.h
*/
#ifndef llex_h
#define llex_h
#include "lobject.h"
#include "lzio.h"
#define FIRST_RESERVED 257
/* maximum length of a reserved word */
#define TOKEN_LEN (sizeof("function")/sizeof(char))
/*
* WARNING: if you change the order of this enumeration,
* grep "ORDER RESERVED"
*/
enum RESERVED {
/* terminal symbols denoted by reserved words */
TK_AND = FIRST_RESERVED, TK_BREAK,
TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
/* other terminal symbols */
TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
TK_NAME, TK_STRING, TK_EOS
};
/* number of reserved words */
#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1))
/* array with token `names' */
LUAI_DATA const char *const luaX_tokens [];
typedef union {
lua_Number r;
TString *ts;
} SemInfo; /* semantics information */
typedef struct Token {
int token;
SemInfo seminfo;
} Token;
typedef struct LexState {
int current; /* current character (charint) */
int linenumber; /* input line counter */
int lastline; /* line of last token `consumed' */
Token t; /* current token */
Token lookahead; /* look ahead token */
struct FuncState *fs; /* `FuncState' is private to the parser */
struct lua_State *L;
ZIO *z; /* input stream */
Mbuffer *buff; /* buffer for tokens */
TString *source; /* current source name */
char decpoint; /* locale decimal point */
} LexState;
LUAI_FUNC void luaX_init (lua_State *L);
LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z,
TString *source);
LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l);
LUAI_FUNC void luaX_next (LexState *ls);
LUAI_FUNC void luaX_lookahead (LexState *ls);
LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token);
LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s);
LUAI_FUNC const char *luaX_token2str (LexState *ls, int token);
#endif
+128
View File
@@ -0,0 +1,128 @@
/*
** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $
** Limits, basic types, and some other `installation-dependent' definitions
** See Copyright Notice in lua.h
*/
#ifndef llimits_h
#define llimits_h
#include <limits.h>
#include <stddef.h>
#include "lua.h"
typedef LUAI_UINT32 lu_int32;
typedef LUAI_UMEM lu_mem;
typedef LUAI_MEM l_mem;
/* chars used as small naturals (so that `char' is reserved for characters) */
typedef unsigned char lu_byte;
#define MAX_SIZET ((size_t)(~(size_t)0)-2)
#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2)
#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */
/*
** conversion of pointer to integer
** this is for hashing only; there is no problem if the integer
** cannot hold the whole pointer value
*/
#define IntPoint(p) ((unsigned int)(lu_mem)(p))
/* type to ensure maximum alignment */
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
/* result of a `usual argument conversion' over lua_Number */
typedef LUAI_UACNUMBER l_uacNumber;
/* internal assertions for in-house debugging */
#ifdef lua_assert
#define check_exp(c,e) (lua_assert(c), (e))
#define api_check(l,e) lua_assert(e)
#else
#define lua_assert(c) ((void)0)
#define check_exp(c,e) (e)
#define api_check luai_apicheck
#endif
#ifndef UNUSED
#define UNUSED(x) ((void)(x)) /* to avoid warnings */
#endif
#ifndef cast
#define cast(t, exp) ((t)(exp))
#endif
#define cast_byte(i) cast(lu_byte, (i))
#define cast_num(i) cast(lua_Number, (i))
#define cast_int(i) cast(int, (i))
/*
** type for virtual-machine instructions
** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h)
*/
typedef lu_int32 Instruction;
/* maximum stack for a Lua function */
#define MAXSTACK 250
/* minimum size for the string table (must be power of 2) */
#ifndef MINSTRTABSIZE
#define MINSTRTABSIZE 32
#endif
/* minimum size for string buffer */
#ifndef LUA_MINBUFFER
#define LUA_MINBUFFER 32
#endif
#ifndef lua_lock
#define lua_lock(L) ((void) 0)
#define lua_unlock(L) ((void) 0)
#endif
#ifndef luai_threadyield
#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);}
#endif
/*
** macro to control inclusion of some hard tests on stack reallocation
*/
#ifndef HARDSTACKTESTS
#define condhardstacktests(x) ((void)0)
#else
#define condhardstacktests(x) x
#endif
#endif
+263
View File
@@ -0,0 +1,263 @@
/*
** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $
** Standard mathematical library
** See Copyright Notice in lua.h
*/
#include <stdlib.h>
#include <math.h>
#define lmathlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#undef PI
#define PI (3.14159265358979323846)
#define RADIANS_PER_DEGREE (PI/180.0)
static int math_abs (lua_State *L) {
lua_pushnumber(L, fabs(luaL_checknumber(L, 1)));
return 1;
}
static int math_sin (lua_State *L) {
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}
static int math_sinh (lua_State *L) {
lua_pushnumber(L, sinh(luaL_checknumber(L, 1)));
return 1;
}
static int math_cos (lua_State *L) {
lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
return 1;
}
static int math_cosh (lua_State *L) {
lua_pushnumber(L, cosh(luaL_checknumber(L, 1)));
return 1;
}
static int math_tan (lua_State *L) {
lua_pushnumber(L, tan(luaL_checknumber(L, 1)));
return 1;
}
static int math_tanh (lua_State *L) {
lua_pushnumber(L, tanh(luaL_checknumber(L, 1)));
return 1;
}
static int math_asin (lua_State *L) {
lua_pushnumber(L, asin(luaL_checknumber(L, 1)));
return 1;
}
static int math_acos (lua_State *L) {
lua_pushnumber(L, acos(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan (lua_State *L) {
lua_pushnumber(L, atan(luaL_checknumber(L, 1)));
return 1;
}
static int math_atan2 (lua_State *L) {
lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_ceil (lua_State *L) {
lua_pushnumber(L, ceil(luaL_checknumber(L, 1)));
return 1;
}
static int math_floor (lua_State *L) {
lua_pushnumber(L, floor(luaL_checknumber(L, 1)));
return 1;
}
static int math_fmod (lua_State *L) {
lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_modf (lua_State *L) {
double ip;
double fp = modf(luaL_checknumber(L, 1), &ip);
lua_pushnumber(L, ip);
lua_pushnumber(L, fp);
return 2;
}
static int math_sqrt (lua_State *L) {
lua_pushnumber(L, sqrt(luaL_checknumber(L, 1)));
return 1;
}
static int math_pow (lua_State *L) {
lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2)));
return 1;
}
static int math_log (lua_State *L) {
lua_pushnumber(L, log(luaL_checknumber(L, 1)));
return 1;
}
static int math_log10 (lua_State *L) {
lua_pushnumber(L, log10(luaL_checknumber(L, 1)));
return 1;
}
static int math_exp (lua_State *L) {
lua_pushnumber(L, exp(luaL_checknumber(L, 1)));
return 1;
}
static int math_deg (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE);
return 1;
}
static int math_rad (lua_State *L) {
lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE);
return 1;
}
static int math_frexp (lua_State *L) {
int e;
lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e));
lua_pushinteger(L, e);
return 2;
}
static int math_ldexp (lua_State *L) {
lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2)));
return 1;
}
static int math_min (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number dmin = luaL_checknumber(L, 1);
int i;
for (i=2; i<=n; i++) {
lua_Number d = luaL_checknumber(L, i);
if (d < dmin)
dmin = d;
}
lua_pushnumber(L, dmin);
return 1;
}
static int math_max (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
lua_Number dmax = luaL_checknumber(L, 1);
int i;
for (i=2; i<=n; i++) {
lua_Number d = luaL_checknumber(L, i);
if (d > dmax)
dmax = d;
}
lua_pushnumber(L, dmax);
return 1;
}
static int math_random (lua_State *L) {
/* the `%' avoids the (rare) case of r==1, and is needed also because on
some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */
lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX;
switch (lua_gettop(L)) { /* check number of arguments */
case 0: { /* no arguments */
lua_pushnumber(L, r); /* Number between 0 and 1 */
break;
}
case 1: { /* only upper limit */
int u = luaL_checkint(L, 1);
luaL_argcheck(L, 1<=u, 1, "interval is empty");
lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */
break;
}
case 2: { /* lower and upper limits */
int l = luaL_checkint(L, 1);
int u = luaL_checkint(L, 2);
luaL_argcheck(L, l<=u, 2, "interval is empty");
lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */
break;
}
default: return luaL_error(L, "wrong number of arguments");
}
return 1;
}
static int math_randomseed (lua_State *L) {
srand(luaL_checkint(L, 1));
return 0;
}
static const luaL_Reg mathlib[] = {
{"abs", math_abs},
{"acos", math_acos},
{"asin", math_asin},
{"atan2", math_atan2},
{"atan", math_atan},
{"ceil", math_ceil},
{"cosh", math_cosh},
{"cos", math_cos},
{"deg", math_deg},
{"exp", math_exp},
{"floor", math_floor},
{"fmod", math_fmod},
{"frexp", math_frexp},
{"ldexp", math_ldexp},
{"log10", math_log10},
{"log", math_log},
{"max", math_max},
{"min", math_min},
{"modf", math_modf},
{"pow", math_pow},
{"rad", math_rad},
{"random", math_random},
{"randomseed", math_randomseed},
{"sinh", math_sinh},
{"sin", math_sin},
{"sqrt", math_sqrt},
{"tanh", math_tanh},
{"tan", math_tan},
{NULL, NULL}
};
/*
** Open math library
*/
LUALIB_API int luaopen_math (lua_State *L) {
luaL_register(L, LUA_MATHLIBNAME, mathlib);
lua_pushnumber(L, PI);
lua_setfield(L, -2, "pi");
lua_pushnumber(L, HUGE_VAL);
lua_setfield(L, -2, "huge");
#if defined(LUA_COMPAT_MOD)
lua_getfield(L, -1, "fmod");
lua_setfield(L, -2, "mod");
#endif
return 1;
}
+86
View File
@@ -0,0 +1,86 @@
/*
** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define lmem_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
/*
** About the realloc function:
** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize);
** (`osize' is the old size, `nsize' is the new size)
**
** Lua ensures that (ptr == NULL) iff (osize == 0).
**
** * frealloc(ud, NULL, 0, x) creates a new block of size `x'
**
** * frealloc(ud, p, x, 0) frees the block `p'
** (in this specific case, frealloc must return NULL).
** particularly, frealloc(ud, NULL, 0, 0) does nothing
** (which is equivalent to free(NULL) in ANSI C)
**
** frealloc returns NULL if it cannot create or reallocate the area
** (any reallocation to an equal or smaller size cannot fail!)
*/
#define MINSIZEARRAY 4
void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems,
int limit, const char *errormsg) {
void *newblock;
int newsize;
if (*size >= limit/2) { /* cannot double it? */
if (*size >= limit) /* cannot grow even a little? */
luaG_runerror(L, errormsg);
newsize = limit; /* still have at least one free place */
}
else {
newsize = (*size)*2;
if (newsize < MINSIZEARRAY)
newsize = MINSIZEARRAY; /* minimum size */
}
newblock = luaM_reallocv(L, block, *size, newsize, size_elems);
*size = newsize; /* update only when everything else is OK */
return newblock;
}
void *luaM_toobig (lua_State *L) {
luaG_runerror(L, "memory allocation error: block too big");
return NULL; /* to avoid warnings */
}
/*
** generic allocation routine.
*/
void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) {
global_State *g = G(L);
lua_assert((osize == 0) == (block == NULL));
block = (*g->frealloc)(g->ud, block, osize, nsize);
if (block == NULL && nsize > 0)
luaD_throw(L, LUA_ERRMEM);
lua_assert((nsize == 0) == (block == NULL));
g->totalbytes = (g->totalbytes - osize) + nsize;
return block;
}
+49
View File
@@ -0,0 +1,49 @@
/*
** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $
** Interface to Memory Manager
** See Copyright Notice in lua.h
*/
#ifndef lmem_h
#define lmem_h
#include <stddef.h>
#include "llimits.h"
#include "lua.h"
#define MEMERRMSG "not enough memory"
#define luaM_reallocv(L,b,on,n,e) \
((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \
luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \
luaM_toobig(L))
#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0)
#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0)
#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t))
#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t))
#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t)))
#define luaM_newvector(L,n,t) \
cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t)))
#define luaM_growvector(L,v,nelems,size,t,limit,e) \
if ((nelems)+1 > (size)) \
((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e)))
#define luaM_reallocvector(L, v,oldn,n,t) \
((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t))))
LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize,
size_t size);
LUAI_FUNC void *luaM_toobig (lua_State *L);
LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size,
size_t size_elem, int limit,
const char *errormsg);
#endif
+666
View File
@@ -0,0 +1,666 @@
/*
** $Id: loadlib.c,v 1.52.1.3 2008/08/06 13:29:28 roberto Exp $
** Dynamic library loader for Lua
** See Copyright Notice in lua.h
**
** This module contains an implementation of loadlib for Unix systems
** that have dlfcn, an implementation for Darwin (Mac OS X), an
** implementation for Windows, and a stub for other systems.
*/
#include <stdlib.h>
#include <string.h>
#define loadlib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
/* separator for open functions in C libraries */
#define LUA_OFSEP "_"
#define LIBPREFIX "LOADLIB: "
#define POF LUA_POF
#define LIB_FAIL "open"
/* error codes for ll_loadfunc */
#define ERRLIB 1
#define ERRFUNC 2
#define setprogdir(L) ((void)0)
static void ll_unloadlib (void *lib);
static void *ll_load (lua_State *L, const char *path);
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym);
#if defined(LUA_DL_DLOPEN)
/*
** {========================================================================
** This is an implementation of loadlib based on the dlfcn interface.
** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD,
** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least
** as an emulation layer on top of native functions.
** =========================================================================
*/
#include <dlfcn.h>
static void ll_unloadlib (void *lib) {
dlclose(lib);
}
static void *ll_load (lua_State *L, const char *path) {
void *lib = dlopen(path, RTLD_NOW);
if (lib == NULL) lua_pushstring(L, dlerror());
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)dlsym(lib, sym);
if (f == NULL) lua_pushstring(L, dlerror());
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DLL)
/*
** {======================================================================
** This is an implementation of loadlib for Windows using native functions.
** =======================================================================
*/
#include <windows.h>
#undef setprogdir
static void setprogdir (lua_State *L) {
char buff[MAX_PATH + 1];
char *lb;
DWORD nsize = sizeof(buff)/sizeof(char);
DWORD n = GetModuleFileNameA(NULL, buff, nsize);
if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL)
luaL_error(L, "unable to get ModuleFileName");
else {
*lb = '\0';
luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff);
lua_remove(L, -2); /* remove original string */
}
}
static void pusherror (lua_State *L) {
int error = GetLastError();
char buffer[128];
if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
NULL, error, 0, buffer, sizeof(buffer), NULL))
lua_pushstring(L, buffer);
else
lua_pushfstring(L, "system error %d\n", error);
}
static void ll_unloadlib (void *lib) {
FreeLibrary((HINSTANCE)lib);
}
static void *ll_load (lua_State *L, const char *path) {
HINSTANCE lib = LoadLibraryA(path);
if (lib == NULL) pusherror(L);
return lib;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym);
if (f == NULL) pusherror(L);
return f;
}
/* }====================================================== */
#elif defined(LUA_DL_DYLD)
/*
** {======================================================================
** Native Mac OS X / Darwin Implementation
** =======================================================================
*/
#include <mach-o/dyld.h>
/* Mac appends a `_' before C function names */
#undef POF
#define POF "_" LUA_POF
static void pusherror (lua_State *L) {
const char *err_str;
const char *err_file;
NSLinkEditErrors err;
int err_num;
NSLinkEditError(&err, &err_num, &err_file, &err_str);
lua_pushstring(L, err_str);
}
static const char *errorfromcode (NSObjectFileImageReturnCode ret) {
switch (ret) {
case NSObjectFileImageInappropriateFile:
return "file is not a bundle";
case NSObjectFileImageArch:
return "library is for wrong CPU type";
case NSObjectFileImageFormat:
return "bad format";
case NSObjectFileImageAccess:
return "cannot access file";
case NSObjectFileImageFailure:
default:
return "unable to load library";
}
}
static void ll_unloadlib (void *lib) {
NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES);
}
static void *ll_load (lua_State *L, const char *path) {
NSObjectFileImage img;
NSObjectFileImageReturnCode ret;
/* this would be a rare case, but prevents crashing if it happens */
if(!_dyld_present()) {
lua_pushliteral(L, "dyld not present");
return NULL;
}
ret = NSCreateObjectFileImageFromFile(path, &img);
if (ret == NSObjectFileImageSuccess) {
NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE |
NSLINKMODULE_OPTION_RETURN_ON_ERROR);
NSDestroyObjectFileImage(img);
if (mod == NULL) pusherror(L);
return mod;
}
lua_pushstring(L, errorfromcode(ret));
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym);
if (nss == NULL) {
lua_pushfstring(L, "symbol " LUA_QS " not found", sym);
return NULL;
}
return (lua_CFunction)NSAddressOfSymbol(nss);
}
/* }====================================================== */
#else
/*
** {======================================================
** Fallback for other systems
** =======================================================
*/
#undef LIB_FAIL
#define LIB_FAIL "absent"
#define DLMSG "dynamic libraries not enabled; check your Lua installation"
static void ll_unloadlib (void *lib) {
(void)lib; /* to avoid warnings */
}
static void *ll_load (lua_State *L, const char *path) {
(void)path; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) {
(void)lib; (void)sym; /* to avoid warnings */
lua_pushliteral(L, DLMSG);
return NULL;
}
/* }====================================================== */
#endif
static void **ll_register (lua_State *L, const char *path) {
void **plib;
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */
if (!lua_isnil(L, -1)) /* is there an entry? */
plib = (void **)lua_touserdata(L, -1);
else { /* no entry yet; create one */
lua_pop(L, 1);
plib = (void **)lua_newuserdata(L, sizeof(const void *));
*plib = NULL;
luaL_getmetatable(L, "_LOADLIB");
lua_setmetatable(L, -2);
lua_pushfstring(L, "%s%s", LIBPREFIX, path);
lua_pushvalue(L, -2);
lua_settable(L, LUA_REGISTRYINDEX);
}
return plib;
}
/*
** __gc tag method: calls library's `ll_unloadlib' function with the lib
** handle
*/
static int gctm (lua_State *L) {
void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB");
if (*lib) ll_unloadlib(*lib);
*lib = NULL; /* mark library as closed */
return 0;
}
static int ll_loadfunc (lua_State *L, const char *path, const char *sym) {
void **reg = ll_register(L, path);
if (*reg == NULL) *reg = ll_load(L, path);
if (*reg == NULL)
return ERRLIB; /* unable to load library */
else {
lua_CFunction f = ll_sym(L, *reg, sym);
if (f == NULL)
return ERRFUNC; /* unable to find function */
lua_pushcfunction(L, f);
return 0; /* return function */
}
}
static int ll_loadlib (lua_State *L) {
const char *path = luaL_checkstring(L, 1);
const char *init = luaL_checkstring(L, 2);
int stat = ll_loadfunc(L, path, init);
if (stat == 0) /* no errors? */
return 1; /* return the loaded function */
else { /* error; error message is on stack top */
lua_pushnil(L);
lua_insert(L, -2);
lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init");
return 3; /* return nil, error message, and where */
}
}
/*
** {======================================================
** 'require' function
** =======================================================
*/
static int readable (const char *filename) {
FILE *f = fopen(filename, "r"); /* try to open file */
if (f == NULL) return 0; /* open failed */
fclose(f);
return 1;
}
static const char *pushnexttemplate (lua_State *L, const char *path) {
const char *l;
while (*path == *LUA_PATHSEP) path++; /* skip separators */
if (*path == '\0') return NULL; /* no more templates */
l = strchr(path, *LUA_PATHSEP); /* find next separator */
if (l == NULL) l = path + strlen(path);
lua_pushlstring(L, path, l - path); /* template */
return l;
}
static const char *findfile (lua_State *L, const char *name,
const char *pname) {
const char *path;
name = luaL_gsub(L, name, ".", LUA_DIRSEP);
lua_getfield(L, LUA_ENVIRONINDEX, pname);
path = lua_tostring(L, -1);
if (path == NULL)
luaL_error(L, LUA_QL("package.%s") " must be a string", pname);
lua_pushliteral(L, ""); /* error accumulator */
while ((path = pushnexttemplate(L, path)) != NULL) {
const char *filename;
filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name);
lua_remove(L, -2); /* remove path template */
if (readable(filename)) /* does file exist and is readable? */
return filename; /* return that file name */
lua_pushfstring(L, "\n\tno file " LUA_QS, filename);
lua_remove(L, -2); /* remove file name */
lua_concat(L, 2); /* add entry to possible error message */
}
return NULL; /* not found */
}
static void loaderror (lua_State *L, const char *filename) {
luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s",
lua_tostring(L, 1), filename, lua_tostring(L, -1));
}
static int loader_Lua (lua_State *L) {
const char *filename;
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
if (luaL_loadfile(L, filename) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static const char *mkfuncname (lua_State *L, const char *modname) {
const char *funcname;
const char *mark = strchr(modname, *LUA_IGMARK);
if (mark) modname = mark + 1;
funcname = luaL_gsub(L, modname, ".", LUA_OFSEP);
funcname = lua_pushfstring(L, POF"%s", funcname);
lua_remove(L, -2); /* remove 'gsub' result */
return funcname;
}
static int loader_C (lua_State *L) {
const char *funcname;
const char *name = luaL_checkstring(L, 1);
const char *filename = findfile(L, name, "cpath");
if (filename == NULL) return 1; /* library not found in this path */
funcname = mkfuncname(L, name);
if (ll_loadfunc(L, filename, funcname) != 0)
loaderror(L, filename);
return 1; /* library loaded successfully */
}
static int loader_Croot (lua_State *L) {
const char *funcname;
const char *filename;
const char *name = luaL_checkstring(L, 1);
const char *p = strchr(name, '.');
int stat;
if (p == NULL) return 0; /* is root */
lua_pushlstring(L, name, p - name);
filename = findfile(L, lua_tostring(L, -1), "cpath");
if (filename == NULL) return 1; /* root not found */
funcname = mkfuncname(L, name);
if ((stat = ll_loadfunc(L, filename, funcname)) != 0) {
if (stat != ERRFUNC) loaderror(L, filename); /* real error */
lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS,
name, filename);
return 1; /* function not found */
}
return 1;
}
static int loader_preload (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
lua_getfield(L, LUA_ENVIRONINDEX, "preload");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.preload") " must be a table");
lua_getfield(L, -1, name);
if (lua_isnil(L, -1)) /* not found? */
lua_pushfstring(L, "\n\tno field package.preload['%s']", name);
return 1;
}
static const int sentinel_ = 0;
#define sentinel ((void *)&sentinel_)
static int ll_require (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
int i;
lua_settop(L, 1); /* _LOADED table will be at index 2 */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, 2, name);
if (lua_toboolean(L, -1)) { /* is it there? */
if (lua_touserdata(L, -1) == sentinel) /* check loops */
luaL_error(L, "loop or previous error loading module " LUA_QS, name);
return 1; /* package is already loaded */
}
/* else must load it; iterate over available loaders */
lua_getfield(L, LUA_ENVIRONINDEX, "loaders");
if (!lua_istable(L, -1))
luaL_error(L, LUA_QL("package.loaders") " must be a table");
lua_pushliteral(L, ""); /* error message accumulator */
for (i=1; ; i++) {
lua_rawgeti(L, -2, i); /* get a loader */
if (lua_isnil(L, -1))
luaL_error(L, "module " LUA_QS " not found:%s",
name, lua_tostring(L, -2));
lua_pushstring(L, name);
lua_call(L, 1, 1); /* call it */
if (lua_isfunction(L, -1)) /* did it find module? */
break; /* module loaded successfully */
else if (lua_isstring(L, -1)) /* loader returned error message? */
lua_concat(L, 2); /* accumulate it */
else
lua_pop(L, 1);
}
lua_pushlightuserdata(L, sentinel);
lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */
lua_pushstring(L, name); /* pass name as argument to module */
lua_call(L, 1, 1); /* run loaded module */
if (!lua_isnil(L, -1)) /* non-nil return? */
lua_setfield(L, 2, name); /* _LOADED[name] = returned value */
lua_getfield(L, 2, name);
if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */
lua_pushboolean(L, 1); /* use true as result */
lua_pushvalue(L, -1); /* extra copy to be returned */
lua_setfield(L, 2, name); /* _LOADED[name] = true */
}
return 1;
}
/* }====================================================== */
/*
** {======================================================
** 'module' function
** =======================================================
*/
static void setfenv (lua_State *L) {
lua_Debug ar;
if (lua_getstack(L, 1, &ar) == 0 ||
lua_getinfo(L, "f", &ar) == 0 || /* get calling function */
lua_iscfunction(L, -1))
luaL_error(L, LUA_QL("module") " not called from a Lua function");
lua_pushvalue(L, -2);
lua_setfenv(L, -2);
lua_pop(L, 1);
}
static void dooptions (lua_State *L, int n) {
int i;
for (i = 2; i <= n; i++) {
lua_pushvalue(L, i); /* get option (a function) */
lua_pushvalue(L, -2); /* module */
lua_call(L, 1, 0);
}
}
static void modinit (lua_State *L, const char *modname) {
const char *dot;
lua_pushvalue(L, -1);
lua_setfield(L, -2, "_M"); /* module._M = module */
lua_pushstring(L, modname);
lua_setfield(L, -2, "_NAME");
dot = strrchr(modname, '.'); /* look for last dot in module name */
if (dot == NULL) dot = modname;
else dot++;
/* set _PACKAGE as package name (full module name minus last part) */
lua_pushlstring(L, modname, dot - modname);
lua_setfield(L, -2, "_PACKAGE");
}
static int ll_module (lua_State *L) {
const char *modname = luaL_checkstring(L, 1);
int loaded = lua_gettop(L) + 1; /* index of _LOADED table */
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
lua_getfield(L, loaded, modname); /* get _LOADED[modname] */
if (!lua_istable(L, -1)) { /* not found? */
lua_pop(L, 1); /* remove previous result */
/* try global variable (and create one if it does not exist) */
if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL)
return luaL_error(L, "name conflict for module " LUA_QS, modname);
lua_pushvalue(L, -1);
lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */
}
/* check whether table already has a _NAME field */
lua_getfield(L, -1, "_NAME");
if (!lua_isnil(L, -1)) /* is table an initialized module? */
lua_pop(L, 1);
else { /* no; initialize it */
lua_pop(L, 1);
modinit(L, modname);
}
lua_pushvalue(L, -1);
setfenv(L);
dooptions(L, loaded - 1);
return 0;
}
static int ll_seeall (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
if (!lua_getmetatable(L, 1)) {
lua_createtable(L, 0, 1); /* create new metatable */
lua_pushvalue(L, -1);
lua_setmetatable(L, 1);
}
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index"); /* mt.__index = _G */
return 0;
}
/* }====================================================== */
/* auxiliary mark (for internal use) */
#define AUXMARK "\1"
static void setpath (lua_State *L, const char *fieldname, const char *envname,
const char *def) {
const char *path = getenv(envname);
if (path == NULL) /* no environment variable? */
lua_pushstring(L, def); /* use default */
else {
/* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */
path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP,
LUA_PATHSEP AUXMARK LUA_PATHSEP);
luaL_gsub(L, path, AUXMARK, def);
lua_remove(L, -2);
}
setprogdir(L);
lua_setfield(L, -2, fieldname);
}
static const luaL_Reg pk_funcs[] = {
{"loadlib", ll_loadlib},
{"seeall", ll_seeall},
{NULL, NULL}
};
static const luaL_Reg ll_funcs[] = {
{"module", ll_module},
{"require", ll_require},
{NULL, NULL}
};
static const lua_CFunction loaders[] =
{loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
LUALIB_API int luaopen_package (lua_State *L) {
int i;
/* create new type _LOADLIB */
luaL_newmetatable(L, "_LOADLIB");
lua_pushcfunction(L, gctm);
lua_setfield(L, -2, "__gc");
/* create `package' table */
luaL_register(L, LUA_LOADLIBNAME, pk_funcs);
#if defined(LUA_COMPAT_LOADLIB)
lua_getfield(L, -1, "loadlib");
lua_setfield(L, LUA_GLOBALSINDEX, "loadlib");
#endif
lua_pushvalue(L, -1);
lua_replace(L, LUA_ENVIRONINDEX);
/* create `loaders' table */
lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1);
/* fill it with pre-defined loaders */
for (i=0; loaders[i] != NULL; i++) {
lua_pushcfunction(L, loaders[i]);
lua_rawseti(L, -2, i+1);
}
lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */
setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */
setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */
/* store config information */
lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n"
LUA_EXECDIR "\n" LUA_IGMARK);
lua_setfield(L, -2, "config");
/* set field `loaded' */
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2);
lua_setfield(L, -2, "loaded");
/* set field `preload' */
lua_newtable(L);
lua_setfield(L, -2, "preload");
lua_pushvalue(L, LUA_GLOBALSINDEX);
luaL_register(L, NULL, ll_funcs); /* open lib into global table */
lua_pop(L, 1);
return 1; /* return 'package' table */
}
+214
View File
@@ -0,0 +1,214 @@
/*
** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
** Some generic functions over Lua objects
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define lobject_c
#define LUA_CORE
#include "lua.h"
#include "ldo.h"
#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "lvm.h"
const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
/*
** converts an integer to a "floating point byte", represented as
** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if
** eeeee != 0 and (xxx) otherwise.
*/
int luaO_int2fb (unsigned int x) {
int e = 0; /* expoent */
while (x >= 16) {
x = (x+1) >> 1;
e++;
}
if (x < 8) return x;
else return ((e+1) << 3) | (cast_int(x) - 8);
}
/* converts back */
int luaO_fb2int (int x) {
int e = (x >> 3) & 31;
if (e == 0) return x;
else return ((x & 7)+8) << (e - 1);
}
int luaO_log2 (unsigned int x) {
static const lu_byte log_2[256] = {
0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
};
int l = -1;
while (x >= 256) { l += 8; x >>= 8; }
return l + log_2[x];
}
int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
if (ttype(t1) != ttype(t2)) return 0;
else switch (ttype(t1)) {
case LUA_TNIL:
return 1;
case LUA_TNUMBER:
return luai_numeq(nvalue(t1), nvalue(t2));
case LUA_TBOOLEAN:
return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */
case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2);
default:
lua_assert(iscollectable(t1));
return gcvalue(t1) == gcvalue(t2);
}
}
int luaO_str2d (const char *s, lua_Number *result) {
char *endptr;
*result = lua_str2number(s, &endptr);
if (endptr == s) return 0; /* conversion failed */
if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */
*result = cast_num(strtoul(s, &endptr, 16));
if (*endptr == '\0') return 1; /* most common case */
while (isspace(cast(unsigned char, *endptr))) endptr++;
if (*endptr != '\0') return 0; /* invalid trailing characters? */
return 1;
}
static void pushstr (lua_State *L, const char *str) {
setsvalue2s(L, L->top, luaS_new(L, str));
incr_top(L);
}
/* this function handles only `%d', `%c', %f, %p, and `%s' formats */
const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
int n = 1;
pushstr(L, "");
for (;;) {
const char *e = strchr(fmt, '%');
if (e == NULL) break;
setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt));
incr_top(L);
switch (*(e+1)) {
case 's': {
const char *s = va_arg(argp, char *);
if (s == NULL) s = "(null)";
pushstr(L, s);
break;
}
case 'c': {
char buff[2];
buff[0] = cast(char, va_arg(argp, int));
buff[1] = '\0';
pushstr(L, buff);
break;
}
case 'd': {
setnvalue(L->top, cast_num(va_arg(argp, int)));
incr_top(L);
break;
}
case 'f': {
setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber)));
incr_top(L);
break;
}
case 'p': {
char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */
sprintf(buff, "%p", va_arg(argp, void *));
pushstr(L, buff);
break;
}
case '%': {
pushstr(L, "%");
break;
}
default: {
char buff[3];
buff[0] = '%';
buff[1] = *(e+1);
buff[2] = '\0';
pushstr(L, buff);
break;
}
}
n += 2;
fmt = e+2;
}
pushstr(L, fmt);
luaV_concat(L, n+1, cast_int(L->top - L->base) - 1);
L->top -= n;
return svalue(L->top - 1);
}
const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) {
const char *msg;
va_list argp;
va_start(argp, fmt);
msg = luaO_pushvfstring(L, fmt, argp);
va_end(argp);
return msg;
}
void luaO_chunkid (char *out, const char *source, size_t bufflen) {
if (*source == '=') {
strncpy(out, source+1, bufflen); /* remove first char */
out[bufflen-1] = '\0'; /* ensures null termination */
}
else { /* out = "source", or "...source" */
if (*source == '@') {
size_t l;
source++; /* skip the `@' */
bufflen -= sizeof(" '...' ");
l = strlen(source);
strcpy(out, "");
if (l > bufflen) {
source += (l-bufflen); /* get last part of file name */
strcat(out, "...");
}
strcat(out, source);
}
else { /* out = [string "string"] */
size_t len = strcspn(source, "\n\r"); /* stop at first newline */
bufflen -= sizeof(" [string \"...\"] ");
if (len > bufflen) len = bufflen;
strcpy(out, "[string \"");
if (source[len] != '\0') { /* must truncate? */
strncat(out, source, len);
strcat(out, "...");
}
else
strcat(out, source);
strcat(out, "\"]");
}
}
}
+381
View File
@@ -0,0 +1,381 @@
/*
** $Id: lobject.h,v 2.20.1.2 2008/08/06 13:29:48 roberto Exp $
** Type definitions for Lua objects
** See Copyright Notice in lua.h
*/
#ifndef lobject_h
#define lobject_h
#include <stdarg.h>
#include "llimits.h"
#include "lua.h"
/* tags for values visible from Lua */
#define LAST_TAG LUA_TTHREAD
#define NUM_TAGS (LAST_TAG+1)
/*
** Extra tags for non-values
*/
#define LUA_TPROTO (LAST_TAG+1)
#define LUA_TUPVAL (LAST_TAG+2)
#define LUA_TDEADKEY (LAST_TAG+3)
/*
** Union of all collectable objects
*/
typedef union GCObject GCObject;
/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
/*
** Common header in struct form
*/
typedef struct GCheader {
CommonHeader;
} GCheader;
/*
** Union of all Lua values
*/
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
/*
** Tagged Values
*/
#define TValuefields Value value; int tt
typedef struct lua_TValue {
TValuefields;
} TValue;
/* Macros to test type */
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
#define ttistable(o) (ttype(o) == LUA_TTABLE)
#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
/* Macros to access values */
#define ttype(o) ((o)->tt)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
#define tsvalue(o) (&rawtsvalue(o)->tsv)
#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
#define uvalue(o) (&rawuvalue(o)->uv)
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
/*
** for internal debug only
*/
#define checkconsistency(obj) \
lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
#define checkliveness(g,obj) \
lua_assert(!iscollectable(obj) || \
((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
/* Macros to set values */
#define setnilvalue(obj) ((obj)->tt=LUA_TNIL)
#define setnvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
#define setpvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }
#define setbvalue(obj,x) \
{ TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; }
#define setsvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \
checkliveness(G(L),i_o); }
#define setuvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \
checkliveness(G(L),i_o); }
#define setthvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \
checkliveness(G(L),i_o); }
#define setclvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \
checkliveness(G(L),i_o); }
#define sethvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \
checkliveness(G(L),i_o); }
#define setptvalue(L,obj,x) \
{ TValue *i_o=(obj); \
i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \
checkliveness(G(L),i_o); }
#define setobj(L,obj1,obj2) \
{ const TValue *o2=(obj2); TValue *o1=(obj1); \
o1->value = o2->value; o1->tt=o2->tt; \
checkliveness(G(L),o1); }
/*
** different types of sets, according to destination
*/
/* from stack to (same) stack */
#define setobjs2s setobj
/* to stack (not from same stack) */
#define setobj2s setobj
#define setsvalue2s setsvalue
#define sethvalue2s sethvalue
#define setptvalue2s setptvalue
/* from table to same table */
#define setobjt2t setobj
/* to table */
#define setobj2t setobj
/* to new object */
#define setobj2n setobj
#define setsvalue2n setsvalue
#define setttype(obj, tt) (ttype(obj) = (tt))
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
typedef TValue *StkId; /* index to stack elements */
/*
** String headers for string table
*/
typedef union TString {
L_Umaxalign dummy; /* ensures maximum alignment for strings */
struct {
CommonHeader;
lu_byte reserved;
unsigned int hash;
size_t len;
} tsv;
} TString;
#define getstr(ts) cast(const char *, (ts) + 1)
#define svalue(o) getstr(rawtsvalue(o))
typedef union Udata {
L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
struct {
CommonHeader;
struct Table *metatable;
struct Table *env;
size_t len;
} uv;
} Udata;
/*
** Function Prototypes
*/
typedef struct Proto {
CommonHeader;
TValue *k; /* constants used by the function */
Instruction *code;
struct Proto **p; /* functions defined inside the function */
int *lineinfo; /* map from opcodes to source lines */
struct LocVar *locvars; /* information about local variables */
TString **upvalues; /* upvalue names */
TString *source;
int sizeupvalues;
int sizek; /* size of `k' */
int sizecode;
int sizelineinfo;
int sizep; /* size of `p' */
int sizelocvars;
int linedefined;
int lastlinedefined;
GCObject *gclist;
lu_byte nups; /* number of upvalues */
lu_byte numparams;
lu_byte is_vararg;
lu_byte maxstacksize;
} Proto;
/* masks for new-style vararg */
#define VARARG_HASARG 1
#define VARARG_ISVARARG 2
#define VARARG_NEEDSARG 4
typedef struct LocVar {
TString *varname;
int startpc; /* first point where variable is active */
int endpc; /* first point where variable is dead */
} LocVar;
/*
** Upvalues
*/
typedef struct UpVal {
CommonHeader;
TValue *v; /* points to stack or to its own value */
union {
TValue value; /* the value (when closed) */
struct { /* double linked list (when open) */
struct UpVal *prev;
struct UpVal *next;
} l;
} u;
} UpVal;
/*
** Closures
*/
#define ClosureHeader \
CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
struct Table *env
typedef struct CClosure {
ClosureHeader;
lua_CFunction f;
TValue upvalue[1];
} CClosure;
typedef struct LClosure {
ClosureHeader;
struct Proto *p;
UpVal *upvals[1];
} LClosure;
typedef union Closure {
CClosure c;
LClosure l;
} Closure;
#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC)
#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC)
/*
** Tables
*/
typedef union TKey {
struct {
TValuefields;
struct Node *next; /* for chaining */
} nk;
TValue tvk;
} TKey;
typedef struct Node {
TValue i_val;
TKey i_key;
} Node;
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
lu_byte lsizenode; /* log2 of size of `node' array */
struct Table *metatable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
GCObject *gclist;
int sizearray; /* size of `array' array */
} Table;
/*
** `module' operation for hashing (size is always a power of 2)
*/
#define lmod(s,size) \
(check_exp((size&(size-1))==0, (cast(int, (s) & ((size)-1)))))
#define twoto(x) (1<<(x))
#define sizenode(t) (twoto((t)->lsizenode))
#define luaO_nilobject (&luaO_nilobject_)
LUAI_DATA const TValue luaO_nilobject_;
#define ceillog2(x) (luaO_log2((x)-1) + 1)
LUAI_FUNC int luaO_log2 (unsigned int x);
LUAI_FUNC int luaO_int2fb (unsigned int x);
LUAI_FUNC int luaO_fb2int (int x);
LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2);
LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result);
LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt,
va_list argp);
LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...);
LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len);
#endif
+102
View File
@@ -0,0 +1,102 @@
/*
** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $
** See Copyright Notice in lua.h
*/
#define lopcodes_c
#define LUA_CORE
#include "lopcodes.h"
/* ORDER OP */
const char *const luaP_opnames[NUM_OPCODES+1] = {
"MOVE",
"LOADK",
"LOADBOOL",
"LOADNIL",
"GETUPVAL",
"GETGLOBAL",
"GETTABLE",
"SETGLOBAL",
"SETUPVAL",
"SETTABLE",
"NEWTABLE",
"SELF",
"ADD",
"SUB",
"MUL",
"DIV",
"MOD",
"POW",
"UNM",
"NOT",
"LEN",
"CONCAT",
"JMP",
"EQ",
"LT",
"LE",
"TEST",
"TESTSET",
"CALL",
"TAILCALL",
"RETURN",
"FORLOOP",
"FORPREP",
"TFORLOOP",
"SETLIST",
"CLOSE",
"CLOSURE",
"VARARG",
NULL
};
#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
const lu_byte luaP_opmodes[NUM_OPCODES] = {
/* T A B C mode opcode */
opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */
,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */
,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */
,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */
,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */
,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */
,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */
,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */
,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */
,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */
,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */
,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */
,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */
,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */
,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */
};
+268
View File
@@ -0,0 +1,268 @@
/*
** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $
** Opcodes for Lua virtual machine
** See Copyright Notice in lua.h
*/
#ifndef lopcodes_h
#define lopcodes_h
#include "llimits.h"
/*===========================================================================
We assume that instructions are unsigned numbers.
All instructions have an opcode in the first 6 bits.
Instructions can have the following fields:
`A' : 8 bits
`B' : 9 bits
`C' : 9 bits
`Bx' : 18 bits (`B' and `C' together)
`sBx' : signed Bx
A signed argument is represented in excess K; that is, the number
value is the unsigned value minus K. K is exactly the maximum value
for that argument (so that -max is represented by 0, and +max is
represented by 2*max), which is half the maximum for the corresponding
unsigned argument.
===========================================================================*/
enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */
/*
** size and position of opcode arguments.
*/
#define SIZE_C 9
#define SIZE_B 9
#define SIZE_Bx (SIZE_C + SIZE_B)
#define SIZE_A 8
#define SIZE_OP 6
#define POS_OP 0
#define POS_A (POS_OP + SIZE_OP)
#define POS_C (POS_A + SIZE_A)
#define POS_B (POS_C + SIZE_C)
#define POS_Bx POS_C
/*
** limits for opcode arguments.
** we use (signed) int to manipulate most arguments,
** so they must fit in LUAI_BITSINT-1 bits (-1 for sign)
*/
#if SIZE_Bx < LUAI_BITSINT-1
#define MAXARG_Bx ((1<<SIZE_Bx)-1)
#define MAXARG_sBx (MAXARG_Bx>>1) /* `sBx' is signed */
#else
#define MAXARG_Bx MAX_INT
#define MAXARG_sBx MAX_INT
#endif
#define MAXARG_A ((1<<SIZE_A)-1)
#define MAXARG_B ((1<<SIZE_B)-1)
#define MAXARG_C ((1<<SIZE_C)-1)
/* creates a mask with `n' 1 bits at position `p' */
#define MASK1(n,p) ((~((~(Instruction)0)<<n))<<p)
/* creates a mask with `n' 0 bits at position `p' */
#define MASK0(n,p) (~MASK1(n,p))
/*
** the following macros help to manipulate instructions
*/
#define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0)))
#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \
((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP))))
#define GETARG_A(i) (cast(int, ((i)>>POS_A) & MASK1(SIZE_A,0)))
#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \
((cast(Instruction, u)<<POS_A)&MASK1(SIZE_A,POS_A))))
#define GETARG_B(i) (cast(int, ((i)>>POS_B) & MASK1(SIZE_B,0)))
#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \
((cast(Instruction, b)<<POS_B)&MASK1(SIZE_B,POS_B))))
#define GETARG_C(i) (cast(int, ((i)>>POS_C) & MASK1(SIZE_C,0)))
#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \
((cast(Instruction, b)<<POS_C)&MASK1(SIZE_C,POS_C))))
#define GETARG_Bx(i) (cast(int, ((i)>>POS_Bx) & MASK1(SIZE_Bx,0)))
#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \
((cast(Instruction, b)<<POS_Bx)&MASK1(SIZE_Bx,POS_Bx))))
#define GETARG_sBx(i) (GETARG_Bx(i)-MAXARG_sBx)
#define SETARG_sBx(i,b) SETARG_Bx((i),cast(unsigned int, (b)+MAXARG_sBx))
#define CREATE_ABC(o,a,b,c) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, b)<<POS_B) \
| (cast(Instruction, c)<<POS_C))
#define CREATE_ABx(o,a,bc) ((cast(Instruction, o)<<POS_OP) \
| (cast(Instruction, a)<<POS_A) \
| (cast(Instruction, bc)<<POS_Bx))
/*
** Macros to operate RK indices
*/
/* this bit 1 means constant (0 means register) */
#define BITRK (1 << (SIZE_B - 1))
/* test whether value is a constant */
#define ISK(x) ((x) & BITRK)
/* gets the index of the constant */
#define INDEXK(r) ((int)(r) & ~BITRK)
#define MAXINDEXRK (BITRK - 1)
/* code a constant index as a RK value */
#define RKASK(x) ((x) | BITRK)
/*
** invalid register that fits in 8 bits
*/
#define NO_REG MAXARG_A
/*
** R(x) - register
** Kst(x) - constant (in constant table)
** RK(x) == if ISK(x) then Kst(INDEXK(x)) else R(x)
*/
/*
** grep "ORDER OP" if you change these enums
*/
typedef enum {
/*----------------------------------------------------------------------
name args description
------------------------------------------------------------------------*/
OP_MOVE,/* A B R(A) := R(B) */
OP_LOADK,/* A Bx R(A) := Kst(Bx) */
OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */
OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */
OP_GETUPVAL,/* A B R(A) := UpValue[B] */
OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */
OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */
OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */
OP_SETUPVAL,/* A B UpValue[B] := R(A) */
OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */
OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */
OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */
OP_ADD,/* A B C R(A) := RK(B) + RK(C) */
OP_SUB,/* A B C R(A) := RK(B) - RK(C) */
OP_MUL,/* A B C R(A) := RK(B) * RK(C) */
OP_DIV,/* A B C R(A) := RK(B) / RK(C) */
OP_MOD,/* A B C R(A) := RK(B) % RK(C) */
OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */
OP_UNM,/* A B R(A) := -R(B) */
OP_NOT,/* A B R(A) := not R(B) */
OP_LEN,/* A B R(A) := length of R(B) */
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP,/* sBx pc+=sBx */
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
OP_TEST,/* A C if not (R(A) <=> C) then pc++ */
OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */
OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */
OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */
OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */
OP_FORLOOP,/* A sBx R(A)+=R(A+2);
if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/
OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */
OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2));
if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */
OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */
OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/
OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */
OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
} OpCode;
#define NUM_OPCODES (cast(int, OP_VARARG) + 1)
/*===========================================================================
Notes:
(*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1,
and can be 0: OP_CALL then sets `top' to last_result+1, so
next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'.
(*) In OP_VARARG, if (B == 0) then use actual number of varargs and
set top (like in OP_CALL with C == 0).
(*) In OP_RETURN, if (B == 0) then return up to `top'
(*) In OP_SETLIST, if (B == 0) then B = `top';
if (C == 0) then next `instruction' is real C
(*) For comparisons, A specifies what condition the test should accept
(true or false).
(*) All `skips' (pc++) assume that next instruction is a jump
===========================================================================*/
/*
** masks for instruction properties. The format is:
** bits 0-1: op mode
** bits 2-3: C arg mode
** bits 4-5: B arg mode
** bit 6: instruction set register A
** bit 7: operator is a test
*/
enum OpArgMask {
OpArgN, /* argument is not used */
OpArgU, /* argument is used */
OpArgR, /* argument is a register or a jump offset */
OpArgK /* argument is a constant or register/constant */
};
LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES];
#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3))
#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3))
#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3))
#define testAMode(m) (luaP_opmodes[m] & (1 << 6))
#define testTMode(m) (luaP_opmodes[m] & (1 << 7))
LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */
/* number of list items to accumulate before a SETLIST instruction */
#define LFIELDS_PER_FLUSH 50
#endif
+243
View File
@@ -0,0 +1,243 @@
/*
** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $
** Standard Operating System library
** See Copyright Notice in lua.h
*/
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define loslib_c
#define LUA_LIB
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static int os_pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
if (i) {
lua_pushboolean(L, 1);
return 1;
}
else {
lua_pushnil(L);
lua_pushfstring(L, "%s: %s", filename, strerror(en));
lua_pushinteger(L, en);
return 3;
}
}
static int os_execute (lua_State *L) {
lua_pushinteger(L, system(luaL_optstring(L, 1, NULL)));
return 1;
}
static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
return os_pushresult(L, remove(filename) == 0, filename);
}
static int os_rename (lua_State *L) {
const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2);
return os_pushresult(L, rename(fromname, toname) == 0, fromname);
}
static int os_tmpname (lua_State *L) {
char buff[LUA_TMPNAMBUFSIZE];
int err;
lua_tmpnam(buff, err);
if (err)
return luaL_error(L, "unable to generate a unique filename");
lua_pushstring(L, buff);
return 1;
}
static int os_getenv (lua_State *L) {
lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */
return 1;
}
static int os_clock (lua_State *L) {
lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
return 1;
}
/*
** {======================================================
** Time/Date operations
** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
** wday=%w+1, yday=%j, isdst=? }
** =======================================================
*/
static void setfield (lua_State *L, const char *key, int value) {
lua_pushinteger(L, value);
lua_setfield(L, -2, key);
}
static void setboolfield (lua_State *L, const char *key, int value) {
if (value < 0) /* undefined? */
return; /* does not set field */
lua_pushboolean(L, value);
lua_setfield(L, -2, key);
}
static int getboolfield (lua_State *L, const char *key) {
int res;
lua_getfield(L, -1, key);
res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
static int getfield (lua_State *L, const char *key, int d) {
int res;
lua_getfield(L, -1, key);
if (lua_isnumber(L, -1))
res = (int)lua_tointeger(L, -1);
else {
if (d < 0)
return luaL_error(L, "field " LUA_QS " missing in date table", key);
res = d;
}
lua_pop(L, 1);
return res;
}
static int os_date (lua_State *L) {
const char *s = luaL_optstring(L, 1, "%c");
time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL));
struct tm *stm;
if (*s == '!') { /* UTC? */
stm = gmtime(&t);
s++; /* skip `!' */
}
else
stm = localtime(&t);
if (stm == NULL) /* invalid date? */
lua_pushnil(L);
else if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour);
setfield(L, "day", stm->tm_mday);
setfield(L, "month", stm->tm_mon+1);
setfield(L, "year", stm->tm_year+1900);
setfield(L, "wday", stm->tm_wday+1);
setfield(L, "yday", stm->tm_yday+1);
setboolfield(L, "isdst", stm->tm_isdst);
}
else {
char cc[3];
luaL_Buffer b;
cc[0] = '%'; cc[2] = '\0';
luaL_buffinit(L, &b);
for (; *s; s++) {
if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */
luaL_addchar(&b, *s);
else {
size_t reslen;
char buff[200]; /* should be big enough for any conversion result */
cc[1] = *(++s);
reslen = strftime(buff, sizeof(buff), cc, stm);
luaL_addlstring(&b, buff, reslen);
}
}
luaL_pushresult(&b);
}
return 1;
}
static int os_time (lua_State *L) {
time_t t;
if (lua_isnoneornil(L, 1)) /* called without args? */
t = time(NULL); /* get current time */
else {
struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0);
ts.tm_min = getfield(L, "min", 0);
ts.tm_hour = getfield(L, "hour", 12);
ts.tm_mday = getfield(L, "day", -1);
ts.tm_mon = getfield(L, "month", -1) - 1;
ts.tm_year = getfield(L, "year", -1) - 1900;
ts.tm_isdst = getboolfield(L, "isdst");
t = mktime(&ts);
}
if (t == (time_t)(-1))
lua_pushnil(L);
else
lua_pushnumber(L, (lua_Number)t);
return 1;
}
static int os_difftime (lua_State *L) {
lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)),
(time_t)(luaL_optnumber(L, 2, 0))));
return 1;
}
/* }====================================================== */
static int os_setlocale (lua_State *L) {
static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
LC_NUMERIC, LC_TIME};
static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
"numeric", "time", NULL};
const char *l = luaL_optstring(L, 1, NULL);
int op = luaL_checkoption(L, 2, "all", catnames);
lua_pushstring(L, setlocale(cat[op], l));
return 1;
}
static int os_exit (lua_State *L) {
exit(luaL_optint(L, 1, EXIT_SUCCESS));
}
static const luaL_Reg syslib[] = {
{"clock", os_clock},
{"date", os_date},
{"difftime", os_difftime},
{"execute", os_execute},
{"exit", os_exit},
{"getenv", os_getenv},
{"remove", os_remove},
{"rename", os_rename},
{"setlocale", os_setlocale},
{"time", os_time},
{"tmpname", os_tmpname},
{NULL, NULL}
};
/* }====================================================== */
LUALIB_API int luaopen_os (lua_State *L) {
luaL_register(L, LUA_OSLIBNAME, syslib);
return 1;
}
+1339
View File
File diff suppressed because it is too large Load Diff
+82
View File
@@ -0,0 +1,82 @@
/*
** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $
** Lua Parser
** See Copyright Notice in lua.h
*/
#ifndef lparser_h
#define lparser_h
#include "llimits.h"
#include "lobject.h"
#include "lzio.h"
/*
** Expression descriptor
*/
typedef enum {
VVOID, /* no value */
VNIL,
VTRUE,
VFALSE,
VK, /* info = index of constant in `k' */
VKNUM, /* nval = numerical value */
VLOCAL, /* info = local register */
VUPVAL, /* info = index of upvalue in `upvalues' */
VGLOBAL, /* info = index of table; aux = index of global name in `k' */
VINDEXED, /* info = table register; aux = index register (or `k') */
VJMP, /* info = instruction pc */
VRELOCABLE, /* info = instruction pc */
VNONRELOC, /* info = result register */
VCALL, /* info = instruction pc */
VVARARG /* info = instruction pc */
} expkind;
typedef struct expdesc {
expkind k;
union {
struct { int info, aux; } s;
lua_Number nval;
} u;
int t; /* patch list of `exit when true' */
int f; /* patch list of `exit when false' */
} expdesc;
typedef struct upvaldesc {
lu_byte k;
lu_byte info;
} upvaldesc;
struct BlockCnt; /* defined in lparser.c */
/* state needed to generate code for a given function */
typedef struct FuncState {
Proto *f; /* current function header */
Table *h; /* table to find (and reuse) elements in `k' */
struct FuncState *prev; /* enclosing function */
struct LexState *ls; /* lexical state */
struct lua_State *L; /* copy of the Lua state */
struct BlockCnt *bl; /* chain of current blocks */
int pc; /* next position to code (equivalent to `ncode') */
int lasttarget; /* `pc' of last `jump target' */
int jpc; /* list of pending jumps to `pc' */
int freereg; /* first free register */
int nk; /* number of elements in `k' */
int np; /* number of elements in `p' */
short nlocvars; /* number of elements in `locvars' */
lu_byte nactvar; /* number of active local variables */
upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */
unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */
} FuncState;
LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
const char *name);
#endif
+214
View File
@@ -0,0 +1,214 @@
/*
** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
#include <stddef.h>
#define lstate_c
#define LUA_CORE
#include "lua.h"
#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "llex.h"
#include "lmem.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE)
#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE)
#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE))
/*
** Main thread combines a thread state and the global state
*/
typedef struct LG {
lua_State l;
global_State g;
} LG;
static void stack_init (lua_State *L1, lua_State *L) {
/* initialize CallInfo array */
L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo);
L1->ci = L1->base_ci;
L1->size_ci = BASIC_CI_SIZE;
L1->end_ci = L1->base_ci + L1->size_ci - 1;
/* initialize stack array */
L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue);
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
L1->top = L1->stack;
L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1;
/* initialize first ci */
L1->ci->func = L1->top;
setnilvalue(L1->top++); /* `function' entry for this `ci' */
L1->base = L1->ci->base = L1->top;
L1->ci->top = L1->top + LUA_MINSTACK;
}
static void freestack (lua_State *L, lua_State *L1) {
luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
luaM_freearray(L, L1->stack, L1->stacksize, TValue);
}
/*
** open parts that may cause memory-allocation errors
*/
static void f_luaopen (lua_State *L, void *ud) {
global_State *g = G(L);
UNUSED(ud);
stack_init(L, L); /* init stack */
sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */
luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */
luaT_init(L);
luaX_init(L);
luaS_fix(luaS_newliteral(L, MEMERRMSG));
g->GCthreshold = 4*g->totalbytes;
}
static void preinit_state (lua_State *L, global_State *g) {
G(L) = g;
L->stack = NULL;
L->stacksize = 0;
L->errorJmp = NULL;
L->hook = NULL;
L->hookmask = 0;
L->basehookcount = 0;
L->allowhook = 1;
resethookcount(L);
L->openupval = NULL;
L->size_ci = 0;
L->nCcalls = L->baseCcalls = 0;
L->status = 0;
L->base_ci = L->ci = NULL;
L->savedpc = NULL;
L->errfunc = 0;
setnilvalue(gt(L));
}
static void close_state (lua_State *L) {
global_State *g = G(L);
luaF_close(L, L->stack); /* close all upvalues for this thread */
luaC_freeall(L); /* collect all objects */
lua_assert(g->rootgc == obj2gco(L));
lua_assert(g->strt.nuse == 0);
luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *);
luaZ_freebuffer(L, &g->buff);
freestack(L, L);
lua_assert(g->totalbytes == sizeof(LG));
(*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0);
}
lua_State *luaE_newthread (lua_State *L) {
lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State)));
luaC_link(L, obj2gco(L1), LUA_TTHREAD);
preinit_state(L1, G(L));
stack_init(L1, L); /* init stack */
setobj2n(L, gt(L1), gt(L)); /* share table of globals */
L1->hookmask = L->hookmask;
L1->basehookcount = L->basehookcount;
L1->hook = L->hook;
resethookcount(L1);
lua_assert(iswhite(obj2gco(L1)));
return L1;
}
void luaE_freethread (lua_State *L, lua_State *L1) {
luaF_close(L1, L1->stack); /* close all upvalues for this thread */
lua_assert(L1->openupval == NULL);
luai_userstatefree(L1);
freestack(L, L1);
luaM_freemem(L, fromstate(L1), state_size(lua_State));
}
LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
int i;
lua_State *L;
global_State *g;
void *l = (*f)(ud, NULL, 0, state_size(LG));
if (l == NULL) return NULL;
L = tostate(l);
g = &((LG *)L)->g;
L->next = NULL;
L->tt = LUA_TTHREAD;
g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT);
L->marked = luaC_white(g);
set2bits(L->marked, FIXEDBIT, SFIXEDBIT);
preinit_state(L, g);
g->frealloc = f;
g->ud = ud;
g->mainthread = L;
g->uvhead.u.l.prev = &g->uvhead;
g->uvhead.u.l.next = &g->uvhead;
g->GCthreshold = 0; /* mark it as unfinished state */
g->strt.size = 0;
g->strt.nuse = 0;
g->strt.hash = NULL;
setnilvalue(registry(L));
luaZ_initbuffer(L, &g->buff);
g->panic = NULL;
g->gcstate = GCSpause;
g->rootgc = obj2gco(L);
g->sweepstrgc = 0;
g->sweepgc = &g->rootgc;
g->gray = NULL;
g->grayagain = NULL;
g->weak = NULL;
g->tmudata = NULL;
g->totalbytes = sizeof(LG);
g->gcpause = LUAI_GCPAUSE;
g->gcstepmul = LUAI_GCMUL;
g->gcdept = 0;
for (i=0; i<NUM_TAGS; i++) g->mt[i] = NULL;
if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) {
/* memory allocation error: free partial state */
close_state(L);
L = NULL;
}
else
luai_userstateopen(L);
return L;
}
static void callallgcTM (lua_State *L, void *ud) {
UNUSED(ud);
luaC_callGCTM(L); /* call GC metamethods for all udata */
}
LUA_API void lua_close (lua_State *L) {
L = G(L)->mainthread; /* only the main thread can be closed */
lua_lock(L);
luaF_close(L, L->stack); /* close all upvalues for this thread */
luaC_separateudata(L, 1); /* separate udata that have GC metamethods */
L->errfunc = 0; /* no error function during GC metamethods */
do { /* repeat until no more errors */
L->ci = L->base_ci;
L->base = L->top = L->ci->base;
L->nCcalls = L->baseCcalls = 0;
} while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0);
lua_assert(G(L)->tmudata == NULL);
luai_userstateclose(L);
close_state(L);
}
+169
View File
@@ -0,0 +1,169 @@
/*
** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $
** Global State
** See Copyright Notice in lua.h
*/
#ifndef lstate_h
#define lstate_h
#include "lua.h"
#include "lobject.h"
#include "ltm.h"
#include "lzio.h"
struct lua_longjmp; /* defined in ldo.c */
/* table of globals */
#define gt(L) (&L->l_gt)
/* registry */
#define registry(L) (&G(L)->l_registry)
/* extra stack space to handle TM calls and some other extras */
#define EXTRA_STACK 5
#define BASIC_CI_SIZE 8
#define BASIC_STACK_SIZE (2*LUA_MINSTACK)
typedef struct stringtable {
GCObject **hash;
lu_int32 nuse; /* number of elements */
int size;
} stringtable;
/*
** informations about a call
*/
typedef struct CallInfo {
StkId base; /* base for this function */
StkId func; /* function index in the stack */
StkId top; /* top for this function */
const Instruction *savedpc;
int nresults; /* expected number of results from this function */
int tailcalls; /* number of tail calls lost under this entry */
} CallInfo;
#define curr_func(L) (clvalue(L->ci->func))
#define ci_func(ci) (clvalue((ci)->func))
#define f_isLua(ci) (!ci_func(ci)->c.isC)
#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
/*
** `global state', shared by all threads of this state
*/
typedef struct global_State {
stringtable strt; /* hash table for strings */
lua_Alloc frealloc; /* function to reallocate memory */
void *ud; /* auxiliary data to `frealloc' */
lu_byte currentwhite;
lu_byte gcstate; /* state of garbage collector */
int sweepstrgc; /* position of sweep in `strt' */
GCObject *rootgc; /* list of all collectable objects */
GCObject **sweepgc; /* position of sweep in `rootgc' */
GCObject *gray; /* list of gray objects */
GCObject *grayagain; /* list of objects to be traversed atomically */
GCObject *weak; /* list of weak tables (to be cleared) */
GCObject *tmudata; /* last element of list of userdata to be GC */
Mbuffer buff; /* temporary buffer for string concatentation */
lu_mem GCthreshold;
lu_mem totalbytes; /* number of bytes currently allocated */
lu_mem estimate; /* an estimate of number of bytes actually in use */
lu_mem gcdept; /* how much GC is `behind schedule' */
int gcpause; /* size of pause between successive GCs */
int gcstepmul; /* GC `granularity' */
lua_CFunction panic; /* to be called in unprotected errors */
TValue l_registry;
struct lua_State *mainthread;
UpVal uvhead; /* head of double-linked list of all open upvalues */
struct Table *mt[NUM_TAGS]; /* metatables for basic types */
TString *tmname[TM_N]; /* array with tag-method names */
} global_State;
/*
** `per thread' state
*/
struct lua_State {
CommonHeader;
lu_byte status;
StkId top; /* first free slot in the stack */
StkId base; /* base of current function */
global_State *l_G;
CallInfo *ci; /* call info for current function */
const Instruction *savedpc; /* `savedpc' of current function */
StkId stack_last; /* last free slot in the stack */
StkId stack; /* stack base */
CallInfo *end_ci; /* points after end of ci array*/
CallInfo *base_ci; /* array of CallInfo's */
int stacksize;
int size_ci; /* size of array `base_ci' */
unsigned short nCcalls; /* number of nested C calls */
unsigned short baseCcalls; /* nested C calls when resuming coroutine */
lu_byte hookmask;
lu_byte allowhook;
int basehookcount;
int hookcount;
lua_Hook hook;
TValue l_gt; /* table of globals */
TValue env; /* temporary place for environments */
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
ptrdiff_t errfunc; /* current error handling function (stack index) */
};
#define G(L) (L->l_G)
/*
** Union of all collectable objects
*/
union GCObject {
GCheader gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th; /* thread */
};
/* macros to convert a GCObject into a specific value */
#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
#define gco2ts(o) (&rawgco2ts(o)->tsv)
#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2u(o) (&rawgco2u(o)->uv)
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define ngcotouv(o) \
check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
/* macro to convert any Lua object into a GCObject */
#define obj2gco(v) (cast(GCObject *, (v)))
LUAI_FUNC lua_State *luaE_newthread (lua_State *L);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
#endif

Some files were not shown because too many files have changed in this diff Show More