forked from minetest-mods/irc
		
	Compare commits
	
		
			1 Commits
		
	
	
		
			612a94bbf1
			...
			rewrite
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | ad0de345d8 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | |||||||
| *~ | Build | ||||||
|  | irc | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,3 @@ | |||||||
| [submodule "irc"] | [submodule "src/LuaIRC"] | ||||||
| 	path = irc | 	path = src/LuaIRC | ||||||
| 	url = https://sys4.fr/gitea/mtcontrib/LuaIRC.git | 	url = https://github.com/ShadowNinja/LuaIRC.git | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								.luacheckrc
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								.luacheckrc
									
									
									
									
									
								
							| @@ -1,14 +0,0 @@ | |||||||
|  |  | ||||||
| allow_defined_top = true |  | ||||||
|  |  | ||||||
| read_globals = { |  | ||||||
| 	"minetest", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| exclude_files = { |  | ||||||
| 	"irc/*", |  | ||||||
| } |  | ||||||
|  |  | ||||||
| globals = { |  | ||||||
| 	"irc", |  | ||||||
| } |  | ||||||
							
								
								
									
										143
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | |||||||
|  |  | ||||||
|  | # :mode=cmake: | ||||||
|  |  | ||||||
|  | cmake_minimum_required(VERSION 2.8) | ||||||
|  |  | ||||||
|  | project(MINETEST_IRC C) | ||||||
|  |  | ||||||
|  | set(MINETEST_IRC_VERSION 0.2.0) | ||||||
|  |  | ||||||
|  | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") | ||||||
|  |  | ||||||
|  | set(LUA_SRCS | ||||||
|  | 	src/lua/lapi.c | ||||||
|  | 	src/lua/lcode.c | ||||||
|  | 	src/lua/ldebug.c | ||||||
|  | 	src/lua/ldo.c | ||||||
|  | 	src/lua/ldump.c | ||||||
|  | 	src/lua/lfunc.c | ||||||
|  | 	src/lua/lgc.c | ||||||
|  | 	src/lua/llex.c | ||||||
|  | 	src/lua/lmem.c | ||||||
|  | 	src/lua/lobject.c | ||||||
|  | 	src/lua/lopcodes.c | ||||||
|  | 	src/lua/lparser.c | ||||||
|  | 	src/lua/lstate.c | ||||||
|  | 	src/lua/lstring.c | ||||||
|  | 	src/lua/ltable.c | ||||||
|  | 	src/lua/ltm.c | ||||||
|  | 	src/lua/lundump.c | ||||||
|  | 	src/lua/lvm.c | ||||||
|  | 	src/lua/lzio.c | ||||||
|  | 	src/lua/lauxlib.c | ||||||
|  | 	src/lua/lbaselib.c | ||||||
|  | 	src/lua/ldblib.c | ||||||
|  | 	src/lua/liolib.c | ||||||
|  | 	src/lua/lmathlib.c | ||||||
|  | 	src/lua/loslib.c | ||||||
|  | 	src/lua/ltablib.c | ||||||
|  | 	src/lua/lstrlib.c | ||||||
|  | 	src/lua/loadlib.c | ||||||
|  | 	src/lua/linit.c | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | set(LUASOCKET_SRCS | ||||||
|  | 	src/luasocket/compat51.c | ||||||
|  | 	src/luasocket/luasocket.c | ||||||
|  | 	src/luasocket/timeout.c | ||||||
|  | 	src/luasocket/buffer.c | ||||||
|  | 	src/luasocket/io.c | ||||||
|  | 	src/luasocket/auxiliar.c | ||||||
|  | 	src/luasocket/options.c | ||||||
|  | 	src/luasocket/inet.c | ||||||
|  | 	src/luasocket/tcp.c | ||||||
|  | 	src/luasocket/udp.c | ||||||
|  | 	src/luasocket/except.c | ||||||
|  | 	src/luasocket/select.c | ||||||
|  | 	src/luasocket/buffer.c | ||||||
|  | 	src/luasocket/auxiliar.c | ||||||
|  | 	src/luasocket/options.c | ||||||
|  | 	src/luasocket/timeout.c | ||||||
|  | 	src/luasocket/io.c | ||||||
|  | 	src/luasocket/mime.c | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | if(WIN32) | ||||||
|  | 	list(APPEND LUASOCKET_SRCS src/luasocket/wsocket.c) | ||||||
|  | 	set(LUASOCKET_EXTRA_LIBS -lwininet) | ||||||
|  | else() | ||||||
|  | 	list(APPEND LUASOCKET_SRCS src/luasocket/usocket.c src/luasocket/unix.c) | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) | ||||||
|  | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/lua) | ||||||
|  | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src/luasocket) | ||||||
|  |  | ||||||
|  | add_library(luasocket_lib MODULE ${LUASOCKET_SRCS} ${LUA_SRCS}) | ||||||
|  |  | ||||||
|  | set_target_properties(luasocket_lib PROPERTIES | ||||||
|  | 	OUTPUT_NAME luasocket) | ||||||
|  |  | ||||||
|  | if(WIN32) | ||||||
|  | 	# When using MinGW32, CMake prefixes DLLs with "lib". Force remove | ||||||
|  | 	#  this prefix regardless of compiler. | ||||||
|  | 	set_target_properties(luasocket_lib PROPERTIES | ||||||
|  | 		PREFIX "") | ||||||
|  | 	if (NOT MSVC) # GCC? | ||||||
|  | 		# The `-fPIC' flag generates a warning on MinGW32, which combined | ||||||
|  | 		#  with `-Werror' makes that an error though `-fPIC' is ignored. | ||||||
|  | 		#  We use `-fno-PIC' to avoid that. | ||||||
|  | 		set_target_properties(luasocket_lib PROPERTIES | ||||||
|  | 			COMPILE_FLAGS "-fno-PIC -Wall -Werror") | ||||||
|  | 	endif() | ||||||
|  | 	find_library(ws2_32_lib NAMES ws2_32) | ||||||
|  | 	target_link_libraries(luasocket_lib ${ws2_32_lib}) | ||||||
|  | else() # Possibly Unix | ||||||
|  | 	set_target_properties(luasocket_lib PROPERTIES | ||||||
|  | 		COMPILE_FLAGS "-Wall -Werror") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | set(dir ${CMAKE_CURRENT_BINARY_DIR}/irc/) | ||||||
|  |  | ||||||
|  | if(WIN32) | ||||||
|  | 	set(lib "${CMAKE_CURRENT_BINARY_DIR}/luasocket.dll") | ||||||
|  | else() | ||||||
|  | 	set(lib "${CMAKE_CURRENT_BINARY_DIR}/libluasocket.so") | ||||||
|  | endif() | ||||||
|  |  | ||||||
|  | add_custom_target(pack_mod ALL | ||||||
|  | 	WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} | ||||||
|  |  | ||||||
|  | 	# LuaIRC | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E make_directory ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy_directory src/LuaIRC ${dir}/irc | ||||||
|  |  | ||||||
|  | 	# luasocket | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E make_directory ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/ftp.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/http.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/ltn12.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/mime.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/smtp.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/socket.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/tp.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/url.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/luasocket/LICENSE.txt ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy ${lib} ${dir} | ||||||
|  |  | ||||||
|  | 	# IRC mod | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E make_directory ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/init.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/hooks.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/messages.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/config.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/callback.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/chatcmds.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/botcmds.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/player_part.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/util.lua ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy README.txt ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/API.txt ${dir} | ||||||
|  | 	COMMAND ${CMAKE_COMMAND} -E copy src/LICENSE.txt ${dir} | ||||||
|  | ) | ||||||
|  |  | ||||||
							
								
								
									
										181
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										181
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,181 +0,0 @@ | |||||||
|  |  | ||||||
| IRC Mod for Minetest |  | ||||||
| ==================== |  | ||||||
|  |  | ||||||
| Introduction |  | ||||||
| ------------ |  | ||||||
|  |  | ||||||
| This mod is just a glue between IRC and Minetest. It provides two-way |  | ||||||
|  communication between the in-game chat, and an arbitrary IRC channel. |  | ||||||
|  |  | ||||||
| The forum topic is [here][forum]. |  | ||||||
|  |  | ||||||
| [forum]: https://forum.minetest.net/viewtopic.php?f=11&t=3905 |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Installing |  | ||||||
| ---------- |  | ||||||
|  |  | ||||||
| Quick one line install for Linux: |  | ||||||
|  |  | ||||||
| 	cd <Mods directory> && git clone --recursive https://github.com/minetest-mods/irc.git |  | ||||||
|  |  | ||||||
| Please change `<Mods directory>` to fit your installation of Minetest. |  | ||||||
| For more information, see [the wiki][wiki]. |  | ||||||
|  |  | ||||||
| The IRC mod's git repository uses submodules, therefore you will have to run |  | ||||||
| `git submodule init` when first installing the mod (unless you used |  | ||||||
| `--recursive` as above), and `git submodule update` every time that a submodule |  | ||||||
| is updated. These steps can be combined into `git submodule update --init`. |  | ||||||
|  |  | ||||||
| You'll need to install LuaSocket. You can do so with your package manager on |  | ||||||
| many distributions, for example: |  | ||||||
|  |  | ||||||
| 	# # On Arch Linux: |  | ||||||
| 	# pacman -S lua51-socket |  | ||||||
| 	# # On Debian/Ubuntu: |  | ||||||
| 	# # Debian/Ubuntu's LuaSocket packages are broken, so use LuaRocks. |  | ||||||
| 	# apt-get install luarocks |  | ||||||
| 	# luarocks install luasocket |  | ||||||
|  |  | ||||||
| You will also need to add IRC to your trusted mods if you haven't disabled mod |  | ||||||
| security. Here's an example configuration line: |  | ||||||
|  |  | ||||||
| 	secure.trusted_mods = irc |  | ||||||
|  |  | ||||||
| [wiki]: https://wiki.minetest.net/Installing_mods |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Settings |  | ||||||
| -------- |  | ||||||
|  |  | ||||||
| All settings are changed in `minetest.conf`. If any of these settings |  | ||||||
| are not set, the default value is used. |  | ||||||
|  |  | ||||||
| * `irc.server` (string): |  | ||||||
|   The address of the IRC server to connect to. |  | ||||||
|  |  | ||||||
| * `irc.channel` (string): |  | ||||||
|   The IRC channel to join. |  | ||||||
|  |  | ||||||
| * `irc.interval` (number, default 2.0): |  | ||||||
|   This prevents the server from flooding. It should be at |  | ||||||
|   least 2.0 but can be higher. After four messages this much |  | ||||||
|   time must pass between folowing messages. |  | ||||||
|  |  | ||||||
| * `irc.nick` (string): |  | ||||||
|   Nickname the server uses when it connects to IRC. |  | ||||||
|  |  | ||||||
| * `irc.password` (string, default nil): |  | ||||||
|   Password to use when connecting to the server. |  | ||||||
|  |  | ||||||
| * `irc.NSPass` (string, default nil): |  | ||||||
|   NickServ password. Don't set this if you use SASL authentication. |  | ||||||
|  |  | ||||||
| * `irc.sasl.pass` (string, default nil): |  | ||||||
|   SASL password, same as nickserv password. |  | ||||||
|   You should use this instead of NickServ authentication |  | ||||||
|   if the server supports it. |  | ||||||
|  |  | ||||||
| * `irc.sasl.user` (string, default `irc.nick`): |  | ||||||
|   The SASL username. This should normaly be set to your |  | ||||||
|   NickServ account name. |  | ||||||
|  |  | ||||||
| * `irc.debug` (boolean, default false): |  | ||||||
|   Whether to output debug information. |  | ||||||
|  |  | ||||||
| * `irc.disable_auto_connect` (boolean, default false): |  | ||||||
|   If false, the bot is connected by default. If true, a player with |  | ||||||
|   the 'irc_admin' privilege has to use the `/irc_connect` command to |  | ||||||
|   connect to the server. |  | ||||||
|  |  | ||||||
| * `irc.disable_auto_join` (boolean, default false): |  | ||||||
|   If false, players join the channel automatically upon entering the |  | ||||||
|   game. If true, each user must manually use the `/join` command to |  | ||||||
|   join the channel. In any case, the players may use the `/part` |  | ||||||
|   command to opt-out of being in the channel. |  | ||||||
|  |  | ||||||
| * `irc.send_join_part` (boolean, default true): |  | ||||||
|   Determines whether to send player join and part messages to the channel. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Usage |  | ||||||
| ----- |  | ||||||
|  |  | ||||||
| Once the game is connected to the IRC channel, chatting in-game will send |  | ||||||
| messages to the channel, and will be visible by anyone. Also, messages sent |  | ||||||
| to the channel will be visible in-game. |  | ||||||
|  |  | ||||||
| Messages that begin with `[off]` from in-game or IRC are not sent to the |  | ||||||
| other side. |  | ||||||
|  |  | ||||||
| This mod also adds a few chat commands: |  | ||||||
|  |  | ||||||
| * `/irc_msg <nick> <message>`: |  | ||||||
|   Send a private message to a IRC user. |  | ||||||
|  |  | ||||||
| * `/join`: |  | ||||||
|   Join the IRC chat. |  | ||||||
|  |  | ||||||
| * `/part`: |  | ||||||
|   Part the IRC chat. |  | ||||||
|  |  | ||||||
| * `/irc_connect`: |  | ||||||
|   Connect the bot manually to the IRC network. |  | ||||||
|  |  | ||||||
| * `/irc_disconnect`: |  | ||||||
|   Disconnect the bot manually from the IRC network (this does not |  | ||||||
|   shutdown the game). |  | ||||||
|  |  | ||||||
| * `/irc_reconnect`: |  | ||||||
|   Equivalent to `/irc_disconnect` followed by `/irc_connect`. |  | ||||||
|  |  | ||||||
| You can also send private messages from IRC to in-game players |  | ||||||
| by sending a private message to the bot (set with the `irc.nick` |  | ||||||
| option above), in the following format: |  | ||||||
|  |  | ||||||
| 	@playername message |  | ||||||
|  |  | ||||||
| For example, if there's a player named `mtuser`, you can send him/her |  | ||||||
| a private message from IRC with: |  | ||||||
|  |  | ||||||
| 	/msg server_nick @mtuser Hello! |  | ||||||
|  |  | ||||||
| The bot also supports some basic commands, which are invoked by saying |  | ||||||
| the bot name followed by either a colon or a comma and the command, or |  | ||||||
| sending a private message to it. For example: `ServerBot: help whereis`. |  | ||||||
|  |  | ||||||
| * `help [<command>]`: |  | ||||||
|   Prints help about a command, or a list of supported commands if no |  | ||||||
|   command is given. |  | ||||||
|  |  | ||||||
| * `uptime`: |  | ||||||
|   Prints the server's running time. |  | ||||||
|  |  | ||||||
| * `whereis <player>`: |  | ||||||
|   Prints the coordinates of the given player. |  | ||||||
|  |  | ||||||
| * `players`: |  | ||||||
|   Lists players currently in the server. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| Thanks |  | ||||||
| ------ |  | ||||||
|  |  | ||||||
| I'd like to thank the users who supported this mod both on the Minetest |  | ||||||
| Forums and on the `#minetest` channel. In no particular order: |  | ||||||
|  |  | ||||||
| 0gb.us, ShadowNinja, Shaun/kizeren, RAPHAEL, DARGON, Calinou, Exio, |  | ||||||
| vortexlabs/mrtux, marveidemanis, marktraceur, jmf/john\_minetest, |  | ||||||
| sdzen/Muadtralk, VanessaE, PilzAdam, sfan5, celeron55, KikaRz, |  | ||||||
| OldCoder, RealBadAngel, and all the people who commented in the |  | ||||||
| forum topic. Thanks to you all! |  | ||||||
|  |  | ||||||
|  |  | ||||||
| License |  | ||||||
| ------- |  | ||||||
|  |  | ||||||
| See `LICENSE.txt` for details. |  | ||||||
|  |  | ||||||
| The files in the `irc` directory are part of the LuaIRC project. |  | ||||||
| See `irc/LICENSE.txt` for details. |  | ||||||
							
								
								
									
										217
									
								
								README.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										217
									
								
								README.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,217 @@ | |||||||
|  | IRC Mod for Minetest | ||||||
|  | (C) 2012 Diego Martínez <kaeza@users.sf.net> | ||||||
|  |  | ||||||
|  | INTRODUCTION | ||||||
|  | ------------ | ||||||
|  | This mod is just a glue between luasocket, LuaIRC, and Minetest. It | ||||||
|  |  provides a two-way communication between the in-game chat, and an | ||||||
|  |  arbitrary IRC channel.  | ||||||
|  |  | ||||||
|  | The forum topic is at http://minetest.net/forum/viewtopic.php?id=3905 | ||||||
|  |  | ||||||
|  |   | ||||||
|  | COMPILING | ||||||
|  | --------- | ||||||
|  | Make sure you have CMake (http://cmake.org/), and of course, a C compiler, | ||||||
|  |  on your system before proceeding. | ||||||
|  | For Windows, try MinGW32 (http://mingw.org/). | ||||||
|  | For Unix-based systems, you should not have any problems with the C compiler | ||||||
|  |  since there's one (almost) always available. Puppy Linux users of course | ||||||
|  |  need a separate 'devx.sfs' (from the same place where you got the Puppy | ||||||
|  |  ISO), since vanilla Puppy does not come with 'gcc'. See your Puppy docs for | ||||||
|  |  more info about how to install additional SFS files. | ||||||
|  |  | ||||||
|  | Quick one line build for linux.   | ||||||
|  |  | ||||||
|  | git clone https://github.com/kaeza/minetest-irc.git && cd minetest-irc && git submodule update --init && ./quick_install.sh <mod directory> | ||||||
|  | Please change <mod directory> to fit your install of minetest. | ||||||
|  |  | ||||||
|  | To compile and pack the mod: | ||||||
|  |  | ||||||
|  |   - Open a command prompt/terminal and CD to the minetest-irc directory. | ||||||
|  |   - (optional) Create a directory named "Build", and CD into it: | ||||||
|  |       mkdir Build | ||||||
|  |       cd Build | ||||||
|  |   - Run CMake to generate the build system (see your CMake docs for more | ||||||
|  |      information about command line options, in particular the '-G' option). | ||||||
|  |       cmake . (cmake .. if you made a seperate build directory) | ||||||
|  |   - Use the build tool for the generated build system to compile the | ||||||
|  |      native library. For example, if using Microsoft Visual Studio, open | ||||||
|  |      the generated workspace and build from there. If using make, just run | ||||||
|  |      "make" from within the Build directory. | ||||||
|  |   - After building you will have a folder named 'irc' in your build folder. | ||||||
|  | 	 Move that to your mod folder. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | INSTALLING | ||||||
|  | ---------- | ||||||
|  | Just put the created 'irc' directory in any of the directories where | ||||||
|  |  Minetest looks for mods. For more information, see: | ||||||
|  |     http://wiki.minetest.com/wiki/Installing_mods | ||||||
|  |  | ||||||
|  |  | ||||||
|  | SETTINGS | ||||||
|  | -------- | ||||||
|  | All settings are changed in 'minetest.conf'. If any of these settings | ||||||
|  |  are either not set or false, the default value is used. | ||||||
|  |  | ||||||
|  |     irc.server (string, default "irc.freenode.net") | ||||||
|  |         This is the IRC server the mod connects to. | ||||||
|  |  | ||||||
|  |     irc.channel (string, default "##mt-irc-mod") | ||||||
|  |         The IRC channel to join. | ||||||
|  |  | ||||||
|  |     irc.interval (number, default 2.0) | ||||||
|  |         This prevents the server from flooding. It should be at | ||||||
|  |         least 2.0 but can be higher. After four messages this much | ||||||
|  |         time must pass between folowing messages. | ||||||
|  |  | ||||||
|  |     irc.timeout (number, default 60.0) | ||||||
|  |         Underlying socket timeout in seconds. This is the time before | ||||||
|  |         the system drops an idle connection. | ||||||
|  |  | ||||||
|  |     irc.nick (string, default "minetest-"..<server-id>) | ||||||
|  |         Nickname used as "proxy" for the in-game chat.  | ||||||
|  |         "<server-id>" is a random 32 bit number. | ||||||
|  |  | ||||||
|  |     irc.password (string, default "") | ||||||
|  |         Password to use when connecting to the server. | ||||||
|  |  | ||||||
|  |     irc.NSPass (string, default nil) | ||||||
|  |         NickServ password. Don't use this if you use SASL authentication. | ||||||
|  |  | ||||||
|  |     irc.SASLPass (string, default nil) | ||||||
|  |         SASL password, same as nickserv password. | ||||||
|  |         You should use this instead of NickServ authentication | ||||||
|  |         if the server supports it. | ||||||
|  |  | ||||||
|  |     irc.SASLUser (string, default irc.nick) | ||||||
|  |         The SASL username. This should normaly be set to your main NickServ account name. | ||||||
|  |  | ||||||
|  |     irc.format_out (string, default "<$(name)> $(message)") | ||||||
|  |         This specifies how to send the messages from in-game to IRC. | ||||||
|  |         The strings can contain "macros" (or variable substitutions), which | ||||||
|  |         are specified as "$(macro_name)". | ||||||
|  |         Currently, these macros are supported: | ||||||
|  |           $(name)       The name of the player sending the message. | ||||||
|  |           $(message)    The actual message text. | ||||||
|  |         Any unrecognized macro will be left in the message verbatim. | ||||||
|  |         For example, if a user named "mtuser" is saying "Hello!", then: | ||||||
|  |           "<$(name)> $(message)" | ||||||
|  |         ...will yield... | ||||||
|  |           "<mtuser> Hello!" | ||||||
|  |         ...and... | ||||||
|  |           "$(name): $(message) $(xyz)" | ||||||
|  |         ...will yield... | ||||||
|  |           "mtuser: Hello! $(xyz)" | ||||||
|  |  | ||||||
|  |     irc.format_in (string, | ||||||
|  |      default "<$(name)@IRC> $(message)") | ||||||
|  |         This specifies how the messages gotten from the IRC channel are | ||||||
|  |         displayed in-game. | ||||||
|  |         The strings can contain "macros" (or variable substitutions), which | ||||||
|  |         are specified as "$(macro_name)". | ||||||
|  |         Currently, these macros are supported: | ||||||
|  |           $(name)       The nickname of the user sending the message. | ||||||
|  |           $(message)    The actual message text. | ||||||
|  |           $(server)     The IRC server. | ||||||
|  |           $(port)       The IRC server port. | ||||||
|  |           $(channel)    The IRC channel. | ||||||
|  |         In the default configuration, this will yield: | ||||||
|  |           <IRCUser@IRC> Hello! | ||||||
|  |  | ||||||
|  |     irc.debug (boolean, default false) | ||||||
|  |         Whether to output debug information. | ||||||
|  |  | ||||||
|  |     irc.disable_auto_connect (boolean, default false) | ||||||
|  |         If false, the bot is connected by default. If true, a player with | ||||||
|  |          the 'irc_admin' privilege has to use the /irc_connect command to | ||||||
|  |          connect to the server. | ||||||
|  |  | ||||||
|  |     irc.disable_auto_join (boolean, default false) | ||||||
|  |         If false, players join the channel automatically upon entering the | ||||||
|  |          game. If true, each user must manually use the /join command to | ||||||
|  |          join the channel. In any case, the players may use the /part | ||||||
|  |          command to opt-out of being in the channel. | ||||||
|  |  | ||||||
|  | USAGE | ||||||
|  | ----- | ||||||
|  | Once the game is connected to the IRC channel, chatting using the 'T' or | ||||||
|  |  F10 hotkeys will send the messages to the channel, and will be visible | ||||||
|  |  by anyone. Also, when someone sends a message to the channel, that text | ||||||
|  |  will be visible in-game. | ||||||
|  |  | ||||||
|  | This mod also adds a few chat commands: | ||||||
|  |  | ||||||
|  |     /irc_msg <nick> <message> | ||||||
|  |         Sends a private message to a IRC user. | ||||||
|  |  | ||||||
|  |     /join | ||||||
|  |         Join the IRC channel. | ||||||
|  |  | ||||||
|  |     /part | ||||||
|  |         Part the IRC channel. | ||||||
|  |  | ||||||
|  |     /irc_connect | ||||||
|  |         Connect the bot manually to the IRC network. | ||||||
|  |  | ||||||
|  |     /irc_disconnect | ||||||
|  |         Disconnect the bot manually from the IRC network (this does not | ||||||
|  |         shutdown the game). | ||||||
|  |  | ||||||
|  |     /irc_reconnect | ||||||
|  |         Equivilant to /irc_disconnect followed by /irc_connect. | ||||||
|  |  | ||||||
|  | You can also send private messages from IRC to in-game players, though | ||||||
|  |  it's a bit tricky. | ||||||
|  |  | ||||||
|  | To do it, you must send a private message to the bot (set with | ||||||
|  |  the 'irc.nick' option above), in the following format: | ||||||
|  |     @playername message | ||||||
|  | For example, if there's a player named 'mtuser', you can send him/her | ||||||
|  |  a private message from IRC with: | ||||||
|  |     /msg server_nick @mtuser Hello! | ||||||
|  |  | ||||||
|  | To avoid possible misunderstandings (since all in-game players use the | ||||||
|  |  same IRC user to converse with you), the "proxy" user will reject any | ||||||
|  |  private messages that are not in that format, and will send back a | ||||||
|  |  nice reminder as a private message. | ||||||
|  |  | ||||||
|  | The bot also supports some basic commands, which are invoked by sending | ||||||
|  |  a private message to it. Use '!help' to get a list of commands, and | ||||||
|  |  '!help <command>' to get help about a specific command. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | THANKS | ||||||
|  | ------ | ||||||
|  | I'd like to thank the users who supported this mod both on the Minetest | ||||||
|  |  Forums and on the #minetest channel. In no particular order: | ||||||
|  |  | ||||||
|  | 	0gb.us, ShadowNinja, Shaun/kizeren, RAPHAEL, DARGON, Calinou, Exio, | ||||||
|  | 	vortexlabs/mrtux, marveidemanis, marktraceur, jmf/john_minetest, | ||||||
|  | 	sdzen/Muadtralk, VanessaE, PilzAdam, sfan5, celeron55, KikaRz, | ||||||
|  | 	OldCoder, RealBadAngel, and all the people who commented in the | ||||||
|  | 	forum topic. Thanks to you all! | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LICENSE | ||||||
|  | ------- | ||||||
|  |             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE  | ||||||
|  |                     Version 2, December 2004  | ||||||
|  |  | ||||||
|  |  Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>  | ||||||
|  |  | ||||||
|  |  Everyone is permitted to copy and distribute verbatim or modified  | ||||||
|  |  copies of this license document, and changing it is allowed as long  | ||||||
|  |  as the name is changed.  | ||||||
|  |  | ||||||
|  |             DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE  | ||||||
|  |    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||||
|  |  | ||||||
|  | 	0. You just DO WHAT THE FUCK YOU WANT TO. | ||||||
|  |  | ||||||
|  | The files 'http.lua', 'ltn12.lua', 'mime.lua', 'smtp.lua', 'socket.lua', | ||||||
|  |  and 'url.lua' are part of the luasocket project | ||||||
|  |  (http://luasocket.luaforge.org/). See 'src/luasocket/LICENSE.txt' for | ||||||
|  |  licensing information. | ||||||
|  |  | ||||||
							
								
								
									
										176
									
								
								botcmds.lua
									
									
									
									
									
								
							
							
						
						
									
										176
									
								
								botcmds.lua
									
									
									
									
									
								
							| @@ -1,176 +0,0 @@ | |||||||
|  |  | ||||||
| irc.bot_commands = {} |  | ||||||
|  |  | ||||||
| -- From RFC1459: |  | ||||||
| -- "Because of IRC’s scandanavian origin, the characters {}| are |  | ||||||
| --  considered to be the lower case equivalents of the characters |  | ||||||
| --  []\, respectively." |  | ||||||
| local irctolower = { ["["]="{", ["\\"]="|", ["]"]="}" } |  | ||||||
|  |  | ||||||
| local function irclower(s) |  | ||||||
| 	return (s:lower():gsub("[%[%]\\]", irctolower)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| local function nickequals(nick1, nick2) |  | ||||||
| 	return irclower(nick1) == irclower(nick2) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.check_botcmd(msg) |  | ||||||
| 	local prefix = irc.config.command_prefix |  | ||||||
| 	local nick = irc.conn.nick |  | ||||||
| 	local text = msg.args[2] |  | ||||||
| 	local nickpart = text:sub(1, #nick) |  | ||||||
| 	local suffix = text:sub(#nick+1, #nick+2) |  | ||||||
|  |  | ||||||
| 	-- First check for a nick prefix |  | ||||||
| 	if nickequals(nickpart, nick) |  | ||||||
| 			and (suffix == ": " or suffix == ", ") then |  | ||||||
| 		irc.bot_command(msg, text:sub(#nick + 3)) |  | ||||||
| 		return true |  | ||||||
| 	-- Then check for the configured prefix |  | ||||||
| 	elseif prefix and text:sub(1, #prefix):lower() == prefix:lower() then |  | ||||||
| 		irc.bot_command(msg, text:sub(#prefix + 1)) |  | ||||||
| 		return true |  | ||||||
| 	end |  | ||||||
| 	return false |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.bot_command(msg, text) |  | ||||||
| 	-- Remove leading whitespace |  | ||||||
| 	text = text:match("^%s*(.*)") |  | ||||||
| 	if text:sub(1, 1) == "@" then |  | ||||||
| 		local _, _, player_to, message = text:find("^.([^%s]+)%s(.+)$") |  | ||||||
| 		if not player_to then |  | ||||||
| 			return |  | ||||||
| 		elseif not minetest.get_player_by_name(player_to) then |  | ||||||
| 			irc.reply("User '"..player_to.."' is not in the game.") |  | ||||||
| 			return |  | ||||||
| 		elseif not irc.joined_players[player_to] then |  | ||||||
| 			irc.reply("User '"..player_to.."' is not using IRC.") |  | ||||||
| 			return |  | ||||||
| 		end |  | ||||||
| 		minetest.chat_send_player(player_to, |  | ||||||
| 				minetest.colorize(irc.config.pm_color, |  | ||||||
| 				"PM from "..msg.user.nick.."@IRC: "..message, false)) |  | ||||||
| 		irc.reply("Message sent!") |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
| 	local pos = text:find(" ", 1, true) |  | ||||||
| 	local cmd, args |  | ||||||
| 	if pos then |  | ||||||
| 		cmd = text:sub(1, pos - 1) |  | ||||||
| 		args = text:sub(pos + 1) |  | ||||||
| 	else |  | ||||||
| 		cmd = text |  | ||||||
| 		args = "" |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	if not irc.bot_commands[cmd] then |  | ||||||
| 		irc.reply("Unknown command '"..cmd.."'. Try 'help'." |  | ||||||
| 			.." Or use @playername <message> to send a private message") |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	local _, message = irc.bot_commands[cmd].func(msg.user, args) |  | ||||||
| 	if message then |  | ||||||
| 		irc.reply(message) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.register_bot_command(name, def) |  | ||||||
| 	if (not def.func) or (type(def.func) ~= "function") then |  | ||||||
| 		error("Erroneous bot command definition. def.func missing.", 2) |  | ||||||
| 	elseif name:sub(1, 1) == "@" then |  | ||||||
| 		error("Erroneous bot command name. Command name begins with '@'.", 2) |  | ||||||
| 	end |  | ||||||
| 	irc.bot_commands[name] = def |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.register_bot_command("help", { |  | ||||||
| 	params = "<command>", |  | ||||||
| 	description = "Get help about a command", |  | ||||||
| 	func = function(_, args) |  | ||||||
| 		if args == "" then |  | ||||||
| 			local cmdlist = { } |  | ||||||
| 			for name in pairs(irc.bot_commands) do |  | ||||||
| 				cmdlist[#cmdlist+1] = name |  | ||||||
| 			end |  | ||||||
| 			return true, "Available commands: "..table.concat(cmdlist, ", ") |  | ||||||
| 					.." -- Use 'help <command name>' to get" |  | ||||||
| 					.." help about a specific command." |  | ||||||
| 		end |  | ||||||
|  |  | ||||||
| 		local cmd = irc.bot_commands[args] |  | ||||||
| 		if not cmd then |  | ||||||
| 			return false, "Unknown command '"..args.."'." |  | ||||||
| 		end |  | ||||||
|  |  | ||||||
| 		return true, ("Usage: %s%s %s -- %s"):format( |  | ||||||
| 				irc.config.command_prefix or "", |  | ||||||
| 				args, |  | ||||||
| 				cmd.params or "<no parameters>", |  | ||||||
| 				cmd.description or "<no description>") |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.register_bot_command("list", { |  | ||||||
| 	params = "", |  | ||||||
| 	description = "List available commands.", |  | ||||||
| 	func = function() |  | ||||||
| 		return false, "The `list` command has been merged into `help`." |  | ||||||
| 				.." Use `help` with no arguments to get a list." |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.register_bot_command("whereis", { |  | ||||||
| 	params = "<player>", |  | ||||||
| 	description = "Tell the location of <player>", |  | ||||||
| 	func = function(_, args) |  | ||||||
| 		if args == "" then |  | ||||||
| 			return false, "Player name required." |  | ||||||
| 		end |  | ||||||
| 		local player = minetest.get_player_by_name(args) |  | ||||||
| 		if not player then |  | ||||||
| 			return false, "There is no player named '"..args.."'" |  | ||||||
| 		end |  | ||||||
| 		local fmt = "Player %s is at (%.2f,%.2f,%.2f)" |  | ||||||
| 		local pos = player:get_pos() |  | ||||||
| 		return true, fmt:format(args, pos.x, pos.y, pos.z) |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| local starttime = os.time() |  | ||||||
| irc.register_bot_command("uptime", { |  | ||||||
| 	description = "Tell how much time the server has been up", |  | ||||||
| 	func = function() |  | ||||||
| 		local cur_time = os.time() |  | ||||||
| 		local diff = os.difftime(cur_time, starttime) |  | ||||||
| 		local fmt = "Server has been running for %d:%02d:%02d" |  | ||||||
| 		return true, fmt:format( |  | ||||||
| 			math.floor(diff / 60 / 60), |  | ||||||
| 			math.floor(diff / 60) % 60, |  | ||||||
| 			math.floor(diff) % 60 |  | ||||||
| 		) |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.register_bot_command("players", { |  | ||||||
| 	description = "List the players on the server", |  | ||||||
| 	func = function() |  | ||||||
| 		local players = minetest.get_connected_players() |  | ||||||
| 		local names = {} |  | ||||||
| 		for _, player in pairs(players) do |  | ||||||
| 			table.insert(names, player:get_player_name()) |  | ||||||
| 		end |  | ||||||
| 		return true, "Connected players: " |  | ||||||
| 				..table.concat(names, ", ") |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
							
								
								
									
										41
									
								
								callback.lua
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								callback.lua
									
									
									
									
									
								
							| @@ -1,41 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_joinplayer(function(player) |  | ||||||
| 	local name = player:get_player_name() |  | ||||||
| 	if irc.connected and irc.config.send_join_part then |  | ||||||
| 		irc.say("*** "..name.." joined the game") |  | ||||||
| 	end |  | ||||||
| end) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_leaveplayer(function(player, timed_out) |  | ||||||
| 	local name = player:get_player_name() |  | ||||||
| 	if irc.connected and irc.config.send_join_part then |  | ||||||
| 		irc.say("*** "..name.." left the game".. |  | ||||||
| 				(timed_out and " (Timed out)" or "")) |  | ||||||
| 	end |  | ||||||
| end) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_chat_message(function(name, message) |  | ||||||
| 	if not irc.connected |  | ||||||
| 	   or message:sub(1, 1) == "/" |  | ||||||
| 	   or message:sub(1, 5) == "[off]" |  | ||||||
| 	   or not irc.joined_players[name] |  | ||||||
| 	   or (not minetest.check_player_privs(name, {shout=true})) then |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
| 	local nl = message:find("\n", 1, true) |  | ||||||
| 	if nl then |  | ||||||
| 		message = message:sub(1, nl - 1) |  | ||||||
| 	end |  | ||||||
| 	irc.say(irc.playerMessage(name, core.strip_colors(message))) |  | ||||||
| end) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_shutdown(function() |  | ||||||
| 	irc.disconnect("Game shutting down.") |  | ||||||
| end) |  | ||||||
|  |  | ||||||
							
								
								
									
										134
									
								
								chatcmds.lua
									
									
									
									
									
								
							
							
						
						
									
										134
									
								
								chatcmds.lua
									
									
									
									
									
								
							| @@ -1,134 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
| -- Note: This file does NOT conatin every chat command, only general ones. |  | ||||||
| -- Feature-specific commands (like /join) are in their own files. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_msg", { |  | ||||||
| 	params = "<name> <message>", |  | ||||||
| 	description = "Send a private message to an IRC user", |  | ||||||
| 	privs = {shout=true}, |  | ||||||
| 	func = function(name, param) |  | ||||||
| 		if not irc.connected then |  | ||||||
| 			return false, "Not connected to IRC. Use /irc_connect to connect." |  | ||||||
| 		end |  | ||||||
| 		local found, _, toname, message = param:find("^([^%s]+)%s(.+)") |  | ||||||
| 		if not found then |  | ||||||
| 			return false, "Invalid usage, see /help irc_msg." |  | ||||||
| 		end |  | ||||||
| 		local toname_l = toname:lower() |  | ||||||
| 		local validNick = false |  | ||||||
| 		local hint = "They have to be in the channel" |  | ||||||
| 		for nick in pairs(irc.conn.channels[irc.config.channel].users) do |  | ||||||
| 			if nick:lower() == toname_l then |  | ||||||
| 				validNick = true |  | ||||||
| 				break |  | ||||||
| 			end |  | ||||||
| 		end |  | ||||||
| 		if toname_l:find("serv$") or toname_l:find("bot$") then |  | ||||||
| 			hint = "it looks like a bot or service" |  | ||||||
| 			validNick = false |  | ||||||
| 		end |  | ||||||
| 		if not validNick then |  | ||||||
| 			return false, "You can not message that user. ("..hint..")" |  | ||||||
| 		end |  | ||||||
| 		irc.say(toname, irc.playerMessage(name, message)) |  | ||||||
| 		return true, "Message sent!" |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_names", { |  | ||||||
| 	params = "", |  | ||||||
| 	description = "List the users in IRC.", |  | ||||||
| 	func = function() |  | ||||||
| 		if not irc.connected then |  | ||||||
| 			return false, "Not connected to IRC. Use /irc_connect to connect." |  | ||||||
| 		end |  | ||||||
| 		local users = { } |  | ||||||
| 		for nick in pairs(irc.conn.channels[irc.config.channel].users) do |  | ||||||
| 			table.insert(users, nick) |  | ||||||
| 		end |  | ||||||
| 		return true, "Users in IRC: "..table.concat(users, ", ") |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_connect", { |  | ||||||
| 	description = "Connect to the IRC server.", |  | ||||||
| 	privs = {irc_admin=true}, |  | ||||||
| 	func = function(name) |  | ||||||
| 		if irc.connected then |  | ||||||
| 			return false, "You are already connected to IRC." |  | ||||||
| 		end |  | ||||||
| 		minetest.chat_send_player(name, "IRC: Connecting...") |  | ||||||
| 		irc.connect() |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_disconnect", { |  | ||||||
| 	params = "[message]", |  | ||||||
| 	description = "Disconnect from the IRC server.", |  | ||||||
| 	privs = {irc_admin=true}, |  | ||||||
| 	func = function(name, param) |  | ||||||
| 		if not irc.connected then |  | ||||||
| 			return false, "Not connected to IRC. Use /irc_connect to connect." |  | ||||||
| 		end |  | ||||||
| 		if param == "" then |  | ||||||
| 			param = "Manual disconnect by "..name |  | ||||||
| 		end |  | ||||||
| 		irc.disconnect(param) |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_reconnect", { |  | ||||||
| 	description = "Reconnect to the IRC server.", |  | ||||||
| 	privs = {irc_admin=true}, |  | ||||||
| 	func = function(name) |  | ||||||
| 		if not irc.connected then |  | ||||||
| 			return false, "Not connected to IRC. Use /irc_connect to connect." |  | ||||||
| 		end |  | ||||||
| 		minetest.chat_send_player(name, "IRC: Reconnecting...") |  | ||||||
| 		irc.disconnect("Reconnecting...") |  | ||||||
| 		irc.connect() |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("irc_quote", { |  | ||||||
| 	params = "<command>", |  | ||||||
| 	description = "Send a raw command to the IRC server.", |  | ||||||
| 	privs = {irc_admin=true}, |  | ||||||
| 	func = function(name, param) |  | ||||||
| 		if not irc.connected then |  | ||||||
| 			return false, "Not connected to IRC. Use /irc_connect to connect." |  | ||||||
| 		end |  | ||||||
| 		irc.queue(param) |  | ||||||
| 		minetest.chat_send_player(name, "Command sent!") |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| local oldme = minetest.chatcommands["me"].func |  | ||||||
| -- luacheck: ignore |  | ||||||
| minetest.chatcommands["me"].func = function(name, param, ...) |  | ||||||
| 	irc.say(("* %s %s"):format(name, param)) |  | ||||||
| 	return oldme(name, param, ...) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| if irc.config.send_kicks and minetest.chatcommands["kick"] then |  | ||||||
| 	local oldkick = minetest.chatcommands["kick"].func |  | ||||||
| 	-- luacheck: ignore |  | ||||||
| 	minetest.chatcommands["kick"].func = function(name, param, ...) |  | ||||||
| 		local plname, reason = param:match("^(%S+)%s*(.*)$") |  | ||||||
| 		if not plname then |  | ||||||
| 			return false, "Usage: /kick player [reason]" |  | ||||||
| 		end |  | ||||||
| 		irc.say(("*** Kicked %s.%s"):format(name, |  | ||||||
| 				reason~="" and " Reason: "..reason or "")) |  | ||||||
| 		return oldkick(name, param, ...) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
							
								
								
									
										22
									
								
								cmake/x-i586-mingw32msvc.cmake
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								cmake/x-i586-mingw32msvc.cmake
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | |||||||
|  |  | ||||||
|  | # :mode=cmake: | ||||||
|  |  | ||||||
|  | set(CMAKE_SYSTEM_NAME Windows) | ||||||
|  |  | ||||||
|  | set(PLATFORM i586-mingw32msvc) | ||||||
|  |  | ||||||
|  | set(MGW_TOOLCHAIN_PATH | ||||||
|  |     /usr/${PLATFORM} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | set(WIN32 1) | ||||||
|  |  | ||||||
|  | set(CMAKE_C_COMPILER /usr/bin/${PLATFORM}-gcc) | ||||||
|  | set(CMAKE_RC_COMPILER /usr/bin/${PLATFORM}-windres) | ||||||
|  |  | ||||||
|  | set(CMAKE_FIND_ROOT_PATH ${MGW_TOOLCHAIN_PATH}) | ||||||
|  |  | ||||||
|  | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | ||||||
|  |  | ||||||
|  | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | ||||||
|  | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) | ||||||
							
								
								
									
										59
									
								
								config.lua
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								config.lua
									
									
									
									
									
								
							| @@ -1,59 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.config = {} |  | ||||||
|  |  | ||||||
| local function setting(stype, name, default, required) |  | ||||||
| 	local value |  | ||||||
| 	if minetest.settings and minetest.settings.get and minetest.settings.get_bool then |  | ||||||
| 		if stype == "bool" then |  | ||||||
| 			value = minetest.settings:get_bool("irc."..name) |  | ||||||
| 		elseif stype == "string" then |  | ||||||
| 			value = minetest.settings:get("irc."..name) |  | ||||||
| 		elseif stype == "number" then |  | ||||||
| 			value = tonumber(minetest.settings:get("irc."..name)) |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| 	if value == nil then |  | ||||||
| 		if required then |  | ||||||
| 			error("Required configuration option irc.".. |  | ||||||
| 				name.." missing.") |  | ||||||
| 		end |  | ||||||
| 		value = default |  | ||||||
| 	end |  | ||||||
| 	irc.config[name] = value |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ------------------------- |  | ||||||
| -- BASIC USER SETTINGS -- |  | ||||||
| ------------------------- |  | ||||||
|  |  | ||||||
| setting("string", "nick", nil, true) -- Nickname |  | ||||||
| setting("string", "server", nil, true) -- Server address to connect to |  | ||||||
| setting("number", "port", 6667) -- Server port to connect to |  | ||||||
| setting("string", "NSPass") -- NickServ password |  | ||||||
| setting("string", "sasl.user", irc.config.nick) -- SASL username |  | ||||||
| setting("string", "username", "Minetest") -- Username/ident |  | ||||||
| setting("string", "realname", "Minetest") -- Real name/GECOS |  | ||||||
| setting("string", "sasl.pass") -- SASL password |  | ||||||
| setting("string", "channel", nil, true) -- Channel to join |  | ||||||
| setting("string", "key") -- Key for the channel |  | ||||||
| setting("bool",   "send_join_part", true) -- Whether to send player join and part messages to the channel |  | ||||||
| setting("bool",   "send_kicks", false) -- Whether to send player kicked messages to the channel |  | ||||||
|  |  | ||||||
| ----------------------- |  | ||||||
| -- ADVANCED SETTINGS -- |  | ||||||
| ----------------------- |  | ||||||
|  |  | ||||||
| setting("string", "password") -- Server password |  | ||||||
| setting("bool",   "secure", false) -- Enable a TLS connection, requires LuaSEC |  | ||||||
| setting("number", "timeout", 60) -- Underlying socket timeout in seconds. |  | ||||||
| setting("number", "reconnect", 600) -- Time between reconnection attempts, in seconds. |  | ||||||
| setting("string", "command_prefix") -- Prefix to use for bot commands |  | ||||||
| setting("bool",   "debug", false) -- Enable debug output |  | ||||||
| setting("bool",   "enable_player_part", true) -- Whether to enable players joining and parting the channel |  | ||||||
| setting("bool",   "auto_join", true) -- Whether to automatically show players in the channel when they join |  | ||||||
| setting("bool",   "auto_connect", true) -- Whether to automatically connect to the server on mod load |  | ||||||
| setting("string", "chat_color", "#339933") -- Color of IRC chat in-game, green by default |  | ||||||
| setting("string", "pm_color", "#8800AA") -- Color of IRC PMs in-game, purple by default |  | ||||||
							
								
								
									
										263
									
								
								hooks.lua
									
									
									
									
									
								
							
							
						
						
									
										263
									
								
								hooks.lua
									
									
									
									
									
								
							| @@ -1,263 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
| local ie = ... |  | ||||||
|  |  | ||||||
| -- MIME is part of LuaSocket |  | ||||||
| local b64e = ie.require("mime").b64 |  | ||||||
|  |  | ||||||
| irc.hooks = {} |  | ||||||
| irc.registered_hooks = {} |  | ||||||
|  |  | ||||||
|  |  | ||||||
| local stripped_chars = "[\2\31]" |  | ||||||
|  |  | ||||||
| local function normalize(text) |  | ||||||
| 	-- Strip colors |  | ||||||
| 	text = text:gsub("\3[0-9][0-9,]*", "") |  | ||||||
|  |  | ||||||
| 	return text:gsub(stripped_chars, "") |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.doHook(conn) |  | ||||||
| 	for name, hook in pairs(irc.registered_hooks) do |  | ||||||
| 		for _, func in pairs(hook) do |  | ||||||
| 			conn:hook(name, func) |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.register_hook(name, func) |  | ||||||
| 	irc.registered_hooks[name] = irc.registered_hooks[name] or {} |  | ||||||
| 	table.insert(irc.registered_hooks[name], func) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.raw(line) |  | ||||||
| 	if irc.config.debug then |  | ||||||
| 		print("RECV: "..line) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.send(line) |  | ||||||
| 	if irc.config.debug then |  | ||||||
| 		print("SEND: "..line) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.chat(msg) |  | ||||||
| 	local channel, text = msg.args[1], msg.args[2] |  | ||||||
| 	if text:sub(1, 1) == string.char(1) then |  | ||||||
| 		irc.conn:invoke("OnCTCP", msg) |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	if channel == irc.conn.nick then |  | ||||||
| 		irc.last_from = msg.user.nick |  | ||||||
| 		irc.conn:invoke("PrivateMessage", msg) |  | ||||||
| 	else |  | ||||||
| 		irc.last_from = channel |  | ||||||
| 		irc.conn:invoke("OnChannelChat", msg) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| local function get_core_version() |  | ||||||
| 	local status = minetest.get_server_status() |  | ||||||
| 	local start_pos = select(2, status:find("version=", 1, true)) |  | ||||||
| 	local end_pos = status:find(",", start_pos, true) |  | ||||||
| 	return status:sub(start_pos + 1, end_pos - 1) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.ctcp(msg) |  | ||||||
| 	local text = msg.args[2]:sub(2, -2)  -- Remove ^C |  | ||||||
| 	local args = text:split(' ') |  | ||||||
| 	local command = args[1]:upper() |  | ||||||
|  |  | ||||||
| 	local function reply(s) |  | ||||||
| 		irc.queue(irc.msgs.notice(msg.user.nick, |  | ||||||
| 				("\1%s %s\1"):format(command, s))) |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	if command == "ACTION" and msg.args[1] == irc.config.channel then |  | ||||||
| 		local action = text:sub(8, -1) |  | ||||||
| 		irc.sendLocal(("* %s@IRC %s"):format(msg.user.nick, action)) |  | ||||||
| 	elseif command == "VERSION" then |  | ||||||
| 		reply(("Minetest version %s, IRC mod version %s.") |  | ||||||
| 			:format(get_core_version(), irc.version)) |  | ||||||
| 	elseif command == "PING" then |  | ||||||
| 		reply(args[2]) |  | ||||||
| 	elseif command == "TIME" then |  | ||||||
| 		reply(os.date()) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.channelChat(msg) |  | ||||||
| 	local text = normalize(msg.args[2]) |  | ||||||
|  |  | ||||||
| 	irc.check_botcmd(msg) |  | ||||||
|  |  | ||||||
| 	-- Don't let a user impersonate someone else by using the nick "IRC" |  | ||||||
| 	local fake = msg.user.nick:lower():match("^[il|]rc$") |  | ||||||
| 	if fake then |  | ||||||
| 		irc.sendLocal("<"..msg.user.nick.."@IRC> "..text) |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	-- Support multiple servers in a channel better by converting: |  | ||||||
| 	-- "<server@IRC> <player> message" into "<player@server> message" |  | ||||||
| 	-- "<server@IRC> *** player joined/left the game" into "*** player joined/left server" |  | ||||||
| 	-- and "<server@IRC> * player orders a pizza" into "* player@server orders a pizza" |  | ||||||
| 	local foundchat, _, chatnick, chatmessage = |  | ||||||
| 		text:find("^<([^>]+)> (.*)$") |  | ||||||
| 	local foundjoin, _, joinnick = |  | ||||||
| 		text:find("^%*%*%* ([^%s]+) joined the game$") |  | ||||||
| 	local foundleave, _, leavenick = |  | ||||||
| 		text:find("^%*%*%* ([^%s]+) left the game$") |  | ||||||
| 	local foundtimedout, _, timedoutnick = |  | ||||||
| 		text:find("^%*%*%* ([^%s]+) left the game %(Timed out%)$") |  | ||||||
| 	local foundaction, _, actionnick, actionmessage = |  | ||||||
| 		text:find("^%* ([^%s]+) (.*)$") |  | ||||||
|  |  | ||||||
| 	if text:sub(1, 5) == "[off]" then |  | ||||||
| 		return |  | ||||||
| 	elseif foundchat then |  | ||||||
| 		irc.sendLocal(("<%s@%s> %s") |  | ||||||
| 				:format(chatnick, msg.user.nick, chatmessage)) |  | ||||||
| 	elseif foundjoin then |  | ||||||
| 		irc.sendLocal(("*** %s joined %s") |  | ||||||
| 				:format(joinnick, msg.user.nick)) |  | ||||||
| 	elseif foundleave then |  | ||||||
| 		irc.sendLocal(("*** %s left %s") |  | ||||||
| 				:format(leavenick, msg.user.nick)) |  | ||||||
| 	elseif foundtimedout then |  | ||||||
| 		irc.sendLocal(("*** %s left %s (Timed out)") |  | ||||||
| 				:format(timedoutnick, msg.user.nick)) |  | ||||||
| 	elseif foundaction then |  | ||||||
| 		irc.sendLocal(("* %s@%s %s") |  | ||||||
| 				:format(actionnick, msg.user.nick, actionmessage)) |  | ||||||
| 	else |  | ||||||
| 		irc.sendLocal(("<%s@IRC> %s"):format(msg.user.nick, text)) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.pm(msg) |  | ||||||
| 	-- Trim prefix if it is found |  | ||||||
| 	local text = msg.args[2] |  | ||||||
| 	local prefix = irc.config.command_prefix |  | ||||||
| 	if prefix and text:sub(1, #prefix) == prefix then |  | ||||||
| 		text = text:sub(#prefix + 1) |  | ||||||
| 	end |  | ||||||
| 	irc.bot_command(msg, text) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.kick(channel, target, prefix, reason) |  | ||||||
| 	if target == irc.conn.nick then |  | ||||||
| 		minetest.chat_send_all("IRC: kicked from "..channel.." by "..prefix.nick..".") |  | ||||||
| 		irc.disconnect("Kicked") |  | ||||||
| 	else |  | ||||||
| 		irc.sendLocal(("-!- %s was kicked from %s by %s [%s]") |  | ||||||
| 				:format(target, channel, prefix.nick, reason)) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.notice(user, target, message) |  | ||||||
| 	if user.nick and target == irc.config.channel then |  | ||||||
| 		irc.sendLocal("-"..user.nick.."@IRC- "..message) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.mode(user, target, modes, ...) |  | ||||||
| 	local by = "" |  | ||||||
| 	if user.nick then |  | ||||||
| 		by = " by "..user.nick |  | ||||||
| 	end |  | ||||||
| 	local options = "" |  | ||||||
| 	if select("#", ...) > 0 then |  | ||||||
| 		options = " " |  | ||||||
| 	end |  | ||||||
| 	options = options .. table.concat({...}, " ") |  | ||||||
| 	minetest.chat_send_all(("-!- mode/%s [%s%s]%s") |  | ||||||
| 			:format(target, modes, options, by)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.nick(user, newNick) |  | ||||||
| 	irc.sendLocal(("-!- %s is now known as %s") |  | ||||||
| 			:format(user.nick, newNick)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.join(user, channel) |  | ||||||
| 	irc.sendLocal(("-!- %s joined %s") |  | ||||||
| 			:format(user.nick, channel)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.part(user, channel, reason) |  | ||||||
| 	reason = reason or "" |  | ||||||
| 	irc.sendLocal(("-!- %s has left %s [%s]") |  | ||||||
| 			:format(user.nick, channel, reason)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.quit(user, reason) |  | ||||||
| 	irc.sendLocal(("-!- %s has quit [%s]") |  | ||||||
| 			:format(user.nick, reason)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.disconnect(_, isError) |  | ||||||
| 	irc.connected = false |  | ||||||
| 	if isError then |  | ||||||
| 		minetest.log("error",  "IRC: Error: Disconnected, reconnecting in one minute.") |  | ||||||
| 		minetest.chat_send_all("IRC: Error: Disconnected, reconnecting in one minute.") |  | ||||||
| 		minetest.after(60, irc.connect, irc) |  | ||||||
| 	else |  | ||||||
| 		minetest.log("action", "IRC: Disconnected.") |  | ||||||
| 		minetest.chat_send_all("IRC: Disconnected.") |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.hooks.preregister(conn) |  | ||||||
| 	if not (irc.config["sasl.user"] and irc.config["sasl.pass"]) then return end |  | ||||||
| 	local authString = b64e( |  | ||||||
| 		("%s\x00%s\x00%s"):format( |  | ||||||
| 		irc.config["sasl.user"], |  | ||||||
| 		irc.config["sasl.user"], |  | ||||||
| 		irc.config["sasl.pass"]) |  | ||||||
| 	) |  | ||||||
| 	conn:send("CAP REQ sasl") |  | ||||||
| 	conn:send("AUTHENTICATE PLAIN") |  | ||||||
| 	conn:send("AUTHENTICATE "..authString) |  | ||||||
| 	conn:send("CAP END") |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| irc.register_hook("PreRegister",     irc.hooks.preregister) |  | ||||||
| irc.register_hook("OnRaw",           irc.hooks.raw) |  | ||||||
| irc.register_hook("OnSend",          irc.hooks.send) |  | ||||||
| irc.register_hook("DoPrivmsg",       irc.hooks.chat) |  | ||||||
| irc.register_hook("OnPart",          irc.hooks.part) |  | ||||||
| irc.register_hook("OnKick",          irc.hooks.kick) |  | ||||||
| irc.register_hook("OnJoin",          irc.hooks.join) |  | ||||||
| irc.register_hook("OnQuit",          irc.hooks.quit) |  | ||||||
| irc.register_hook("NickChange",      irc.hooks.nick) |  | ||||||
| irc.register_hook("OnCTCP",          irc.hooks.ctcp) |  | ||||||
| irc.register_hook("PrivateMessage",  irc.hooks.pm) |  | ||||||
| irc.register_hook("OnNotice",        irc.hooks.notice) |  | ||||||
| irc.register_hook("OnChannelChat",   irc.hooks.channelChat) |  | ||||||
| irc.register_hook("OnModeChange",    irc.hooks.mode) |  | ||||||
| irc.register_hook("OnDisconnect",    irc.hooks.disconnect) |  | ||||||
|  |  | ||||||
							
								
								
									
										227
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						
									
										227
									
								
								init.lua
									
									
									
									
									
								
							| @@ -1,227 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
| local modpath = minetest.get_modpath(minetest.get_current_modname()) |  | ||||||
|  |  | ||||||
| -- Handle mod security if needed |  | ||||||
| local ie, req_ie = _G, minetest.request_insecure_environment |  | ||||||
| if req_ie then ie = req_ie() end |  | ||||||
| if not ie then |  | ||||||
| 	error("The IRC mod requires access to insecure functions in order ".. |  | ||||||
| 		"to work.  Please add the irc mod to your secure.trusted_mods ".. |  | ||||||
| 		"setting or disable the irc mod.") |  | ||||||
| end |  | ||||||
|  |  | ||||||
| ie.package.path = |  | ||||||
| 		-- To find LuaIRC's init.lua |  | ||||||
| 		modpath.."/?/init.lua;" |  | ||||||
| 		-- For LuaIRC to find its files |  | ||||||
| 		..modpath.."/?.lua;" |  | ||||||
| 		..ie.package.path |  | ||||||
|  |  | ||||||
| -- The build of Lua that Minetest comes with only looks for libraries under |  | ||||||
| -- /usr/local/share and /usr/local/lib but LuaSocket is often installed under |  | ||||||
| -- /usr/share and /usr/lib. |  | ||||||
| if not rawget(_G, "jit") and package.config:sub(1, 1) == "/" then |  | ||||||
|  |  | ||||||
| 	ie.package.path = ie.package.path.. |  | ||||||
| 			";/usr/share/lua/5.1/?.lua".. |  | ||||||
| 			";/usr/share/lua/5.1/?/init.lua" |  | ||||||
|  |  | ||||||
| 	ie.package.cpath = ie.package.cpath.. |  | ||||||
| 			";/usr/lib/lua/5.1/?.so".. |  | ||||||
| 			";/usr/lib64/lua/5.1/?.so" |  | ||||||
|  |  | ||||||
| 	ie.package.cpath = "/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;"..ie.package.cpath |  | ||||||
|  |  | ||||||
|  |  | ||||||
| end |  | ||||||
|  |  | ||||||
| -- Temporarily set require so that LuaIRC can access it |  | ||||||
| local old_require = require |  | ||||||
| require = ie.require |  | ||||||
|  |  | ||||||
| -- Silence warnings about `module` in `ltn12`. |  | ||||||
| local old_module = rawget(_G, "module") |  | ||||||
| rawset(_G, "module", ie.module) |  | ||||||
|  |  | ||||||
| local lib = ie.require("irc") |  | ||||||
|  |  | ||||||
| irc = { |  | ||||||
| 	version = "0.2.0", |  | ||||||
| 	connected = false, |  | ||||||
| 	cur_time = 0, |  | ||||||
| 	message_buffer = {}, |  | ||||||
| 	recent_message_count = 0, |  | ||||||
| 	joined_players = {}, |  | ||||||
| 	modpath = modpath, |  | ||||||
| 	lib = lib, |  | ||||||
| } |  | ||||||
|  |  | ||||||
| -- Compatibility |  | ||||||
| rawset(_G, "mt_irc", irc) |  | ||||||
|  |  | ||||||
| local getinfo = debug.getinfo |  | ||||||
| local warned = { } |  | ||||||
|  |  | ||||||
| local function warn_deprecated(k) |  | ||||||
| 	local info = getinfo(3) |  | ||||||
| 	local loc = info.source..":"..info.currentline |  | ||||||
| 	if warned[loc] then return end |  | ||||||
| 	warned[loc] = true |  | ||||||
| 	print("COLON: "..tostring(k)) |  | ||||||
| 	minetest.log("warning", "Deprecated use of colon notation when calling" |  | ||||||
| 			.." method `"..tostring(k).."` at "..loc) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| -- This is a hack. |  | ||||||
| setmetatable(irc, { |  | ||||||
| 	__newindex = function(t, k, v) |  | ||||||
| 		if type(v) == "function" then |  | ||||||
| 			local f = v |  | ||||||
| 			v = function(me, ...) |  | ||||||
| 				if rawequal(me, t) then |  | ||||||
| 					warn_deprecated(k) |  | ||||||
| 					return f(...) |  | ||||||
| 				else |  | ||||||
| 					return f(me, ...) |  | ||||||
| 				end |  | ||||||
| 			end |  | ||||||
| 		end |  | ||||||
| 		rawset(t, k, v) |  | ||||||
| 	end, |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| dofile(modpath.."/config.lua") |  | ||||||
| dofile(modpath.."/messages.lua") |  | ||||||
| loadfile(modpath.."/hooks.lua")(ie) |  | ||||||
| dofile(modpath.."/callback.lua") |  | ||||||
| dofile(modpath.."/chatcmds.lua") |  | ||||||
| dofile(modpath.."/botcmds.lua") |  | ||||||
|  |  | ||||||
| -- Restore old (safe) functions |  | ||||||
| require = old_require |  | ||||||
| rawset(_G, "module", old_module) |  | ||||||
|  |  | ||||||
| if irc.config.enable_player_part then |  | ||||||
| 	dofile(modpath.."/player_part.lua") |  | ||||||
| else |  | ||||||
| 	setmetatable(irc.joined_players, {__index = function() return true end}) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| minetest.register_privilege("irc_admin", { |  | ||||||
| 	description = "Allow IRC administrative tasks to be performed.", |  | ||||||
| 	give_to_singleplayer = true, |  | ||||||
| 	give_to_admin = true, |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| local stepnum = 0 |  | ||||||
|  |  | ||||||
| minetest.register_globalstep(function(dtime) return irc.step(dtime) end) |  | ||||||
|  |  | ||||||
| function irc.step() |  | ||||||
| 	if stepnum == 3 then |  | ||||||
| 		if irc.config.auto_connect then |  | ||||||
| 			irc.connect() |  | ||||||
| 		end |  | ||||||
| 	end |  | ||||||
| 	stepnum = stepnum + 1 |  | ||||||
|  |  | ||||||
| 	if not irc.connected then return end |  | ||||||
|  |  | ||||||
| 	-- Hooks will manage incoming messages and errors |  | ||||||
| 	local good, err = xpcall(function() irc.conn:think() end, debug.traceback) |  | ||||||
| 	if not good then |  | ||||||
| 		print(err) |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.connect() |  | ||||||
| 	if irc.connected then |  | ||||||
| 		minetest.log("error", "IRC: Ignoring attempt to connect when already connected.") |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
| 	irc.conn = irc.lib.new({ |  | ||||||
| 		nick = irc.config.nick, |  | ||||||
| 		username = irc.config.username, |  | ||||||
| 		realname = irc.config.realname, |  | ||||||
| 	}) |  | ||||||
| 	irc.doHook(irc.conn) |  | ||||||
|  |  | ||||||
| 	-- We need to swap the `require` function again since |  | ||||||
| 	-- LuaIRC `require`s `ssl` if `irc.secure` is true. |  | ||||||
| 	old_require = require |  | ||||||
| 	require = ie.require |  | ||||||
|  |  | ||||||
| 	local good, message = pcall(function() |  | ||||||
| 		irc.conn:connect({ |  | ||||||
| 			host = irc.config.server, |  | ||||||
| 			port = irc.config.port, |  | ||||||
| 			password = irc.config.password, |  | ||||||
| 			timeout = irc.config.timeout, |  | ||||||
| 			reconnect = irc.config.reconnect, |  | ||||||
| 			secure = irc.config.secure |  | ||||||
| 		}) |  | ||||||
| 	end) |  | ||||||
|  |  | ||||||
| 	require = old_require |  | ||||||
|  |  | ||||||
| 	if not good then |  | ||||||
| 		minetest.log("error", ("IRC: Connection error: %s: %s -- Reconnecting in %d seconds...") |  | ||||||
| 					:format(irc.config.server, message, irc.config.reconnect)) |  | ||||||
| 		minetest.after(irc.config.reconnect, function() irc.connect() end) |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	if irc.config.NSPass then |  | ||||||
| 		irc.conn:queue(irc.msgs.privmsg( |  | ||||||
| 				"NickServ", "IDENTIFY "..irc.config.NSPass)) |  | ||||||
| 	end |  | ||||||
|  |  | ||||||
| 	irc.conn:join(irc.config.channel, irc.config.key) |  | ||||||
| 	irc.connected = true |  | ||||||
| 	minetest.log("action", "IRC: Connected!") |  | ||||||
| 	minetest.chat_send_all("IRC: Connected!") |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.disconnect(message) |  | ||||||
| 	if irc.connected then |  | ||||||
| 		--The OnDisconnect hook will clear irc.connected and print a disconnect message |  | ||||||
| 		irc.conn:disconnect(message) |  | ||||||
| 	end |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.say(to, message) |  | ||||||
| 	if not message then |  | ||||||
| 		message = to |  | ||||||
| 		to = irc.config.channel |  | ||||||
| 	end |  | ||||||
| 	to = to or irc.config.channel |  | ||||||
|  |  | ||||||
| 	irc.queue(irc.msgs.privmsg(to, message)) |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.reply(message) |  | ||||||
| 	if not irc.last_from then |  | ||||||
| 		return |  | ||||||
| 	end |  | ||||||
| 	message = message:gsub("[\r\n%z]", " \\n ") |  | ||||||
| 	irc.say(irc.last_from, message) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.send(msg) |  | ||||||
| 	if not irc.connected then return end |  | ||||||
| 	irc.conn:send(msg) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.queue(msg) |  | ||||||
| 	if not irc.connected then return end |  | ||||||
| 	irc.conn:queue(msg) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| minetest.log("action", "[irc] loaded.") |  | ||||||
							
								
								
									
										1
									
								
								irc
									
									
									
									
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								irc
									
									
									
									
									
								
							 Submodule irc deleted from b8d594e651
									
								
							
							
								
								
									
										17
									
								
								messages.lua
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								messages.lua
									
									
									
									
									
								
							| @@ -1,17 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
| irc.msgs = irc.lib.msgs |  | ||||||
|  |  | ||||||
| function irc.logChat(message) |  | ||||||
| 	minetest.log("action", "IRC CHAT: "..message) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.sendLocal(message) |  | ||||||
| 	minetest.chat_send_all(minetest.colorize(irc.config.chat_color, message)) |  | ||||||
| 	irc.logChat(message) |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.playerMessage(name, message) |  | ||||||
| 	return ("<%s> %s"):format(name, message) |  | ||||||
| end |  | ||||||
							
								
								
									
										5
									
								
								mod.conf
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								mod.conf
									
									
									
									
									
								
							| @@ -1,5 +0,0 @@ | |||||||
| name = irc |  | ||||||
| description = """ |  | ||||||
| This mod is just a glue between IRC and Minetest. |  | ||||||
| It provides two-way communication between the in-game chat, and an arbitrary IRC channel. |  | ||||||
| """ |  | ||||||
| @@ -1,72 +0,0 @@ | |||||||
| -- This file is licensed under the terms of the BSD 2-clause license. |  | ||||||
| -- See LICENSE.txt for details. |  | ||||||
|  |  | ||||||
|  |  | ||||||
| function irc.player_part(name) |  | ||||||
| 	if not irc.joined_players[name] then |  | ||||||
| 		return false, "You are not in the channel" |  | ||||||
| 	end |  | ||||||
| 	irc.joined_players[name] = nil |  | ||||||
| 	return true, "You left the channel" |  | ||||||
| end |  | ||||||
|  |  | ||||||
| function irc.player_join(name) |  | ||||||
| 	if irc.joined_players[name] then |  | ||||||
| 		return false, "You are already in the channel" |  | ||||||
| 	elseif not minetest.get_player_by_name(name) then |  | ||||||
| 		return false, "You need to be in-game to join the channel" |  | ||||||
| 	end |  | ||||||
| 	irc.joined_players[name] = true |  | ||||||
| 	return true, "You joined the channel" |  | ||||||
| end |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("join", { |  | ||||||
| 	description = "Join the IRC channel", |  | ||||||
| 	privs = {shout=true}, |  | ||||||
| 	func = function(name) |  | ||||||
| 		return irc.player_join(name) |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("part", { |  | ||||||
| 	description = "Part the IRC channel", |  | ||||||
| 	privs = {shout=true}, |  | ||||||
| 	func = function(name) |  | ||||||
| 		return irc.player_part(name) |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("who", { |  | ||||||
| 	description = "Tell who is currently on the channel", |  | ||||||
| 	privs = {}, |  | ||||||
| 	func = function() |  | ||||||
| 		local out, n = { }, 0 |  | ||||||
| 		for plname in pairs(irc.joined_players) do |  | ||||||
| 			n = n + 1 |  | ||||||
| 			out[n] = plname |  | ||||||
| 		end |  | ||||||
| 		table.sort(out) |  | ||||||
| 		return true, "Players in channel: "..table.concat(out, ", ") |  | ||||||
| 	end |  | ||||||
| }) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_joinplayer(function(player) |  | ||||||
| 	local name = player:get_player_name() |  | ||||||
| 	irc.joined_players[name] = irc.config.auto_join |  | ||||||
| end) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_on_leaveplayer(function(player) |  | ||||||
| 	local name = player:get_player_name() |  | ||||||
| 	irc.joined_players[name] = nil |  | ||||||
| end) |  | ||||||
|  |  | ||||||
| function irc.sendLocal(message) |  | ||||||
| 	for name, _ in pairs(irc.joined_players) do |  | ||||||
| 		minetest.chat_send_player(name, |  | ||||||
| 					minetest.colorize(irc.config.chat_color, message)) |  | ||||||
| 	end |  | ||||||
| 	irc.logChat(message) |  | ||||||
| end |  | ||||||
							
								
								
									
										8
									
								
								quick_install.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										8
									
								
								quick_install.sh
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,8 @@ | |||||||
|  | #! /bin/sh | ||||||
|  |  | ||||||
|  | mkdir -p Build  \ | ||||||
|  | && cd Build     \ | ||||||
|  | && cmake ..     \ | ||||||
|  | && make         \ | ||||||
|  | && cd ..        \ | ||||||
|  | && cp -r Build/irc $1 | ||||||
| @@ -1,75 +0,0 @@ | |||||||
|  |  | ||||||
| [Basic] |  | ||||||
|  |  | ||||||
| # Whether to automatically connect to the server on mod load. |  | ||||||
| # If false, you must use /irc_connect to connect. |  | ||||||
| irc.auto_connect (Auto-connect on load) bool true |  | ||||||
|  |  | ||||||
| # Nickname for the bot. May only contain characters A-Z, 0-9 |  | ||||||
| # '{', '}', '[', ']', '|', '^', '-', or '_'. |  | ||||||
| irc.nick (Bot nickname) string Minetest |  | ||||||
|  |  | ||||||
| # Server to connect to. |  | ||||||
| irc.server (IRC server) string irc.freenode.net |  | ||||||
|  |  | ||||||
| # Server port. |  | ||||||
| # The standard IRC protocol port is 6667 for regular servers, |  | ||||||
| # or 6697 for SSL-enabled servers. |  | ||||||
| # If unsure, leave at 6667. |  | ||||||
| irc.port (IRC server port) int 6667 1 65535 |  | ||||||
|  |  | ||||||
| # Channel the bot joins after connecting. |  | ||||||
| irc.channel (Channel to join) string ##mt-irc-mod |  | ||||||
|  |  | ||||||
| [Authentication] |  | ||||||
|  |  | ||||||
| # Password for authenticating to NickServ. |  | ||||||
| # Leave empty to not authenticate with NickServ. |  | ||||||
| irc.NSPass (NickServ password) string |  | ||||||
|  |  | ||||||
| # IRC server password. |  | ||||||
| # Leave empty for no password. |  | ||||||
| irc.password (Server password) string |  | ||||||
|  |  | ||||||
| # Password for joining the channel. |  | ||||||
| # Leave empty if your channel is not protected. |  | ||||||
| irc.key (Channel key) string |  | ||||||
|  |  | ||||||
| # Enable TLS connection. |  | ||||||
| # Requires LuaSEC <https://github.com/brunoos/luasec>. |  | ||||||
| irc.secure (Use TLS) bool false |  | ||||||
|  |  | ||||||
| # Username for SASL authentication. |  | ||||||
| # Leave empty to use the nickname. |  | ||||||
| irc.sasl.user (SASL username) string |  | ||||||
|  |  | ||||||
| # Password for SASL authentication. |  | ||||||
| # Leave empty to not authenticate via SASL. |  | ||||||
| irc.sasl.pass (SASL password) string |  | ||||||
|  |  | ||||||
| [Advanced] |  | ||||||
|  |  | ||||||
| # Enable this to make the bot send messages when players join |  | ||||||
| # or leave the game server. |  | ||||||
| irc.send_join_part (Send join and part messages) bool true |  | ||||||
|  |  | ||||||
| # Enable this to make the bot send messages when players are kicked. |  | ||||||
| irc.send_kicks (Send kick messages) bool false |  | ||||||
|  |  | ||||||
| # Underlying socket timeout in seconds. |  | ||||||
| irc.timeout (Timeout) int 60 1 |  | ||||||
|  |  | ||||||
| # Time between reconnection attempts, in seconds. |  | ||||||
| irc.reconnect (Reconnect delay) int 600 1 |  | ||||||
|  |  | ||||||
| # Prefix to use for bot commands. |  | ||||||
| irc.command_prefix (Command prefix) string |  | ||||||
|  |  | ||||||
| # Enable debug output. |  | ||||||
| irc.debug (Debug mode) bool false |  | ||||||
|  |  | ||||||
| # Whether to enable players joining and parting the channel. |  | ||||||
| irc.enable_player_part (Allow player join/part) bool true |  | ||||||
|  |  | ||||||
| # Whether to automatically show players in the channel when they join. |  | ||||||
| irc.auto_join (Auto join players) bool true |  | ||||||
| @@ -1,26 +1,24 @@ | |||||||
| IRC Mod API | IRC Mod API | ||||||
| =========== | ----------- | ||||||
| 
 |  | ||||||
| This file documents the Minetest IRC mod API. | This file documents the Minetest IRC mod API. | ||||||
| 
 | 
 | ||||||
| Basics | BASICS | ||||||
| ------ | ------ | ||||||
| 
 | In order to allow your mod to interface with this mod, you must add 'irc' | ||||||
| In order to allow your mod to interface with this mod, you must add `irc` |  (without the quotes) to your mod's 'depends.txt' file. | ||||||
| to your mod's `depends.txt` file. |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Reference | REFERENCE | ||||||
| --------- | --------- | ||||||
| 
 | 
 | ||||||
| irc.say([name,] message) | mt_irc:say([name, ]message) | ||||||
| Sends <message> to either the channel (if <name> is nil or not specified), | Sends <message> to either the channel (if <name> is nil or not specified), | ||||||
| or to the given user (if <name> is specified). | or to the given user (if <name> is specified). | ||||||
| Example: | Example: | ||||||
| 	irc.say("Hello, Channel!") | 	mt_irc:say("Hello, Channel!") | ||||||
| 	irc.say("john1234", "How are you?") | 	mt_irc:say("john1234", "How are you?") | ||||||
| 
 | 
 | ||||||
| irc.register_bot_command(name, cmdDef) | mt_irc:register_bot_command(name, cmdDef) | ||||||
| 	Registers a new bot command named <name>. | 	Registers a new bot command named <name>. | ||||||
| 	When an user sends a private message to the bot with the command name, the | 	When an user sends a private message to the bot with the command name, the | ||||||
| 	command's function is called. | 	command's function is called. | ||||||
| @@ -28,39 +26,38 @@ irc.register_bot_command(name, cmdDef) | |||||||
| 	cmdDef = { | 	cmdDef = { | ||||||
| 		params = "<param1> ...",      -- A description of the command's parameters | 		params = "<param1> ...",      -- A description of the command's parameters | ||||||
| 		description = "My command",   -- A description of what the command does. (one-liner) | 		description = "My command",   -- A description of what the command does. (one-liner) | ||||||
| 			func = function(user, args) | 		func = function(user, param) | ||||||
| 			-- This function gets called when the command is invoked. | 			-- This function gets called when the command is invoked. | ||||||
| 			-- <user> is a user table for the user that ran the command. | 			-- <user> is a user table for the user that ran the command. | ||||||
| 			--   (See the LuaIRC documentation for details.) | 			--   (See the LuaIRC documentation for details.) | ||||||
| 			--   It contains fields such as 'nick' and 'ident' | 			--   It contains fields such as 'nick' and 'ident' | ||||||
| 				-- <args> is a string of arguments to the command (may be "") | 			-- <param> is a string of parameters to the command (may be "") | ||||||
| 				-- This function should return boolean success and a message. |  | ||||||
| 		end, | 		end, | ||||||
| 	}; | 	}; | ||||||
| 	Example: | 	Example: | ||||||
| 		irc.register_bot_command("hello", { | 	mt_irc:register_bot_command("hello", { | ||||||
| 		params = "", | 		params = "", | ||||||
| 		description = "Greet user", | 		description = "Greet user", | ||||||
| 		func = function(user, param) | 		func = function(user, param) | ||||||
| 				return true, "Hello!" | 			mt_irc:say(user.nick, "Hello!") | ||||||
| 		end, | 		end, | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| irc.joined_players[name] | mt_irc.joined_players[name] | ||||||
| 	This table holds the players who are currently on the channel (may be less | 	This table holds the players who are currently on the channel (may be less | ||||||
| 	than the players in the game). It is modified by the /part and /join chat | 	than the players in the game). It is modified by the /part and /join chat | ||||||
| 	commands. | 	commands. | ||||||
| 	Example: | 	Example: | ||||||
| 	if irc.joined_players["joe"] then | 	if mt_irc.joined_players["joe"] then | ||||||
| 		-- Joe is talking on IRC | 		-- Joe is talking on IRC | ||||||
| 	end | 	end | ||||||
| 
 | 
 | ||||||
| irc.register_hook(name, func) | mt_irc:register_hook(name, func) | ||||||
| 	Registers a function to be called when an event happens. <name> is the name | 	Registers a function to be called when an event happens. <name> is the name | ||||||
| 	of the event, and <func> is the function to be called. See HOOKS below | 	of the event, and <func> is the function to be called. See HOOKS below | ||||||
| 	for more information | 	for more information | ||||||
| 	Example: | 	Example: | ||||||
| 	irc.register_hook("OnSend", function(line) | 	mt_irc:register_hook("OnSend", function(line) | ||||||
| 		print("SEND: "..line) | 		print("SEND: "..line) | ||||||
| 	end) | 	end) | ||||||
| 
 | 
 | ||||||
| @@ -75,15 +72,15 @@ string.expandvars(string, vars) | |||||||
| 	local s = tpl:expandvars({ foo=1, bar="Hello" }) | 	local s = tpl:expandvars({ foo=1, bar="Hello" }) | ||||||
| 	assert(s == "1 Hello $(baz)")  | 	assert(s == "1 Hello $(baz)")  | ||||||
| 
 | 
 | ||||||
| In addition, all the configuration options decribed in `README.txt` are | In addition, all the configuration options decribed in `README.txt' are | ||||||
| available to other mods, though they should be considered read-only. Do | available to other mods, though they should be considered "read only". Do | ||||||
| not modify these settings at runtime or you might crash the server! | not modify these settings at runtime or you will most likely crash the | ||||||
|  | server! | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Hooks | HOOKS | ||||||
| ----- | --------- | ||||||
| 
 | The 'mt_irc:register_hook' function can register functions to be called | ||||||
| The `irc.register_hook` function can register functions to be called |  | ||||||
| when some events happen. The events supported are the same as the LuaIRC | when some events happen. The events supported are the same as the LuaIRC | ||||||
| ones with a few added (mostly for internal use). | ones with a few added (mostly for internal use). | ||||||
| See src/LuaIRC/doc/irc.luadoc for more information. | See src/LuaIRC/doc/irc.luadoc for more information. | ||||||
| @@ -1,17 +1,17 @@ | |||||||
| Copyright (c) 2013, Diego Martinez (kaeza) | Copyright (c) 2013, Diego Martinez (kaeza) | ||||||
| All rights reserved. | All rights reserved. | ||||||
| 
 | 
 | ||||||
| Redistribution and use in source and binary forms, with or without | Redistribution and use in source and binary forms, with or without modification, | ||||||
| modification, are permitted provided that the following conditions are met: | are permitted provided that the following conditions are met: | ||||||
|  - Redistributions of source code must retain the above copyright notice, |  - Redistributions of source code must retain the above copyright notice, | ||||||
|    this list of conditions and the following disclaimer. |    this list of conditions and the following disclaimer. | ||||||
|  - Redistributions in binary form must reproduce the above copyright notice, |  - Redistributions in binary form must reproduce the above copyright notice, | ||||||
|    this list of conditions and the following disclaimer in the documentation |    this list of conditions and the following disclaimer in the documentation | ||||||
|    and/or other materials provided with the distribution. |    and/or other materials provided with the distribution. | ||||||
| 
 | 
 | ||||||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND | ||||||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||||||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||||||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | ||||||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||||||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||||||
							
								
								
									
										1
									
								
								src/LuaIRC
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								src/LuaIRC
									
									
									
									
									
										Submodule
									
								
							 Submodule src/LuaIRC added at bc79606de0
									
								
							
							
								
								
									
										106
									
								
								src/botcmds.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								src/botcmds.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | mt_irc.bot_commands = {} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:bot_command(user, message) | ||||||
|  | 	local pos = message:find(" ", 1, true) | ||||||
|  | 	local cmd, args | ||||||
|  | 	if pos then | ||||||
|  | 		cmd = message:sub(1, pos - 1) | ||||||
|  | 		args = message:sub(pos + 1) | ||||||
|  | 	else | ||||||
|  | 		cmd = message | ||||||
|  | 		args = "" | ||||||
|  | 	end | ||||||
|  |   | ||||||
|  | 	if not self.bot_commands[cmd] then | ||||||
|  | 		self:say(user.nick, "Unknown command '"..cmd.."'. Try `!help'." | ||||||
|  | 			.." Or use @playername <message> to send a private message") | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  |   | ||||||
|  | 	self.bot_commands[cmd].func(user, args) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:register_bot_command(name, def) | ||||||
|  | 	if (not def.func) or (type(def.func) ~= "function") then | ||||||
|  | 		error("Erroneous bot command definition. def.func missing.", 2) | ||||||
|  | 	end | ||||||
|  | 	self.bot_commands[name] = def | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc:register_bot_command("help", { | ||||||
|  | 	params = "<command>", | ||||||
|  | 	description = "Get help about a command", | ||||||
|  | 	func = function(user, args) | ||||||
|  | 		if args == "" then | ||||||
|  | 			mt_irc:say(user.nick, "No command name specified. Use 'list' for a list of cammands") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  |  | ||||||
|  | 		local cmd = mt_irc.bot_commands[args] | ||||||
|  | 		if not cmd then | ||||||
|  | 			mt_irc:say(user.nick, "Unknown command '"..cmdname.."'.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  |  | ||||||
|  | 		local usage = ("Usage: %c%s %s -- %s"):format( | ||||||
|  | 				mt_irc.config.command_prefix, | ||||||
|  | 				args, | ||||||
|  | 				cmd.params or "<no parameters>", | ||||||
|  | 				cmd.description or "<no description>") | ||||||
|  | 		mt_irc:say(user.nick, usage)		 | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc:register_bot_command("list", { | ||||||
|  | 	params = "", | ||||||
|  | 	description = "List available commands.", | ||||||
|  | 	func = function(user, args) | ||||||
|  | 		local cmdlist = "Available commands: " | ||||||
|  | 		for name, cmd in pairs(mt_irc.bot_commands) do | ||||||
|  | 			cmdlist = cmdlist..name..", " | ||||||
|  | 		end | ||||||
|  | 		mt_irc:say(user.nick, cmdlist | ||||||
|  | 			.." -- Use 'help <command name>' to get help about a specific command.") | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc:register_bot_command("whereis", { | ||||||
|  | 	params = "<player>", | ||||||
|  | 	description = "Tell the location of <player>", | ||||||
|  | 	func = function(user, args) | ||||||
|  | 		if args == "" then | ||||||
|  | 			mt_irc:bot_help(user, "whereis") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		local player = minetest.env:get_player_by_name(args) | ||||||
|  | 		if player then | ||||||
|  | 			local fmt = "Player %s is at (%.2f,%.2f,%.2f)" | ||||||
|  | 			local pos = player:getpos() | ||||||
|  | 			mt_irc:say(user.nick, fmt:format(args, pos.x, pos.y, pos.z)) | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		mt_irc:say(user.nick, "There is No player named '"..args.."'") | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | local starttime = os.time() | ||||||
|  | mt_irc:register_bot_command("uptime", { | ||||||
|  | 	description = "Tell how much time the server has been up", | ||||||
|  | 	func = function(user, args) | ||||||
|  | 		local cur_time = os.time() | ||||||
|  | 		local diff = os.difftime(cur_time, starttime) | ||||||
|  | 		local fmt = "Server has been running for %d:%02d:%02d" | ||||||
|  | 		mt_irc:say(user.nick, fmt:format( | ||||||
|  | 			math.floor(diff / 60 / 60), | ||||||
|  | 			math.mod(math.floor(diff / 60), 60), | ||||||
|  | 			math.mod(math.floor(diff), 60) | ||||||
|  | 		)) | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
							
								
								
									
										35
									
								
								src/callback.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/callback.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | -- This file is licensed under the terms of the BSD 2-clause license. | ||||||
|  | -- See LICENSE.txt for details. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_on_joinplayer(function(player) | ||||||
|  | 	local name = player:get_player_name() | ||||||
|  | 	if mt_irc.connected then | ||||||
|  | 		mt_irc:say("*** "..name.." joined the game") | ||||||
|  | 	end | ||||||
|  | end) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_on_leaveplayer(function(player) | ||||||
|  | 	local name = player:get_player_name() | ||||||
|  | 	if mt_irc.connected then | ||||||
|  | 		mt_irc:say("*** "..name.." left the game") | ||||||
|  | 	end | ||||||
|  | end) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_on_chat_message(function(name, message) | ||||||
|  | 	if not mt_irc.connected | ||||||
|  | 	   or message:sub(1, 1) == "/" | ||||||
|  | 	   or not mt_irc.joined_players[name] | ||||||
|  | 	   or (not minetest.check_player_privs(name, {shout=true})) then | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  | 	mt_irc:queueMsg(mt_irc.msgs.playerMessage(mt_irc.config.channel, name, message)) | ||||||
|  | end) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_on_shutdown(function() | ||||||
|  | 	mt_irc:disconnect("Game shutting down.") | ||||||
|  | end) | ||||||
|  |  | ||||||
							
								
								
									
										104
									
								
								src/chatcmds.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/chatcmds.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | |||||||
|  | -- This file is licensed under the terms of the BSD 2-clause license. | ||||||
|  | -- See LICENSE.txt for details. | ||||||
|  |  | ||||||
|  | -- Note: This file does NOT conatin every chat command, only general ones. | ||||||
|  | -- Feature-specific commands (like /join) are in their own files. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_chatcommand("irc_msg", { | ||||||
|  | 	params = "<name> <message>", | ||||||
|  | 	description = "Send a private message to an IRC user", | ||||||
|  | 	privs = {shout=true}, | ||||||
|  | 	func = function(name, param) | ||||||
|  | 		if not mt_irc.connected then | ||||||
|  | 			minetest.chat_send_player(name, "Not connected to IRC. Use /irc_connect to connect.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		local found, _, toname, message = param:find("^([^%s]+)%s(.+)") | ||||||
|  | 		if not found then | ||||||
|  | 			minetest.chat_send_player(name, "Invalid usage, see /help irc_msg.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		local validNick = false | ||||||
|  | 		for nick, user in pairs(mt_irc.conn.channels[mt_irc.config.channel].users) do | ||||||
|  | 			if nick:lower() == toname:lower() then | ||||||
|  | 				validNick = true | ||||||
|  | 				break | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 		if toname:find("Serv|Bot") then | ||||||
|  | 			validNick = false | ||||||
|  | 		end | ||||||
|  | 		if not validNick then | ||||||
|  | 			minetest.chat_send_player(name, | ||||||
|  | 				"You can not message that user. (Hint: They have to be in the channel)") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		mt_irc:queueMsg(mt_irc.msgs.playerMessage(toname, name, message)) | ||||||
|  | 		minetest.chat_send_player(name, "Message sent!") | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_chatcommand("irc_connect", { | ||||||
|  | 	description = "Connect to the IRC server.", | ||||||
|  | 	privs = {irc_admin=true}, | ||||||
|  | 	func = function(name, param) | ||||||
|  | 		if mt_irc.connected then | ||||||
|  | 			minetest.chat_send_player(name, "You are already connected to IRC.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		minetest.chat_send_player(name, "IRC: Connecting...") | ||||||
|  | 		mt_irc:connect() | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_chatcommand("irc_disconnect", { | ||||||
|  | 	description = "Disconnect from the IRC server.", | ||||||
|  | 	privs = {irc_admin=true}, | ||||||
|  | 	func = function(name, param) | ||||||
|  | 		if not mt_irc.connected then | ||||||
|  | 			minetest.chat_send_player(name, "You are not connected to IRC.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		mt_irc:disconnect("Manual disconnect.") | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_chatcommand("irc_reconnect", { | ||||||
|  | 	description = "Reconnect to the IRC server.", | ||||||
|  | 	privs = {irc_admin=true}, | ||||||
|  | 	func = function(name, param) | ||||||
|  | 		if not mt_irc.connected then | ||||||
|  | 			minetest.chat_send_player(name, "You are not connected to IRC.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		mt_irc:disconnect("Reconnecting...") | ||||||
|  | 		mt_irc:connect() | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_chatcommand("irc_quote", { | ||||||
|  | 	params = "<command>", | ||||||
|  | 	description = "Send a raw command to the IRC server.", | ||||||
|  | 	privs = {irc_admin=true}, | ||||||
|  | 	func = function(name, param) | ||||||
|  | 		if not mt_irc.connected then | ||||||
|  | 			minetest.chat_send_player(name, "You are not connected to IRC.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		mt_irc:queueMsg(param) | ||||||
|  | 		minetest.chat_send_player(name, "Command sent!") | ||||||
|  | 	end | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | local oldme = minetest.chatcommands["me"].func | ||||||
|  | minetest.chatcommands["me"].func = function(name, param) | ||||||
|  | 	oldme(name, param) | ||||||
|  | 	mt_irc:say(("* %s %s"):format(name, param)) | ||||||
|  | end | ||||||
|  |  | ||||||
							
								
								
									
										91
									
								
								src/config.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/config.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | |||||||
|  | -- This file is licensed under the terms of the BSD 2-clause license. | ||||||
|  | -- See LICENSE.txt for details. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | local config = {} | ||||||
|  |  | ||||||
|  | ------------------------- | ||||||
|  | -- BASIC USER SETTINGS -- | ||||||
|  | ------------------------- | ||||||
|  |  | ||||||
|  | -- Nickname (string, default "minetest-"..<server-id>) | ||||||
|  | --  (<server-id> is a random string of 6 hexidecimal numbers). | ||||||
|  | config.nick = minetest.setting_get("irc.nick") | ||||||
|  |  | ||||||
|  | -- Server to connect on joinplayer (string, default "irc.freenode.net") | ||||||
|  | config.server = minetest.setting_get("irc.server") or "irc.freenode.net" | ||||||
|  |  | ||||||
|  | -- Port to connect on joinplayer (number, default 6667) | ||||||
|  | config.port = tonumber(minetest.setting_get("irc.port")) or 6667 | ||||||
|  |  | ||||||
|  | -- NickServ password | ||||||
|  | config.NSPass = minetest.setting_get("irc.NSPass") | ||||||
|  |  | ||||||
|  | -- SASL password (Blank to disable SASL authentication) | ||||||
|  | config.SASLPass = minetest.setting_get("irc.SASLPass") | ||||||
|  |  | ||||||
|  | -- Channel to connect on joinplayer (string, default "##mt-irc-mod") | ||||||
|  | config.channel = minetest.setting_get("irc.channel") or "##mt-irc-mod" | ||||||
|  |  | ||||||
|  | -- Key for the channel (string, default nil) | ||||||
|  | config.key = minetest.setting_get("irc.key") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ----------------------- | ||||||
|  | -- ADVANCED SETTINGS -- | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | -- Server password (string, default "") | ||||||
|  | config.password = minetest.setting_get("irc.password") | ||||||
|  |  | ||||||
|  | -- SASL username | ||||||
|  | config.SASLUser = minetest.setting_get("irc.SASLUser") or config.nick | ||||||
|  |  | ||||||
|  | -- Enable a TLS connection, requires LuaSEC (bool, default false) | ||||||
|  | config.secure = minetest.setting_getbool("irc.secure") | ||||||
|  |  | ||||||
|  | -- Time between chat updates in seconds (number, default 2.1). Setting this too low can cause "Excess flood" disconnects. | ||||||
|  | config.interval = tonumber(minetest.setting_get("irc.interval")) or 2.0 | ||||||
|  |  | ||||||
|  | -- Underlying socket timeout in seconds (number, default 60.0). | ||||||
|  | config.timeout = tonumber(minetest.setting_get("irc.timeout")) or 60.0 | ||||||
|  |  | ||||||
|  | -- Prefix to use for bot commands (char, default '!') | ||||||
|  | config.command_prefix = minetest.setting_get("irc.command_prefix") or '!' | ||||||
|  | config.command_prefix = config.command_prefix:sub(1, 1) | ||||||
|  |  | ||||||
|  | -- The format of messages sent to IRC server (string, default "<$(name)> $(message)") | ||||||
|  | -- See `README.txt' for the macros supported here. | ||||||
|  | config.format_out = minetest.setting_get("irc.format_out") or "<$(name)> $(message)" | ||||||
|  |  | ||||||
|  | -- The format of messages sent to IRC server (string, default "<$(name)@IRC> $(message)") | ||||||
|  | -- See `README.txt' for the macros supported here. | ||||||
|  | config.format_in = minetest.setting_get("irc.format_in") or "<$(name)@IRC> $(message)" | ||||||
|  |  | ||||||
|  | -- Enable debug output (boolean, default false) | ||||||
|  | config.debug = minetest.setting_getbool("irc.debug") | ||||||
|  |  | ||||||
|  | -- Whether to enable players joining and parting the channel | ||||||
|  | config.enable_player_part = not minetest.setting_getbool("irc.disable_player_part") | ||||||
|  |  | ||||||
|  | -- Whether to automatically join the channel when player joins | ||||||
|  | --  (boolean, default true) | ||||||
|  | config.auto_join = not minetest.setting_getbool("irc.disable_auto_join") | ||||||
|  |  | ||||||
|  | -- Whether to automatically connect to the server on mod load | ||||||
|  | --  (boolean, default true) | ||||||
|  | config.auto_connect = not minetest.setting_getbool("irc.disable_auto_connect") | ||||||
|  |  | ||||||
|  | -- Set default server nick if not specified. | ||||||
|  | if not config.nick then | ||||||
|  | 	local pr = PseudoRandom(os.time()) | ||||||
|  | 	-- Workaround for bad distribution in minetest PRNG implementation. | ||||||
|  | 	config.nick = ("MT-%02X%02X%02X"):format( | ||||||
|  | 		pr:next(0, 255), | ||||||
|  | 		pr:next(0, 255), | ||||||
|  | 		pr:next(0, 255) | ||||||
|  | 	) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | mt_irc.config = config | ||||||
|  |  | ||||||
							
								
								
									
										211
									
								
								src/hooks.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								src/hooks.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | |||||||
|  | -- This file is licensed under the terms of the BSD 2-clause license. | ||||||
|  | -- See LICENSE.txt for details. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc.hooks = {} | ||||||
|  | mt_irc.registered_hooks = {} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:doHook(conn) | ||||||
|  | 	for name, hook in pairs(self.registered_hooks) do | ||||||
|  | 		for _, func in pairs(hook) do | ||||||
|  | 			conn:hook(name, func) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:register_hook(name, func) | ||||||
|  | 	self.registered_hooks[name] = self.registered_hooks[name] or {} | ||||||
|  | 	table.insert(self.registered_hooks[name], func) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.raw(line) | ||||||
|  | 	if mt_irc.config.debug then | ||||||
|  | 		print("RECV: "..line) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.send(line) | ||||||
|  | 	if mt_irc.config.debug then | ||||||
|  | 		print("SEND: "..line) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.chat(user, channel, message) | ||||||
|  | 	-- Strip bold, underline, and colors | ||||||
|  | 	message = message:gsub('\2', '') | ||||||
|  | 	message = message:gsub('\31', '') | ||||||
|  | 	message = message:gsub('\3[0-9][0-9,]*', '') | ||||||
|  | 	if channel == mt_irc.conn.nick then | ||||||
|  | 		mt_irc.conn:invoke("PrivateMessage", user, message) | ||||||
|  | 	else | ||||||
|  | 		local c = string.char(1) | ||||||
|  | 		local found, _, action = message:find(("^%sACTION ([^%s]*)%s$"):format(c, c, c)) | ||||||
|  | 		if found then | ||||||
|  | 			mt_irc.conn:invoke("OnChannelAction", user, channel, action) | ||||||
|  | 		else | ||||||
|  | 			mt_irc.conn:invoke("OnChannelChat", user, channel, message) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.channelChat(user, channel, message) | ||||||
|  | 	local t = { | ||||||
|  | 		access=user.access, | ||||||
|  | 		name=user.nick, | ||||||
|  | 		message=message, | ||||||
|  | 		server=mt_irc.conn.host, | ||||||
|  | 		port=mt_irc.conn.port, | ||||||
|  | 		channel=channel | ||||||
|  | 	} | ||||||
|  | 	local text = mt_irc.config.format_in:expandvars(t) | ||||||
|  | 	mt_irc:sendLocal(text) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.pm(user, message) | ||||||
|  | 	local player_to | ||||||
|  | 	local msg | ||||||
|  | 	if message:sub(1, 1) == "@" then | ||||||
|  | 		local found, _, player_to, message = message:find("^.([^%s]+)%s(.+)$") | ||||||
|  | 		if not mt_irc.joined_players[player_to] then | ||||||
|  | 			mt_irc:say(user.nick, "User '"..player_to.."' has parted.") | ||||||
|  | 			return | ||||||
|  | 		elseif not minetest.get_player_by_name(player_to) then | ||||||
|  | 			mt_irc:say(user.nick, "User '"..player_to.."' is not in the game.") | ||||||
|  | 			return | ||||||
|  | 		end | ||||||
|  | 		local t = { | ||||||
|  | 			name=user.nick, | ||||||
|  | 			message=message, | ||||||
|  | 			server=mt_irc.server, | ||||||
|  | 			port=mt_irc.port, | ||||||
|  | 			channel=mt_irc.channel | ||||||
|  | 		} | ||||||
|  | 		local text = mt_irc.config.format_in:expandvars(t) | ||||||
|  | 		minetest.chat_send_player(player_to, "PM: "..text, false) | ||||||
|  | 		mt_irc:say(user.nick, "Message sent!") | ||||||
|  | 	elseif message:sub(1, 1) == "!" then | ||||||
|  | 		mt_irc:bot_command(user, message:sub(2)) | ||||||
|  | 		return | ||||||
|  | 	else | ||||||
|  | 		mt_irc:say(user.nick, "Invalid command. Use '" | ||||||
|  | 				..mt_irc.config.command_prefix | ||||||
|  | 				.."list' to see possible commands.") | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.kick(channel, target, prefix, reason) | ||||||
|  | 	if target == mt_irc.conn.nick then | ||||||
|  | 		minetest.chat_send_all("IRC: kicked from "..channel.." by "..prefix.nick..".") | ||||||
|  | 		mt_irc:disconnect("Kicked") | ||||||
|  | 	else | ||||||
|  | 		mt_irc:sendLocal(("-!- %s was kicked from %s by %s [%s]") | ||||||
|  | 				:format(target, channel, prefix.nick, reason)) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.notice(user, target, message) | ||||||
|  | 	if not user.nick then return end --Server NOTICEs | ||||||
|  | 	if target == mt_irc.conn.nick then return end | ||||||
|  | 	mt_irc:sendLocal("--"..user.nick.."@IRC-- "..message) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.mode(user, target, modes, ...) | ||||||
|  | 	local by = "" | ||||||
|  | 	if user.nick then | ||||||
|  | 		by = " by "..user.nick | ||||||
|  | 	end | ||||||
|  | 	local options = "" | ||||||
|  | 	for _, option in pairs({...}) do | ||||||
|  | 		options = options.." "..option | ||||||
|  | 	end | ||||||
|  | 	minetest.chat_send_all(("-!- mode/%s [%s%s]%s") | ||||||
|  | 			:format(target, modes, options, by)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.nick(user, newNick) | ||||||
|  | 	mt_irc:sendLocal(("-!- %s is now known as %s") | ||||||
|  | 			:format(user.nick, newNick)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.join(user, channel) | ||||||
|  | 	mt_irc:sendLocal(("-!- %s joined %s") | ||||||
|  | 			:format(user.nick, channel)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.part(user, channel, reason) | ||||||
|  | 	reason = reason or "" | ||||||
|  | 	mt_irc:sendLocal(("-!- %s has left %s [%s]") | ||||||
|  | 			:format(user.nick, channel, reason)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.quit(user, reason) | ||||||
|  | 	mt_irc:sendLocal(("-!- %s has quit [%s]") | ||||||
|  | 			:format(user.nick, reason)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.action(user, channel, message) | ||||||
|  | 	mt_irc:sendLocal(("* %s@IRC %s") | ||||||
|  | 			:format(user.nick, message)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.disconnect(message, isError) | ||||||
|  | 	mt_irc.connected = false | ||||||
|  | 	if isError then | ||||||
|  | 		minetest.log("error",  "IRC: Error: Disconnected, reconnecting in one minute.") | ||||||
|  | 		minetest.chat_send_all("IRC: Error: Disconnected, reconnecting in one minute.") | ||||||
|  | 		minetest.after(60, mt_irc.connect) | ||||||
|  | 	else | ||||||
|  | 		minetest.log("action", "IRC: Disconnected.") | ||||||
|  | 		minetest.chat_send_all("IRC: Disconnected.") | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc.hooks.preregister(conn) | ||||||
|  | 	if not (mt_irc.config.SASLUser and mt_irc.config.SASLPass) then return end | ||||||
|  | 	local authString = mt_irc.b64e( | ||||||
|  | 		("%s\x00%s\x00%s"):format( | ||||||
|  | 		mt_irc.config.SASLUser, | ||||||
|  | 		mt_irc.config.SASLUser, | ||||||
|  | 		mt_irc.config.SASLPass) | ||||||
|  | 	) | ||||||
|  | 	conn:send("CAP REQ sasl") | ||||||
|  | 	conn:send("AUTHENTICATE PLAIN") | ||||||
|  | 	conn:send("AUTHENTICATE "..authString) | ||||||
|  | 	--LuaIRC will send CAP END | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc:register_hook("PreRegister",     mt_irc.hooks.preregister) | ||||||
|  | mt_irc:register_hook("OnRaw",           mt_irc.hooks.raw) | ||||||
|  | mt_irc:register_hook("OnSend",          mt_irc.hooks.send) | ||||||
|  | mt_irc:register_hook("OnChat",          mt_irc.hooks.chat) | ||||||
|  | mt_irc:register_hook("OnPart",          mt_irc.hooks.part) | ||||||
|  | mt_irc:register_hook("OnKick",          mt_irc.hooks.kick) | ||||||
|  | mt_irc:register_hook("OnJoin",          mt_irc.hooks.join) | ||||||
|  | mt_irc:register_hook("OnQuit",          mt_irc.hooks.quit) | ||||||
|  | mt_irc:register_hook("NickChange",      mt_irc.hooks.nick) | ||||||
|  | mt_irc:register_hook("OnChannelAction", mt_irc.hooks.action) | ||||||
|  | mt_irc:register_hook("PrivateMessage",  mt_irc.hooks.pm) | ||||||
|  | mt_irc:register_hook("OnNotice",        mt_irc.hooks.notice) | ||||||
|  | mt_irc:register_hook("OnChannelChat",   mt_irc.hooks.channelChat) | ||||||
|  | mt_irc:register_hook("OnModeChange",    mt_irc.hooks.mode) | ||||||
|  | mt_irc:register_hook("OnDisconnect",    mt_irc.hooks.disconnect) | ||||||
|  |  | ||||||
							
								
								
									
										144
									
								
								src/init.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										144
									
								
								src/init.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,144 @@ | |||||||
|  | -- This file is licensed under the terms of the BSD 2-clause license. | ||||||
|  | -- See LICENSE.txt for details. | ||||||
|  |  | ||||||
|  |  | ||||||
|  | mt_irc = { | ||||||
|  | 	connected = false, | ||||||
|  | 	cur_time = 0, | ||||||
|  | 	message_buffer = {}, | ||||||
|  | 	recent_message_count = 0, | ||||||
|  | 	joined_players = {}, | ||||||
|  | 	modpath = minetest.get_modpath("irc") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | -- To find LuaIRC and LuaSocket | ||||||
|  | package.path = mt_irc.modpath.."/?/init.lua;" | ||||||
|  | 		..mt_irc.modpath.."/irc/?.lua;" | ||||||
|  | 		..mt_irc.modpath.."/?.lua;" | ||||||
|  | 		..package.path | ||||||
|  | package.cpath = mt_irc.modpath.."/lib?.so;" | ||||||
|  | 		..mt_irc.modpath.."/?.dll;" | ||||||
|  | 		..package.cpath | ||||||
|  |  | ||||||
|  | local irc = require('irc') | ||||||
|  |  | ||||||
|  | dofile(mt_irc.modpath.."/config.lua") | ||||||
|  | dofile(mt_irc.modpath.."/messages.lua") | ||||||
|  | dofile(mt_irc.modpath.."/hooks.lua") | ||||||
|  | dofile(mt_irc.modpath.."/callback.lua") | ||||||
|  | dofile(mt_irc.modpath.."/chatcmds.lua") | ||||||
|  | dofile(mt_irc.modpath.."/botcmds.lua") | ||||||
|  | dofile(mt_irc.modpath.."/util.lua") | ||||||
|  | if mt_irc.config.enable_player_part then | ||||||
|  | 	dofile(mt_irc.modpath.."/player_part.lua") | ||||||
|  | else | ||||||
|  | 	setmetatable(mt_irc.joined_players, {__index = function(index) return true end}) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | minetest.register_privilege("irc_admin", { | ||||||
|  | 	description = "Allow IRC administrative tasks to be performed.", | ||||||
|  | 	give_to_singleplayer = true | ||||||
|  | }) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | minetest.register_globalstep(function(dtime) return mt_irc:step(dtime) end) | ||||||
|  |  | ||||||
|  | function mt_irc:step(dtime) | ||||||
|  | 	if not self.connected then return end | ||||||
|  |  | ||||||
|  | 	-- Tick down the recent message count | ||||||
|  | 	self.cur_time = self.cur_time + dtime | ||||||
|  | 	if self.cur_time >= self.config.interval then | ||||||
|  | 		if self.recent_message_count > 0 then | ||||||
|  | 			self.recent_message_count = self.recent_message_count - 1 | ||||||
|  | 		end | ||||||
|  | 		self.cur_time = self.cur_time - self.config.interval | ||||||
|  | 	end | ||||||
|  |  | ||||||
|  | 	-- Hooks will manage incoming messages and errors | ||||||
|  | 	if not pcall(function() mt_irc.conn:think() end) then | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  |  | ||||||
|  | 	-- Send messages in the buffer | ||||||
|  | 	if #self.message_buffer > 10 then | ||||||
|  | 		minetest.log("error", "IRC: Message buffer overflow, clearing.") | ||||||
|  | 		self.message_buffer = {} | ||||||
|  | 	elseif #self.message_buffer > 0 then | ||||||
|  | 		for i=1, #self.message_buffer do | ||||||
|  | 			if self.recent_message_count > 4 then break end | ||||||
|  | 			self.recent_message_count = self.recent_message_count + 1 | ||||||
|  | 			local msg = table.remove(self.message_buffer, 1) --Pop the first message | ||||||
|  | 			self:send(msg) | ||||||
|  | 		end | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:connect() | ||||||
|  | 	if self.connected then | ||||||
|  | 		minetest.log("error", "IRC: Ignoring attempt to connect when already connected.") | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  | 	self.conn = irc.new({ | ||||||
|  | 		nick = self.config.nick, | ||||||
|  | 		username = "Minetest", | ||||||
|  | 		realname = "Minetest", | ||||||
|  | 	}) | ||||||
|  | 	self:doHook(self.conn) | ||||||
|  | 	good, message = pcall(function() | ||||||
|  | 		mt_irc.conn:connect({ | ||||||
|  | 			host = mt_irc.config.server, | ||||||
|  | 			port = mt_irc.config.port, | ||||||
|  | 			pass = mt_irc.config.password, | ||||||
|  | 			timeout = mt_irc.config.timeout, | ||||||
|  | 			secure = mt_irc.config.secure | ||||||
|  | 		}) | ||||||
|  | 	end) | ||||||
|  |  | ||||||
|  | 	if not good then | ||||||
|  | 		minetest.log("error", ("IRC: Connection error: %s: %s -- Reconnecting in ten minutes...") | ||||||
|  | 					:format(self.config.server, message)) | ||||||
|  | 		minetest.after(600, function() mt_irc:connect() end) | ||||||
|  | 		return | ||||||
|  | 	end | ||||||
|  |  | ||||||
|  | 	if self.config.NSPass then | ||||||
|  | 		self:say("NickServ", "IDENTIFY "..self.config.NSPass) | ||||||
|  | 	end | ||||||
|  |  | ||||||
|  | 	self.conn:join(self.config.channel, self.config.key) | ||||||
|  | 	self.connected = true | ||||||
|  | 	minetest.log("action", "IRC: Connected!") | ||||||
|  | 	minetest.chat_send_all("IRC: Connected!") | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:disconnect(message) | ||||||
|  | 	if self.connected then | ||||||
|  | 		--The OnDisconnect hook will clear self.connected and print a disconnect message | ||||||
|  | 		self.conn:disconnect(message) | ||||||
|  | 	end | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:say(to, message) | ||||||
|  | 	if not message then | ||||||
|  | 		message = to | ||||||
|  | 		to = self.config.channel | ||||||
|  | 	end | ||||||
|  | 	to = to or self.config.channel | ||||||
|  |  | ||||||
|  | 	self:queueMsg(self.msgs.privmsg(to, message)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | function mt_irc:send(line) | ||||||
|  | 	self.conn:send(line) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | if mt_irc.config.auto_connect then | ||||||
|  | 	mt_irc:connect() | ||||||
|  | end | ||||||
|  |  | ||||||
							
								
								
									
										16
									
								
								src/lua/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/lua/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  |  | ||||||
|  | # :mode=cmake:noTabs=true:tabSize=4: | ||||||
|  |  | ||||||
|  | set(LUA_SRCS | ||||||
|  |     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 | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | add_library(lua_lib ${LUA_SRCS}) | ||||||
|  | set_target_properties(lua_lib | ||||||
|  |     PROPERTIES | ||||||
|  |     OUTPUT_NAME lua51-minetest-irc | ||||||
|  | ) | ||||||
							
								
								
									
										1077
									
								
								src/lua/lapi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1077
									
								
								src/lua/lapi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										16
									
								
								src/lua/lapi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/lua/lapi.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lapi.h,v 2.2 2005/04/25 19:24:10 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 | ||||||
							
								
								
									
										647
									
								
								src/lua/lauxlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										647
									
								
								src/lua/lauxlib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,647 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lauxlib.c,v 1.158 2006/01/16 12:42:21 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); | ||||||
|  |   lua_getfield(L, LUA_REGISTRYINDEX, tname);  /* get correct metatable */ | ||||||
|  |   if (p == NULL || !lua_getmetatable(L, ud) || !lua_rawequal(L, -1, -2)) | ||||||
|  |     luaL_typerror(L, ud, tname); | ||||||
|  |   lua_pop(L, 2);  /* remove both metatables */ | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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", size); | ||||||
|  |     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, LUAL_BUFFERSIZE, 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] && lf.f != stdin) {  /* binary file? */ | ||||||
|  |     fclose(lf.f); | ||||||
|  |     lf.f = fopen(filename, "rb");  /* 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 (lf.f != stdin) 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; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										172
									
								
								src/lua/lauxlib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								src/lua/lauxlib.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lauxlib.h,v 1.87 2005/12/29 15:32:11 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, 0, 0)) | ||||||
|  |  | ||||||
|  | #define luaL_dostring(L, s)	(luaL_loadstring(L, s) || lua_pcall(L, 0, 0, 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 | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										643
									
								
								src/lua/lbaselib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										643
									
								
								src/lua/lbaselib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,643 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lbaselib.c,v 1.189 2006/01/18 11:49:12 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) { | ||||||
|  |   if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); | ||||||
|  |   else { | ||||||
|  |     lua_Debug ar; | ||||||
|  |     int level = luaL_optint(L, 1, 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); | ||||||
|  |   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); | ||||||
|  |   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)); | ||||||
|  |   n = e - i + 1;  /* number of elements */ | ||||||
|  |   if (n <= 0) return 0;  /* empty range */ | ||||||
|  |   luaL_checkstack(L, n, "table too big to unpack"); | ||||||
|  |   for (; i<=e; i++)  /* push arg[i...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 | ||||||
|  | ** ======================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | static int auxresume (lua_State *L, lua_State *co, int narg) { | ||||||
|  |   int status; | ||||||
|  |   if (!lua_checkstack(co, narg)) | ||||||
|  |     luaL_error(L, "too many arguments to resume"); | ||||||
|  |   if (lua_status(co) == 0 && lua_gettop(co) == 0) { | ||||||
|  |     lua_pushliteral(L, "cannot resume dead coroutine"); | ||||||
|  |     return -1;  /* error flag */ | ||||||
|  |   } | ||||||
|  |   lua_xmove(L, co, narg); | ||||||
|  |   status = lua_resume(co, narg); | ||||||
|  |   if (status == 0 || status == LUA_YIELD) { | ||||||
|  |     int nres = lua_gettop(co); | ||||||
|  |     if (!lua_checkstack(L, nres)) | ||||||
|  |       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_costatus (lua_State *L) { | ||||||
|  |   lua_State *co = lua_tothread(L, 1); | ||||||
|  |   luaL_argcheck(L, co, 1, "coroutine expected"); | ||||||
|  |   if (L == co) lua_pushliteral(L, "running"); | ||||||
|  |   else { | ||||||
|  |     switch (lua_status(co)) { | ||||||
|  |       case LUA_YIELD: | ||||||
|  |         lua_pushliteral(L, "suspended"); | ||||||
|  |         break; | ||||||
|  |       case 0: { | ||||||
|  |         lua_Debug ar; | ||||||
|  |         if (lua_getstack(co, 0, &ar) > 0)  /* does it have frames? */ | ||||||
|  |           lua_pushliteral(L, "normal");  /* it is running */ | ||||||
|  |         else if (lua_gettop(co) == 0) | ||||||
|  |             lua_pushliteral(L, "dead"); | ||||||
|  |         else | ||||||
|  |           lua_pushliteral(L, "suspended");  /* initial state */ | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       default:  /* some error occured */ | ||||||
|  |         lua_pushliteral(L, "dead"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int luaB_corunning (lua_State *L) { | ||||||
|  |   if (lua_pushthread(L)) | ||||||
|  |     return 0;  /* main thread is not a coroutine */ | ||||||
|  |   else | ||||||
|  |     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; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										825
									
								
								src/lua/lcode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										825
									
								
								src/lua/lcode.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,825 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lcode.c,v 2.24 2005/12/22 16:19:56 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? */ | ||||||
|  |       return;  /* positions are already clean */ | ||||||
|  |     if (GET_OPCODE(*(previous = &fs->f->code[fs->pc-1])) == 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 o1 = luaK_exp2RK(fs, e1); | ||||||
|  |     int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; | ||||||
|  |     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 (e->k == VK) | ||||||
|  |         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; | ||||||
|  |     } | ||||||
|  |     default: { | ||||||
|  |       if (!isnumeral(v)) 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, &e1->f, e2->f); | ||||||
|  |       e1->k = e2->k; e1->u.s.info = e2->u.s.info; | ||||||
|  |       e1->u.s.aux = e2->u.s.aux; e1->t = e2->t; | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case OPR_OR: { | ||||||
|  |       lua_assert(e1->f == NO_JUMP);  /* list must be closed */ | ||||||
|  |       luaK_dischargevars(fs, e2); | ||||||
|  |       luaK_concat(fs, &e1->t, e2->t); | ||||||
|  |       e1->k = e2->k; e1->u.s.info = e2->u.s.info; | ||||||
|  |       e1->u.s.aux = e2->u.s.aux; e1->f = e2->f; | ||||||
|  |       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 = e2->k; 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 */ | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										77
									
								
								src/lua/lcode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/lua/lcode.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,77 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lcode.h,v 1.47 2005/11/08 19:44:31 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; | ||||||
|  |  | ||||||
|  | #define binopistest(op)	((op) >= OPR_NE) | ||||||
|  |  | ||||||
|  | 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
									
								
								src/lua/ldblib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										397
									
								
								src/lua/ldblib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,397 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldblib.c,v 1.104 2005/12/29 15:32:11 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; | ||||||
|  |   lua_State *L1 = getthread(L, &arg); | ||||||
|  |   if (lua_isnoneornil(L, arg+1)) { | ||||||
|  |     lua_settop(L, arg+1); | ||||||
|  |     lua_sethook(L1, NULL, 0, 0);  /* turn off hooks */ | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     const char *smask = luaL_checkstring(L, arg+2); | ||||||
|  |     int count = luaL_optint(L, arg+3, 0); | ||||||
|  |     luaL_checktype(L, arg+1, LUA_TFUNCTION); | ||||||
|  |     lua_sethook(L1, hookf, makemask(smask, count), count); | ||||||
|  |   } | ||||||
|  |   gethooktable(L1); | ||||||
|  |   lua_pushlightuserdata(L1, L1); | ||||||
|  |   lua_pushvalue(L, arg+1); | ||||||
|  |   lua_xmove(L, L1, 1); | ||||||
|  |   lua_rawset(L1, -3);  /* set new hook */ | ||||||
|  |   lua_pop(L1, 1);  /* remove hook table */ | ||||||
|  |   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(L1); | ||||||
|  |     lua_pushlightuserdata(L1, L1); | ||||||
|  |     lua_rawget(L1, -2);   /* get hook */ | ||||||
|  |     lua_remove(L1, -2);  /* remove hook table */ | ||||||
|  |     lua_xmove(L1, L, 1); | ||||||
|  |   } | ||||||
|  |   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; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										620
									
								
								src/lua/ldebug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										620
									
								
								src/lua/ldebug.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,620 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldebug.c,v 2.29 2005/12/22 16:19:56 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); | ||||||
|  |   lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); | ||||||
|  |   lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) || | ||||||
|  |               (pt->is_vararg & VARARG_HASARG)); | ||||||
|  |   check(pt->sizeupvalues <= pt->nups); | ||||||
|  |   check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); | ||||||
|  |   check(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) { | ||||||
|  |             /* cannot jump to a setlist count */ | ||||||
|  |             Instruction d = pt->code[dest-1]; | ||||||
|  |             check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 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: { | ||||||
|  |         check(c == 0 || pc+2 < pt->sizecode);  /* check its jump */ | ||||||
|  |         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++; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       case OP_CLOSURE: { | ||||||
|  |         int nup; | ||||||
|  |         check(b < pt->sizep); | ||||||
|  |         nup = pt->p[b]->nups; | ||||||
|  |         check(pc + nup < pt->sizecode); | ||||||
|  |         for (; nup>0; nup--) { | ||||||
|  |           OpCode op1 = GET_OPCODE(pt->code[pc+nup]); | ||||||
|  |           check(op1 == OP_GETUPVAL || op1 == OP_MOVE); | ||||||
|  |         } | ||||||
|  |         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)) p1 = p2; | ||||||
|  |   lua_assert(!ttisstring(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
									
								
								src/lua/ldebug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/lua/ldebug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldebug.h,v 2.3 2005/04/25 19:24:10 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 | ||||||
							
								
								
									
										515
									
								
								src/lua/ldo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										515
									
								
								src/lua/ldo.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,515 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldo.c,v 2.37 2005/12/22 16:19:56 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 = 0; | ||||||
|  |   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->ci->tailcalls--)  /* call hook for eventual 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 != LUA_YIELD) {  /* 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 */ | ||||||
|  |     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; | ||||||
|  |   } | ||||||
|  |   L->status = 0; | ||||||
|  |   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) { | ||||||
|  |     if (L->status != 0) | ||||||
|  |       return resume_error(L, "cannot resume dead coroutine"); | ||||||
|  |     else if (L->ci != L->base_ci) | ||||||
|  |       return resume_error(L, "cannot resume non-suspended coroutine"); | ||||||
|  |   } | ||||||
|  |   luai_userstateresume(L, nargs); | ||||||
|  |   lua_assert(L->errfunc == 0 && L->nCcalls == 0); | ||||||
|  |   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 | ||||||
|  |     status = L->status; | ||||||
|  |   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 > 0) | ||||||
|  |     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
									
								
								src/lua/ldo.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/lua/ldo.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldo.h,v 2.7 2005/08/24 16:15:49 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
									
								
								src/lua/ldump.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								src/lua/ldump.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ldump.c,v 1.15 2006/02/16 15:53:49 lhf 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
									
								
								src/lua/lfunc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/lua/lfunc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,174 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lfunc.c,v 2.12 2005/12/22 16:19:56 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 ((p = ngcotouv(*pp)) != NULL && p->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 ((uv = ngcotouv(L->openupval)) != NULL && uv->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
									
								
								src/lua/lfunc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/lua/lfunc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lfunc.h,v 2.4 2005/04/25 19:24:10 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 | ||||||
							
								
								
									
										707
									
								
								src/lua/lgc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										707
									
								
								src/lua/lgc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,707 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lgc.c,v 2.37 2005/12/22 16:19:56 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 void propagateall (global_State *g) { | ||||||
|  |   while (g->gray) propagatemark(g); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** 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 */ | ||||||
|  |   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); | ||||||
|  |         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
									
								
								src/lua/lgc.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/lua/lgc.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lgc.h,v 2.15 2005/08/24 16:15:49 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
									
								
								src/lua/linit.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								src/lua/linit.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,38 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: linit.c,v 1.14 2005/12/29 15:32:11 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); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										532
									
								
								src/lua/liolib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										532
									
								
								src/lua/liolib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,532 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: liolib.c,v 2.72 2006/01/28 12:59:13 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 topfile(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 = topfile(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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** this function has a separated environment, which defines the | ||||||
|  | ** correct __close for 'popen' files | ||||||
|  | */ | ||||||
|  | static int io_pclose (lua_State *L) { | ||||||
|  |   FILE **p = topfile(L); | ||||||
|  |   int ok = lua_pclose(L, *p); | ||||||
|  |   if (ok) *p = NULL; | ||||||
|  |   return pushresult(L, ok, NULL); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int io_fclose (lua_State *L) { | ||||||
|  |   FILE **p = topfile(L); | ||||||
|  |   int ok = (fclose(*p) == 0); | ||||||
|  |   if (ok) *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 = *topfile(L); | ||||||
|  |   /* ignore closed files and standard files */ | ||||||
|  |   if (f != NULL && f != stdin && f != stdout && f != stderr) | ||||||
|  |     aux_close(L); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int io_tostring (lua_State *L) { | ||||||
|  |   FILE *f = *topfile(L); | ||||||
|  |   if (f == NULL) | ||||||
|  |     lua_pushstring(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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 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_strlen(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_strlen(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_setfield(L, -2, fname); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUALIB_API int luaopen_io (lua_State *L) { | ||||||
|  |   createmeta(L); | ||||||
|  |   /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ | ||||||
|  |   lua_createtable(L, 2, 1); | ||||||
|  |   lua_replace(L, LUA_ENVIRONINDEX); | ||||||
|  |   /* open library */ | ||||||
|  |   luaL_register(L, LUA_IOLIBNAME, iolib); | ||||||
|  |   /* create (and set) default files */ | ||||||
|  |   createstdfile(L, stdin, IO_INPUT, "stdin"); | ||||||
|  |   createstdfile(L, stdout, IO_OUTPUT, "stdout"); | ||||||
|  |   createstdfile(L, stderr, 0, "stderr"); | ||||||
|  |   /* create environment for 'popen' */ | ||||||
|  |   lua_getfield(L, -1, "popen"); | ||||||
|  |   lua_createtable(L, 0, 1); | ||||||
|  |   lua_pushcfunction(L, io_pclose); | ||||||
|  |   lua_setfield(L, -2, "__close"); | ||||||
|  |   lua_setfenv(L, -2); | ||||||
|  |   lua_pop(L, 1);  /* pop 'popen' */ | ||||||
|  |   /* set default close function */ | ||||||
|  |   lua_pushcfunction(L, io_fclose); | ||||||
|  |   lua_setfield(L, LUA_ENVIRONINDEX, "__close"); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										460
									
								
								src/lua/llex.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										460
									
								
								src/lua/llex.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,460 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: llex.c,v 2.19 2006/02/06 18:28:16 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 "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
									
								
								src/lua/llex.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/lua/llex.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: llex.h,v 1.57 2005/12/07 15:43:05 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
									
								
								src/lua/llimits.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/lua/llimits.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: llimits.h,v 1.69 2005/12/27 17:12:00 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
									
								
								src/lua/lmathlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/lua/lmathlib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lmathlib.c,v 1.67 2005/08/26 17:36:32 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
									
								
								src/lua/lmem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/lua/lmem.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,86 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lmem.c,v 1.70 2005/12/26 13:35:47 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
									
								
								src/lua/lmem.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/lua/lmem.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lmem.h,v 1.31 2005/04/25 19:24:10 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 | ||||||
|  |  | ||||||
							
								
								
									
										667
									
								
								src/lua/loadlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										667
									
								
								src/lua/loadlib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,667 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: loadlib.c,v 1.51 2005/12/29 15:32:11 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 "lauxlib.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lualib.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* environment variables that hold the search path for packages */ | ||||||
|  | #define LUA_PATH	"LUA_PATH" | ||||||
|  | #define LUA_CPATH	"LUA_CPATH" | ||||||
|  |  | ||||||
|  | /* 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 = GetModuleFileName(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 (FormatMessage(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 = LoadLibrary(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_pushstring(L, "");  /* error accumulator */ | ||||||
|  |   while ((path = pushnexttemplate(L, path)) != NULL) { | ||||||
|  |     const char *filename; | ||||||
|  |     filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); | ||||||
|  |     if (readable(filename))  /* does file exist and is readable? */ | ||||||
|  |       return filename;  /* return that file name */ | ||||||
|  |     lua_pop(L, 2);  /* remove path template and file name */  | ||||||
|  |     luaO_pushfstring(L, "\n\tno file " LUA_QS, filename); | ||||||
|  |     lua_concat(L, 2); | ||||||
|  |   } | ||||||
|  |   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 */ | ||||||
|  |     luaO_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? */ | ||||||
|  |     luaO_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_pushstring(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; | ||||||
|  |   lua_getstack(L, 1, &ar); | ||||||
|  |   lua_getinfo(L, "f", &ar); | ||||||
|  |   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_pushstring(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
									
								
								src/lua/lobject.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/lua/lobject.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lobject.c,v 2.22 2006/02/10 17:43:52 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
									
								
								src/lua/lobject.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								src/lua/lobject.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,381 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lobject.h,v 2.20 2006/01/18 11:37:34 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(tsvalue(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
									
								
								src/lua/lopcodes.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								src/lua/lopcodes.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,102 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lopcodes.c,v 1.37 2005/11/08 19:45:36 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
									
								
								src/lua/lopcodes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										268
									
								
								src/lua/lopcodes.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,268 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lopcodes.h,v 1.124 2005/12/02 18:42:08 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+3+C) := R(A)(R(A+1), R(A+2));  | ||||||
|  |                         if R(A+3) ~= nil then { pc++; R(A+2)=R(A+3); }	*/  | ||||||
|  | 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 | ||||||
							
								
								
									
										238
									
								
								src/lua/loslib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								src/lua/loslib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: loslib.c,v 1.17 2006/01/27 13:54:31 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); | ||||||
|  |     if (filename) | ||||||
|  |       lua_pushfstring(L, "%s: %s", filename, strerror(en)); | ||||||
|  |     else | ||||||
|  |       lua_pushfstring(L, "%s", 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 = lua_isnoneornil(L, 2) ? time(NULL) : | ||||||
|  |                                      (time_t)luaL_checknumber(L, 2); | ||||||
|  |   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 b[256]; | ||||||
|  |     if (strftime(b, sizeof(b), s, stm)) | ||||||
|  |       lua_pushstring(L, b); | ||||||
|  |     else | ||||||
|  |       return luaL_error(L, LUA_QL("date") " format too long"); | ||||||
|  |   } | ||||||
|  |   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 = lua_tostring(L, 1); | ||||||
|  |   int op = luaL_checkoption(L, 2, "all", catnames); | ||||||
|  |   luaL_argcheck(L, l || lua_isnoneornil(L, 1), 1, "string expected"); | ||||||
|  |   lua_pushstring(L, setlocale(cat[op], l)); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int os_exit (lua_State *L) { | ||||||
|  |   exit(luaL_optint(L, 1, EXIT_SUCCESS)); | ||||||
|  |   return 0;  /* to avoid warnings */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										1336
									
								
								src/lua/lparser.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1336
									
								
								src/lua/lparser.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										83
									
								
								src/lua/lparser.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/lua/lparser.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,83 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lparser.h,v 1.56 2005/10/03 14:02:40 roberto Exp $ | ||||||
|  | ** Lua Parser | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef lparser_h | ||||||
|  | #define lparser_h | ||||||
|  |  | ||||||
|  | #include "llimits.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "ltable.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
									
								
								src/lua/lstate.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/lua/lstate.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,214 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lstate.c,v 2.35 2005/10/06 20:46:25 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 = 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 */ | ||||||
|  |   luai_userstateclose(L); | ||||||
|  |   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 = 0; | ||||||
|  |   } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); | ||||||
|  |   lua_assert(G(L)->tmudata == NULL); | ||||||
|  |   close_state(L); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										168
									
								
								src/lua/lstate.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										168
									
								
								src/lua/lstate.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,168 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lstate.h,v 2.24 2006/02/06 18:27:59 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 */ | ||||||
|  |   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 | ||||||
|  |  | ||||||
							
								
								
									
										111
									
								
								src/lua/lstring.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/lua/lstring.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,111 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lstring.c,v 2.8 2005/12/22 16:19:56 roberto Exp $ | ||||||
|  | ** String table (keeps all strings handled by Lua) | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lstring_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lmem.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lstate.h" | ||||||
|  | #include "lstring.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaS_resize (lua_State *L, int newsize) { | ||||||
|  |   GCObject **newhash; | ||||||
|  |   stringtable *tb; | ||||||
|  |   int i; | ||||||
|  |   if (G(L)->gcstate == GCSsweepstring) | ||||||
|  |     return;  /* cannot resize during GC traverse */ | ||||||
|  |   newhash = luaM_newvector(L, newsize, GCObject *); | ||||||
|  |   tb = &G(L)->strt; | ||||||
|  |   for (i=0; i<newsize; i++) newhash[i] = NULL; | ||||||
|  |   /* rehash */ | ||||||
|  |   for (i=0; i<tb->size; i++) { | ||||||
|  |     GCObject *p = tb->hash[i]; | ||||||
|  |     while (p) {  /* for each node in the list */ | ||||||
|  |       GCObject *next = p->gch.next;  /* save next */ | ||||||
|  |       unsigned int h = gco2ts(p)->hash; | ||||||
|  |       int h1 = lmod(h, newsize);  /* new position */ | ||||||
|  |       lua_assert(cast_int(h%newsize) == lmod(h, newsize)); | ||||||
|  |       p->gch.next = newhash[h1];  /* chain it */ | ||||||
|  |       newhash[h1] = p; | ||||||
|  |       p = next; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   luaM_freearray(L, tb->hash, tb->size, TString *); | ||||||
|  |   tb->size = newsize; | ||||||
|  |   tb->hash = newhash; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static TString *newlstr (lua_State *L, const char *str, size_t l, | ||||||
|  |                                        unsigned int h) { | ||||||
|  |   TString *ts; | ||||||
|  |   stringtable *tb; | ||||||
|  |   if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) | ||||||
|  |     luaM_toobig(L); | ||||||
|  |   ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); | ||||||
|  |   ts->tsv.len = l; | ||||||
|  |   ts->tsv.hash = h; | ||||||
|  |   ts->tsv.marked = luaC_white(G(L)); | ||||||
|  |   ts->tsv.tt = LUA_TSTRING; | ||||||
|  |   ts->tsv.reserved = 0; | ||||||
|  |   memcpy(ts+1, str, l*sizeof(char)); | ||||||
|  |   ((char *)(ts+1))[l] = '\0';  /* ending 0 */ | ||||||
|  |   tb = &G(L)->strt; | ||||||
|  |   h = lmod(h, tb->size); | ||||||
|  |   ts->tsv.next = tb->hash[h];  /* chain new entry */ | ||||||
|  |   tb->hash[h] = obj2gco(ts); | ||||||
|  |   tb->nuse++; | ||||||
|  |   if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) | ||||||
|  |     luaS_resize(L, tb->size*2);  /* too crowded */ | ||||||
|  |   return ts; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { | ||||||
|  |   GCObject *o; | ||||||
|  |   unsigned int h = cast(unsigned int, l);  /* seed */ | ||||||
|  |   size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */ | ||||||
|  |   size_t l1; | ||||||
|  |   for (l1=l; l1>=step; l1-=step)  /* compute hash */ | ||||||
|  |     h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); | ||||||
|  |   for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; | ||||||
|  |        o != NULL; | ||||||
|  |        o = o->gch.next) { | ||||||
|  |     TString *ts = rawgco2ts(o); | ||||||
|  |     if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { | ||||||
|  |       /* string may be dead */ | ||||||
|  |       if (isdead(G(L), o)) changewhite(o); | ||||||
|  |       return ts; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return newlstr(L, str, l, h);  /* not found */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { | ||||||
|  |   Udata *u; | ||||||
|  |   if (s > MAX_SIZET - sizeof(Udata)) | ||||||
|  |     luaM_toobig(L); | ||||||
|  |   u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); | ||||||
|  |   u->uv.marked = luaC_white(G(L));  /* is not finalized */ | ||||||
|  |   u->uv.tt = LUA_TUSERDATA; | ||||||
|  |   u->uv.len = s; | ||||||
|  |   u->uv.metatable = NULL; | ||||||
|  |   u->uv.env = e; | ||||||
|  |   /* chain it on udata list (after main thread) */ | ||||||
|  |   u->uv.next = G(L)->mainthread->next; | ||||||
|  |   G(L)->mainthread->next = obj2gco(u); | ||||||
|  |   return u; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										31
									
								
								src/lua/lstring.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/lua/lstring.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lstring.h,v 1.43 2005/04/25 19:24:10 roberto Exp $ | ||||||
|  | ** String table (keep all strings handled by Lua) | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef lstring_h | ||||||
|  | #define lstring_h | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "lgc.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lstate.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define sizestring(s)	(sizeof(union TString)+((s)->len+1)*sizeof(char)) | ||||||
|  |  | ||||||
|  | #define sizeudata(u)	(sizeof(union Udata)+(u)->len) | ||||||
|  |  | ||||||
|  | #define luaS_new(L, s)	(luaS_newlstr(L, s, strlen(s))) | ||||||
|  | #define luaS_newliteral(L, s)	(luaS_newlstr(L, "" s, \ | ||||||
|  |                                  (sizeof(s)/sizeof(char))-1)) | ||||||
|  |  | ||||||
|  | #define luaS_fix(s)	l_setbit((s)->tsv.marked, FIXEDBIT) | ||||||
|  |  | ||||||
|  | LUAI_FUNC void luaS_resize (lua_State *L, int newsize); | ||||||
|  | LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); | ||||||
|  | LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										863
									
								
								src/lua/lstrlib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										863
									
								
								src/lua/lstrlib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,863 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lstrlib.c,v 1.130 2005/12/29 15:32:11 roberto Exp $ | ||||||
|  | ** Standard library for string operations and pattern-matching | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <stddef.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lstrlib_c | ||||||
|  | #define LUA_LIB | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lauxlib.h" | ||||||
|  | #include "lualib.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* macro to `unsign' a character */ | ||||||
|  | #define uchar(c)        ((unsigned char)(c)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_len (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   luaL_checklstring(L, 1, &l); | ||||||
|  |   lua_pushinteger(L, l); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { | ||||||
|  |   /* relative string position: negative means back from end */ | ||||||
|  |   return (pos>=0) ? pos : (ptrdiff_t)len+pos+1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_sub (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); | ||||||
|  |   ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); | ||||||
|  |   if (start < 1) start = 1; | ||||||
|  |   if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; | ||||||
|  |   if (start <= end) | ||||||
|  |     lua_pushlstring(L, s+start-1, end-start+1); | ||||||
|  |   else lua_pushliteral(L, ""); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_reverse (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   while (l--) luaL_addchar(&b, s[l]); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_lower (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   size_t i; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   for (i=0; i<l; i++) | ||||||
|  |     luaL_addchar(&b, tolower(uchar(s[i]))); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_upper (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   size_t i; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   for (i=0; i<l; i++) | ||||||
|  |     luaL_addchar(&b, toupper(uchar(s[i]))); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int str_rep (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   int n = luaL_checkint(L, 2); | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   while (n-- > 0) | ||||||
|  |     luaL_addlstring(&b, s, l); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_byte (lua_State *L) { | ||||||
|  |   size_t l; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l); | ||||||
|  |   ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); | ||||||
|  |   ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); | ||||||
|  |   int n, i; | ||||||
|  |   if (posi <= 0) posi = 1; | ||||||
|  |   if ((size_t)pose > l) pose = l; | ||||||
|  |   if (posi > pose) return 0;  /* empty interval; return no values */ | ||||||
|  |   n = (int)(pose -  posi + 1); | ||||||
|  |   if (posi + n <= pose)  /* overflow? */ | ||||||
|  |     luaL_error(L, "string slice too long"); | ||||||
|  |   luaL_checkstack(L, n, "string slice too long"); | ||||||
|  |   for (i=0; i<n; i++) | ||||||
|  |     lua_pushinteger(L, uchar(s[posi+i-1])); | ||||||
|  |   return n; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_char (lua_State *L) { | ||||||
|  |   int n = lua_gettop(L);  /* number of arguments */ | ||||||
|  |   int i; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   for (i=1; i<=n; i++) { | ||||||
|  |     int c = luaL_checkint(L, i); | ||||||
|  |     luaL_argcheck(L, uchar(c) == c, i, "invalid value"); | ||||||
|  |     luaL_addchar(&b, uchar(c)); | ||||||
|  |   } | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int writer (lua_State *L, const void* b, size_t size, void* B) { | ||||||
|  |   (void)L; | ||||||
|  |   luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_dump (lua_State *L) { | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   luaL_checktype(L, 1, LUA_TFUNCTION); | ||||||
|  |   lua_settop(L, 1); | ||||||
|  |   luaL_buffinit(L,&b); | ||||||
|  |   if (lua_dump(L, writer, &b) != 0) | ||||||
|  |     luaL_error(L, "unable to dump given function"); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {====================================================== | ||||||
|  | ** PATTERN MATCHING | ||||||
|  | ** ======================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define CAP_UNFINISHED	(-1) | ||||||
|  | #define CAP_POSITION	(-2) | ||||||
|  |  | ||||||
|  | typedef struct MatchState { | ||||||
|  |   const char *src_init;  /* init of source string */ | ||||||
|  |   const char *src_end;  /* end (`\0') of source string */ | ||||||
|  |   lua_State *L; | ||||||
|  |   int level;  /* total number of captures (finished or unfinished) */ | ||||||
|  |   struct { | ||||||
|  |     const char *init; | ||||||
|  |     ptrdiff_t len; | ||||||
|  |   } capture[LUA_MAXCAPTURES]; | ||||||
|  | } MatchState; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define L_ESC		'%' | ||||||
|  | #define SPECIALS	"^$*+?.([%-" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int check_capture (MatchState *ms, int l) { | ||||||
|  |   l -= '1'; | ||||||
|  |   if (l < 0 || l >= ms->level || ms->capture[l].len == CAP_UNFINISHED) | ||||||
|  |     return luaL_error(ms->L, "invalid capture index"); | ||||||
|  |   return l; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int capture_to_close (MatchState *ms) { | ||||||
|  |   int level = ms->level; | ||||||
|  |   for (level--; level>=0; level--) | ||||||
|  |     if (ms->capture[level].len == CAP_UNFINISHED) return level; | ||||||
|  |   return luaL_error(ms->L, "invalid pattern capture"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *classend (MatchState *ms, const char *p) { | ||||||
|  |   switch (*p++) { | ||||||
|  |     case L_ESC: { | ||||||
|  |       if (*p == '\0') | ||||||
|  |         luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); | ||||||
|  |       return p+1; | ||||||
|  |     } | ||||||
|  |     case '[': { | ||||||
|  |       if (*p == '^') p++; | ||||||
|  |       do {  /* look for a `]' */ | ||||||
|  |         if (*p == '\0') | ||||||
|  |           luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); | ||||||
|  |         if (*(p++) == L_ESC && *p != '\0') | ||||||
|  |           p++;  /* skip escapes (e.g. `%]') */ | ||||||
|  |       } while (*p != ']'); | ||||||
|  |       return p+1; | ||||||
|  |     } | ||||||
|  |     default: { | ||||||
|  |       return p; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int match_class (int c, int cl) { | ||||||
|  |   int res; | ||||||
|  |   switch (tolower(cl)) { | ||||||
|  |     case 'a' : res = isalpha(c); break; | ||||||
|  |     case 'c' : res = iscntrl(c); break; | ||||||
|  |     case 'd' : res = isdigit(c); break; | ||||||
|  |     case 'l' : res = islower(c); break; | ||||||
|  |     case 'p' : res = ispunct(c); break; | ||||||
|  |     case 's' : res = isspace(c); break; | ||||||
|  |     case 'u' : res = isupper(c); break; | ||||||
|  |     case 'w' : res = isalnum(c); break; | ||||||
|  |     case 'x' : res = isxdigit(c); break; | ||||||
|  |     case 'z' : res = (c == 0); break; | ||||||
|  |     default: return (cl == c); | ||||||
|  |   } | ||||||
|  |   return (islower(cl) ? res : !res); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int matchbracketclass (int c, const char *p, const char *ec) { | ||||||
|  |   int sig = 1; | ||||||
|  |   if (*(p+1) == '^') { | ||||||
|  |     sig = 0; | ||||||
|  |     p++;  /* skip the `^' */ | ||||||
|  |   } | ||||||
|  |   while (++p < ec) { | ||||||
|  |     if (*p == L_ESC) { | ||||||
|  |       p++; | ||||||
|  |       if (match_class(c, uchar(*p))) | ||||||
|  |         return sig; | ||||||
|  |     } | ||||||
|  |     else if ((*(p+1) == '-') && (p+2 < ec)) { | ||||||
|  |       p+=2; | ||||||
|  |       if (uchar(*(p-2)) <= c && c <= uchar(*p)) | ||||||
|  |         return sig; | ||||||
|  |     } | ||||||
|  |     else if (uchar(*p) == c) return sig; | ||||||
|  |   } | ||||||
|  |   return !sig; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int singlematch (int c, const char *p, const char *ep) { | ||||||
|  |   switch (*p) { | ||||||
|  |     case '.': return 1;  /* matches any char */ | ||||||
|  |     case L_ESC: return match_class(c, uchar(*(p+1))); | ||||||
|  |     case '[': return matchbracketclass(c, p, ep-1); | ||||||
|  |     default:  return (uchar(*p) == c); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *match (MatchState *ms, const char *s, const char *p); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *matchbalance (MatchState *ms, const char *s, | ||||||
|  |                                    const char *p) { | ||||||
|  |   if (*p == 0 || *(p+1) == 0) | ||||||
|  |     luaL_error(ms->L, "unbalanced pattern"); | ||||||
|  |   if (*s != *p) return NULL; | ||||||
|  |   else { | ||||||
|  |     int b = *p; | ||||||
|  |     int e = *(p+1); | ||||||
|  |     int cont = 1; | ||||||
|  |     while (++s < ms->src_end) { | ||||||
|  |       if (*s == e) { | ||||||
|  |         if (--cont == 0) return s+1; | ||||||
|  |       } | ||||||
|  |       else if (*s == b) cont++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return NULL;  /* string ends out of balance */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *max_expand (MatchState *ms, const char *s, | ||||||
|  |                                  const char *p, const char *ep) { | ||||||
|  |   ptrdiff_t i = 0;  /* counts maximum expand for item */ | ||||||
|  |   while ((s+i)<ms->src_end && singlematch(uchar(*(s+i)), p, ep)) | ||||||
|  |     i++; | ||||||
|  |   /* keeps trying to match with the maximum repetitions */ | ||||||
|  |   while (i>=0) { | ||||||
|  |     const char *res = match(ms, (s+i), ep+1); | ||||||
|  |     if (res) return res; | ||||||
|  |     i--;  /* else didn't match; reduce 1 repetition to try again */ | ||||||
|  |   } | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *min_expand (MatchState *ms, const char *s, | ||||||
|  |                                  const char *p, const char *ep) { | ||||||
|  |   for (;;) { | ||||||
|  |     const char *res = match(ms, s, ep+1); | ||||||
|  |     if (res != NULL) | ||||||
|  |       return res; | ||||||
|  |     else if (s<ms->src_end && singlematch(uchar(*s), p, ep)) | ||||||
|  |       s++;  /* try with one more repetition */ | ||||||
|  |     else return NULL; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *start_capture (MatchState *ms, const char *s, | ||||||
|  |                                     const char *p, int what) { | ||||||
|  |   const char *res; | ||||||
|  |   int level = ms->level; | ||||||
|  |   if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); | ||||||
|  |   ms->capture[level].init = s; | ||||||
|  |   ms->capture[level].len = what; | ||||||
|  |   ms->level = level+1; | ||||||
|  |   if ((res=match(ms, s, p)) == NULL)  /* match failed? */ | ||||||
|  |     ms->level--;  /* undo capture */ | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *end_capture (MatchState *ms, const char *s, | ||||||
|  |                                   const char *p) { | ||||||
|  |   int l = capture_to_close(ms); | ||||||
|  |   const char *res; | ||||||
|  |   ms->capture[l].len = s - ms->capture[l].init;  /* close capture */ | ||||||
|  |   if ((res = match(ms, s, p)) == NULL)  /* match failed? */ | ||||||
|  |     ms->capture[l].len = CAP_UNFINISHED;  /* undo capture */ | ||||||
|  |   return res; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *match_capture (MatchState *ms, const char *s, int l) { | ||||||
|  |   size_t len; | ||||||
|  |   l = check_capture(ms, l); | ||||||
|  |   len = ms->capture[l].len; | ||||||
|  |   if ((size_t)(ms->src_end-s) >= len && | ||||||
|  |       memcmp(ms->capture[l].init, s, len) == 0) | ||||||
|  |     return s+len; | ||||||
|  |   else return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *match (MatchState *ms, const char *s, const char *p) { | ||||||
|  |   init: /* using goto's to optimize tail recursion */ | ||||||
|  |   switch (*p) { | ||||||
|  |     case '(': {  /* start capture */ | ||||||
|  |       if (*(p+1) == ')')  /* position capture? */ | ||||||
|  |         return start_capture(ms, s, p+2, CAP_POSITION); | ||||||
|  |       else | ||||||
|  |         return start_capture(ms, s, p+1, CAP_UNFINISHED); | ||||||
|  |     } | ||||||
|  |     case ')': {  /* end capture */ | ||||||
|  |       return end_capture(ms, s, p+1); | ||||||
|  |     } | ||||||
|  |     case L_ESC: { | ||||||
|  |       switch (*(p+1)) { | ||||||
|  |         case 'b': {  /* balanced string? */ | ||||||
|  |           s = matchbalance(ms, s, p+2); | ||||||
|  |           if (s == NULL) return NULL; | ||||||
|  |           p+=4; goto init;  /* else return match(ms, s, p+4); */ | ||||||
|  |         } | ||||||
|  |         case 'f': {  /* frontier? */ | ||||||
|  |           const char *ep; char previous; | ||||||
|  |           p += 2; | ||||||
|  |           if (*p != '[') | ||||||
|  |             luaL_error(ms->L, "missing " LUA_QL("[") " after " | ||||||
|  |                                LUA_QL("%%f") " in pattern"); | ||||||
|  |           ep = classend(ms, p);  /* points to what is next */ | ||||||
|  |           previous = (s == ms->src_init) ? '\0' : *(s-1); | ||||||
|  |           if (matchbracketclass(uchar(previous), p, ep-1) || | ||||||
|  |              !matchbracketclass(uchar(*s), p, ep-1)) return NULL; | ||||||
|  |           p=ep; goto init;  /* else return match(ms, s, ep); */ | ||||||
|  |         } | ||||||
|  |         default: { | ||||||
|  |           if (isdigit(uchar(*(p+1)))) {  /* capture results (%0-%9)? */ | ||||||
|  |             s = match_capture(ms, s, uchar(*(p+1))); | ||||||
|  |             if (s == NULL) return NULL; | ||||||
|  |             p+=2; goto init;  /* else return match(ms, s, p+2) */ | ||||||
|  |           } | ||||||
|  |           goto dflt;  /* case default */ | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     case '\0': {  /* end of pattern */ | ||||||
|  |       return s;  /* match succeeded */ | ||||||
|  |     } | ||||||
|  |     case '$': { | ||||||
|  |       if (*(p+1) == '\0')  /* is the `$' the last char in pattern? */ | ||||||
|  |         return (s == ms->src_end) ? s : NULL;  /* check end of string */ | ||||||
|  |       else goto dflt; | ||||||
|  |     } | ||||||
|  |     default: dflt: {  /* it is a pattern item */ | ||||||
|  |       const char *ep = classend(ms, p);  /* points to what is next */ | ||||||
|  |       int m = s<ms->src_end && singlematch(uchar(*s), p, ep); | ||||||
|  |       switch (*ep) { | ||||||
|  |         case '?': {  /* optional */ | ||||||
|  |           const char *res; | ||||||
|  |           if (m && ((res=match(ms, s+1, ep+1)) != NULL)) | ||||||
|  |             return res; | ||||||
|  |           p=ep+1; goto init;  /* else return match(ms, s, ep+1); */ | ||||||
|  |         } | ||||||
|  |         case '*': {  /* 0 or more repetitions */ | ||||||
|  |           return max_expand(ms, s, p, ep); | ||||||
|  |         } | ||||||
|  |         case '+': {  /* 1 or more repetitions */ | ||||||
|  |           return (m ? max_expand(ms, s+1, p, ep) : NULL); | ||||||
|  |         } | ||||||
|  |         case '-': {  /* 0 or more repetitions (minimum) */ | ||||||
|  |           return min_expand(ms, s, p, ep); | ||||||
|  |         } | ||||||
|  |         default: { | ||||||
|  |           if (!m) return NULL; | ||||||
|  |           s++; p=ep; goto init;  /* else return match(ms, s+1, ep); */ | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *lmemfind (const char *s1, size_t l1, | ||||||
|  |                                const char *s2, size_t l2) { | ||||||
|  |   if (l2 == 0) return s1;  /* empty strings are everywhere */ | ||||||
|  |   else if (l2 > l1) return NULL;  /* avoids a negative `l1' */ | ||||||
|  |   else { | ||||||
|  |     const char *init;  /* to search for a `*s2' inside `s1' */ | ||||||
|  |     l2--;  /* 1st char will be checked by `memchr' */ | ||||||
|  |     l1 = l1-l2;  /* `s2' cannot be found after that */ | ||||||
|  |     while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { | ||||||
|  |       init++;   /* 1st char is already checked */ | ||||||
|  |       if (memcmp(init, s2+1, l2) == 0) | ||||||
|  |         return init-1; | ||||||
|  |       else {  /* correct `l1' and `s1' to try again */ | ||||||
|  |         l1 -= init-s1; | ||||||
|  |         s1 = init; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     return NULL;  /* not found */ | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void push_onecapture (MatchState *ms, int i, const char *s, | ||||||
|  |                                                     const char *e) { | ||||||
|  |   if (i >= ms->level) { | ||||||
|  |     if (i == 0)  /* ms->level == 0, too */ | ||||||
|  |       lua_pushlstring(ms->L, s, e - s);  /* add whole match */ | ||||||
|  |     else | ||||||
|  |       luaL_error(ms->L, "invalid capture index"); | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     ptrdiff_t l = ms->capture[i].len; | ||||||
|  |     if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); | ||||||
|  |     if (l == CAP_POSITION) | ||||||
|  |       lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); | ||||||
|  |     else | ||||||
|  |       lua_pushlstring(ms->L, ms->capture[i].init, l); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int push_captures (MatchState *ms, const char *s, const char *e) { | ||||||
|  |   int i; | ||||||
|  |   int nlevels = (ms->level == 0 && s) ? 1 : ms->level; | ||||||
|  |   luaL_checkstack(ms->L, nlevels, "too many captures"); | ||||||
|  |   for (i = 0; i < nlevels; i++) | ||||||
|  |     push_onecapture(ms, i, s, e); | ||||||
|  |   return nlevels;  /* number of strings pushed */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_find_aux (lua_State *L, int find) { | ||||||
|  |   size_t l1, l2; | ||||||
|  |   const char *s = luaL_checklstring(L, 1, &l1); | ||||||
|  |   const char *p = luaL_checklstring(L, 2, &l2); | ||||||
|  |   ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; | ||||||
|  |   if (init < 0) init = 0; | ||||||
|  |   else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; | ||||||
|  |   if (find && (lua_toboolean(L, 4) ||  /* explicit request? */ | ||||||
|  |       strpbrk(p, SPECIALS) == NULL)) {  /* or no special characters? */ | ||||||
|  |     /* do a plain search */ | ||||||
|  |     const char *s2 = lmemfind(s+init, l1-init, p, l2); | ||||||
|  |     if (s2) { | ||||||
|  |       lua_pushinteger(L, s2-s+1); | ||||||
|  |       lua_pushinteger(L, s2-s+l2); | ||||||
|  |       return 2; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     MatchState ms; | ||||||
|  |     int anchor = (*p == '^') ? (p++, 1) : 0; | ||||||
|  |     const char *s1=s+init; | ||||||
|  |     ms.L = L; | ||||||
|  |     ms.src_init = s; | ||||||
|  |     ms.src_end = s+l1; | ||||||
|  |     do { | ||||||
|  |       const char *res; | ||||||
|  |       ms.level = 0; | ||||||
|  |       if ((res=match(&ms, s1, p)) != NULL) { | ||||||
|  |         if (find) { | ||||||
|  |           lua_pushinteger(L, s1-s+1);  /* start */ | ||||||
|  |           lua_pushinteger(L, res-s);   /* end */ | ||||||
|  |           return push_captures(&ms, NULL, 0) + 2; | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |           return push_captures(&ms, s1, res); | ||||||
|  |       } | ||||||
|  |     } while (s1++ < ms.src_end && !anchor); | ||||||
|  |   } | ||||||
|  |   lua_pushnil(L);  /* not found */ | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_find (lua_State *L) { | ||||||
|  |   return str_find_aux(L, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_match (lua_State *L) { | ||||||
|  |   return str_find_aux(L, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int gmatch_aux (lua_State *L) { | ||||||
|  |   MatchState ms; | ||||||
|  |   size_t ls; | ||||||
|  |   const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); | ||||||
|  |   const char *p = lua_tostring(L, lua_upvalueindex(2)); | ||||||
|  |   const char *src; | ||||||
|  |   ms.L = L; | ||||||
|  |   ms.src_init = s; | ||||||
|  |   ms.src_end = s+ls; | ||||||
|  |   for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); | ||||||
|  |        src <= ms.src_end; | ||||||
|  |        src++) { | ||||||
|  |     const char *e; | ||||||
|  |     ms.level = 0; | ||||||
|  |     if ((e = match(&ms, src, p)) != NULL) { | ||||||
|  |       lua_Integer newstart = e-s; | ||||||
|  |       if (e == src) newstart++;  /* empty match? go at least one position */ | ||||||
|  |       lua_pushinteger(L, newstart); | ||||||
|  |       lua_replace(L, lua_upvalueindex(3)); | ||||||
|  |       return push_captures(&ms, src, e); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  /* not found */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int gmatch (lua_State *L) { | ||||||
|  |   luaL_checkstring(L, 1); | ||||||
|  |   luaL_checkstring(L, 2); | ||||||
|  |   lua_settop(L, 2); | ||||||
|  |   lua_pushinteger(L, 0); | ||||||
|  |   lua_pushcclosure(L, gmatch_aux, 3); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int gfind_nodef (lua_State *L) { | ||||||
|  |   return luaL_error(L, LUA_QL("string.gfind") " was renamed to " | ||||||
|  |                        LUA_QL("string.gmatch")); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, | ||||||
|  |                                                    const char *e) { | ||||||
|  |   size_t l, i; | ||||||
|  |   const char *news = lua_tolstring(ms->L, 3, &l); | ||||||
|  |   for (i = 0; i < l; i++) { | ||||||
|  |     if (news[i] != L_ESC) | ||||||
|  |       luaL_addchar(b, news[i]); | ||||||
|  |     else { | ||||||
|  |       i++;  /* skip ESC */ | ||||||
|  |       if (!isdigit(uchar(news[i]))) | ||||||
|  |         luaL_addchar(b, news[i]); | ||||||
|  |       else if (news[i] == '0') | ||||||
|  |           luaL_addlstring(b, s, e - s); | ||||||
|  |       else { | ||||||
|  |         push_onecapture(ms, news[i] - '1', s, e); | ||||||
|  |         luaL_addvalue(b);  /* add capture to accumulated result */ | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, | ||||||
|  |                                                        const char *e) { | ||||||
|  |   lua_State *L = ms->L; | ||||||
|  |   switch (lua_type(L, 3)) { | ||||||
|  |     case LUA_TNUMBER: | ||||||
|  |     case LUA_TSTRING: { | ||||||
|  |       add_s(ms, b, s, e); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     case LUA_TFUNCTION: { | ||||||
|  |       int n; | ||||||
|  |       lua_pushvalue(L, 3); | ||||||
|  |       n = push_captures(ms, s, e); | ||||||
|  |       lua_call(L, n, 1); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case LUA_TTABLE: { | ||||||
|  |       push_onecapture(ms, 0, s, e); | ||||||
|  |       lua_gettable(L, 3); | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     default: { | ||||||
|  |       luaL_argerror(L, 3, "string/function/table expected");  | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (!lua_toboolean(L, -1)) {  /* nil or false? */ | ||||||
|  |     lua_pop(L, 1); | ||||||
|  |     lua_pushlstring(L, s, e - s);  /* keep original text */ | ||||||
|  |   } | ||||||
|  |   else if (!lua_isstring(L, -1)) | ||||||
|  |     luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));  | ||||||
|  |   luaL_addvalue(b);  /* add result to accumulator */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_gsub (lua_State *L) { | ||||||
|  |   size_t srcl; | ||||||
|  |   const char *src = luaL_checklstring(L, 1, &srcl); | ||||||
|  |   const char *p = luaL_checkstring(L, 2); | ||||||
|  |   int max_s = luaL_optint(L, 4, srcl+1); | ||||||
|  |   int anchor = (*p == '^') ? (p++, 1) : 0; | ||||||
|  |   int n = 0; | ||||||
|  |   MatchState ms; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   ms.L = L; | ||||||
|  |   ms.src_init = src; | ||||||
|  |   ms.src_end = src+srcl; | ||||||
|  |   while (n < max_s) { | ||||||
|  |     const char *e; | ||||||
|  |     ms.level = 0; | ||||||
|  |     e = match(&ms, src, p); | ||||||
|  |     if (e) { | ||||||
|  |       n++; | ||||||
|  |       add_value(&ms, &b, src, e); | ||||||
|  |     } | ||||||
|  |     if (e && e>src) /* non empty match? */ | ||||||
|  |       src = e;  /* skip it */ | ||||||
|  |     else if (src < ms.src_end) | ||||||
|  |       luaL_addchar(&b, *src++); | ||||||
|  |     else break; | ||||||
|  |     if (anchor) break; | ||||||
|  |   } | ||||||
|  |   luaL_addlstring(&b, src, ms.src_end-src); | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   lua_pushinteger(L, n);  /* number of substitutions */ | ||||||
|  |   return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* }====================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ | ||||||
|  | #define MAX_ITEM	512 | ||||||
|  | /* valid flags in a format specification */ | ||||||
|  | #define FLAGS	"-+ #0" | ||||||
|  | /* | ||||||
|  | ** maximum size of each format specification (such as '%-099.99d') | ||||||
|  | ** (+10 accounts for %99.99x plus margin of error) | ||||||
|  | */ | ||||||
|  | #define MAX_FORMAT	(sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { | ||||||
|  |   size_t l; | ||||||
|  |   const char *s = luaL_checklstring(L, arg, &l); | ||||||
|  |   luaL_addchar(b, '"'); | ||||||
|  |   while (l--) { | ||||||
|  |     switch (*s) { | ||||||
|  |       case '"': case '\\': case '\n': { | ||||||
|  |         luaL_addchar(b, '\\'); | ||||||
|  |         luaL_addchar(b, *s); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       case '\0': { | ||||||
|  |         luaL_addlstring(b, "\\000", 4); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       default: { | ||||||
|  |         luaL_addchar(b, *s); | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     s++; | ||||||
|  |   } | ||||||
|  |   luaL_addchar(b, '"'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { | ||||||
|  |   const char *p = strfrmt; | ||||||
|  |   while (strchr(FLAGS, *p)) p++;  /* skip flags */ | ||||||
|  |   if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) | ||||||
|  |     luaL_error(L, "invalid format (repeated flags)"); | ||||||
|  |   if (isdigit(uchar(*p))) p++;  /* skip width */ | ||||||
|  |   if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */ | ||||||
|  |   if (*p == '.') { | ||||||
|  |     p++; | ||||||
|  |     if (isdigit(uchar(*p))) p++;  /* skip precision */ | ||||||
|  |     if (isdigit(uchar(*p))) p++;  /* (2 digits at most) */ | ||||||
|  |   } | ||||||
|  |   if (isdigit(uchar(*p))) | ||||||
|  |     luaL_error(L, "invalid format (width or precision too long)"); | ||||||
|  |   *(form++) = '%'; | ||||||
|  |   strncpy(form, strfrmt, p - strfrmt + 1); | ||||||
|  |   form += p - strfrmt + 1; | ||||||
|  |   *form = '\0'; | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void addintlen (char *form) { | ||||||
|  |   size_t l = strlen(form); | ||||||
|  |   char spec = form[l - 1]; | ||||||
|  |   strcpy(form + l - 1, LUA_INTFRMLEN); | ||||||
|  |   form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; | ||||||
|  |   form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int str_format (lua_State *L) { | ||||||
|  |   int arg = 1; | ||||||
|  |   size_t sfl; | ||||||
|  |   const char *strfrmt = luaL_checklstring(L, arg, &sfl); | ||||||
|  |   const char *strfrmt_end = strfrmt+sfl; | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   while (strfrmt < strfrmt_end) { | ||||||
|  |     if (*strfrmt != L_ESC) | ||||||
|  |       luaL_addchar(&b, *strfrmt++); | ||||||
|  |     else if (*++strfrmt == L_ESC) | ||||||
|  |       luaL_addchar(&b, *strfrmt++);  /* %% */ | ||||||
|  |     else { /* format item */ | ||||||
|  |       char form[MAX_FORMAT];  /* to store the format (`%...') */ | ||||||
|  |       char buff[MAX_ITEM];  /* to store the formatted item */ | ||||||
|  |       arg++; | ||||||
|  |       strfrmt = scanformat(L, strfrmt, form); | ||||||
|  |       switch (*strfrmt++) { | ||||||
|  |         case 'c': { | ||||||
|  |           sprintf(buff, form, (int)luaL_checknumber(L, arg)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'd':  case 'i': { | ||||||
|  |           addintlen(form); | ||||||
|  |           sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'o':  case 'u':  case 'x':  case 'X': { | ||||||
|  |           addintlen(form); | ||||||
|  |           sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'e':  case 'E': case 'f': | ||||||
|  |         case 'g': case 'G': { | ||||||
|  |           sprintf(buff, form, (double)luaL_checknumber(L, arg)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         case 'q': { | ||||||
|  |           addquoted(L, &b, arg); | ||||||
|  |           continue;  /* skip the 'addsize' at the end */ | ||||||
|  |         } | ||||||
|  |         case 's': { | ||||||
|  |           size_t l; | ||||||
|  |           const char *s = luaL_checklstring(L, arg, &l); | ||||||
|  |           if (!strchr(form, '.') && l >= 100) { | ||||||
|  |             /* no precision and string is too long to be formatted; | ||||||
|  |                keep original string */ | ||||||
|  |             lua_pushvalue(L, arg); | ||||||
|  |             luaL_addvalue(&b); | ||||||
|  |             continue;  /* skip the `addsize' at the end */ | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             sprintf(buff, form, s); | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         default: {  /* also treat cases `pnLlh' */ | ||||||
|  |           return luaL_error(L, "invalid option to " LUA_QL("format")); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       luaL_addlstring(&b, buff, strlen(buff)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const luaL_Reg strlib[] = { | ||||||
|  |   {"byte", str_byte}, | ||||||
|  |   {"char", str_char}, | ||||||
|  |   {"dump", str_dump}, | ||||||
|  |   {"find", str_find}, | ||||||
|  |   {"format", str_format}, | ||||||
|  |   {"gfind", gfind_nodef}, | ||||||
|  |   {"gmatch", gmatch}, | ||||||
|  |   {"gsub", str_gsub}, | ||||||
|  |   {"len", str_len}, | ||||||
|  |   {"lower", str_lower}, | ||||||
|  |   {"match", str_match}, | ||||||
|  |   {"rep", str_rep}, | ||||||
|  |   {"reverse", str_reverse}, | ||||||
|  |   {"sub", str_sub}, | ||||||
|  |   {"upper", str_upper}, | ||||||
|  |   {NULL, NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void createmetatable (lua_State *L) { | ||||||
|  |   lua_createtable(L, 0, 1);  /* create metatable for strings */ | ||||||
|  |   lua_pushliteral(L, "");  /* dummy string */ | ||||||
|  |   lua_pushvalue(L, -2); | ||||||
|  |   lua_setmetatable(L, -2);  /* set string metatable */ | ||||||
|  |   lua_pop(L, 1);  /* pop dummy string */ | ||||||
|  |   lua_pushvalue(L, -2);  /* string library... */ | ||||||
|  |   lua_setfield(L, -2, "__index");  /* ...is the __index metamethod */ | ||||||
|  |   lua_pop(L, 1);  /* pop metatable */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Open string library | ||||||
|  | */ | ||||||
|  | LUALIB_API int luaopen_string (lua_State *L) { | ||||||
|  |   luaL_register(L, LUA_STRLIBNAME, strlib); | ||||||
|  | #if defined(LUA_COMPAT_GFIND) | ||||||
|  |   lua_getfield(L, -1, "gmatch"); | ||||||
|  |   lua_setfield(L, -2, "gfind"); | ||||||
|  | #endif | ||||||
|  |   createmetatable(L); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										588
									
								
								src/lua/ltable.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										588
									
								
								src/lua/ltable.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,588 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ltable.c,v 2.32 2006/01/18 11:49:02 roberto Exp $ | ||||||
|  | ** Lua tables (hash) | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Implementation of tables (aka arrays, objects, or hash tables). | ||||||
|  | ** Tables keep its elements in two parts: an array part and a hash part. | ||||||
|  | ** Non-negative integer keys are all candidates to be kept in the array | ||||||
|  | ** part. The actual size of the array is the largest `n' such that at | ||||||
|  | ** least half the slots between 0 and n are in use. | ||||||
|  | ** Hash uses a mix of chained scatter table with Brent's variation. | ||||||
|  | ** A main invariant of these tables is that, if an element is not | ||||||
|  | ** in its main position (i.e. the `original' position that its hash gives | ||||||
|  | ** to it), then the colliding element is in its own main position. | ||||||
|  | ** Hence even when the load factor reaches 100%, performance remains good. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <math.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define ltable_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "ldebug.h" | ||||||
|  | #include "ldo.h" | ||||||
|  | #include "lgc.h" | ||||||
|  | #include "lmem.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lstate.h" | ||||||
|  | #include "ltable.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** max size of array part is 2^MAXBITS | ||||||
|  | */ | ||||||
|  | #if LUAI_BITSINT > 26 | ||||||
|  | #define MAXBITS		26 | ||||||
|  | #else | ||||||
|  | #define MAXBITS		(LUAI_BITSINT-2) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define MAXASIZE	(1 << MAXBITS) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define hashpow2(t,n)      (gnode(t, lmod((n), sizenode(t)))) | ||||||
|  |    | ||||||
|  | #define hashstr(t,str)  hashpow2(t, (str)->tsv.hash) | ||||||
|  | #define hashboolean(t,p)        hashpow2(t, p) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** for some types, it is better to avoid modulus by power of 2, as | ||||||
|  | ** they tend to have many 2 factors. | ||||||
|  | */ | ||||||
|  | #define hashmod(t,n)	(gnode(t, ((n) % ((sizenode(t)-1)|1)))) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define hashpointer(t,p)	hashmod(t, IntPoint(p)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** number of ints inside a lua_Number | ||||||
|  | */ | ||||||
|  | #define numints		cast_int(sizeof(lua_Number)/sizeof(int)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define dummynode		(&dummynode_) | ||||||
|  |  | ||||||
|  | static const Node dummynode_ = { | ||||||
|  |   {{NULL}, LUA_TNIL},  /* value */ | ||||||
|  |   {{{NULL}, LUA_TNIL, NULL}}  /* key */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** hash for lua_Numbers | ||||||
|  | */ | ||||||
|  | static Node *hashnum (const Table *t, lua_Number n) { | ||||||
|  |   unsigned int a[numints]; | ||||||
|  |   int i; | ||||||
|  |   n += 1;  /* normalize number (avoid -0) */ | ||||||
|  |   lua_assert(sizeof(a) <= sizeof(n)); | ||||||
|  |   memcpy(a, &n, sizeof(a)); | ||||||
|  |   for (i = 1; i < numints; i++) a[0] += a[i]; | ||||||
|  |   return hashmod(t, a[0]); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** returns the `main' position of an element in a table (that is, the index | ||||||
|  | ** of its hash value) | ||||||
|  | */ | ||||||
|  | static Node *mainposition (const Table *t, const TValue *key) { | ||||||
|  |   switch (ttype(key)) { | ||||||
|  |     case LUA_TNUMBER: | ||||||
|  |       return hashnum(t, nvalue(key)); | ||||||
|  |     case LUA_TSTRING: | ||||||
|  |       return hashstr(t, rawtsvalue(key)); | ||||||
|  |     case LUA_TBOOLEAN: | ||||||
|  |       return hashboolean(t, bvalue(key)); | ||||||
|  |     case LUA_TLIGHTUSERDATA: | ||||||
|  |       return hashpointer(t, pvalue(key)); | ||||||
|  |     default: | ||||||
|  |       return hashpointer(t, gcvalue(key)); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** returns the index for `key' if `key' is an appropriate key to live in | ||||||
|  | ** the array part of the table, -1 otherwise. | ||||||
|  | */ | ||||||
|  | static int arrayindex (const TValue *key) { | ||||||
|  |   if (ttisnumber(key)) { | ||||||
|  |     lua_Number n = nvalue(key); | ||||||
|  |     int k; | ||||||
|  |     lua_number2int(k, n); | ||||||
|  |     if (luai_numeq(cast_num(k), n)) | ||||||
|  |       return k; | ||||||
|  |   } | ||||||
|  |   return -1;  /* `key' did not match some condition */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** returns the index of a `key' for table traversals. First goes all | ||||||
|  | ** elements in the array part, then elements in the hash part. The | ||||||
|  | ** beginning of a traversal is signalled by -1. | ||||||
|  | */ | ||||||
|  | static int findindex (lua_State *L, Table *t, StkId key) { | ||||||
|  |   int i; | ||||||
|  |   if (ttisnil(key)) return -1;  /* first iteration */ | ||||||
|  |   i = arrayindex(key); | ||||||
|  |   if (0 < i && i <= t->sizearray)  /* is `key' inside array part? */ | ||||||
|  |     return i-1;  /* yes; that's the index (corrected to C) */ | ||||||
|  |   else { | ||||||
|  |     Node *n = mainposition(t, key); | ||||||
|  |     do {  /* check whether `key' is somewhere in the chain */ | ||||||
|  |       /* key may be dead already, but it is ok to use it in `next' */ | ||||||
|  |       if (luaO_rawequalObj(key2tval(n), key) || | ||||||
|  |             (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && | ||||||
|  |              gcvalue(gkey(n)) == gcvalue(key))) { | ||||||
|  |         i = cast_int(n - gnode(t, 0));  /* key index in hash table */ | ||||||
|  |         /* hash elements are numbered after array ones */ | ||||||
|  |         return i + t->sizearray; | ||||||
|  |       } | ||||||
|  |       else n = gnext(n); | ||||||
|  |     } while (n); | ||||||
|  |     luaG_runerror(L, "invalid key to " LUA_QL("next"));  /* key not found */ | ||||||
|  |     return 0;  /* to avoid warnings */ | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaH_next (lua_State *L, Table *t, StkId key) { | ||||||
|  |   int i = findindex(L, t, key);  /* find original element */ | ||||||
|  |   for (i++; i < t->sizearray; i++) {  /* try first array part */ | ||||||
|  |     if (!ttisnil(&t->array[i])) {  /* a non-nil value? */ | ||||||
|  |       setnvalue(key, cast_num(i+1)); | ||||||
|  |       setobj2s(L, key+1, &t->array[i]); | ||||||
|  |       return 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   for (i -= t->sizearray; i < sizenode(t); i++) {  /* then hash part */ | ||||||
|  |     if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */ | ||||||
|  |       setobj2s(L, key, key2tval(gnode(t, i))); | ||||||
|  |       setobj2s(L, key+1, gval(gnode(t, i))); | ||||||
|  |       return 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  /* no more elements */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {============================================================= | ||||||
|  | ** Rehash | ||||||
|  | ** ============================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int computesizes (int nums[], int *narray) { | ||||||
|  |   int i; | ||||||
|  |   int twotoi;  /* 2^i */ | ||||||
|  |   int a = 0;  /* number of elements smaller than 2^i */ | ||||||
|  |   int na = 0;  /* number of elements to go to array part */ | ||||||
|  |   int n = 0;  /* optimal size for array part */ | ||||||
|  |   for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { | ||||||
|  |     if (nums[i] > 0) { | ||||||
|  |       a += nums[i]; | ||||||
|  |       if (a > twotoi/2) {  /* more than half elements present? */ | ||||||
|  |         n = twotoi;  /* optimal size (till now) */ | ||||||
|  |         na = a;  /* all elements smaller than n will go to array part */ | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (a == *narray) break;  /* all elements already counted */ | ||||||
|  |   } | ||||||
|  |   *narray = n; | ||||||
|  |   lua_assert(*narray/2 <= na && na <= *narray); | ||||||
|  |   return na; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int countint (const TValue *key, int *nums) { | ||||||
|  |   int k = arrayindex(key); | ||||||
|  |   if (0 < k && k <= MAXASIZE) {  /* is `key' an appropriate array index? */ | ||||||
|  |     nums[ceillog2(k)]++;  /* count as such */ | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int numusearray (const Table *t, int *nums) { | ||||||
|  |   int lg; | ||||||
|  |   int ttlg;  /* 2^lg */ | ||||||
|  |   int ause = 0;  /* summation of `nums' */ | ||||||
|  |   int i = 1;  /* count to traverse all array keys */ | ||||||
|  |   for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) {  /* for each slice */ | ||||||
|  |     int lc = 0;  /* counter */ | ||||||
|  |     int lim = ttlg; | ||||||
|  |     if (lim > t->sizearray) { | ||||||
|  |       lim = t->sizearray;  /* adjust upper limit */ | ||||||
|  |       if (i > lim) | ||||||
|  |         break;  /* no more elements to count */ | ||||||
|  |     } | ||||||
|  |     /* count elements in range (2^(lg-1), 2^lg] */ | ||||||
|  |     for (; i <= lim; i++) { | ||||||
|  |       if (!ttisnil(&t->array[i-1])) | ||||||
|  |         lc++; | ||||||
|  |     } | ||||||
|  |     nums[lg] += lc; | ||||||
|  |     ause += lc; | ||||||
|  |   } | ||||||
|  |   return ause; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int numusehash (const Table *t, int *nums, int *pnasize) { | ||||||
|  |   int totaluse = 0;  /* total number of elements */ | ||||||
|  |   int ause = 0;  /* summation of `nums' */ | ||||||
|  |   int i = sizenode(t); | ||||||
|  |   while (i--) { | ||||||
|  |     Node *n = &t->node[i]; | ||||||
|  |     if (!ttisnil(gval(n))) { | ||||||
|  |       ause += countint(key2tval(n), nums); | ||||||
|  |       totaluse++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   *pnasize += ause; | ||||||
|  |   return totaluse; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void setarrayvector (lua_State *L, Table *t, int size) { | ||||||
|  |   int i; | ||||||
|  |   luaM_reallocvector(L, t->array, t->sizearray, size, TValue); | ||||||
|  |   for (i=t->sizearray; i<size; i++) | ||||||
|  |      setnilvalue(&t->array[i]); | ||||||
|  |   t->sizearray = size; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void setnodevector (lua_State *L, Table *t, int size) { | ||||||
|  |   int lsize; | ||||||
|  |   if (size == 0) {  /* no elements to hash part? */ | ||||||
|  |     t->node = cast(Node *, dummynode);  /* use common `dummynode' */ | ||||||
|  |     lsize = 0; | ||||||
|  |   } | ||||||
|  |   else { | ||||||
|  |     int i; | ||||||
|  |     lsize = ceillog2(size); | ||||||
|  |     if (lsize > MAXBITS) | ||||||
|  |       luaG_runerror(L, "table overflow"); | ||||||
|  |     size = twoto(lsize); | ||||||
|  |     t->node = luaM_newvector(L, size, Node); | ||||||
|  |     for (i=0; i<size; i++) { | ||||||
|  |       Node *n = gnode(t, i); | ||||||
|  |       gnext(n) = NULL; | ||||||
|  |       setnilvalue(gkey(n)); | ||||||
|  |       setnilvalue(gval(n)); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   t->lsizenode = cast_byte(lsize); | ||||||
|  |   t->lastfree = gnode(t, size);  /* all positions are free */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void resize (lua_State *L, Table *t, int nasize, int nhsize) { | ||||||
|  |   int i; | ||||||
|  |   int oldasize = t->sizearray; | ||||||
|  |   int oldhsize = t->lsizenode; | ||||||
|  |   Node *nold = t->node;  /* save old hash ... */ | ||||||
|  |   if (nasize > oldasize)  /* array part must grow? */ | ||||||
|  |     setarrayvector(L, t, nasize); | ||||||
|  |   /* create new hash part with appropriate size */ | ||||||
|  |   setnodevector(L, t, nhsize);   | ||||||
|  |   if (nasize < oldasize) {  /* array part must shrink? */ | ||||||
|  |     t->sizearray = nasize; | ||||||
|  |     /* re-insert elements from vanishing slice */ | ||||||
|  |     for (i=nasize; i<oldasize; i++) { | ||||||
|  |       if (!ttisnil(&t->array[i])) | ||||||
|  |         setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); | ||||||
|  |     } | ||||||
|  |     /* shrink array */ | ||||||
|  |     luaM_reallocvector(L, t->array, oldasize, nasize, TValue); | ||||||
|  |   } | ||||||
|  |   /* re-insert elements from hash part */ | ||||||
|  |   for (i = twoto(oldhsize) - 1; i >= 0; i--) { | ||||||
|  |     Node *old = nold+i; | ||||||
|  |     if (!ttisnil(gval(old))) | ||||||
|  |       setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); | ||||||
|  |   } | ||||||
|  |   if (nold != dummynode) | ||||||
|  |     luaM_freearray(L, nold, twoto(oldhsize), Node);  /* free old array */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaH_resizearray (lua_State *L, Table *t, int nasize) { | ||||||
|  |   int nsize = (t->node == dummynode) ? 0 : sizenode(t); | ||||||
|  |   resize(L, t, nasize, nsize); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void rehash (lua_State *L, Table *t, const TValue *ek) { | ||||||
|  |   int nasize, na; | ||||||
|  |   int nums[MAXBITS+1];  /* nums[i] = number of keys between 2^(i-1) and 2^i */ | ||||||
|  |   int i; | ||||||
|  |   int totaluse; | ||||||
|  |   for (i=0; i<=MAXBITS; i++) nums[i] = 0;  /* reset counts */ | ||||||
|  |   nasize = numusearray(t, nums);  /* count keys in array part */ | ||||||
|  |   totaluse = nasize;  /* all those keys are integer keys */ | ||||||
|  |   totaluse += numusehash(t, nums, &nasize);  /* count keys in hash part */ | ||||||
|  |   /* count extra key */ | ||||||
|  |   nasize += countint(ek, nums); | ||||||
|  |   totaluse++; | ||||||
|  |   /* compute new size for array part */ | ||||||
|  |   na = computesizes(nums, &nasize); | ||||||
|  |   /* resize the table to new computed sizes */ | ||||||
|  |   resize(L, t, nasize, totaluse - na); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** }============================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | Table *luaH_new (lua_State *L, int narray, int nhash) { | ||||||
|  |   Table *t = luaM_new(L, Table); | ||||||
|  |   luaC_link(L, obj2gco(t), LUA_TTABLE); | ||||||
|  |   t->metatable = NULL; | ||||||
|  |   t->flags = cast_byte(~0); | ||||||
|  |   /* temporary values (kept only if some malloc fails) */ | ||||||
|  |   t->array = NULL; | ||||||
|  |   t->sizearray = 0; | ||||||
|  |   t->lsizenode = 0; | ||||||
|  |   t->node = cast(Node *, dummynode); | ||||||
|  |   setarrayvector(L, t, narray); | ||||||
|  |   setnodevector(L, t, nhash); | ||||||
|  |   return t; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaH_free (lua_State *L, Table *t) { | ||||||
|  |   if (t->node != dummynode) | ||||||
|  |     luaM_freearray(L, t->node, sizenode(t), Node); | ||||||
|  |   luaM_freearray(L, t->array, t->sizearray, TValue); | ||||||
|  |   luaM_free(L, t); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static Node *getfreepos (Table *t) { | ||||||
|  |   while (t->lastfree-- > t->node) { | ||||||
|  |     if (ttisnil(gkey(t->lastfree))) | ||||||
|  |       return t->lastfree; | ||||||
|  |   } | ||||||
|  |   return NULL;  /* could not find a free place */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** inserts a new key into a hash table; first, check whether key's main  | ||||||
|  | ** position is free. If not, check whether colliding node is in its main  | ||||||
|  | ** position or not: if it is not, move colliding node to an empty place and  | ||||||
|  | ** put new key in its main position; otherwise (colliding node is in its main  | ||||||
|  | ** position), new key goes to an empty position.  | ||||||
|  | */ | ||||||
|  | static TValue *newkey (lua_State *L, Table *t, const TValue *key) { | ||||||
|  |   Node *mp = mainposition(t, key); | ||||||
|  |   if (!ttisnil(gval(mp)) || mp == dummynode) { | ||||||
|  |     Node *othern; | ||||||
|  |     Node *n = getfreepos(t);  /* get a free place */ | ||||||
|  |     if (n == NULL) {  /* cannot find a free place? */ | ||||||
|  |       rehash(L, t, key);  /* grow table */ | ||||||
|  |       return luaH_set(L, t, key);  /* re-insert key into grown table */ | ||||||
|  |     } | ||||||
|  |     lua_assert(n != dummynode); | ||||||
|  |     othern = mainposition(t, key2tval(mp)); | ||||||
|  |     if (othern != mp) {  /* is colliding node out of its main position? */ | ||||||
|  |       /* yes; move colliding node into free position */ | ||||||
|  |       while (gnext(othern) != mp) othern = gnext(othern);  /* find previous */ | ||||||
|  |       gnext(othern) = n;  /* redo the chain with `n' in place of `mp' */ | ||||||
|  |       *n = *mp;  /* copy colliding node into free pos. (mp->next also goes) */ | ||||||
|  |       gnext(mp) = NULL;  /* now `mp' is free */ | ||||||
|  |       setnilvalue(gval(mp)); | ||||||
|  |     } | ||||||
|  |     else {  /* colliding node is in its own main position */ | ||||||
|  |       /* new node will go into free position */ | ||||||
|  |       gnext(n) = gnext(mp);  /* chain new position */ | ||||||
|  |       gnext(mp) = n; | ||||||
|  |       mp = n; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; | ||||||
|  |   luaC_barriert(L, t, key); | ||||||
|  |   lua_assert(ttisnil(gval(mp))); | ||||||
|  |   return gval(mp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** search function for integers | ||||||
|  | */ | ||||||
|  | const TValue *luaH_getnum (Table *t, int key) { | ||||||
|  |   /* (1 <= key && key <= t->sizearray) */ | ||||||
|  |   if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) | ||||||
|  |     return &t->array[key-1]; | ||||||
|  |   else { | ||||||
|  |     lua_Number nk = cast_num(key); | ||||||
|  |     Node *n = hashnum(t, nk); | ||||||
|  |     do {  /* check whether `key' is somewhere in the chain */ | ||||||
|  |       if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) | ||||||
|  |         return gval(n);  /* that's it */ | ||||||
|  |       else n = gnext(n); | ||||||
|  |     } while (n); | ||||||
|  |     return luaO_nilobject; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** search function for strings | ||||||
|  | */ | ||||||
|  | const TValue *luaH_getstr (Table *t, TString *key) { | ||||||
|  |   Node *n = hashstr(t, key); | ||||||
|  |   do {  /* check whether `key' is somewhere in the chain */ | ||||||
|  |     if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) | ||||||
|  |       return gval(n);  /* that's it */ | ||||||
|  |     else n = gnext(n); | ||||||
|  |   } while (n); | ||||||
|  |   return luaO_nilobject; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** main search function | ||||||
|  | */ | ||||||
|  | const TValue *luaH_get (Table *t, const TValue *key) { | ||||||
|  |   switch (ttype(key)) { | ||||||
|  |     case LUA_TNIL: return luaO_nilobject; | ||||||
|  |     case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); | ||||||
|  |     case LUA_TNUMBER: { | ||||||
|  |       int k; | ||||||
|  |       lua_Number n = nvalue(key); | ||||||
|  |       lua_number2int(k, n); | ||||||
|  |       if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ | ||||||
|  |         return luaH_getnum(t, k);  /* use specialized version */ | ||||||
|  |       /* else go through */ | ||||||
|  |     } | ||||||
|  |     default: { | ||||||
|  |       Node *n = mainposition(t, key); | ||||||
|  |       do {  /* check whether `key' is somewhere in the chain */ | ||||||
|  |         if (luaO_rawequalObj(key2tval(n), key)) | ||||||
|  |           return gval(n);  /* that's it */ | ||||||
|  |         else n = gnext(n); | ||||||
|  |       } while (n); | ||||||
|  |       return luaO_nilobject; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { | ||||||
|  |   const TValue *p = luaH_get(t, key); | ||||||
|  |   t->flags = 0; | ||||||
|  |   if (p != luaO_nilobject) | ||||||
|  |     return cast(TValue *, p); | ||||||
|  |   else { | ||||||
|  |     if (ttisnil(key)) luaG_runerror(L, "table index is nil"); | ||||||
|  |     else if (ttisnumber(key) && luai_numisnan(nvalue(key))) | ||||||
|  |       luaG_runerror(L, "table index is NaN"); | ||||||
|  |     return newkey(L, t, key); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | TValue *luaH_setnum (lua_State *L, Table *t, int key) { | ||||||
|  |   const TValue *p = luaH_getnum(t, key); | ||||||
|  |   if (p != luaO_nilobject) | ||||||
|  |     return cast(TValue *, p); | ||||||
|  |   else { | ||||||
|  |     TValue k; | ||||||
|  |     setnvalue(&k, cast_num(key)); | ||||||
|  |     return newkey(L, t, &k); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { | ||||||
|  |   const TValue *p = luaH_getstr(t, key); | ||||||
|  |   if (p != luaO_nilobject) | ||||||
|  |     return cast(TValue *, p); | ||||||
|  |   else { | ||||||
|  |     TValue k; | ||||||
|  |     setsvalue(L, &k, key); | ||||||
|  |     return newkey(L, t, &k); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int unbound_search (Table *t, unsigned int j) { | ||||||
|  |   unsigned int i = j;  /* i is zero or a present index */ | ||||||
|  |   j++; | ||||||
|  |   /* find `i' and `j' such that i is present and j is not */ | ||||||
|  |   while (!ttisnil(luaH_getnum(t, j))) { | ||||||
|  |     i = j; | ||||||
|  |     j *= 2; | ||||||
|  |     if (j > cast(unsigned int, MAX_INT)) {  /* overflow? */ | ||||||
|  |       /* table was built with bad purposes: resort to linear search */ | ||||||
|  |       i = 1; | ||||||
|  |       while (!ttisnil(luaH_getnum(t, i))) i++; | ||||||
|  |       return i - 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   /* now do a binary search between them */ | ||||||
|  |   while (j - i > 1) { | ||||||
|  |     unsigned int m = (i+j)/2; | ||||||
|  |     if (ttisnil(luaH_getnum(t, m))) j = m; | ||||||
|  |     else i = m; | ||||||
|  |   } | ||||||
|  |   return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Try to find a boundary in table `t'. A `boundary' is an integer index | ||||||
|  | ** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). | ||||||
|  | */ | ||||||
|  | int luaH_getn (Table *t) { | ||||||
|  |   unsigned int j = t->sizearray; | ||||||
|  |   if (j > 0 && ttisnil(&t->array[j - 1])) { | ||||||
|  |     /* there is a boundary in the array part: (binary) search for it */ | ||||||
|  |     unsigned int i = 0; | ||||||
|  |     while (j - i > 1) { | ||||||
|  |       unsigned int m = (i+j)/2; | ||||||
|  |       if (ttisnil(&t->array[m - 1])) j = m; | ||||||
|  |       else i = m; | ||||||
|  |     } | ||||||
|  |     return i; | ||||||
|  |   } | ||||||
|  |   /* else must find a boundary in hash part */ | ||||||
|  |   else if (t->node == dummynode)  /* hash part is empty? */ | ||||||
|  |     return j;  /* that is easy... */ | ||||||
|  |   else return unbound_search(t, j); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #if defined(LUA_DEBUG) | ||||||
|  |  | ||||||
|  | Node *luaH_mainposition (const Table *t, const TValue *key) { | ||||||
|  |   return mainposition(t, key); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int luaH_isdummy (Node *n) { return n == dummynode; } | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										40
									
								
								src/lua/ltable.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/lua/ltable.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ltable.h,v 2.10 2006/01/10 13:13:06 roberto Exp $ | ||||||
|  | ** Lua tables (hash) | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef ltable_h | ||||||
|  | #define ltable_h | ||||||
|  |  | ||||||
|  | #include "lobject.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define gnode(t,i)	(&(t)->node[i]) | ||||||
|  | #define gkey(n)		(&(n)->i_key.nk) | ||||||
|  | #define gval(n)		(&(n)->i_val) | ||||||
|  | #define gnext(n)	((n)->i_key.nk.next) | ||||||
|  |  | ||||||
|  | #define key2tval(n)	(&(n)->i_key.tvk) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); | ||||||
|  | LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); | ||||||
|  | LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); | ||||||
|  | LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); | ||||||
|  | LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); | ||||||
|  | LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); | ||||||
|  | LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); | ||||||
|  | LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); | ||||||
|  | LUAI_FUNC void luaH_free (lua_State *L, Table *t); | ||||||
|  | LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); | ||||||
|  | LUAI_FUNC int luaH_getn (Table *t); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #if defined(LUA_DEBUG) | ||||||
|  | LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); | ||||||
|  | LUAI_FUNC int luaH_isdummy (Node *n); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										278
									
								
								src/lua/ltablib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								src/lua/ltablib.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,278 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ltablib.c,v 1.38 2005/10/23 17:38:15 roberto Exp $ | ||||||
|  | ** Library for Table Manipulation | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  | #define ltablib_c | ||||||
|  | #define LUA_LIB | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lauxlib.h" | ||||||
|  | #include "lualib.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define aux_getn(L,n)	(luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int foreachi (lua_State *L) { | ||||||
|  |   int i; | ||||||
|  |   int n = aux_getn(L, 1); | ||||||
|  |   luaL_checktype(L, 2, LUA_TFUNCTION); | ||||||
|  |   for (i=1; i <= n; i++) { | ||||||
|  |     lua_pushvalue(L, 2);  /* function */ | ||||||
|  |     lua_pushinteger(L, i);  /* 1st argument */ | ||||||
|  |     lua_rawgeti(L, 1, i);  /* 2nd argument */ | ||||||
|  |     lua_call(L, 2, 1); | ||||||
|  |     if (!lua_isnil(L, -1)) | ||||||
|  |       return 1; | ||||||
|  |     lua_pop(L, 1);  /* remove nil result */ | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int foreach (lua_State *L) { | ||||||
|  |   luaL_checktype(L, 1, LUA_TTABLE); | ||||||
|  |   luaL_checktype(L, 2, LUA_TFUNCTION); | ||||||
|  |   lua_pushnil(L);  /* first key */ | ||||||
|  |   while (lua_next(L, 1)) { | ||||||
|  |     lua_pushvalue(L, 2);  /* function */ | ||||||
|  |     lua_pushvalue(L, -3);  /* key */ | ||||||
|  |     lua_pushvalue(L, -3);  /* value */ | ||||||
|  |     lua_call(L, 2, 1); | ||||||
|  |     if (!lua_isnil(L, -1)) | ||||||
|  |       return 1; | ||||||
|  |     lua_pop(L, 2);  /* remove value and result */ | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int maxn (lua_State *L) { | ||||||
|  |   lua_Number max = 0; | ||||||
|  |   luaL_checktype(L, 1, LUA_TTABLE); | ||||||
|  |   lua_pushnil(L);  /* first key */ | ||||||
|  |   while (lua_next(L, 1)) { | ||||||
|  |     lua_pop(L, 1);  /* remove value */ | ||||||
|  |     if (lua_type(L, -1) == LUA_TNUMBER) { | ||||||
|  |       lua_Number v = lua_tonumber(L, -1); | ||||||
|  |       if (v > max) max = v; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   lua_pushnumber(L, max); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int getn (lua_State *L) { | ||||||
|  |   lua_pushinteger(L, aux_getn(L, 1)); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int setn (lua_State *L) { | ||||||
|  |   luaL_checktype(L, 1, LUA_TTABLE); | ||||||
|  | #ifndef luaL_setn | ||||||
|  |   luaL_setn(L, 1, luaL_checkint(L, 2)); | ||||||
|  | #else | ||||||
|  |   luaL_error(L, LUA_QL("setn") " is obsolete"); | ||||||
|  | #endif | ||||||
|  |   lua_pushvalue(L, 1); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int tinsert (lua_State *L) { | ||||||
|  |   int e = aux_getn(L, 1) + 1;  /* first empty element */ | ||||||
|  |   int pos;  /* where to insert new element */ | ||||||
|  |   switch (lua_gettop(L)) { | ||||||
|  |     case 2: {  /* called with only 2 arguments */ | ||||||
|  |       pos = e;  /* insert new element at the end */ | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     case 3: { | ||||||
|  |       int i; | ||||||
|  |       pos = luaL_checkint(L, 2);  /* 2nd argument is the position */ | ||||||
|  |       if (pos > e) e = pos;  /* `grow' array if necessary */ | ||||||
|  |       for (i = e; i > pos; i--) {  /* move up elements */ | ||||||
|  |         lua_rawgeti(L, 1, i-1); | ||||||
|  |         lua_rawseti(L, 1, i);  /* t[i] = t[i-1] */ | ||||||
|  |       } | ||||||
|  |       break; | ||||||
|  |     } | ||||||
|  |     default: { | ||||||
|  |       return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   luaL_setn(L, 1, e);  /* new size */ | ||||||
|  |   lua_rawseti(L, 1, pos);  /* t[pos] = v */ | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int tremove (lua_State *L) { | ||||||
|  |   int e = aux_getn(L, 1); | ||||||
|  |   int pos = luaL_optint(L, 2, e); | ||||||
|  |   if (e == 0) return 0;  /* table is `empty' */ | ||||||
|  |   luaL_setn(L, 1, e - 1);  /* t.n = n-1 */ | ||||||
|  |   lua_rawgeti(L, 1, pos);  /* result = t[pos] */ | ||||||
|  |   for ( ;pos<e; pos++) { | ||||||
|  |     lua_rawgeti(L, 1, pos+1); | ||||||
|  |     lua_rawseti(L, 1, pos);  /* t[pos] = t[pos+1] */ | ||||||
|  |   } | ||||||
|  |   lua_pushnil(L); | ||||||
|  |   lua_rawseti(L, 1, e);  /* t[e] = nil */ | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int tconcat (lua_State *L) { | ||||||
|  |   luaL_Buffer b; | ||||||
|  |   size_t lsep; | ||||||
|  |   int i, last; | ||||||
|  |   const char *sep = luaL_optlstring(L, 2, "", &lsep); | ||||||
|  |   luaL_checktype(L, 1, LUA_TTABLE); | ||||||
|  |   i = luaL_optint(L, 3, 1); | ||||||
|  |   last = luaL_opt(L, luaL_checkint, 4, luaL_getn(L, 1)); | ||||||
|  |   luaL_buffinit(L, &b); | ||||||
|  |   for (; i <= last; i++) { | ||||||
|  |     lua_rawgeti(L, 1, i); | ||||||
|  |     luaL_argcheck(L, lua_isstring(L, -1), 1, "table contains non-strings"); | ||||||
|  |     luaL_addvalue(&b); | ||||||
|  |     if (i != last) | ||||||
|  |       luaL_addlstring(&b, sep, lsep); | ||||||
|  |   } | ||||||
|  |   luaL_pushresult(&b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {====================================================== | ||||||
|  | ** Quicksort | ||||||
|  | ** (based on `Algorithms in MODULA-3', Robert Sedgewick; | ||||||
|  | **  Addison-Wesley, 1993.) | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void set2 (lua_State *L, int i, int j) { | ||||||
|  |   lua_rawseti(L, 1, i); | ||||||
|  |   lua_rawseti(L, 1, j); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sort_comp (lua_State *L, int a, int b) { | ||||||
|  |   if (!lua_isnil(L, 2)) {  /* function? */ | ||||||
|  |     int res; | ||||||
|  |     lua_pushvalue(L, 2); | ||||||
|  |     lua_pushvalue(L, a-1);  /* -1 to compensate function */ | ||||||
|  |     lua_pushvalue(L, b-2);  /* -2 to compensate function and `a' */ | ||||||
|  |     lua_call(L, 2, 1); | ||||||
|  |     res = lua_toboolean(L, -1); | ||||||
|  |     lua_pop(L, 1); | ||||||
|  |     return res; | ||||||
|  |   } | ||||||
|  |   else  /* a < b? */ | ||||||
|  |     return lua_lessthan(L, a, b); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void auxsort (lua_State *L, int l, int u) { | ||||||
|  |   while (l < u) {  /* for tail recursion */ | ||||||
|  |     int i, j; | ||||||
|  |     /* sort elements a[l], a[(l+u)/2] and a[u] */ | ||||||
|  |     lua_rawgeti(L, 1, l); | ||||||
|  |     lua_rawgeti(L, 1, u); | ||||||
|  |     if (sort_comp(L, -1, -2))  /* a[u] < a[l]? */ | ||||||
|  |       set2(L, l, u);  /* swap a[l] - a[u] */ | ||||||
|  |     else | ||||||
|  |       lua_pop(L, 2); | ||||||
|  |     if (u-l == 1) break;  /* only 2 elements */ | ||||||
|  |     i = (l+u)/2; | ||||||
|  |     lua_rawgeti(L, 1, i); | ||||||
|  |     lua_rawgeti(L, 1, l); | ||||||
|  |     if (sort_comp(L, -2, -1))  /* a[i]<a[l]? */ | ||||||
|  |       set2(L, i, l); | ||||||
|  |     else { | ||||||
|  |       lua_pop(L, 1);  /* remove a[l] */ | ||||||
|  |       lua_rawgeti(L, 1, u); | ||||||
|  |       if (sort_comp(L, -1, -2))  /* a[u]<a[i]? */ | ||||||
|  |         set2(L, i, u); | ||||||
|  |       else | ||||||
|  |         lua_pop(L, 2); | ||||||
|  |     } | ||||||
|  |     if (u-l == 2) break;  /* only 3 elements */ | ||||||
|  |     lua_rawgeti(L, 1, i);  /* Pivot */ | ||||||
|  |     lua_pushvalue(L, -1); | ||||||
|  |     lua_rawgeti(L, 1, u-1); | ||||||
|  |     set2(L, i, u-1); | ||||||
|  |     /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ | ||||||
|  |     i = l; j = u-1; | ||||||
|  |     for (;;) {  /* invariant: a[l..i] <= P <= a[j..u] */ | ||||||
|  |       /* repeat ++i until a[i] >= P */ | ||||||
|  |       while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { | ||||||
|  |         if (i>u) luaL_error(L, "invalid order function for sorting"); | ||||||
|  |         lua_pop(L, 1);  /* remove a[i] */ | ||||||
|  |       } | ||||||
|  |       /* repeat --j until a[j] <= P */ | ||||||
|  |       while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { | ||||||
|  |         if (j<l) luaL_error(L, "invalid order function for sorting"); | ||||||
|  |         lua_pop(L, 1);  /* remove a[j] */ | ||||||
|  |       } | ||||||
|  |       if (j<i) { | ||||||
|  |         lua_pop(L, 3);  /* pop pivot, a[i], a[j] */ | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       set2(L, i, j); | ||||||
|  |     } | ||||||
|  |     lua_rawgeti(L, 1, u-1); | ||||||
|  |     lua_rawgeti(L, 1, i); | ||||||
|  |     set2(L, u-1, i);  /* swap pivot (a[u-1]) with a[i] */ | ||||||
|  |     /* a[l..i-1] <= a[i] == P <= a[i+1..u] */ | ||||||
|  |     /* adjust so that smaller half is in [j..i] and larger one in [l..u] */ | ||||||
|  |     if (i-l < u-i) { | ||||||
|  |       j=l; i=i-1; l=i+2; | ||||||
|  |     } | ||||||
|  |     else { | ||||||
|  |       j=i+1; i=u; u=j-2; | ||||||
|  |     } | ||||||
|  |     auxsort(L, j, i);  /* call recursively the smaller one */ | ||||||
|  |   }  /* repeat the routine for the larger one */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int sort (lua_State *L) { | ||||||
|  |   int n = aux_getn(L, 1); | ||||||
|  |   luaL_checkstack(L, 40, "");  /* assume array is smaller than 2^40 */ | ||||||
|  |   if (!lua_isnoneornil(L, 2))  /* is there a 2nd argument? */ | ||||||
|  |     luaL_checktype(L, 2, LUA_TFUNCTION); | ||||||
|  |   lua_settop(L, 2);  /* make sure there is two arguments */ | ||||||
|  |   auxsort(L, 1, n); | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* }====================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const luaL_Reg tab_funcs[] = { | ||||||
|  |   {"concat", tconcat}, | ||||||
|  |   {"foreach", foreach}, | ||||||
|  |   {"foreachi", foreachi}, | ||||||
|  |   {"getn", getn}, | ||||||
|  |   {"maxn", maxn}, | ||||||
|  |   {"insert", tinsert}, | ||||||
|  |   {"remove", tremove}, | ||||||
|  |   {"setn", setn}, | ||||||
|  |   {"sort", sort}, | ||||||
|  |   {NULL, NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUALIB_API int luaopen_table (lua_State *L) { | ||||||
|  |   luaL_register(L, LUA_TABLIBNAME, tab_funcs); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										75
									
								
								src/lua/ltm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/lua/ltm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,75 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ltm.c,v 2.8 2006/01/10 12:50:00 roberto Exp $ | ||||||
|  | ** Tag methods | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define ltm_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lstate.h" | ||||||
|  | #include "lstring.h" | ||||||
|  | #include "ltable.h" | ||||||
|  | #include "ltm.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | const char *const luaT_typenames[] = { | ||||||
|  |   "nil", "boolean", "userdata", "number", | ||||||
|  |   "string", "table", "function", "userdata", "thread", | ||||||
|  |   "proto", "upval" | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaT_init (lua_State *L) { | ||||||
|  |   static const char *const luaT_eventname[] = {  /* ORDER TM */ | ||||||
|  |     "__index", "__newindex", | ||||||
|  |     "__gc", "__mode", "__eq", | ||||||
|  |     "__add", "__sub", "__mul", "__div", "__mod", | ||||||
|  |     "__pow", "__unm", "__len", "__lt", "__le", | ||||||
|  |     "__concat", "__call" | ||||||
|  |   }; | ||||||
|  |   int i; | ||||||
|  |   for (i=0; i<TM_N; i++) { | ||||||
|  |     G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]); | ||||||
|  |     luaS_fix(G(L)->tmname[i]);  /* never collect these names */ | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** function to be used with macro "fasttm": optimized for absence of | ||||||
|  | ** tag methods | ||||||
|  | */ | ||||||
|  | const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { | ||||||
|  |   const TValue *tm = luaH_getstr(events, ename); | ||||||
|  |   lua_assert(event <= TM_EQ); | ||||||
|  |   if (ttisnil(tm)) {  /* no tag method? */ | ||||||
|  |     events->flags |= cast_byte(1u<<event);  /* cache this fact */ | ||||||
|  |     return NULL; | ||||||
|  |   } | ||||||
|  |   else return tm; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) { | ||||||
|  |   Table *mt; | ||||||
|  |   switch (ttype(o)) { | ||||||
|  |     case LUA_TTABLE: | ||||||
|  |       mt = hvalue(o)->metatable; | ||||||
|  |       break; | ||||||
|  |     case LUA_TUSERDATA: | ||||||
|  |       mt = uvalue(o)->metatable; | ||||||
|  |       break; | ||||||
|  |     default: | ||||||
|  |       mt = G(L)->mt[ttype(o)]; | ||||||
|  |   } | ||||||
|  |   return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										54
									
								
								src/lua/ltm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/lua/ltm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: ltm.h,v 2.6 2005/06/06 13:30:25 roberto Exp $ | ||||||
|  | ** Tag methods | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef ltm_h | ||||||
|  | #define ltm_h | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "lobject.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * WARNING: if you change the order of this enumeration, | ||||||
|  | * grep "ORDER TM" | ||||||
|  | */ | ||||||
|  | typedef enum { | ||||||
|  |   TM_INDEX, | ||||||
|  |   TM_NEWINDEX, | ||||||
|  |   TM_GC, | ||||||
|  |   TM_MODE, | ||||||
|  |   TM_EQ,  /* last tag method with `fast' access */ | ||||||
|  |   TM_ADD, | ||||||
|  |   TM_SUB, | ||||||
|  |   TM_MUL, | ||||||
|  |   TM_DIV, | ||||||
|  |   TM_MOD, | ||||||
|  |   TM_POW, | ||||||
|  |   TM_UNM, | ||||||
|  |   TM_LEN, | ||||||
|  |   TM_LT, | ||||||
|  |   TM_LE, | ||||||
|  |   TM_CONCAT, | ||||||
|  |   TM_CALL, | ||||||
|  |   TM_N		/* number of elements in the enum */ | ||||||
|  | } TMS; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define gfasttm(g,et,e) ((et) == NULL ? NULL : \ | ||||||
|  |   ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) | ||||||
|  |  | ||||||
|  | #define fasttm(l,et,e)	gfasttm(G(l), et, e) | ||||||
|  |  | ||||||
|  | LUAI_DATA const char *const luaT_typenames[]; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); | ||||||
|  | LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, | ||||||
|  |                                                        TMS event); | ||||||
|  | LUAI_FUNC void luaT_init (lua_State *L); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										377
									
								
								src/lua/lua.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								src/lua/lua.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,377 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lua.c,v 1.157 2005/12/29 16:23:32 roberto Exp $ | ||||||
|  | ** Lua stand-alone interpreter | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <signal.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lua_c | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lauxlib.h" | ||||||
|  | #include "lualib.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static lua_State *globalL = NULL; | ||||||
|  |  | ||||||
|  | static const char *progname = LUA_PROGNAME; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void lstop (lua_State *L, lua_Debug *ar) { | ||||||
|  |   (void)ar;  /* unused arg. */ | ||||||
|  |   lua_sethook(L, NULL, 0, 0); | ||||||
|  |   luaL_error(L, "interrupted!"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void laction (int i) { | ||||||
|  |   signal(i, SIG_DFL); /* if another SIGINT happens before lstop, | ||||||
|  |                               terminate process (default action) */ | ||||||
|  |   lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void print_usage (void) { | ||||||
|  |   fprintf(stderr, | ||||||
|  |   "usage: %s [options] [script [args]].\n" | ||||||
|  |   "Available options are:\n" | ||||||
|  |   "  -e stat  execute string " LUA_QL("stat") "\n" | ||||||
|  |   "  -l name  require library " LUA_QL("name") "\n" | ||||||
|  |   "  -i       enter interactive mode after executing " LUA_QL("script") "\n" | ||||||
|  |   "  -v       show version information\n" | ||||||
|  |   "  --       stop handling options\n" | ||||||
|  |   "  -        execute stdin and stop handling options\n" | ||||||
|  |   , | ||||||
|  |   progname); | ||||||
|  |   fflush(stderr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void l_message (const char *pname, const char *msg) { | ||||||
|  |   if (pname) fprintf(stderr, "%s: ", pname); | ||||||
|  |   fprintf(stderr, "%s\n", msg); | ||||||
|  |   fflush(stderr); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int report (lua_State *L, int status) { | ||||||
|  |   if (status && !lua_isnil(L, -1)) { | ||||||
|  |     const char *msg = lua_tostring(L, -1); | ||||||
|  |     if (msg == NULL) msg = "(error object is not a string)"; | ||||||
|  |     l_message(progname, msg); | ||||||
|  |     lua_pop(L, 1); | ||||||
|  |   } | ||||||
|  |   return status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int traceback (lua_State *L) { | ||||||
|  |   lua_getfield(L, LUA_GLOBALSINDEX, "debug"); | ||||||
|  |   if (!lua_istable(L, -1)) { | ||||||
|  |     lua_pop(L, 1); | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   lua_getfield(L, -1, "traceback"); | ||||||
|  |   if (!lua_isfunction(L, -1)) { | ||||||
|  |     lua_pop(L, 2); | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  |   lua_pushvalue(L, 1);  /* pass error message */ | ||||||
|  |   lua_pushinteger(L, 2);  /* skip this function and traceback */ | ||||||
|  |   lua_call(L, 2, 1);  /* call debug.traceback */ | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int docall (lua_State *L, int narg, int clear) { | ||||||
|  |   int status; | ||||||
|  |   int base = lua_gettop(L) - narg;  /* function index */ | ||||||
|  |   lua_pushcfunction(L, traceback);  /* push traceback function */ | ||||||
|  |   lua_insert(L, base);  /* put it under chunk and args */ | ||||||
|  |   signal(SIGINT, laction); | ||||||
|  |   status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); | ||||||
|  |   signal(SIGINT, SIG_DFL); | ||||||
|  |   lua_remove(L, base);  /* remove traceback function */ | ||||||
|  |   /* force a complete garbage collection in case of errors */ | ||||||
|  |   if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); | ||||||
|  |   return status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void print_version (void) { | ||||||
|  |   l_message(NULL, LUA_VERSION "  " LUA_COPYRIGHT); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int getargs (lua_State *L, char **argv, int n) { | ||||||
|  |   int narg; | ||||||
|  |   int i; | ||||||
|  |   int argc = 0; | ||||||
|  |   while (argv[argc]) argc++;  /* count total number of arguments */ | ||||||
|  |   narg = argc - (n + 1);  /* number of arguments to the script */ | ||||||
|  |   luaL_checkstack(L, narg + 3, "too many arguments to script"); | ||||||
|  |   for (i=n+1; i < argc; i++) | ||||||
|  |     lua_pushstring(L, argv[i]); | ||||||
|  |   lua_createtable(L, narg, n + 1); | ||||||
|  |   for (i=0; i < argc; i++) { | ||||||
|  |     lua_pushstring(L, argv[i]); | ||||||
|  |     lua_rawseti(L, -2, i - n); | ||||||
|  |   } | ||||||
|  |   return narg; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int dofile (lua_State *L, const char *name) { | ||||||
|  |   int status = luaL_loadfile(L, name) || docall(L, 0, 1); | ||||||
|  |   return report(L, status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int dostring (lua_State *L, const char *s, const char *name) { | ||||||
|  |   int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); | ||||||
|  |   return report(L, status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int dolibrary (lua_State *L, const char *name) { | ||||||
|  |   lua_getglobal(L, "require"); | ||||||
|  |   lua_pushstring(L, name); | ||||||
|  |   return report(L, lua_pcall(L, 1, 0, 0)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const char *get_prompt (lua_State *L, int firstline) { | ||||||
|  |   const char *p; | ||||||
|  |   lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); | ||||||
|  |   p = lua_tostring(L, -1); | ||||||
|  |   if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); | ||||||
|  |   lua_pop(L, 1);  /* remove global */ | ||||||
|  |   return p; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int incomplete (lua_State *L, int status) { | ||||||
|  |   if (status == LUA_ERRSYNTAX) { | ||||||
|  |     size_t lmsg; | ||||||
|  |     const char *msg = lua_tolstring(L, -1, &lmsg); | ||||||
|  |     const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1); | ||||||
|  |     if (strstr(msg, LUA_QL("<eof>")) == tp) { | ||||||
|  |       lua_pop(L, 1); | ||||||
|  |       return 1; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0;  /* else... */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int pushline (lua_State *L, int firstline) { | ||||||
|  |   char buffer[LUA_MAXINPUT]; | ||||||
|  |   char *b = buffer; | ||||||
|  |   size_t l; | ||||||
|  |   const char *prmt = get_prompt(L, firstline); | ||||||
|  |   if (lua_readline(L, b, prmt) == 0) | ||||||
|  |     return 0;  /* no input */ | ||||||
|  |   l = strlen(b); | ||||||
|  |   if (l > 0 && b[l-1] == '\n')  /* line ends with newline? */ | ||||||
|  |     b[l-1] = '\0';  /* remove it */ | ||||||
|  |   if (firstline && b[0] == '=')  /* first line starts with `=' ? */ | ||||||
|  |     lua_pushfstring(L, "return %s", b+1);  /* change it to `return' */ | ||||||
|  |   else | ||||||
|  |     lua_pushstring(L, b); | ||||||
|  |   lua_freeline(L, b); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int loadline (lua_State *L) { | ||||||
|  |   int status; | ||||||
|  |   lua_settop(L, 0); | ||||||
|  |   if (!pushline(L, 1)) | ||||||
|  |     return -1;  /* no input */ | ||||||
|  |   for (;;) {  /* repeat until gets a complete line */ | ||||||
|  |     status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); | ||||||
|  |     if (!incomplete(L, status)) break;  /* cannot try to add lines? */ | ||||||
|  |     if (!pushline(L, 0))  /* no more input? */ | ||||||
|  |       return -1; | ||||||
|  |     lua_pushliteral(L, "\n");  /* add a new line... */ | ||||||
|  |     lua_insert(L, -2);  /* ...between the two lines */ | ||||||
|  |     lua_concat(L, 3);  /* join them */ | ||||||
|  |   } | ||||||
|  |   lua_saveline(L, 1); | ||||||
|  |   lua_remove(L, 1);  /* remove line */ | ||||||
|  |   return status; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void dotty (lua_State *L) { | ||||||
|  |   int status; | ||||||
|  |   const char *oldprogname = progname; | ||||||
|  |   progname = NULL; | ||||||
|  |   while ((status = loadline(L)) != -1) { | ||||||
|  |     if (status == 0) status = docall(L, 0, 0); | ||||||
|  |     report(L, status); | ||||||
|  |     if (status == 0 && lua_gettop(L) > 0) {  /* any result to print? */ | ||||||
|  |       lua_getglobal(L, "print"); | ||||||
|  |       lua_insert(L, 1); | ||||||
|  |       if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) | ||||||
|  |         l_message(progname, lua_pushfstring(L, | ||||||
|  |                                "error calling " LUA_QL("print") " (%s)", | ||||||
|  |                                lua_tostring(L, -1))); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   lua_settop(L, 0);  /* clear stack */ | ||||||
|  |   fputs("\n", stdout); | ||||||
|  |   fflush(stdout); | ||||||
|  |   progname = oldprogname; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int handle_script (lua_State *L, char **argv, int n) { | ||||||
|  |   int status; | ||||||
|  |   const char *fname; | ||||||
|  |   int narg = getargs(L, argv, n);  /* collect arguments */ | ||||||
|  |   lua_setglobal(L, "arg"); | ||||||
|  |   fname = argv[n]; | ||||||
|  |   if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0)  | ||||||
|  |     fname = NULL;  /* stdin */ | ||||||
|  |   status = luaL_loadfile(L, fname); | ||||||
|  |   lua_insert(L, -(narg+1)); | ||||||
|  |   if (status == 0) | ||||||
|  |     status = docall(L, narg, 0); | ||||||
|  |   else | ||||||
|  |     lua_pop(L, narg);       | ||||||
|  |   return report(L, status); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int collectargs (char **argv, int *pi, int *pv, int *pe) { | ||||||
|  |   int i; | ||||||
|  |   for (i = 1; argv[i] != NULL; i++) { | ||||||
|  |     if (argv[i][0] != '-')  /* not an option? */ | ||||||
|  |         return i; | ||||||
|  |     switch (argv[i][1]) {  /* option */ | ||||||
|  |       case '-': return (argv[i+1] != NULL ? i+1 : 0); | ||||||
|  |       case '\0': return i; | ||||||
|  |       case 'i': *pi = 1;  /* go through */ | ||||||
|  |       case 'v': *pv = 1; break; | ||||||
|  |       case 'e': *pe = 1;  /* go through */ | ||||||
|  |       case 'l': | ||||||
|  |         if (argv[i][2] == '\0') { | ||||||
|  |           i++; | ||||||
|  |           if (argv[i] == NULL) return -1; | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |       default: return -1;  /* invalid option */ | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int runargs (lua_State *L, char **argv, int n) { | ||||||
|  |   int i; | ||||||
|  |   for (i = 1; i < n; i++) { | ||||||
|  |     if (argv[i] == NULL) continue; | ||||||
|  |     lua_assert(argv[i][0] == '-'); | ||||||
|  |     switch (argv[i][1]) {  /* option */ | ||||||
|  |       case 'e': { | ||||||
|  |         const char *chunk = argv[i] + 2; | ||||||
|  |         if (*chunk == '\0') chunk = argv[++i]; | ||||||
|  |         lua_assert(chunk != NULL); | ||||||
|  |         if (dostring(L, chunk, "=(command line)") != 0) | ||||||
|  |           return 1; | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       case 'l': { | ||||||
|  |         const char *filename = argv[i] + 2; | ||||||
|  |         if (*filename == '\0') filename = argv[++i]; | ||||||
|  |         lua_assert(filename != NULL); | ||||||
|  |         if (dolibrary(L, filename)) | ||||||
|  |           return 1;  /* stop if file fails */ | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       default: break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int handle_luainit (lua_State *L) { | ||||||
|  |   const char *init = getenv("LUA_INIT"); | ||||||
|  |   if (init == NULL) return 0;  /* status OK */ | ||||||
|  |   else if (init[0] == '@') | ||||||
|  |     return dofile(L, init+1); | ||||||
|  |   else | ||||||
|  |     return dostring(L, init, "=LUA_INIT"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct Smain { | ||||||
|  |   int argc; | ||||||
|  |   char **argv; | ||||||
|  |   int status; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int pmain (lua_State *L) { | ||||||
|  |   struct Smain *s = (struct Smain *)lua_touserdata(L, 1); | ||||||
|  |   char **argv = s->argv; | ||||||
|  |   int script; | ||||||
|  |   int has_i = 0, has_v = 0, has_e = 0; | ||||||
|  |   globalL = L; | ||||||
|  |   if (argv[0] && argv[0][0]) progname = argv[0]; | ||||||
|  |   lua_gc(L, LUA_GCSTOP, 0);  /* stop collector during initialization */ | ||||||
|  |   luaL_openlibs(L);  /* open libraries */ | ||||||
|  |   lua_gc(L, LUA_GCRESTART, 0); | ||||||
|  |   s->status = handle_luainit(L); | ||||||
|  |   if (s->status != 0) return 0; | ||||||
|  |   script = collectargs(argv, &has_i, &has_v, &has_e); | ||||||
|  |   if (script < 0) {  /* invalid args? */ | ||||||
|  |     print_usage(); | ||||||
|  |     s->status = 1; | ||||||
|  |     return 0; | ||||||
|  |   } | ||||||
|  |   if (has_v) print_version(); | ||||||
|  |   s->status = runargs(L, argv, (script > 0) ? script : s->argc); | ||||||
|  |   if (s->status != 0) return 0; | ||||||
|  |   if (script) | ||||||
|  |     s->status = handle_script(L, argv, script); | ||||||
|  |   if (s->status != 0) return 0; | ||||||
|  |   if (has_i) | ||||||
|  |     dotty(L); | ||||||
|  |   else if (script == 0 && !has_e && !has_v) { | ||||||
|  |     if (lua_stdin_is_tty()) { | ||||||
|  |       print_version(); | ||||||
|  |       dotty(L); | ||||||
|  |     } | ||||||
|  |     else dofile(L, NULL);  /* executes stdin as a file */ | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int main (int argc, char **argv) { | ||||||
|  |   int status; | ||||||
|  |   struct Smain s; | ||||||
|  |   lua_State *L = lua_open();  /* create state */ | ||||||
|  |   if (L == NULL) { | ||||||
|  |     l_message(argv[0], "cannot create state: not enough memory"); | ||||||
|  |     return EXIT_FAILURE; | ||||||
|  |   } | ||||||
|  |   s.argc = argc; | ||||||
|  |   s.argv = argv; | ||||||
|  |   status = lua_cpcall(L, &pmain, &s); | ||||||
|  |   report(L, status); | ||||||
|  |   lua_close(L); | ||||||
|  |   return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										384
									
								
								src/lua/lua.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										384
									
								
								src/lua/lua.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,384 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lua.h,v 1.216 2006/01/10 12:50:13 roberto Exp $ | ||||||
|  | ** Lua - An Extensible Extension Language | ||||||
|  | ** Lua.org, PUC-Rio, Brazil (http://www.lua.org) | ||||||
|  | ** See Copyright Notice at the end of this file | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lua_h | ||||||
|  | #define lua_h | ||||||
|  |  | ||||||
|  | #include <stdarg.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "luaconf.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define LUA_VERSION	"Lua 5.1" | ||||||
|  | #define LUA_VERSION_NUM	501 | ||||||
|  | #define LUA_COPYRIGHT	"Copyright (C) 1994-2006 Lua.org, PUC-Rio" | ||||||
|  | #define LUA_AUTHORS 	"R. Ierusalimschy, L. H. de Figueiredo & W. Celes" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* mark for precompiled code (`<esc>Lua') */ | ||||||
|  | #define	LUA_SIGNATURE	"\033Lua" | ||||||
|  |  | ||||||
|  | /* option for multiple returns in `lua_pcall' and `lua_call' */ | ||||||
|  | #define LUA_MULTRET	(-1) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** pseudo-indices | ||||||
|  | */ | ||||||
|  | #define LUA_REGISTRYINDEX	(-10000) | ||||||
|  | #define LUA_ENVIRONINDEX	(-10001) | ||||||
|  | #define LUA_GLOBALSINDEX	(-10002) | ||||||
|  | #define lua_upvalueindex(i)	(LUA_GLOBALSINDEX-(i)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* thread status; 0 is OK */ | ||||||
|  | #define LUA_YIELD	1 | ||||||
|  | #define LUA_ERRRUN	2 | ||||||
|  | #define LUA_ERRSYNTAX	3 | ||||||
|  | #define LUA_ERRMEM	4 | ||||||
|  | #define LUA_ERRERR	5 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | typedef struct lua_State lua_State; | ||||||
|  |  | ||||||
|  | typedef int (*lua_CFunction) (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** functions that read/write blocks when loading/dumping Lua chunks | ||||||
|  | */ | ||||||
|  | typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); | ||||||
|  |  | ||||||
|  | typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** prototype for memory-allocation functions | ||||||
|  | */ | ||||||
|  | typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** basic types | ||||||
|  | */ | ||||||
|  | #define LUA_TNONE		(-1) | ||||||
|  |  | ||||||
|  | #define LUA_TNIL		0 | ||||||
|  | #define LUA_TBOOLEAN		1 | ||||||
|  | #define LUA_TLIGHTUSERDATA	2 | ||||||
|  | #define LUA_TNUMBER		3 | ||||||
|  | #define LUA_TSTRING		4 | ||||||
|  | #define LUA_TTABLE		5 | ||||||
|  | #define LUA_TFUNCTION		6 | ||||||
|  | #define LUA_TUSERDATA		7 | ||||||
|  | #define LUA_TTHREAD		8 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* minimum Lua stack available to a C function */ | ||||||
|  | #define LUA_MINSTACK	20 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** generic extra include file | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USER_H) | ||||||
|  | #include LUA_USER_H | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* type of numbers in Lua */ | ||||||
|  | typedef LUA_NUMBER lua_Number; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* type for integer functions */ | ||||||
|  | typedef LUA_INTEGER lua_Integer; | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** state manipulation | ||||||
|  | */ | ||||||
|  | LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); | ||||||
|  | LUA_API void       (lua_close) (lua_State *L); | ||||||
|  | LUA_API lua_State *(lua_newthread) (lua_State *L); | ||||||
|  |  | ||||||
|  | LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** basic stack manipulation | ||||||
|  | */ | ||||||
|  | LUA_API int   (lua_gettop) (lua_State *L); | ||||||
|  | LUA_API void  (lua_settop) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_pushvalue) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_remove) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_insert) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_replace) (lua_State *L, int idx); | ||||||
|  | LUA_API int   (lua_checkstack) (lua_State *L, int sz); | ||||||
|  |  | ||||||
|  | LUA_API void  (lua_xmove) (lua_State *from, lua_State *to, int n); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** access functions (stack -> C) | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | LUA_API int             (lua_isnumber) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_isstring) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_iscfunction) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_isuserdata) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_type) (lua_State *L, int idx); | ||||||
|  | LUA_API const char     *(lua_typename) (lua_State *L, int tp); | ||||||
|  |  | ||||||
|  | LUA_API int            (lua_equal) (lua_State *L, int idx1, int idx2); | ||||||
|  | LUA_API int            (lua_rawequal) (lua_State *L, int idx1, int idx2); | ||||||
|  | LUA_API int            (lua_lessthan) (lua_State *L, int idx1, int idx2); | ||||||
|  |  | ||||||
|  | LUA_API lua_Number      (lua_tonumber) (lua_State *L, int idx); | ||||||
|  | LUA_API lua_Integer     (lua_tointeger) (lua_State *L, int idx); | ||||||
|  | LUA_API int             (lua_toboolean) (lua_State *L, int idx); | ||||||
|  | LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len); | ||||||
|  | LUA_API size_t          (lua_objlen) (lua_State *L, int idx); | ||||||
|  | LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx); | ||||||
|  | LUA_API void	       *(lua_touserdata) (lua_State *L, int idx); | ||||||
|  | LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx); | ||||||
|  | LUA_API const void     *(lua_topointer) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** push functions (C -> stack) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_pushnil) (lua_State *L); | ||||||
|  | LUA_API void  (lua_pushnumber) (lua_State *L, lua_Number n); | ||||||
|  | LUA_API void  (lua_pushinteger) (lua_State *L, lua_Integer n); | ||||||
|  | LUA_API void  (lua_pushlstring) (lua_State *L, const char *s, size_t l); | ||||||
|  | LUA_API void  (lua_pushstring) (lua_State *L, const char *s); | ||||||
|  | LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, | ||||||
|  |                                                       va_list argp); | ||||||
|  | LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); | ||||||
|  | LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); | ||||||
|  | LUA_API void  (lua_pushboolean) (lua_State *L, int b); | ||||||
|  | LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p); | ||||||
|  | LUA_API int   (lua_pushthread) (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** get functions (Lua -> stack) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_gettable) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_getfield) (lua_State *L, int idx, const char *k); | ||||||
|  | LUA_API void  (lua_rawget) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_rawgeti) (lua_State *L, int idx, int n); | ||||||
|  | LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec); | ||||||
|  | LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); | ||||||
|  | LUA_API int   (lua_getmetatable) (lua_State *L, int objindex); | ||||||
|  | LUA_API void  (lua_getfenv) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** set functions (stack -> Lua) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_settable) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k); | ||||||
|  | LUA_API void  (lua_rawset) (lua_State *L, int idx); | ||||||
|  | LUA_API void  (lua_rawseti) (lua_State *L, int idx, int n); | ||||||
|  | LUA_API int   (lua_setmetatable) (lua_State *L, int objindex); | ||||||
|  | LUA_API int   (lua_setfenv) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** `load' and `call' functions (load and run Lua code) | ||||||
|  | */ | ||||||
|  | LUA_API void  (lua_call) (lua_State *L, int nargs, int nresults); | ||||||
|  | LUA_API int   (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); | ||||||
|  | LUA_API int   (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); | ||||||
|  | LUA_API int   (lua_load) (lua_State *L, lua_Reader reader, void *dt, | ||||||
|  |                                         const char *chunkname); | ||||||
|  |  | ||||||
|  | LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** coroutine functions | ||||||
|  | */ | ||||||
|  | LUA_API int  (lua_yield) (lua_State *L, int nresults); | ||||||
|  | LUA_API int  (lua_resume) (lua_State *L, int narg); | ||||||
|  | LUA_API int  (lua_status) (lua_State *L); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** garbage-collection function and options | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define LUA_GCSTOP		0 | ||||||
|  | #define LUA_GCRESTART		1 | ||||||
|  | #define LUA_GCCOLLECT		2 | ||||||
|  | #define LUA_GCCOUNT		3 | ||||||
|  | #define LUA_GCCOUNTB		4 | ||||||
|  | #define LUA_GCSTEP		5 | ||||||
|  | #define LUA_GCSETPAUSE		6 | ||||||
|  | #define LUA_GCSETSTEPMUL	7 | ||||||
|  |  | ||||||
|  | LUA_API int (lua_gc) (lua_State *L, int what, int data); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** miscellaneous functions | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | LUA_API int   (lua_error) (lua_State *L); | ||||||
|  |  | ||||||
|  | LUA_API int   (lua_next) (lua_State *L, int idx); | ||||||
|  |  | ||||||
|  | LUA_API void  (lua_concat) (lua_State *L, int n); | ||||||
|  |  | ||||||
|  | LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); | ||||||
|  | LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /*  | ||||||
|  | ** =============================================================== | ||||||
|  | ** some useful macros | ||||||
|  | ** =============================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define lua_pop(L,n)		lua_settop(L, -(n)-1) | ||||||
|  |  | ||||||
|  | #define lua_newtable(L)		lua_createtable(L, 0, 0) | ||||||
|  |  | ||||||
|  | #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) | ||||||
|  |  | ||||||
|  | #define lua_pushcfunction(L,f)	lua_pushcclosure(L, (f), 0) | ||||||
|  |  | ||||||
|  | #define lua_strlen(L,i)		lua_objlen(L, (i)) | ||||||
|  |  | ||||||
|  | #define lua_isfunction(L,n)	(lua_type(L, (n)) == LUA_TFUNCTION) | ||||||
|  | #define lua_istable(L,n)	(lua_type(L, (n)) == LUA_TTABLE) | ||||||
|  | #define lua_islightuserdata(L,n)	(lua_type(L, (n)) == LUA_TLIGHTUSERDATA) | ||||||
|  | #define lua_isnil(L,n)		(lua_type(L, (n)) == LUA_TNIL) | ||||||
|  | #define lua_isboolean(L,n)	(lua_type(L, (n)) == LUA_TBOOLEAN) | ||||||
|  | #define lua_isthread(L,n)	(lua_type(L, (n)) == LUA_TTHREAD) | ||||||
|  | #define lua_isnone(L,n)		(lua_type(L, (n)) == LUA_TNONE) | ||||||
|  | #define lua_isnoneornil(L, n)	(lua_type(L, (n)) <= 0) | ||||||
|  |  | ||||||
|  | #define lua_pushliteral(L, s)	\ | ||||||
|  | 	lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) | ||||||
|  |  | ||||||
|  | #define lua_setglobal(L,s)	lua_setfield(L, LUA_GLOBALSINDEX, (s)) | ||||||
|  | #define lua_getglobal(L,s)	lua_getfield(L, LUA_GLOBALSINDEX, (s)) | ||||||
|  |  | ||||||
|  | #define lua_tostring(L,i)	lua_tolstring(L, (i), NULL) | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** compatibility macros and functions | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define lua_open()	luaL_newstate() | ||||||
|  |  | ||||||
|  | #define lua_getregistry(L)	lua_pushvalue(L, LUA_REGISTRYINDEX) | ||||||
|  |  | ||||||
|  | #define lua_getgccount(L)	lua_gc(L, LUA_GCCOUNT, 0) | ||||||
|  |  | ||||||
|  | #define lua_Chunkreader		lua_Reader | ||||||
|  | #define lua_Chunkwriter		lua_Writer | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {====================================================================== | ||||||
|  | ** Debug API | ||||||
|  | ** ======================================================================= | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Event codes | ||||||
|  | */ | ||||||
|  | #define LUA_HOOKCALL	0 | ||||||
|  | #define LUA_HOOKRET	1 | ||||||
|  | #define LUA_HOOKLINE	2 | ||||||
|  | #define LUA_HOOKCOUNT	3 | ||||||
|  | #define LUA_HOOKTAILRET 4 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Event masks | ||||||
|  | */ | ||||||
|  | #define LUA_MASKCALL	(1 << LUA_HOOKCALL) | ||||||
|  | #define LUA_MASKRET	(1 << LUA_HOOKRET) | ||||||
|  | #define LUA_MASKLINE	(1 << LUA_HOOKLINE) | ||||||
|  | #define LUA_MASKCOUNT	(1 << LUA_HOOKCOUNT) | ||||||
|  |  | ||||||
|  | typedef struct lua_Debug lua_Debug;  /* activation record */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Functions to be called by the debuger in specific events */ | ||||||
|  | typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); | ||||||
|  | LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); | ||||||
|  | LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); | ||||||
|  | LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); | ||||||
|  | LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); | ||||||
|  | LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); | ||||||
|  |  | ||||||
|  | LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); | ||||||
|  | LUA_API lua_Hook lua_gethook (lua_State *L); | ||||||
|  | LUA_API int lua_gethookmask (lua_State *L); | ||||||
|  | LUA_API int lua_gethookcount (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | struct lua_Debug { | ||||||
|  |   int event; | ||||||
|  |   const char *name;	/* (n) */ | ||||||
|  |   const char *namewhat;	/* (n) `global', `local', `field', `method' */ | ||||||
|  |   const char *what;	/* (S) `Lua', `C', `main', `tail' */ | ||||||
|  |   const char *source;	/* (S) */ | ||||||
|  |   int currentline;	/* (l) */ | ||||||
|  |   int nups;		/* (u) number of upvalues */ | ||||||
|  |   int linedefined;	/* (S) */ | ||||||
|  |   int lastlinedefined;	/* (S) */ | ||||||
|  |   char short_src[LUA_IDSIZE]; /* (S) */ | ||||||
|  |   /* private part */ | ||||||
|  |   int i_ci;  /* active function */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* }====================================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /****************************************************************************** | ||||||
|  | * Copyright (C) 1994-2006 Lua.org, PUC-Rio.  All rights reserved. | ||||||
|  | * | ||||||
|  | * 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. | ||||||
|  | ******************************************************************************/ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										196
									
								
								src/lua/luac.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/lua/luac.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: luac.c,v 1.52 2005/11/11 14:03:13 lhf Exp $ | ||||||
|  | ** Lua compiler (saves bytecodes to files; also list bytecodes) | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <errno.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define luac_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  |  | ||||||
|  | #include "ldo.h" | ||||||
|  | #include "lfunc.h" | ||||||
|  | #include "lmem.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lopcodes.h" | ||||||
|  | #include "lstring.h" | ||||||
|  | #include "lundump.h" | ||||||
|  |  | ||||||
|  | #define PROGNAME	"luac"		/* default program name */ | ||||||
|  | #define	OUTPUT		PROGNAME ".out"	/* default output file */ | ||||||
|  |  | ||||||
|  | static int listing=0;			/* list bytecodes? */ | ||||||
|  | static int dumping=1;			/* dump bytecodes? */ | ||||||
|  | static int stripping=0;			/* strip debug information? */ | ||||||
|  | static char Output[]={ OUTPUT };	/* default output file name */ | ||||||
|  | static const char* output=Output;	/* actual output file name */ | ||||||
|  | static const char* progname=PROGNAME;	/* actual program name */ | ||||||
|  |  | ||||||
|  | static void fatal(const char* message) | ||||||
|  | { | ||||||
|  |  fprintf(stderr,"%s: %s\n",progname,message); | ||||||
|  |  exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void cannot(const char* what) | ||||||
|  | { | ||||||
|  |  fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); | ||||||
|  |  exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void usage(const char* message) | ||||||
|  | { | ||||||
|  |  if (*message=='-') | ||||||
|  |   fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); | ||||||
|  |  else | ||||||
|  |   fprintf(stderr,"%s: %s\n",progname,message); | ||||||
|  |  fprintf(stderr, | ||||||
|  |  "usage: %s [options] [filenames].\n" | ||||||
|  |  "Available options are:\n" | ||||||
|  |  "  -        process stdin\n" | ||||||
|  |  "  -l       list\n" | ||||||
|  |  "  -o name  output to file " LUA_QL("name") " (default is \"%s\")\n" | ||||||
|  |  "  -p       parse only\n" | ||||||
|  |  "  -s       strip debug information\n" | ||||||
|  |  "  -v       show version information\n" | ||||||
|  |  "  --       stop handling options\n", | ||||||
|  |  progname,Output); | ||||||
|  |  exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define	IS(s)	(strcmp(argv[i],s)==0) | ||||||
|  |  | ||||||
|  | static int doargs(int argc, char* argv[]) | ||||||
|  | { | ||||||
|  |  int i; | ||||||
|  |  if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; | ||||||
|  |  for (i=1; i<argc; i++) | ||||||
|  |  { | ||||||
|  |   if (*argv[i]!='-')			/* end of options; keep it */ | ||||||
|  |    break; | ||||||
|  |   else if (IS("--"))			/* end of options; skip it */ | ||||||
|  |   { | ||||||
|  |    ++i; | ||||||
|  |    break; | ||||||
|  |   } | ||||||
|  |   else if (IS("-"))			/* end of options; use stdin */ | ||||||
|  |    break; | ||||||
|  |   else if (IS("-l"))			/* list */ | ||||||
|  |    ++listing; | ||||||
|  |   else if (IS("-o"))			/* output file */ | ||||||
|  |   { | ||||||
|  |    output=argv[++i]; | ||||||
|  |    if (output==NULL || *output==0) usage(LUA_QL("-o") " needs argument"); | ||||||
|  |    if (IS("-")) output=NULL; | ||||||
|  |   } | ||||||
|  |   else if (IS("-p"))			/* parse only */ | ||||||
|  |    dumping=0; | ||||||
|  |   else if (IS("-s"))			/* strip debug information */ | ||||||
|  |    stripping=1; | ||||||
|  |   else if (IS("-v"))			/* show version */ | ||||||
|  |   { | ||||||
|  |    printf("%s  %s\n",LUA_VERSION,LUA_COPYRIGHT); | ||||||
|  |    if (argc==2) exit(EXIT_SUCCESS); | ||||||
|  |   } | ||||||
|  |   else					/* unknown option */ | ||||||
|  |    usage(argv[i]); | ||||||
|  |  } | ||||||
|  |  if (i==argc && (listing || !dumping)) | ||||||
|  |  { | ||||||
|  |   dumping=0; | ||||||
|  |   argv[--i]=Output; | ||||||
|  |  } | ||||||
|  |  return i; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define toproto(L,i) (clvalue(L->top+(i))->l.p) | ||||||
|  |  | ||||||
|  | static Proto* combine(lua_State* L, int n) | ||||||
|  | { | ||||||
|  |  if (n==1) | ||||||
|  |   return toproto(L,-1); | ||||||
|  |  else | ||||||
|  |  { | ||||||
|  |   int i,pc; | ||||||
|  |   Proto* f=luaF_newproto(L); | ||||||
|  |   setptvalue2s(L,L->top,f); incr_top(L); | ||||||
|  |   f->source=luaS_newliteral(L,"=(" PROGNAME ")"); | ||||||
|  |   f->maxstacksize=1; | ||||||
|  |   pc=2*n+1; | ||||||
|  |   f->code=luaM_newvector(L,pc,Instruction); | ||||||
|  |   f->sizecode=pc; | ||||||
|  |   f->p=luaM_newvector(L,n,Proto*); | ||||||
|  |   f->sizep=n; | ||||||
|  |   pc=0; | ||||||
|  |   for (i=0; i<n; i++) | ||||||
|  |   { | ||||||
|  |    f->p[i]=toproto(L,i-n-1); | ||||||
|  |    f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i); | ||||||
|  |    f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1); | ||||||
|  |   } | ||||||
|  |   f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0); | ||||||
|  |   return f; | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int writer(lua_State* L, const void* p, size_t size, void* u) | ||||||
|  | { | ||||||
|  |  UNUSED(L); | ||||||
|  |  return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | struct Smain { | ||||||
|  |  int argc; | ||||||
|  |  char** argv; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static int pmain(lua_State* L) | ||||||
|  | { | ||||||
|  |  struct Smain* s = (struct Smain*)lua_touserdata(L, 1); | ||||||
|  |  int argc=s->argc; | ||||||
|  |  char** argv=s->argv; | ||||||
|  |  Proto* f; | ||||||
|  |  int i; | ||||||
|  |  if (!lua_checkstack(L,argc)) fatal("too many input files"); | ||||||
|  |  for (i=0; i<argc; i++) | ||||||
|  |  { | ||||||
|  |   const char* filename=IS("-") ? NULL : argv[i]; | ||||||
|  |   if (luaL_loadfile(L,filename)!=0) fatal(lua_tostring(L,-1)); | ||||||
|  |  } | ||||||
|  |  f=combine(L,argc); | ||||||
|  |  if (listing) luaU_print(f,listing>1); | ||||||
|  |  if (dumping) | ||||||
|  |  { | ||||||
|  |   FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); | ||||||
|  |   if (D==NULL) cannot("open"); | ||||||
|  |   lua_lock(L); | ||||||
|  |   luaU_dump(L,f,writer,D,stripping); | ||||||
|  |   lua_unlock(L); | ||||||
|  |   if (ferror(D)) cannot("write"); | ||||||
|  |   if (fclose(D)) cannot("close"); | ||||||
|  |  } | ||||||
|  |  return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int main(int argc, char* argv[]) | ||||||
|  | { | ||||||
|  |  lua_State* L; | ||||||
|  |  struct Smain s; | ||||||
|  |  int i=doargs(argc,argv); | ||||||
|  |  argc-=i; argv+=i; | ||||||
|  |  if (argc<=0) usage("no input files given"); | ||||||
|  |  L=lua_open(); | ||||||
|  |  if (L==NULL) fatal("not enough memory for state"); | ||||||
|  |  s.argc=argc; | ||||||
|  |  s.argv=argv; | ||||||
|  |  if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1)); | ||||||
|  |  lua_close(L); | ||||||
|  |  return EXIT_SUCCESS; | ||||||
|  | } | ||||||
							
								
								
									
										736
									
								
								src/lua/luaconf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										736
									
								
								src/lua/luaconf.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,736 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: luaconf.h,v 1.81 2006/02/10 17:44:06 roberto Exp $ | ||||||
|  | ** Configuration file for Lua | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lconfig_h | ||||||
|  | #define lconfig_h | ||||||
|  |  | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stddef.h> | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** ================================================================== | ||||||
|  | ** Search for "@@" to find all configurable definitions. | ||||||
|  | ** =================================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_ANSI controls the use of non-ansi features. | ||||||
|  | ** CHANGE it (define it) if you want Lua to avoid the use of any | ||||||
|  | ** non-ansi feature or library. | ||||||
|  | */ | ||||||
|  | #if defined(__STRICT_ANSI__) | ||||||
|  | #define LUA_ANSI | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #if !defined(LUA_ANSI) && defined(_WIN32) | ||||||
|  | #define LUA_WIN | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if defined(LUA_USE_LINUX) | ||||||
|  | #define LUA_USE_POSIX | ||||||
|  | #define LUA_USE_DLOPEN		/* needs an extra library: -ldl */ | ||||||
|  | #define LUA_USE_READLINE	/* needs some extra libraries */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if defined(LUA_USE_MACOSX) | ||||||
|  | #define LUA_USE_POSIX | ||||||
|  | #define LUA_DL_DYLD		/* does not need extra library */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_USE_POSIX includes all functionallity listed as X/Open System | ||||||
|  | @* Interfaces Extension (XSI). | ||||||
|  | ** CHANGE it (define it) if your system is XSI compatible. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_POSIX) | ||||||
|  | #define LUA_USE_MKSTEMP | ||||||
|  | #define LUA_USE_ISATTY | ||||||
|  | #define LUA_USE_POPEN | ||||||
|  | #define LUA_USE_ULONGJMP | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_PATH_DEFAULT is the default path that Lua uses to look for | ||||||
|  | @* Lua libraries. | ||||||
|  | @@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for | ||||||
|  | @* C libraries. | ||||||
|  | ** CHANGE them if your machine has a non-conventional directory | ||||||
|  | ** hierarchy or if you want to install your libraries in | ||||||
|  | ** non-conventional directories. | ||||||
|  | */ | ||||||
|  | #if defined(_WIN32) | ||||||
|  | /* | ||||||
|  | ** In Windows, any exclamation mark ('!') in the path is replaced by the | ||||||
|  | ** path of the directory of the executable file of the current process. | ||||||
|  | */ | ||||||
|  | #define LUA_LDIR	"!\\lua\\" | ||||||
|  | #define LUA_CDIR	"!\\" | ||||||
|  | #define LUA_PATH_DEFAULT  \ | ||||||
|  | 		".\\?.lua;"  LUA_LDIR"?.lua;"  LUA_LDIR"?\\init.lua;" \ | ||||||
|  | 		             LUA_CDIR"?.lua;"  LUA_CDIR"?\\init.lua" | ||||||
|  | #define LUA_CPATH_DEFAULT \ | ||||||
|  | 	".\\?.dll;"  LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | #define LUA_ROOT	"/usr/local/" | ||||||
|  | #define LUA_LDIR	LUA_ROOT "share/lua/5.1/" | ||||||
|  | #define LUA_CDIR	LUA_ROOT "lib/lua/5.1/" | ||||||
|  | #define LUA_PATH_DEFAULT  \ | ||||||
|  | 		"./?.lua;"  LUA_LDIR"?.lua;"  LUA_LDIR"?/init.lua;" \ | ||||||
|  | 		            LUA_CDIR"?.lua;"  LUA_CDIR"?/init.lua" | ||||||
|  | #define LUA_CPATH_DEFAULT \ | ||||||
|  | 	"./?.so;"  LUA_CDIR"?.so;" LUA_CDIR"loadall.so" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_DIRSEP is the directory separator (for submodules). | ||||||
|  | ** CHANGE it if your machine does not use "/" as the directory separator | ||||||
|  | ** and is not Windows. (On Windows Lua automatically uses "\".) | ||||||
|  | */ | ||||||
|  | #if defined(_WIN32) | ||||||
|  | #define LUA_DIRSEP	"\\" | ||||||
|  | #else | ||||||
|  | #define LUA_DIRSEP	"/" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_PATHSEP is the character that separates templates in a path. | ||||||
|  | @@ LUA_PATH_MARK is the string that marks the substitution points in a | ||||||
|  | @* template. | ||||||
|  | @@ LUA_EXECDIR in a Windows path is replaced by the executable's | ||||||
|  | @* directory. | ||||||
|  | @@ LUA_IGMARK is a mark to ignore all before it when bulding the | ||||||
|  | @* luaopen_ function name. | ||||||
|  | ** CHANGE them if for some reason your system cannot use those | ||||||
|  | ** characters. (E.g., if one of those characters is a common character | ||||||
|  | ** in file/directory names.) Probably you do not need to change them. | ||||||
|  | */ | ||||||
|  | #define LUA_PATHSEP	";" | ||||||
|  | #define LUA_PATH_MARK	"?" | ||||||
|  | #define LUA_EXECDIR	"!" | ||||||
|  | #define LUA_IGMARK	"-" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. | ||||||
|  | ** CHANGE that if ptrdiff_t is not adequate on your machine. (On most | ||||||
|  | ** machines, ptrdiff_t gives a good choice between int or long.) | ||||||
|  | */ | ||||||
|  | #define LUA_INTEGER	ptrdiff_t | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_API is a mark for all core API functions. | ||||||
|  | @@ LUALIB_API is a mark for all standard library functions. | ||||||
|  | ** CHANGE them if you need to define those functions in some special way. | ||||||
|  | ** For instance, if you want to create one Windows DLL with the core and | ||||||
|  | ** the libraries, you may want to use the following definition (define | ||||||
|  | ** LUA_BUILD_AS_DLL to get it). | ||||||
|  | */ | ||||||
|  | #if defined(LUA_BUILD_AS_DLL) | ||||||
|  |  | ||||||
|  | #if defined(LUA_CORE) || defined(LUA_LIB) | ||||||
|  | #define LUA_API __declspec(dllexport) | ||||||
|  | #else | ||||||
|  | #define LUA_API __declspec(dllimport) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | #define LUA_API		extern | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* more often than not the libs go together with the core */ | ||||||
|  | #define LUALIB_API	LUA_API | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_FUNC is a mark for all extern functions that are not to be | ||||||
|  | @* exported to outside modules. | ||||||
|  | @@ LUAI_DATA is a mark for all extern (const) variables that are not to | ||||||
|  | @* be exported to outside modules. | ||||||
|  | ** CHANGE them if you need to mark them in some special way. Elf/gcc | ||||||
|  | ** (versions 3.2 and later) mark them as "hidden" to optimize access | ||||||
|  | ** when Lua is compiled as a shared library. | ||||||
|  | */ | ||||||
|  | #if defined(luaall_c) | ||||||
|  | #define LUAI_FUNC	static | ||||||
|  | #define LUAI_DATA	/* empty */ | ||||||
|  |  | ||||||
|  | #elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ | ||||||
|  |       defined(__ELF__) | ||||||
|  | #define LUAI_FUNC	__attribute__((visibility("hidden"))) extern | ||||||
|  | #define LUAI_DATA	LUAI_FUNC | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | #define LUAI_FUNC	extern | ||||||
|  | #define LUAI_DATA	extern | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_QL describes how error messages quote program elements. | ||||||
|  | ** CHANGE it if you want a different appearance. | ||||||
|  | */ | ||||||
|  | #define LUA_QL(x)	"'" x "'" | ||||||
|  | #define LUA_QS		LUA_QL("%s") | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_IDSIZE gives the maximum size for the description of the source | ||||||
|  | @* of a function in debug information. | ||||||
|  | ** CHANGE it if you want a different size. | ||||||
|  | */ | ||||||
|  | #define LUA_IDSIZE	60 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {================================================================== | ||||||
|  | ** Stand-alone configuration | ||||||
|  | ** =================================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #if defined(lua_c) || defined(luaall_c) | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that | ||||||
|  | @* is, whether we're running lua interactively). | ||||||
|  | ** CHANGE it if you have a better definition for non-POSIX/non-Windows | ||||||
|  | ** systems. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_ISATTY) | ||||||
|  | #include <unistd.h> | ||||||
|  | #define lua_stdin_is_tty()	isatty(0) | ||||||
|  | #elif defined(LUA_WIN) | ||||||
|  | #include <io.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #define lua_stdin_is_tty()	_isatty(_fileno(stdin)) | ||||||
|  | #else | ||||||
|  | #define lua_stdin_is_tty()	1  /* assume stdin is a tty */ | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_PROMPT is the default prompt used by stand-alone Lua. | ||||||
|  | @@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. | ||||||
|  | ** CHANGE them if you want different prompts. (You can also change the | ||||||
|  | ** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) | ||||||
|  | */ | ||||||
|  | #define LUA_PROMPT		"> " | ||||||
|  | #define LUA_PROMPT2		">> " | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_PROGNAME is the default name for the stand-alone Lua program. | ||||||
|  | ** CHANGE it if your stand-alone interpreter has a different name and | ||||||
|  | ** your system is not able to detect that name automatically. | ||||||
|  | */ | ||||||
|  | #define LUA_PROGNAME		"lua" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_MAXINPUT is the maximum length for an input line in the | ||||||
|  | @* stand-alone interpreter. | ||||||
|  | ** CHANGE it if you need longer lines. | ||||||
|  | */ | ||||||
|  | #define LUA_MAXINPUT	512 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ lua_readline defines how to show a prompt and then read a line from | ||||||
|  | @* the standard input. | ||||||
|  | @@ lua_saveline defines how to "save" a read line in a "history". | ||||||
|  | @@ lua_freeline defines how to free a line read by lua_readline. | ||||||
|  | ** CHANGE them if you want to improve this functionality (e.g., by using | ||||||
|  | ** GNU readline and history facilities). | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_READLINE) | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <readline/readline.h> | ||||||
|  | #include <readline/history.h> | ||||||
|  | #define lua_readline(L,b,p)	((void)L, ((b)=readline(p)) != NULL) | ||||||
|  | #define lua_saveline(L,idx) \ | ||||||
|  | 	if (lua_strlen(L,idx) > 0)  /* non-empty line? */ \ | ||||||
|  | 	  add_history(lua_tostring(L, idx));  /* add it to history */ | ||||||
|  | #define lua_freeline(L,b)	((void)L, free(b)) | ||||||
|  | #else | ||||||
|  | #define lua_readline(L,b,p)	\ | ||||||
|  | 	((void)L, fputs(p, stdout), fflush(stdout),  /* show prompt */ \ | ||||||
|  | 	fgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */ | ||||||
|  | #define lua_saveline(L,idx)	{ (void)L; (void)idx; } | ||||||
|  | #define lua_freeline(L,b)	{ (void)L; (void)b; } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* }================================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles | ||||||
|  | @* as a percentage. | ||||||
|  | ** CHANGE it if you want the GC to run faster or slower (higher values | ||||||
|  | ** mean larger pauses which mean slower collection.) You can also change | ||||||
|  | ** this value dynamically. | ||||||
|  | */ | ||||||
|  | #define LUAI_GCPAUSE	200  /* 200% (wait memory to double before next GC) */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_GCMUL defines the default speed of garbage collection relative to | ||||||
|  | @* memory allocation as a percentage. | ||||||
|  | ** CHANGE it if you want to change the granularity of the garbage | ||||||
|  | ** collection. (Higher values mean coarser collections. 0 represents | ||||||
|  | ** infinity, where each step performs a full collection.) You can also | ||||||
|  | ** change this value dynamically. | ||||||
|  | */ | ||||||
|  | #define LUAI_GCMUL	200 /* GC runs 'twice the speed' of memory allocation */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_GETN controls compatibility with old getn behavior. | ||||||
|  | ** CHANGE it (define it) if you want exact compatibility with the | ||||||
|  | ** behavior of setn/getn in Lua 5.0. | ||||||
|  | */ | ||||||
|  | #undef LUA_COMPAT_GETN | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. | ||||||
|  | ** CHANGE it to undefined as soon as you do not need a global 'loadlib' | ||||||
|  | ** function (the function is still available as 'package.loadlib'). | ||||||
|  | */ | ||||||
|  | #undef LUA_COMPAT_LOADLIB | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. | ||||||
|  | ** CHANGE it to undefined as soon as your programs use only '...' to | ||||||
|  | ** access vararg parameters (instead of the old 'arg' table). | ||||||
|  | */ | ||||||
|  | #define LUA_COMPAT_VARARG | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_MOD controls compatibility with old math.mod function. | ||||||
|  | ** CHANGE it to undefined as soon as your programs use 'math.fmod' or | ||||||
|  | ** the new '%' operator instead of 'math.mod'. | ||||||
|  | */ | ||||||
|  | #define LUA_COMPAT_MOD | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_LSTR controls compatibility with old long string nesting | ||||||
|  | @* facility. | ||||||
|  | ** CHANGE it to 2 if you want the old behaviour, or undefine it to turn | ||||||
|  | ** off the advisory error when nesting [[...]]. | ||||||
|  | */ | ||||||
|  | #define LUA_COMPAT_LSTR		1 | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. | ||||||
|  | ** CHANGE it to undefined as soon as you rename 'string.gfind' to | ||||||
|  | ** 'string.gmatch'. | ||||||
|  | */ | ||||||
|  | #define LUA_COMPAT_GFIND | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' | ||||||
|  | @* behavior. | ||||||
|  | ** CHANGE it to undefined as soon as you replace to 'luaL_registry' | ||||||
|  | ** your uses of 'luaL_openlib' | ||||||
|  | */ | ||||||
|  | #define LUA_COMPAT_OPENLIB | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ luai_apicheck is the assert macro used by the Lua-C API. | ||||||
|  | ** CHANGE luai_apicheck if you want Lua to perform some checks in the | ||||||
|  | ** parameters it gets from API calls. This may slow down the interpreter | ||||||
|  | ** a bit, but may be quite useful when debugging C code that interfaces | ||||||
|  | ** with Lua. A useful redefinition is to use assert.h. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_APICHECK) | ||||||
|  | #include <assert.h> | ||||||
|  | #define luai_apicheck(L,o)	{ (void)L; assert(o); } | ||||||
|  | #else | ||||||
|  | #define luai_apicheck(L,o)	{ (void)L; } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_BITSINT defines the number of bits in an int. | ||||||
|  | ** CHANGE here if Lua cannot automatically detect the number of bits of | ||||||
|  | ** your machine. Probably you do not need to change this. | ||||||
|  | */ | ||||||
|  | /* avoid overflows in comparison */ | ||||||
|  | #if INT_MAX-20 < 32760 | ||||||
|  | #define LUAI_BITSINT	16 | ||||||
|  | #elif INT_MAX > 2147483640L | ||||||
|  | /* int has at least 32 bits */ | ||||||
|  | #define LUAI_BITSINT	32 | ||||||
|  | #else | ||||||
|  | #error "you must define LUA_BITSINT with number of bits in an integer" | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_UINT32 is an unsigned integer with at least 32 bits. | ||||||
|  | @@ LUAI_INT32 is an signed integer with at least 32 bits. | ||||||
|  | @@ LUAI_UMEM is an unsigned integer big enough to count the total | ||||||
|  | @* memory used by Lua. | ||||||
|  | @@ LUAI_MEM is a signed integer big enough to count the total memory | ||||||
|  | @* used by Lua. | ||||||
|  | ** CHANGE here if for some weird reason the default definitions are not | ||||||
|  | ** good enough for your machine. (The definitions in the 'else' | ||||||
|  | ** part always works, but may waste space on machines with 64-bit | ||||||
|  | ** longs.) Probably you do not need to change this. | ||||||
|  | */ | ||||||
|  | #if LUAI_BITSINT >= 32 | ||||||
|  | #define LUAI_UINT32	unsigned int | ||||||
|  | #define LUAI_INT32	int | ||||||
|  | #define LUAI_MAXINT32	INT_MAX | ||||||
|  | #define LUAI_UMEM	size_t | ||||||
|  | #define LUAI_MEM	ptrdiff_t | ||||||
|  | #else | ||||||
|  | /* 16-bit ints */ | ||||||
|  | #define LUAI_UINT32	unsigned long | ||||||
|  | #define LUAI_INT32	long | ||||||
|  | #define LUAI_MAXINT32	LONG_MAX | ||||||
|  | #define LUAI_UMEM	unsigned long | ||||||
|  | #define LUAI_MEM	long | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_MAXCALLS limits the number of nested calls. | ||||||
|  | ** CHANGE it if you need really deep recursive calls. This limit is | ||||||
|  | ** arbitrary; its only purpose is to stop infinite recursion before | ||||||
|  | ** exhausting memory. | ||||||
|  | */ | ||||||
|  | #define LUAI_MAXCALLS	20000 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function | ||||||
|  | @* can use. | ||||||
|  | ** CHANGE it if you need lots of (Lua) stack space for your C | ||||||
|  | ** functions. This limit is arbitrary; its only purpose is to stop C | ||||||
|  | ** functions to consume unlimited stack space. | ||||||
|  | */ | ||||||
|  | #define LUAI_MAXCSTACK	2048 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {================================================================== | ||||||
|  | ** CHANGE (to smaller values) the following definitions if your system | ||||||
|  | ** has a small C stack. (Or you may want to change them to larger | ||||||
|  | ** values if your system has a large C stack and these limits are | ||||||
|  | ** too rigid for you.) Some of these constants control the size of | ||||||
|  | ** stack-allocated arrays used by the compiler or the interpreter, while | ||||||
|  | ** others limit the maximum number of recursive calls that the compiler | ||||||
|  | ** or the interpreter can perform. Values too large may cause a C stack | ||||||
|  | ** overflow for some forms of deep constructs. | ||||||
|  | ** =================================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and | ||||||
|  | @* syntactical nested non-terminals in a program. | ||||||
|  | */ | ||||||
|  | #define LUAI_MAXCCALLS		200 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_MAXVARS is the maximum number of local variables per function | ||||||
|  | @* (must be smaller than 250). | ||||||
|  | */ | ||||||
|  | #define LUAI_MAXVARS		200 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_MAXUPVALUES is the maximum number of upvalues per function | ||||||
|  | @* (must be smaller than 250). | ||||||
|  | */ | ||||||
|  | #define LUAI_MAXUPVALUES	60 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. | ||||||
|  | */ | ||||||
|  | #define LUAL_BUFFERSIZE		BUFSIZ | ||||||
|  |  | ||||||
|  | /* }================================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** {================================================================== | ||||||
|  | @@ LUA_NUMBER is the type of numbers in Lua. | ||||||
|  | ** CHANGE the following definitions only if you want to build Lua | ||||||
|  | ** with a number type different from double. You may also need to | ||||||
|  | ** change lua_number2int & lua_number2integer. | ||||||
|  | ** =================================================================== | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define LUA_NUMBER_DOUBLE | ||||||
|  | #define LUA_NUMBER	double | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_UACNUMBER is the result of an 'usual argument conversion' | ||||||
|  | @* over a number. | ||||||
|  | */ | ||||||
|  | #define LUAI_UACNUMBER	double | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_NUMBER_SCAN is the format for reading numbers. | ||||||
|  | @@ LUA_NUMBER_FMT is the format for writing numbers. | ||||||
|  | @@ lua_number2str converts a number to a string. | ||||||
|  | @@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. | ||||||
|  | @@ lua_str2number converts a string to a number. | ||||||
|  | */ | ||||||
|  | #define LUA_NUMBER_SCAN		"%lf" | ||||||
|  | #define LUA_NUMBER_FMT		"%.14g" | ||||||
|  | #define lua_number2str(s,n)	sprintf((s), LUA_NUMBER_FMT, (n)) | ||||||
|  | #define LUAI_MAXNUMBER2STR	32 /* 16 digits, sign, point, and \0 */ | ||||||
|  | #define lua_str2number(s,p)	strtod((s), (p)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ The luai_num* macros define the primitive operations over numbers. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_CORE) | ||||||
|  | #include <math.h> | ||||||
|  | #define luai_numadd(a,b)	((a)+(b)) | ||||||
|  | #define luai_numsub(a,b)	((a)-(b)) | ||||||
|  | #define luai_nummul(a,b)	((a)*(b)) | ||||||
|  | #define luai_numdiv(a,b)	((a)/(b)) | ||||||
|  | #define luai_nummod(a,b)	((a) - floor((a)/(b))*(b)) | ||||||
|  | #define luai_numpow(a,b)	(pow(a,b)) | ||||||
|  | #define luai_numunm(a)		(-(a)) | ||||||
|  | #define luai_numeq(a,b)		((a)==(b)) | ||||||
|  | #define luai_numlt(a,b)		((a)<(b)) | ||||||
|  | #define luai_numle(a,b)		((a)<=(b)) | ||||||
|  | #define luai_numisnan(a)	(!luai_numeq((a), (a))) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ lua_number2int is a macro to convert lua_Number to int. | ||||||
|  | @@ lua_number2integer is a macro to convert lua_Number to lua_Integer. | ||||||
|  | ** CHANGE them if you know a faster way to convert a lua_Number to | ||||||
|  | ** int (with any rounding method and without throwing errors) in your | ||||||
|  | ** system. In Pentium machines, a naive typecast from double to int | ||||||
|  | ** in C is extremely slow, so any alternative is worth trying. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | /* On a Pentium, resort to a trick */ | ||||||
|  | #if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ | ||||||
|  |     (defined(__i386) || defined (_M_IX86) || defined(__i386__)) | ||||||
|  | union luai_Cast { double l_d; long l_l; }; | ||||||
|  | #define lua_number2int(i,d) \ | ||||||
|  |   { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } | ||||||
|  | #define lua_number2integer(i,n)		lua_number2int(i, n) | ||||||
|  |  | ||||||
|  | /* this option always works, but may be slow */ | ||||||
|  | #else | ||||||
|  | #define lua_number2int(i,d)	((i)=(int)(d)) | ||||||
|  | #define lua_number2integer(i,d)	((i)=(lua_Integer)(d)) | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* }================================================================== */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. | ||||||
|  | ** CHANGE it if your system requires alignments larger than double. (For | ||||||
|  | ** instance, if your system supports long doubles and they must be | ||||||
|  | ** aligned in 16-byte boundaries, then you should add long double in the | ||||||
|  | ** union.) Probably you do not need to change this. | ||||||
|  | */ | ||||||
|  | #define LUAI_USER_ALIGNMENT_T	union { double u; void *s; long l; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. | ||||||
|  | ** CHANGE them if you prefer to use longjmp/setjmp even with C++ | ||||||
|  | ** or if want/don't to use _longjmp/_setjmp instead of regular | ||||||
|  | ** longjmp/setjmp. By default, Lua handles errors with exceptions when | ||||||
|  | ** compiling as C++ code, with _longjmp/_setjmp when asked to use them, | ||||||
|  | ** and with longjmp/setjmp otherwise. | ||||||
|  | */ | ||||||
|  | #if defined(__cplusplus) | ||||||
|  | /* C++ exceptions */ | ||||||
|  | #define LUAI_THROW(L,c)	throw(c) | ||||||
|  | #define LUAI_TRY(L,c,a)	try { a } catch(...) \ | ||||||
|  | 	{ if ((c)->status == 0) (c)->status = -1; } | ||||||
|  | #define luai_jmpbuf	int  /* dummy variable */ | ||||||
|  |  | ||||||
|  | #elif defined(LUA_USE_ULONGJMP) | ||||||
|  | /* in Unix, try _longjmp/_setjmp (more efficient) */ | ||||||
|  | #define LUAI_THROW(L,c)	_longjmp((c)->b, 1) | ||||||
|  | #define LUAI_TRY(L,c,a)	if (_setjmp((c)->b) == 0) { a } | ||||||
|  | #define luai_jmpbuf	jmp_buf | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | /* default handling with long jumps */ | ||||||
|  | #define LUAI_THROW(L,c)	longjmp((c)->b, 1) | ||||||
|  | #define LUAI_TRY(L,c,a)	if (setjmp((c)->b) == 0) { a } | ||||||
|  | #define luai_jmpbuf	jmp_buf | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_MAXCAPTURES is the maximum number of captures that a pattern | ||||||
|  | @* can do during pattern-matching. | ||||||
|  | ** CHANGE it if you need more captures. This limit is arbitrary. | ||||||
|  | */ | ||||||
|  | #define LUA_MAXCAPTURES		32 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ lua_tmpnam is the function that the OS library uses to create a | ||||||
|  | @* temporary name. | ||||||
|  | @@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. | ||||||
|  | ** CHANGE them if you have an alternative to tmpnam (which is considered | ||||||
|  | ** insecure) or if you want the original tmpnam anyway.  By default, Lua | ||||||
|  | ** uses tmpnam except when POSIX is available, where it uses mkstemp. | ||||||
|  | */ | ||||||
|  | #if defined(loslib_c) || defined(luaall_c) | ||||||
|  |  | ||||||
|  | #if defined(LUA_USE_MKSTEMP) | ||||||
|  | #include <unistd.h> | ||||||
|  | #define LUA_TMPNAMBUFSIZE	32 | ||||||
|  | #define lua_tmpnam(b,e)	{ \ | ||||||
|  | 	strcpy(b, "/tmp/lua_XXXXXX"); \ | ||||||
|  | 	e = mkstemp(b); \ | ||||||
|  | 	if (e != -1) close(e); \ | ||||||
|  | 	e = (e == -1); } | ||||||
|  |  | ||||||
|  | #else | ||||||
|  | #define LUA_TMPNAMBUFSIZE	L_tmpnam | ||||||
|  | #define lua_tmpnam(b,e)		{ e = (tmpnam(b) == NULL); } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ lua_popen spawns a new process connected to the current one through | ||||||
|  | @* the file streams. | ||||||
|  | ** CHANGE it if you have a way to implement it in your system. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_POPEN) | ||||||
|  |  | ||||||
|  | #define lua_popen(L,c,m)	((void)L, popen(c,m)) | ||||||
|  | #define lua_pclose(L,file)	((void)L, (pclose(file) != -1)) | ||||||
|  |  | ||||||
|  | #elif defined(LUA_WIN) | ||||||
|  |  | ||||||
|  | #define lua_popen(L,c,m)	((void)L, _popen(c,m)) | ||||||
|  | #define lua_pclose(L,file)	((void)L, (_pclose(file) != -1)) | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | #define lua_popen(L,c,m)	((void)((void)c, m),  \ | ||||||
|  | 		luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) | ||||||
|  | #define lua_pclose(L,file)		((void)((void)L, file), 0) | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_DL_* define which dynamic-library system Lua should use. | ||||||
|  | ** CHANGE here if Lua has problems choosing the appropriate | ||||||
|  | ** dynamic-library system for your platform (either Windows' DLL, Mac's | ||||||
|  | ** dyld, or Unix's dlopen). If your system is some kind of Unix, there | ||||||
|  | ** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for | ||||||
|  | ** it.  To use dlopen you also need to adapt the src/Makefile (probably | ||||||
|  | ** adding -ldl to the linker options), so Lua does not select it | ||||||
|  | ** automatically.  (When you change the makefile to add -ldl, you must | ||||||
|  | ** also add -DLUA_USE_DLOPEN.) | ||||||
|  | ** If you do not want any kind of dynamic library, undefine all these | ||||||
|  | ** options. | ||||||
|  | ** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. | ||||||
|  | */ | ||||||
|  | #if defined(LUA_USE_DLOPEN) | ||||||
|  | #define LUA_DL_DLOPEN | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #if defined(LUA_WIN) | ||||||
|  | #define LUA_DL_DLL | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State | ||||||
|  | @* (the data goes just *before* the lua_State pointer). | ||||||
|  | ** CHANGE (define) this if you really need that. This value must be | ||||||
|  | ** a multiple of the maximum alignment required for your machine. | ||||||
|  | */ | ||||||
|  | #define LUAI_EXTRASPACE		0 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ luai_userstate* allow user-specific actions on threads. | ||||||
|  | ** CHANGE them if you defined LUAI_EXTRASPACE and need to do something | ||||||
|  | ** extra when a thread is created/deleted/resumed/yielded. | ||||||
|  | */ | ||||||
|  | #define luai_userstateopen(L)		((void)L) | ||||||
|  | #define luai_userstateclose(L)		((void)L) | ||||||
|  | #define luai_userstatethread(L,L1)	((void)L) | ||||||
|  | #define luai_userstatefree(L)		((void)L) | ||||||
|  | #define luai_userstateresume(L,n)	((void)L) | ||||||
|  | #define luai_userstateyield(L,n)	((void)L) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | @@ LUA_INTFRMLEN is the length modifier for integer conversions | ||||||
|  | @* in 'string.format'. | ||||||
|  | @@ LUA_INTFRM_T is the integer type correspoding to the previous length | ||||||
|  | @* modifier. | ||||||
|  | ** CHANGE them if your system supports long long or does not support long. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #if defined(LUA_USELONGLONG) | ||||||
|  |  | ||||||
|  | #define LUA_INTFRMLEN		"ll" | ||||||
|  | #define LUA_INTFRM_T		long long | ||||||
|  |  | ||||||
|  | #else | ||||||
|  |  | ||||||
|  | #define LUA_INTFRMLEN		"l" | ||||||
|  | #define LUA_INTFRM_T		long | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* =================================================================== */ | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** Local configuration. You can use this space to add your redefinitions | ||||||
|  | ** without modifying the main part of the file. | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
|  |  | ||||||
							
								
								
									
										53
									
								
								src/lua/lualib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/lua/lualib.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lualib.h,v 1.36 2005/12/27 17:12:00 roberto Exp $ | ||||||
|  | ** Lua standard libraries | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lualib_h | ||||||
|  | #define lualib_h | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* Key to file-handle type */ | ||||||
|  | #define LUA_FILEHANDLE		"FILE*" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define LUA_COLIBNAME	"coroutine" | ||||||
|  | LUALIB_API int (luaopen_base) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_TABLIBNAME	"table" | ||||||
|  | LUALIB_API int (luaopen_table) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_IOLIBNAME	"io" | ||||||
|  | LUALIB_API int (luaopen_io) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_OSLIBNAME	"os" | ||||||
|  | LUALIB_API int (luaopen_os) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_STRLIBNAME	"string" | ||||||
|  | LUALIB_API int (luaopen_string) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_MATHLIBNAME	"math" | ||||||
|  | LUALIB_API int (luaopen_math) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_DBLIBNAME	"debug" | ||||||
|  | LUALIB_API int (luaopen_debug) (lua_State *L); | ||||||
|  |  | ||||||
|  | #define LUA_LOADLIBNAME	"package" | ||||||
|  | LUALIB_API int (luaopen_package) (lua_State *L); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* open all previous libraries */ | ||||||
|  | LUALIB_API void (luaL_openlibs) (lua_State *L);  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lua_assert | ||||||
|  | #define lua_assert(x)	((void)0) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										223
									
								
								src/lua/lundump.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										223
									
								
								src/lua/lundump.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,223 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lundump.c,v 1.60 2006/02/16 15:53:49 lhf Exp $ | ||||||
|  | ** load precompiled Lua chunks | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lundump_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "ldebug.h" | ||||||
|  | #include "ldo.h" | ||||||
|  | #include "lfunc.h" | ||||||
|  | #include "lmem.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lstring.h" | ||||||
|  | #include "lundump.h" | ||||||
|  | #include "lzio.h" | ||||||
|  |  | ||||||
|  | typedef struct { | ||||||
|  |  lua_State* L; | ||||||
|  |  ZIO* Z; | ||||||
|  |  Mbuffer* b; | ||||||
|  |  const char* name; | ||||||
|  | } LoadState; | ||||||
|  |  | ||||||
|  | #ifdef LUAC_TRUST_BINARIES | ||||||
|  | #define IF(c,s) | ||||||
|  | #else | ||||||
|  | #define IF(c,s)		if (c) error(S,s) | ||||||
|  |  | ||||||
|  | static void error(LoadState* S, const char* why) | ||||||
|  | { | ||||||
|  |  luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); | ||||||
|  |  luaD_throw(S->L,LUA_ERRSYNTAX); | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #define LoadMem(S,b,n,size)	LoadBlock(S,b,(n)*(size)) | ||||||
|  | #define	LoadByte(S)		(lu_byte)LoadChar(S) | ||||||
|  | #define LoadVar(S,x)		LoadMem(S,&x,1,sizeof(x)) | ||||||
|  | #define LoadVector(S,b,n,size)	LoadMem(S,b,n,size) | ||||||
|  |  | ||||||
|  | static void LoadBlock(LoadState* S, void* b, size_t size) | ||||||
|  | { | ||||||
|  |  size_t r=luaZ_read(S->Z,b,size); | ||||||
|  |  IF (r!=0, "unexpected end"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int LoadChar(LoadState* S) | ||||||
|  | { | ||||||
|  |  char x; | ||||||
|  |  LoadVar(S,x); | ||||||
|  |  return x; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int LoadInt(LoadState* S) | ||||||
|  | { | ||||||
|  |  int x; | ||||||
|  |  LoadVar(S,x); | ||||||
|  |  IF (x<0, "bad integer"); | ||||||
|  |  return x; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static lua_Number LoadNumber(LoadState* S) | ||||||
|  | { | ||||||
|  |  lua_Number x; | ||||||
|  |  LoadVar(S,x); | ||||||
|  |  return x; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static TString* LoadString(LoadState* S) | ||||||
|  | { | ||||||
|  |  size_t size; | ||||||
|  |  LoadVar(S,size); | ||||||
|  |  if (size==0) | ||||||
|  |   return NULL; | ||||||
|  |  else | ||||||
|  |  { | ||||||
|  |   char* s=luaZ_openspace(S->L,S->b,size); | ||||||
|  |   LoadBlock(S,s,size); | ||||||
|  |   return luaS_newlstr(S->L,s,size-1);		/* remove trailing '\0' */ | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void LoadCode(LoadState* S, Proto* f) | ||||||
|  | { | ||||||
|  |  int n=LoadInt(S); | ||||||
|  |  f->code=luaM_newvector(S->L,n,Instruction); | ||||||
|  |  f->sizecode=n; | ||||||
|  |  LoadVector(S,f->code,n,sizeof(Instruction)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Proto* LoadFunction(LoadState* S, TString* p); | ||||||
|  |  | ||||||
|  | static void LoadConstants(LoadState* S, Proto* f) | ||||||
|  | { | ||||||
|  |  int i,n; | ||||||
|  |  n=LoadInt(S); | ||||||
|  |  f->k=luaM_newvector(S->L,n,TValue); | ||||||
|  |  f->sizek=n; | ||||||
|  |  for (i=0; i<n; i++) setnilvalue(&f->k[i]); | ||||||
|  |  for (i=0; i<n; i++) | ||||||
|  |  { | ||||||
|  |   TValue* o=&f->k[i]; | ||||||
|  |   int t=LoadChar(S); | ||||||
|  |   switch (t) | ||||||
|  |   { | ||||||
|  |    case LUA_TNIL: | ||||||
|  |    	setnilvalue(o); | ||||||
|  | 	break; | ||||||
|  |    case LUA_TBOOLEAN: | ||||||
|  |    	setbvalue(o,LoadChar(S)); | ||||||
|  | 	break; | ||||||
|  |    case LUA_TNUMBER: | ||||||
|  | 	setnvalue(o,LoadNumber(S)); | ||||||
|  | 	break; | ||||||
|  |    case LUA_TSTRING: | ||||||
|  | 	setsvalue2n(S->L,o,LoadString(S)); | ||||||
|  | 	break; | ||||||
|  |    default: | ||||||
|  | 	IF (1, "bad constant"); | ||||||
|  | 	break; | ||||||
|  |   } | ||||||
|  |  } | ||||||
|  |  n=LoadInt(S); | ||||||
|  |  f->p=luaM_newvector(S->L,n,Proto*); | ||||||
|  |  f->sizep=n; | ||||||
|  |  for (i=0; i<n; i++) f->p[i]=NULL; | ||||||
|  |  for (i=0; i<n; i++) f->p[i]=LoadFunction(S,f->source); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void LoadDebug(LoadState* S, Proto* f) | ||||||
|  | { | ||||||
|  |  int i,n; | ||||||
|  |  n=LoadInt(S); | ||||||
|  |  f->lineinfo=luaM_newvector(S->L,n,int); | ||||||
|  |  f->sizelineinfo=n; | ||||||
|  |  LoadVector(S,f->lineinfo,n,sizeof(int)); | ||||||
|  |  n=LoadInt(S); | ||||||
|  |  f->locvars=luaM_newvector(S->L,n,LocVar); | ||||||
|  |  f->sizelocvars=n; | ||||||
|  |  for (i=0; i<n; i++) f->locvars[i].varname=NULL; | ||||||
|  |  for (i=0; i<n; i++) | ||||||
|  |  { | ||||||
|  |   f->locvars[i].varname=LoadString(S); | ||||||
|  |   f->locvars[i].startpc=LoadInt(S); | ||||||
|  |   f->locvars[i].endpc=LoadInt(S); | ||||||
|  |  } | ||||||
|  |  n=LoadInt(S); | ||||||
|  |  f->upvalues=luaM_newvector(S->L,n,TString*); | ||||||
|  |  f->sizeupvalues=n; | ||||||
|  |  for (i=0; i<n; i++) f->upvalues[i]=NULL; | ||||||
|  |  for (i=0; i<n; i++) f->upvalues[i]=LoadString(S); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static Proto* LoadFunction(LoadState* S, TString* p) | ||||||
|  | { | ||||||
|  |  Proto* f=luaF_newproto(S->L); | ||||||
|  |  setptvalue2s(S->L,S->L->top,f); incr_top(S->L); | ||||||
|  |  f->source=LoadString(S); if (f->source==NULL) f->source=p; | ||||||
|  |  f->linedefined=LoadInt(S); | ||||||
|  |  f->lastlinedefined=LoadInt(S); | ||||||
|  |  f->nups=LoadByte(S); | ||||||
|  |  f->numparams=LoadByte(S); | ||||||
|  |  f->is_vararg=LoadByte(S); | ||||||
|  |  f->maxstacksize=LoadByte(S); | ||||||
|  |  LoadCode(S,f); | ||||||
|  |  LoadConstants(S,f); | ||||||
|  |  LoadDebug(S,f); | ||||||
|  |  IF (!luaG_checkcode(f), "bad code"); | ||||||
|  |  S->L->top--; | ||||||
|  |  return f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void LoadHeader(LoadState* S) | ||||||
|  | { | ||||||
|  |  char h[LUAC_HEADERSIZE]; | ||||||
|  |  char s[LUAC_HEADERSIZE]; | ||||||
|  |  luaU_header(h); | ||||||
|  |  LoadBlock(S,s,LUAC_HEADERSIZE); | ||||||
|  |  IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** load precompiled chunk | ||||||
|  | */ | ||||||
|  | Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) | ||||||
|  | { | ||||||
|  |  LoadState S; | ||||||
|  |  if (*name=='@' || *name=='=') | ||||||
|  |   S.name=name+1; | ||||||
|  |  else if (*name==LUA_SIGNATURE[0]) | ||||||
|  |   S.name="binary string"; | ||||||
|  |  else | ||||||
|  |   S.name=name; | ||||||
|  |  S.L=L; | ||||||
|  |  S.Z=Z; | ||||||
|  |  S.b=buff; | ||||||
|  |  LoadHeader(&S); | ||||||
|  |  return LoadFunction(&S,luaS_newliteral(L,"=?")); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | * make header | ||||||
|  | */ | ||||||
|  | void luaU_header (char* h) | ||||||
|  | { | ||||||
|  |  int x=1; | ||||||
|  |  memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); | ||||||
|  |  h+=sizeof(LUA_SIGNATURE)-1; | ||||||
|  |  *h++=(char)LUAC_VERSION; | ||||||
|  |  *h++=(char)LUAC_FORMAT; | ||||||
|  |  *h++=(char)*(char*)&x;				/* endianness */ | ||||||
|  |  *h++=(char)sizeof(int); | ||||||
|  |  *h++=(char)sizeof(size_t); | ||||||
|  |  *h++=(char)sizeof(Instruction); | ||||||
|  |  *h++=(char)sizeof(lua_Number); | ||||||
|  |  *h++=(char)(((lua_Number)0.5)==0);		/* is lua_Number integral? */ | ||||||
|  | } | ||||||
							
								
								
									
										36
									
								
								src/lua/lundump.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/lua/lundump.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lundump.h,v 1.40 2005/11/11 14:03:13 lhf Exp $ | ||||||
|  | ** load precompiled Lua chunks | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef lundump_h | ||||||
|  | #define lundump_h | ||||||
|  |  | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lzio.h" | ||||||
|  |  | ||||||
|  | /* load one chunk; from lundump.c */ | ||||||
|  | LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); | ||||||
|  |  | ||||||
|  | /* make header; from lundump.c */ | ||||||
|  | LUAI_FUNC void luaU_header (char* h); | ||||||
|  |  | ||||||
|  | /* dump one chunk; from ldump.c */ | ||||||
|  | LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); | ||||||
|  |  | ||||||
|  | #ifdef luac_c | ||||||
|  | /* print one chunk; from print.c */ | ||||||
|  | LUAI_FUNC void luaU_print (const Proto* f, int full); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /* for header of binary files -- this is Lua 5.1 */ | ||||||
|  | #define LUAC_VERSION		0x51 | ||||||
|  |  | ||||||
|  | /* for header of binary files -- this is the official format */ | ||||||
|  | #define LUAC_FORMAT		0 | ||||||
|  |  | ||||||
|  | /* size of header of binary files */ | ||||||
|  | #define LUAC_HEADERSIZE		12 | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										762
									
								
								src/lua/lvm.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										762
									
								
								src/lua/lvm.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,762 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lvm.c,v 2.62 2006/01/23 19:51:43 roberto Exp $ | ||||||
|  | ** Lua virtual machine | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lvm_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "ldebug.h" | ||||||
|  | #include "ldo.h" | ||||||
|  | #include "lfunc.h" | ||||||
|  | #include "lgc.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lopcodes.h" | ||||||
|  | #include "lstate.h" | ||||||
|  | #include "lstring.h" | ||||||
|  | #include "ltable.h" | ||||||
|  | #include "ltm.h" | ||||||
|  | #include "lvm.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* limit for table tag-method chains (to avoid loops) */ | ||||||
|  | #define MAXTAGLOOP	100 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | const TValue *luaV_tonumber (const TValue *obj, TValue *n) { | ||||||
|  |   lua_Number num; | ||||||
|  |   if (ttisnumber(obj)) return obj; | ||||||
|  |   if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { | ||||||
|  |     setnvalue(n, num); | ||||||
|  |     return n; | ||||||
|  |   } | ||||||
|  |   else | ||||||
|  |     return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaV_tostring (lua_State *L, StkId obj) { | ||||||
|  |   if (!ttisnumber(obj)) | ||||||
|  |     return 0; | ||||||
|  |   else { | ||||||
|  |     char s[LUAI_MAXNUMBER2STR]; | ||||||
|  |     lua_Number n = nvalue(obj); | ||||||
|  |     lua_number2str(s, n); | ||||||
|  |     setsvalue2s(L, obj, luaS_new(L, s)); | ||||||
|  |     return 1; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void traceexec (lua_State *L, const Instruction *pc) { | ||||||
|  |   lu_byte mask = L->hookmask; | ||||||
|  |   const Instruction *oldpc = L->savedpc; | ||||||
|  |   L->savedpc = pc; | ||||||
|  |   if (mask > LUA_MASKLINE) {  /* instruction-hook set? */ | ||||||
|  |     if (L->hookcount == 0) { | ||||||
|  |       resethookcount(L); | ||||||
|  |       luaD_callhook(L, LUA_HOOKCOUNT, -1); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   if (mask & LUA_MASKLINE) { | ||||||
|  |     Proto *p = ci_func(L->ci)->l.p; | ||||||
|  |     int npc = pcRel(pc, p); | ||||||
|  |     int newline = getline(p, npc); | ||||||
|  |     /* call linehook when enter a new function, when jump back (loop), | ||||||
|  |        or when enter a new line */ | ||||||
|  |     if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) | ||||||
|  |       luaD_callhook(L, LUA_HOOKLINE, newline); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void callTMres (lua_State *L, StkId res, const TValue *f, | ||||||
|  |                         const TValue *p1, const TValue *p2) { | ||||||
|  |   ptrdiff_t result = savestack(L, res); | ||||||
|  |   setobj2s(L, L->top, f);  /* push function */ | ||||||
|  |   setobj2s(L, L->top+1, p1);  /* 1st argument */ | ||||||
|  |   setobj2s(L, L->top+2, p2);  /* 2nd argument */ | ||||||
|  |   luaD_checkstack(L, 3); | ||||||
|  |   L->top += 3; | ||||||
|  |   luaD_call(L, L->top - 3, 1); | ||||||
|  |   res = restorestack(L, result); | ||||||
|  |   L->top--; | ||||||
|  |   setobjs2s(L, res, L->top); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void callTM (lua_State *L, const TValue *f, const TValue *p1, | ||||||
|  |                     const TValue *p2, const TValue *p3) { | ||||||
|  |   setobj2s(L, L->top, f);  /* push function */ | ||||||
|  |   setobj2s(L, L->top+1, p1);  /* 1st argument */ | ||||||
|  |   setobj2s(L, L->top+2, p2);  /* 2nd argument */ | ||||||
|  |   setobj2s(L, L->top+3, p3);  /* 3th argument */ | ||||||
|  |   luaD_checkstack(L, 4); | ||||||
|  |   L->top += 4; | ||||||
|  |   luaD_call(L, L->top - 4, 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { | ||||||
|  |   int loop; | ||||||
|  |   for (loop = 0; loop < MAXTAGLOOP; loop++) { | ||||||
|  |     const TValue *tm; | ||||||
|  |     if (ttistable(t)) {  /* `t' is a table? */ | ||||||
|  |       Table *h = hvalue(t); | ||||||
|  |       const TValue *res = luaH_get(h, key); /* do a primitive get */ | ||||||
|  |       if (!ttisnil(res) ||  /* result is no nil? */ | ||||||
|  |           (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ | ||||||
|  |         setobj2s(L, val, res); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       /* else will try the tag method */ | ||||||
|  |     } | ||||||
|  |     else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) | ||||||
|  |       luaG_typeerror(L, t, "index"); | ||||||
|  |     if (ttisfunction(tm)) { | ||||||
|  |       callTMres(L, val, tm, t, key); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     t = tm;  /* else repeat with `tm' */  | ||||||
|  |   } | ||||||
|  |   luaG_runerror(L, "loop in gettable"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { | ||||||
|  |   int loop; | ||||||
|  |   for (loop = 0; loop < MAXTAGLOOP; loop++) { | ||||||
|  |     const TValue *tm; | ||||||
|  |     if (ttistable(t)) {  /* `t' is a table? */ | ||||||
|  |       Table *h = hvalue(t); | ||||||
|  |       TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ | ||||||
|  |       if (!ttisnil(oldval) ||  /* result is no nil? */ | ||||||
|  |           (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ | ||||||
|  |         setobj2t(L, oldval, val); | ||||||
|  |         luaC_barriert(L, h, val); | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       /* else will try the tag method */ | ||||||
|  |     } | ||||||
|  |     else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) | ||||||
|  |       luaG_typeerror(L, t, "index"); | ||||||
|  |     if (ttisfunction(tm)) { | ||||||
|  |       callTM(L, tm, t, key, val); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  |     t = tm;  /* else repeat with `tm' */  | ||||||
|  |   } | ||||||
|  |   luaG_runerror(L, "loop in settable"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, | ||||||
|  |                        StkId res, TMS event) { | ||||||
|  |   const TValue *tm = luaT_gettmbyobj(L, p1, event);  /* try first operand */ | ||||||
|  |   if (ttisnil(tm)) | ||||||
|  |     tm = luaT_gettmbyobj(L, p2, event);  /* try second operand */ | ||||||
|  |   if (!ttisfunction(tm)) return 0; | ||||||
|  |   callTMres(L, res, tm, p1, p2); | ||||||
|  |   return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, | ||||||
|  |                                   TMS event) { | ||||||
|  |   const TValue *tm1 = fasttm(L, mt1, event); | ||||||
|  |   const TValue *tm2; | ||||||
|  |   if (tm1 == NULL) return NULL;  /* no metamethod */ | ||||||
|  |   if (mt1 == mt2) return tm1;  /* same metatables => same metamethods */ | ||||||
|  |   tm2 = fasttm(L, mt2, event); | ||||||
|  |   if (tm2 == NULL) return NULL;  /* no metamethod */ | ||||||
|  |   if (luaO_rawequalObj(tm1, tm2))  /* same metamethods? */ | ||||||
|  |     return tm1; | ||||||
|  |   return NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, | ||||||
|  |                          TMS event) { | ||||||
|  |   const TValue *tm1 = luaT_gettmbyobj(L, p1, event); | ||||||
|  |   const TValue *tm2; | ||||||
|  |   if (ttisnil(tm1)) return -1;  /* no metamethod? */ | ||||||
|  |   tm2 = luaT_gettmbyobj(L, p2, event); | ||||||
|  |   if (!luaO_rawequalObj(tm1, tm2))  /* different metamethods? */ | ||||||
|  |     return -1; | ||||||
|  |   callTMres(L, L->top, tm1, p1, p2); | ||||||
|  |   return !l_isfalse(L->top); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int l_strcmp (const TString *ls, const TString *rs) { | ||||||
|  |   const char *l = getstr(ls); | ||||||
|  |   size_t ll = ls->tsv.len; | ||||||
|  |   const char *r = getstr(rs); | ||||||
|  |   size_t lr = rs->tsv.len; | ||||||
|  |   for (;;) { | ||||||
|  |     int temp = strcoll(l, r); | ||||||
|  |     if (temp != 0) return temp; | ||||||
|  |     else {  /* strings are equal up to a `\0' */ | ||||||
|  |       size_t len = strlen(l);  /* index of first `\0' in both strings */ | ||||||
|  |       if (len == lr)  /* r is finished? */ | ||||||
|  |         return (len == ll) ? 0 : 1; | ||||||
|  |       else if (len == ll)  /* l is finished? */ | ||||||
|  |         return -1;  /* l is smaller than r (because r is not finished) */ | ||||||
|  |       /* both strings longer than `len'; go on comparing (after the `\0') */ | ||||||
|  |       len++; | ||||||
|  |       l += len; ll -= len; r += len; lr -= len; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { | ||||||
|  |   int res; | ||||||
|  |   if (ttype(l) != ttype(r)) | ||||||
|  |     return luaG_ordererror(L, l, r); | ||||||
|  |   else if (ttisnumber(l)) | ||||||
|  |     return luai_numlt(nvalue(l), nvalue(r)); | ||||||
|  |   else if (ttisstring(l)) | ||||||
|  |     return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; | ||||||
|  |   else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) | ||||||
|  |     return res; | ||||||
|  |   return luaG_ordererror(L, l, r); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int lessequal (lua_State *L, const TValue *l, const TValue *r) { | ||||||
|  |   int res; | ||||||
|  |   if (ttype(l) != ttype(r)) | ||||||
|  |     return luaG_ordererror(L, l, r); | ||||||
|  |   else if (ttisnumber(l)) | ||||||
|  |     return luai_numle(nvalue(l), nvalue(r)); | ||||||
|  |   else if (ttisstring(l)) | ||||||
|  |     return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; | ||||||
|  |   else if ((res = call_orderTM(L, l, r, TM_LE)) != -1)  /* first try `le' */ | ||||||
|  |     return res; | ||||||
|  |   else if ((res = call_orderTM(L, r, l, TM_LT)) != -1)  /* else try `lt' */ | ||||||
|  |     return !res; | ||||||
|  |   return luaG_ordererror(L, l, r); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { | ||||||
|  |   const TValue *tm; | ||||||
|  |   lua_assert(ttype(t1) == ttype(t2)); | ||||||
|  |   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);  /* true must be 1 !! */ | ||||||
|  |     case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); | ||||||
|  |     case LUA_TUSERDATA: { | ||||||
|  |       if (uvalue(t1) == uvalue(t2)) return 1; | ||||||
|  |       tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, | ||||||
|  |                          TM_EQ); | ||||||
|  |       break;  /* will try TM */ | ||||||
|  |     } | ||||||
|  |     case LUA_TTABLE: { | ||||||
|  |       if (hvalue(t1) == hvalue(t2)) return 1; | ||||||
|  |       tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); | ||||||
|  |       break;  /* will try TM */ | ||||||
|  |     } | ||||||
|  |     default: return gcvalue(t1) == gcvalue(t2); | ||||||
|  |   } | ||||||
|  |   if (tm == NULL) return 0;  /* no TM? */ | ||||||
|  |   callTMres(L, L->top, tm, t1, t2);  /* call TM */ | ||||||
|  |   return !l_isfalse(L->top); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaV_concat (lua_State *L, int total, int last) { | ||||||
|  |   do { | ||||||
|  |     StkId top = L->base + last + 1; | ||||||
|  |     int n = 2;  /* number of elements handled in this pass (at least 2) */ | ||||||
|  |     if (!tostring(L, top-2) || !tostring(L, top-1)) { | ||||||
|  |       if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) | ||||||
|  |         luaG_concaterror(L, top-2, top-1); | ||||||
|  |     } else if (tsvalue(top-1)->len > 0) {  /* if len=0, do nothing */ | ||||||
|  |       /* at least two string values; get as many as possible */ | ||||||
|  |       size_t tl = tsvalue(top-1)->len; | ||||||
|  |       char *buffer; | ||||||
|  |       int i; | ||||||
|  |       /* collect total length */ | ||||||
|  |       for (n = 1; n < total && tostring(L, top-n-1); n++) { | ||||||
|  |         size_t l = tsvalue(top-n-1)->len; | ||||||
|  |         if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); | ||||||
|  |         tl += l; | ||||||
|  |       } | ||||||
|  |       buffer = luaZ_openspace(L, &G(L)->buff, tl); | ||||||
|  |       tl = 0; | ||||||
|  |       for (i=n; i>0; i--) {  /* concat all strings */ | ||||||
|  |         size_t l = tsvalue(top-i)->len; | ||||||
|  |         memcpy(buffer+tl, svalue(top-i), l); | ||||||
|  |         tl += l; | ||||||
|  |       } | ||||||
|  |       setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); | ||||||
|  |     } | ||||||
|  |     total -= n-1;  /* got `n' strings to create 1 new */ | ||||||
|  |     last -= n-1; | ||||||
|  |   } while (total > 1);  /* repeat until only 1 result left */ | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static void Arith (lua_State *L, StkId ra, const TValue *rb, | ||||||
|  |                    const TValue *rc, TMS op) { | ||||||
|  |   TValue tempb, tempc; | ||||||
|  |   const TValue *b, *c; | ||||||
|  |   if ((b = luaV_tonumber(rb, &tempb)) != NULL && | ||||||
|  |       (c = luaV_tonumber(rc, &tempc)) != NULL) { | ||||||
|  |     lua_Number nb = nvalue(b), nc = nvalue(c); | ||||||
|  |     switch (op) { | ||||||
|  |       case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; | ||||||
|  |       case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; | ||||||
|  |       case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; | ||||||
|  |       case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break; | ||||||
|  |       case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break; | ||||||
|  |       case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; | ||||||
|  |       case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; | ||||||
|  |       default: lua_assert(0); break; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   else if (!call_binTM(L, rb, rc, ra, op)) | ||||||
|  |     luaG_aritherror(L, rb, rc); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | ** some macros for common tasks in `luaV_execute' | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #define runtime_check(L, c)	{ if (!(c)) break; } | ||||||
|  |  | ||||||
|  | #define RA(i)	(base+GETARG_A(i)) | ||||||
|  | /* to be used after possible stack reallocation */ | ||||||
|  | #define RB(i)	check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) | ||||||
|  | #define RC(i)	check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) | ||||||
|  | #define RKB(i)	check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ | ||||||
|  | 	ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) | ||||||
|  | #define RKC(i)	check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ | ||||||
|  | 	ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) | ||||||
|  | #define KBx(i)	check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define dojump(L,pc,i)	{(pc) += (i); luai_threadyield(L);} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define Protect(x)	{ L->savedpc = pc; {x;}; base = L->base; } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define arith_op(op,tm) { \ | ||||||
|  |         TValue *rb = RKB(i); \ | ||||||
|  |         TValue *rc = RKC(i); \ | ||||||
|  |         if (ttisnumber(rb) && ttisnumber(rc)) { \ | ||||||
|  |           lua_Number nb = nvalue(rb), nc = nvalue(rc); \ | ||||||
|  |           setnvalue(ra, op(nb, nc)); \ | ||||||
|  |         } \ | ||||||
|  |         else \ | ||||||
|  |           Protect(Arith(L, ra, rb, rc, tm)); \ | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaV_execute (lua_State *L, int nexeccalls) { | ||||||
|  |   LClosure *cl; | ||||||
|  |   StkId base; | ||||||
|  |   TValue *k; | ||||||
|  |   const Instruction *pc; | ||||||
|  |  reentry:  /* entry point */ | ||||||
|  |   pc = L->savedpc; | ||||||
|  |   cl = &clvalue(L->ci->func)->l; | ||||||
|  |   base = L->base; | ||||||
|  |   k = cl->p->k; | ||||||
|  |   /* main loop of interpreter */ | ||||||
|  |   for (;;) { | ||||||
|  |     const Instruction i = *pc++; | ||||||
|  |     StkId ra; | ||||||
|  |     if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && | ||||||
|  |         (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { | ||||||
|  |       traceexec(L, pc); | ||||||
|  |       if (L->status == LUA_YIELD) {  /* did hook yield? */ | ||||||
|  |         L->savedpc = pc - 1; | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       base = L->base; | ||||||
|  |     } | ||||||
|  |     /* warning!! several calls may realloc the stack and invalidate `ra' */ | ||||||
|  |     ra = RA(i); | ||||||
|  |     lua_assert(base == L->base && L->base == L->ci->base); | ||||||
|  |     lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); | ||||||
|  |     lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); | ||||||
|  |     switch (GET_OPCODE(i)) { | ||||||
|  |       case OP_MOVE: { | ||||||
|  |         setobjs2s(L, ra, RB(i)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LOADK: { | ||||||
|  |         setobj2s(L, ra, KBx(i)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LOADBOOL: { | ||||||
|  |         setbvalue(ra, GETARG_B(i)); | ||||||
|  |         if (GETARG_C(i)) pc++;  /* skip next instruction (if C) */ | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LOADNIL: { | ||||||
|  |         TValue *rb = RB(i); | ||||||
|  |         do { | ||||||
|  |           setnilvalue(rb--); | ||||||
|  |         } while (rb >= ra); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_GETUPVAL: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         setobj2s(L, ra, cl->upvals[b]->v); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_GETGLOBAL: { | ||||||
|  |         TValue g; | ||||||
|  |         TValue *rb = KBx(i); | ||||||
|  |         sethvalue(L, &g, cl->env); | ||||||
|  |         lua_assert(ttisstring(rb)); | ||||||
|  |         Protect(luaV_gettable(L, &g, rb, ra)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_GETTABLE: { | ||||||
|  |         Protect(luaV_gettable(L, RB(i), RKC(i), ra)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SETGLOBAL: { | ||||||
|  |         TValue g; | ||||||
|  |         sethvalue(L, &g, cl->env); | ||||||
|  |         lua_assert(ttisstring(KBx(i))); | ||||||
|  |         Protect(luaV_settable(L, &g, KBx(i), ra)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SETUPVAL: { | ||||||
|  |         UpVal *uv = cl->upvals[GETARG_B(i)]; | ||||||
|  |         setobj(L, uv->v, ra); | ||||||
|  |         luaC_barrier(L, uv, ra); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SETTABLE: { | ||||||
|  |         Protect(luaV_settable(L, ra, RKB(i), RKC(i))); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_NEWTABLE: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         int c = GETARG_C(i); | ||||||
|  |         sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); | ||||||
|  |         Protect(luaC_checkGC(L)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SELF: { | ||||||
|  |         StkId rb = RB(i); | ||||||
|  |         setobjs2s(L, ra+1, rb); | ||||||
|  |         Protect(luaV_gettable(L, rb, RKC(i), ra)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_ADD: { | ||||||
|  |         arith_op(luai_numadd, TM_ADD); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SUB: { | ||||||
|  |         arith_op(luai_numsub, TM_SUB); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_MUL: { | ||||||
|  |         arith_op(luai_nummul, TM_MUL); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_DIV: { | ||||||
|  |         arith_op(luai_numdiv, TM_DIV); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_MOD: { | ||||||
|  |         arith_op(luai_nummod, TM_MOD); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_POW: { | ||||||
|  |         arith_op(luai_numpow, TM_POW); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_UNM: { | ||||||
|  |         TValue *rb = RB(i); | ||||||
|  |         if (ttisnumber(rb)) { | ||||||
|  |           lua_Number nb = nvalue(rb); | ||||||
|  |           setnvalue(ra, luai_numunm(nb)); | ||||||
|  |         } | ||||||
|  |         else { | ||||||
|  |           Protect(Arith(L, ra, rb, rb, TM_UNM)); | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_NOT: { | ||||||
|  |         int res = l_isfalse(RB(i));  /* next assignment may change this value */ | ||||||
|  |         setbvalue(ra, res); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LEN: { | ||||||
|  |         const TValue *rb = RB(i); | ||||||
|  |         switch (ttype(rb)) { | ||||||
|  |           case LUA_TTABLE: { | ||||||
|  |             setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           case LUA_TSTRING: { | ||||||
|  |             setnvalue(ra, cast_num(tsvalue(rb)->len)); | ||||||
|  |             break; | ||||||
|  |           } | ||||||
|  |           default: {  /* try metamethod */ | ||||||
|  |             Protect( | ||||||
|  |               if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) | ||||||
|  |                 luaG_typeerror(L, rb, "get length of"); | ||||||
|  |             ) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_CONCAT: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         int c = GETARG_C(i); | ||||||
|  |         Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); | ||||||
|  |         setobjs2s(L, RA(i), base+b); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_JMP: { | ||||||
|  |         dojump(L, pc, GETARG_sBx(i)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_EQ: { | ||||||
|  |         TValue *rb = RKB(i); | ||||||
|  |         TValue *rc = RKC(i); | ||||||
|  |         Protect( | ||||||
|  |           if (equalobj(L, rb, rc) == GETARG_A(i)) | ||||||
|  |             dojump(L, pc, GETARG_sBx(*pc)); | ||||||
|  |         ) | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LT: { | ||||||
|  |         Protect( | ||||||
|  |           if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) | ||||||
|  |             dojump(L, pc, GETARG_sBx(*pc)); | ||||||
|  |         ) | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_LE: { | ||||||
|  |         Protect( | ||||||
|  |           if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) | ||||||
|  |             dojump(L, pc, GETARG_sBx(*pc)); | ||||||
|  |         ) | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_TEST: { | ||||||
|  |         if (l_isfalse(ra) != GETARG_C(i)) | ||||||
|  |           dojump(L, pc, GETARG_sBx(*pc)); | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_TESTSET: { | ||||||
|  |         TValue *rb = RB(i); | ||||||
|  |         if (l_isfalse(rb) != GETARG_C(i)) { | ||||||
|  |           setobjs2s(L, ra, rb); | ||||||
|  |           dojump(L, pc, GETARG_sBx(*pc)); | ||||||
|  |         } | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_CALL: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         int nresults = GETARG_C(i) - 1; | ||||||
|  |         if (b != 0) L->top = ra+b;  /* else previous instruction set top */ | ||||||
|  |         L->savedpc = pc; | ||||||
|  |         switch (luaD_precall(L, ra, nresults)) { | ||||||
|  |           case PCRLUA: { | ||||||
|  |             nexeccalls++; | ||||||
|  |             goto reentry;  /* restart luaV_execute over new Lua function */ | ||||||
|  |           } | ||||||
|  |           case PCRC: { | ||||||
|  |             /* it was a C function (`precall' called it); adjust results */ | ||||||
|  |             if (nresults >= 0) L->top = L->ci->top; | ||||||
|  |             base = L->base; | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           default: { | ||||||
|  |             return;  /* yield */ | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       case OP_TAILCALL: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         if (b != 0) L->top = ra+b;  /* else previous instruction set top */ | ||||||
|  |         L->savedpc = pc; | ||||||
|  |         lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); | ||||||
|  |         switch (luaD_precall(L, ra, LUA_MULTRET)) { | ||||||
|  |           case PCRLUA: { | ||||||
|  |             /* tail call: put new frame in place of previous one */ | ||||||
|  |             CallInfo *ci = L->ci - 1;  /* previous frame */ | ||||||
|  |             int aux; | ||||||
|  |             StkId func = ci->func; | ||||||
|  |             StkId pfunc = (ci+1)->func;  /* previous function index */ | ||||||
|  |             if (L->openupval) luaF_close(L, ci->base); | ||||||
|  |             L->base = ci->base = ci->func + ((ci+1)->base - pfunc); | ||||||
|  |             for (aux = 0; pfunc+aux < L->top; aux++)  /* move frame down */ | ||||||
|  |               setobjs2s(L, func+aux, pfunc+aux); | ||||||
|  |             ci->top = L->top = func+aux;  /* correct top */ | ||||||
|  |             lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); | ||||||
|  |             ci->savedpc = L->savedpc; | ||||||
|  |             ci->tailcalls++;  /* one more call lost */ | ||||||
|  |             L->ci--;  /* remove new frame */ | ||||||
|  |             goto reentry; | ||||||
|  |           } | ||||||
|  |           case PCRC: {  /* it was a C function (`precall' called it) */ | ||||||
|  |             base = L->base; | ||||||
|  |             continue; | ||||||
|  |           } | ||||||
|  |           default: { | ||||||
|  |             return;  /* yield */ | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       case OP_RETURN: { | ||||||
|  |         int b = GETARG_B(i); | ||||||
|  |         if (b != 0) L->top = ra+b-1; | ||||||
|  |         if (L->openupval) luaF_close(L, base); | ||||||
|  |         L->savedpc = pc; | ||||||
|  |         b = luaD_poscall(L, ra); | ||||||
|  |         if (--nexeccalls == 0)  /* was previous function running `here'? */ | ||||||
|  |           return;  /* no: return */ | ||||||
|  |         else {  /* yes: continue its execution */ | ||||||
|  |           if (b) L->top = L->ci->top; | ||||||
|  |           lua_assert(isLua(L->ci)); | ||||||
|  |           lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); | ||||||
|  |           goto reentry; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       case OP_FORLOOP: { | ||||||
|  |         lua_Number step = nvalue(ra+2); | ||||||
|  |         lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ | ||||||
|  |         lua_Number limit = nvalue(ra+1); | ||||||
|  |         if (luai_numlt(0, step) ? luai_numle(idx, limit) | ||||||
|  |                                 : luai_numle(limit, idx)) { | ||||||
|  |           dojump(L, pc, GETARG_sBx(i));  /* jump back */ | ||||||
|  |           setnvalue(ra, idx);  /* update internal index... */ | ||||||
|  |           setnvalue(ra+3, idx);  /* ...and external index */ | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_FORPREP: { | ||||||
|  |         const TValue *init = ra; | ||||||
|  |         const TValue *plimit = ra+1; | ||||||
|  |         const TValue *pstep = ra+2; | ||||||
|  |         L->savedpc = pc;  /* next steps may throw errors */ | ||||||
|  |         if (!tonumber(init, ra)) | ||||||
|  |           luaG_runerror(L, LUA_QL("for") " initial value must be a number"); | ||||||
|  |         else if (!tonumber(plimit, ra+1)) | ||||||
|  |           luaG_runerror(L, LUA_QL("for") " limit must be a number"); | ||||||
|  |         else if (!tonumber(pstep, ra+2)) | ||||||
|  |           luaG_runerror(L, LUA_QL("for") " step must be a number"); | ||||||
|  |         setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); | ||||||
|  |         dojump(L, pc, GETARG_sBx(i)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_TFORLOOP: { | ||||||
|  |         StkId cb = ra + 3;  /* call base */ | ||||||
|  |         setobjs2s(L, cb+2, ra+2); | ||||||
|  |         setobjs2s(L, cb+1, ra+1); | ||||||
|  |         setobjs2s(L, cb, ra); | ||||||
|  |         L->top = cb+3;  /* func. + 2 args (state and index) */ | ||||||
|  |         Protect(luaD_call(L, cb, GETARG_C(i))); | ||||||
|  |         L->top = L->ci->top; | ||||||
|  |         cb = RA(i) + 3;  /* previous call may change the stack */ | ||||||
|  |         if (!ttisnil(cb)) {  /* continue loop? */ | ||||||
|  |           setobjs2s(L, cb-1, cb);  /* save control variable */ | ||||||
|  |           dojump(L, pc, GETARG_sBx(*pc));  /* jump back */ | ||||||
|  |         } | ||||||
|  |         pc++; | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_SETLIST: { | ||||||
|  |         int n = GETARG_B(i); | ||||||
|  |         int c = GETARG_C(i); | ||||||
|  |         int last; | ||||||
|  |         Table *h; | ||||||
|  |         if (n == 0) { | ||||||
|  |           n = cast_int(L->top - ra) - 1; | ||||||
|  |           L->top = L->ci->top; | ||||||
|  |         } | ||||||
|  |         if (c == 0) c = cast_int(*pc++); | ||||||
|  |         runtime_check(L, ttistable(ra)); | ||||||
|  |         h = hvalue(ra); | ||||||
|  |         last = ((c-1)*LFIELDS_PER_FLUSH) + n; | ||||||
|  |         if (last > h->sizearray)  /* needs more space? */ | ||||||
|  |           luaH_resizearray(L, h, last);  /* pre-alloc it at once */ | ||||||
|  |         for (; n > 0; n--) { | ||||||
|  |           TValue *val = ra+n; | ||||||
|  |           setobj2t(L, luaH_setnum(L, h, last--), val); | ||||||
|  |           luaC_barriert(L, h, val); | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_CLOSE: { | ||||||
|  |         luaF_close(L, ra); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_CLOSURE: { | ||||||
|  |         Proto *p; | ||||||
|  |         Closure *ncl; | ||||||
|  |         int nup, j; | ||||||
|  |         p = cl->p->p[GETARG_Bx(i)]; | ||||||
|  |         nup = p->nups; | ||||||
|  |         ncl = luaF_newLclosure(L, nup, cl->env); | ||||||
|  |         ncl->l.p = p; | ||||||
|  |         for (j=0; j<nup; j++, pc++) { | ||||||
|  |           if (GET_OPCODE(*pc) == OP_GETUPVAL) | ||||||
|  |             ncl->l.upvals[j] = cl->upvals[GETARG_B(*pc)]; | ||||||
|  |           else { | ||||||
|  |             lua_assert(GET_OPCODE(*pc) == OP_MOVE); | ||||||
|  |             ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         setclvalue(L, ra, ncl); | ||||||
|  |         Protect(luaC_checkGC(L)); | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |       case OP_VARARG: { | ||||||
|  |         int b = GETARG_B(i) - 1; | ||||||
|  |         int j; | ||||||
|  |         CallInfo *ci = L->ci; | ||||||
|  |         int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; | ||||||
|  |         if (b == LUA_MULTRET) { | ||||||
|  |           Protect(luaD_checkstack(L, n)); | ||||||
|  |           ra = RA(i);  /* previous call may change the stack */ | ||||||
|  |           b = n; | ||||||
|  |           L->top = ra + n; | ||||||
|  |         } | ||||||
|  |         for (j = 0; j < b; j++) { | ||||||
|  |           if (j < n) { | ||||||
|  |             setobjs2s(L, ra + j, ci->base - n + j); | ||||||
|  |           } | ||||||
|  |           else { | ||||||
|  |             setnilvalue(ra + j); | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |         continue; | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								src/lua/lvm.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/lua/lvm.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lvm.h,v 2.5 2005/08/22 18:54:49 roberto Exp $ | ||||||
|  | ** Lua virtual machine | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef lvm_h | ||||||
|  | #define lvm_h | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include "ldo.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "ltm.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) | ||||||
|  |  | ||||||
|  | #define tonumber(o,n)	(ttype(o) == LUA_TNUMBER || \ | ||||||
|  |                          (((o) = luaV_tonumber(o,n)) != NULL)) | ||||||
|  |  | ||||||
|  | #define equalobj(L,o1,o2) \ | ||||||
|  | 	(ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); | ||||||
|  | LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); | ||||||
|  | LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); | ||||||
|  | LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); | ||||||
|  | LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, | ||||||
|  |                                             StkId val); | ||||||
|  | LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, | ||||||
|  |                                             StkId val); | ||||||
|  | LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); | ||||||
|  | LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										82
									
								
								src/lua/lzio.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/lua/lzio.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,82 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lzio.c,v 1.31 2005/06/03 20:15:29 roberto Exp $ | ||||||
|  | ** a generic input stream interface | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #define lzio_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "llimits.h" | ||||||
|  | #include "lmem.h" | ||||||
|  | #include "lstate.h" | ||||||
|  | #include "lzio.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaZ_fill (ZIO *z) { | ||||||
|  |   size_t size; | ||||||
|  |   lua_State *L = z->L; | ||||||
|  |   const char *buff; | ||||||
|  |   lua_unlock(L); | ||||||
|  |   buff = z->reader(L, z->data, &size); | ||||||
|  |   lua_lock(L); | ||||||
|  |   if (buff == NULL || size == 0) return EOZ; | ||||||
|  |   z->n = size - 1; | ||||||
|  |   z->p = buff; | ||||||
|  |   return char2int(*(z->p++)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | int luaZ_lookahead (ZIO *z) { | ||||||
|  |   if (z->n == 0) { | ||||||
|  |     if (luaZ_fill(z) == EOZ) | ||||||
|  |       return EOZ; | ||||||
|  |     else { | ||||||
|  |       z->n++;  /* luaZ_fill removed first byte; put back it */ | ||||||
|  |       z->p--; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return char2int(*z->p); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { | ||||||
|  |   z->L = L; | ||||||
|  |   z->reader = reader; | ||||||
|  |   z->data = data; | ||||||
|  |   z->n = 0; | ||||||
|  |   z->p = NULL; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* --------------------------------------------------------------- read --- */ | ||||||
|  | size_t luaZ_read (ZIO *z, void *b, size_t n) { | ||||||
|  |   while (n) { | ||||||
|  |     size_t m; | ||||||
|  |     if (luaZ_lookahead(z) == EOZ) | ||||||
|  |       return n;  /* return number of missing bytes */ | ||||||
|  |     m = (n <= z->n) ? n : z->n;  /* min. between n and z->n */ | ||||||
|  |     memcpy(b, z->p, m); | ||||||
|  |     z->n -= m; | ||||||
|  |     z->p += m; | ||||||
|  |     b = (char *)b + m; | ||||||
|  |     n -= m; | ||||||
|  |   } | ||||||
|  |   return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /* ------------------------------------------------------------------------ */ | ||||||
|  | char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { | ||||||
|  |   if (n > buff->buffsize) { | ||||||
|  |     if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; | ||||||
|  |     luaZ_resizebuffer(L, buff, n); | ||||||
|  |   } | ||||||
|  |   return buff->buffer; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										67
									
								
								src/lua/lzio.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/lua/lzio.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: lzio.h,v 1.21 2005/05/17 19:49:15 roberto Exp $ | ||||||
|  | ** Buffered streams | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #ifndef lzio_h | ||||||
|  | #define lzio_h | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "lmem.h" | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define EOZ	(-1)			/* end of stream */ | ||||||
|  |  | ||||||
|  | typedef struct Zio ZIO; | ||||||
|  |  | ||||||
|  | #define char2int(c)	cast(int, cast(unsigned char, (c))) | ||||||
|  |  | ||||||
|  | #define zgetc(z)  (((z)->n--)>0 ?  char2int(*(z)->p++) : luaZ_fill(z)) | ||||||
|  |  | ||||||
|  | typedef struct Mbuffer { | ||||||
|  |   char *buffer; | ||||||
|  |   size_t n; | ||||||
|  |   size_t buffsize; | ||||||
|  | } Mbuffer; | ||||||
|  |  | ||||||
|  | #define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) | ||||||
|  |  | ||||||
|  | #define luaZ_buffer(buff)	((buff)->buffer) | ||||||
|  | #define luaZ_sizebuffer(buff)	((buff)->buffsize) | ||||||
|  | #define luaZ_bufflen(buff)	((buff)->n) | ||||||
|  |  | ||||||
|  | #define luaZ_resetbuffer(buff) ((buff)->n = 0) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | #define luaZ_resizebuffer(L, buff, size) \ | ||||||
|  | 	(luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ | ||||||
|  | 	(buff)->buffsize = size) | ||||||
|  |  | ||||||
|  | #define luaZ_freebuffer(L, buff)	luaZ_resizebuffer(L, buff, 0) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); | ||||||
|  | LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, | ||||||
|  |                                         void *data); | ||||||
|  | LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n);	/* read next n bytes */ | ||||||
|  | LUAI_FUNC int luaZ_lookahead (ZIO *z); | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /* --------- Private Part ------------------ */ | ||||||
|  |  | ||||||
|  | struct Zio { | ||||||
|  |   size_t n;			/* bytes still unread */ | ||||||
|  |   const char *p;		/* current position in buffer */ | ||||||
|  |   lua_Reader reader; | ||||||
|  |   void* data;			/* additional data */ | ||||||
|  |   lua_State *L;			/* Lua state (for reader) */ | ||||||
|  | }; | ||||||
|  |  | ||||||
|  |  | ||||||
|  | LUAI_FUNC int luaZ_fill (ZIO *z); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										224
									
								
								src/lua/print.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										224
									
								
								src/lua/print.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,224 @@ | |||||||
|  | /* | ||||||
|  | ** $Id: print.c,v 1.54 2006/01/11 22:49:27 lhf Exp $ | ||||||
|  | ** print bytecodes | ||||||
|  | ** See Copyright Notice in lua.h | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <ctype.h> | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #define luac_c | ||||||
|  | #define LUA_CORE | ||||||
|  |  | ||||||
|  | #include "ldebug.h" | ||||||
|  | #include "lobject.h" | ||||||
|  | #include "lopcodes.h" | ||||||
|  | #include "lundump.h" | ||||||
|  |  | ||||||
|  | #define PrintFunction	luaU_print | ||||||
|  |  | ||||||
|  | #define Sizeof(x)	((int)sizeof(x)) | ||||||
|  | #define VOID(p)		((const void*)(p)) | ||||||
|  |  | ||||||
|  | static void PrintString(const Proto* f, int n) | ||||||
|  | { | ||||||
|  |  const char* s=svalue(&f->k[n]); | ||||||
|  |  putchar('"'); | ||||||
|  |  for (; *s; s++) | ||||||
|  |  { | ||||||
|  |   switch (*s) | ||||||
|  |   { | ||||||
|  |    case '"': printf("\\\""); break; | ||||||
|  |    case '\a': printf("\\a"); break; | ||||||
|  |    case '\b': printf("\\b"); break; | ||||||
|  |    case '\f': printf("\\f"); break; | ||||||
|  |    case '\n': printf("\\n"); break; | ||||||
|  |    case '\r': printf("\\r"); break; | ||||||
|  |    case '\t': printf("\\t"); break; | ||||||
|  |    case '\v': printf("\\v"); break; | ||||||
|  |    default:	if (isprint((unsigned char)*s)) | ||||||
|  |    			printf("%c",*s); | ||||||
|  | 		else | ||||||
|  | 			printf("\\%03u",(unsigned char)*s); | ||||||
|  |   } | ||||||
|  |  } | ||||||
|  |  putchar('"'); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void PrintConstant(const Proto* f, int i) | ||||||
|  | { | ||||||
|  |  const TValue* o=&f->k[i]; | ||||||
|  |  switch (ttype(o)) | ||||||
|  |  { | ||||||
|  |   case LUA_TNIL: | ||||||
|  | 	printf("nil"); | ||||||
|  | 	break; | ||||||
|  |   case LUA_TBOOLEAN: | ||||||
|  | 	printf(bvalue(o) ? "true" : "false"); | ||||||
|  | 	break; | ||||||
|  |   case LUA_TNUMBER: | ||||||
|  | 	printf(LUA_NUMBER_FMT,nvalue(o)); | ||||||
|  | 	break; | ||||||
|  |   case LUA_TSTRING: | ||||||
|  | 	PrintString(f,i); | ||||||
|  | 	break; | ||||||
|  |   default:				/* cannot happen */ | ||||||
|  | 	printf("? type=%d",ttype(o)); | ||||||
|  | 	break; | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void PrintCode(const Proto* f) | ||||||
|  | { | ||||||
|  |  const Instruction* code=f->code; | ||||||
|  |  int pc,n=f->sizecode; | ||||||
|  |  for (pc=0; pc<n; pc++) | ||||||
|  |  { | ||||||
|  |   Instruction i=code[pc]; | ||||||
|  |   OpCode o=GET_OPCODE(i); | ||||||
|  |   int a=GETARG_A(i); | ||||||
|  |   int b=GETARG_B(i); | ||||||
|  |   int c=GETARG_C(i); | ||||||
|  |   int bx=GETARG_Bx(i); | ||||||
|  |   int sbx=GETARG_sBx(i); | ||||||
|  |   int line=getline(f,pc); | ||||||
|  |   printf("\t%d\t",pc+1); | ||||||
|  |   if (line>0) printf("[%d]\t",line); else printf("[-]\t"); | ||||||
|  |   printf("%-9s\t",luaP_opnames[o]); | ||||||
|  |   switch (getOpMode(o)) | ||||||
|  |   { | ||||||
|  |    case iABC: | ||||||
|  |     printf("%d",a); | ||||||
|  |     if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); | ||||||
|  |     if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); | ||||||
|  |     break; | ||||||
|  |    case iABx: | ||||||
|  |     if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); | ||||||
|  |     break; | ||||||
|  |    case iAsBx: | ||||||
|  |     if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); | ||||||
|  |     break; | ||||||
|  |   } | ||||||
|  |   switch (o) | ||||||
|  |   { | ||||||
|  |    case OP_LOADK: | ||||||
|  |     printf("\t; "); PrintConstant(f,bx); | ||||||
|  |     break; | ||||||
|  |    case OP_GETUPVAL: | ||||||
|  |    case OP_SETUPVAL: | ||||||
|  |     printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); | ||||||
|  |     break; | ||||||
|  |    case OP_GETGLOBAL: | ||||||
|  |    case OP_SETGLOBAL: | ||||||
|  |     printf("\t; %s",svalue(&f->k[bx])); | ||||||
|  |     break; | ||||||
|  |    case OP_GETTABLE: | ||||||
|  |    case OP_SELF: | ||||||
|  |     if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } | ||||||
|  |     break; | ||||||
|  |    case OP_SETTABLE: | ||||||
|  |    case OP_ADD: | ||||||
|  |    case OP_SUB: | ||||||
|  |    case OP_MUL: | ||||||
|  |    case OP_DIV: | ||||||
|  |    case OP_POW: | ||||||
|  |    case OP_EQ: | ||||||
|  |    case OP_LT: | ||||||
|  |    case OP_LE: | ||||||
|  |     if (ISK(b) || ISK(c)) | ||||||
|  |     { | ||||||
|  |      printf("\t; "); | ||||||
|  |      if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); | ||||||
|  |      printf(" "); | ||||||
|  |      if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); | ||||||
|  |     } | ||||||
|  |     break; | ||||||
|  |    case OP_JMP: | ||||||
|  |    case OP_FORLOOP: | ||||||
|  |    case OP_FORPREP: | ||||||
|  |     printf("\t; to %d",sbx+pc+2); | ||||||
|  |     break; | ||||||
|  |    case OP_CLOSURE: | ||||||
|  |     printf("\t; %p",VOID(f->p[bx])); | ||||||
|  |     break; | ||||||
|  |    case OP_SETLIST: | ||||||
|  |     if (c==0) printf("\t; %d",(int)code[++pc]); | ||||||
|  |     else printf("\t; %d",c); | ||||||
|  |     break; | ||||||
|  |    default: | ||||||
|  |     break; | ||||||
|  |   } | ||||||
|  |   printf("\n"); | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define SS(x)	(x==1)?"":"s" | ||||||
|  | #define S(x)	x,SS(x) | ||||||
|  |  | ||||||
|  | static void PrintHeader(const Proto* f) | ||||||
|  | { | ||||||
|  |  const char* s=getstr(f->source); | ||||||
|  |  if (*s=='@' || *s=='=') | ||||||
|  |   s++; | ||||||
|  |  else if (*s==LUA_SIGNATURE[0]) | ||||||
|  |   s="(bstring)"; | ||||||
|  |  else | ||||||
|  |   s="(string)"; | ||||||
|  |  printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", | ||||||
|  |  	(f->linedefined==0)?"main":"function",s, | ||||||
|  | 	f->linedefined,f->lastlinedefined, | ||||||
|  | 	S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); | ||||||
|  |  printf("%d%s param%s, %d slot%s, %d upvalue%s, ", | ||||||
|  | 	f->numparams,f->is_vararg?"+":"",SS(f->numparams), | ||||||
|  | 	S(f->maxstacksize),S(f->nups)); | ||||||
|  |  printf("%d local%s, %d constant%s, %d function%s\n", | ||||||
|  | 	S(f->sizelocvars),S(f->sizek),S(f->sizep)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void PrintConstants(const Proto* f) | ||||||
|  | { | ||||||
|  |  int i,n=f->sizek; | ||||||
|  |  printf("constants (%d) for %p:\n",n,VOID(f)); | ||||||
|  |  for (i=0; i<n; i++) | ||||||
|  |  { | ||||||
|  |   printf("\t%d\t",i+1); | ||||||
|  |   PrintConstant(f,i); | ||||||
|  |   printf("\n"); | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void PrintLocals(const Proto* f) | ||||||
|  | { | ||||||
|  |  int i,n=f->sizelocvars; | ||||||
|  |  printf("locals (%d) for %p:\n",n,VOID(f)); | ||||||
|  |  for (i=0; i<n; i++) | ||||||
|  |  { | ||||||
|  |   printf("\t%d\t%s\t%d\t%d\n", | ||||||
|  |   i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void PrintUpvalues(const Proto* f) | ||||||
|  | { | ||||||
|  |  int i,n=f->sizeupvalues; | ||||||
|  |  printf("upvalues (%d) for %p:\n",n,VOID(f)); | ||||||
|  |  if (f->upvalues==NULL) return; | ||||||
|  |  for (i=0; i<n; i++) | ||||||
|  |  { | ||||||
|  |   printf("\t%d\t%s\n",i,getstr(f->upvalues[i])); | ||||||
|  |  } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void PrintFunction(const Proto* f, int full) | ||||||
|  | { | ||||||
|  |  int i,n=f->sizep; | ||||||
|  |  PrintHeader(f); | ||||||
|  |  PrintCode(f); | ||||||
|  |  if (full) | ||||||
|  |  { | ||||||
|  |   PrintConstants(f); | ||||||
|  |   PrintLocals(f); | ||||||
|  |   PrintUpvalues(f); | ||||||
|  |  } | ||||||
|  |  for (i=0; i<n; i++) PrintFunction(f->p[i],full); | ||||||
|  | } | ||||||
							
								
								
									
										20
									
								
								src/luasocket/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/luasocket/LICENSE.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | |||||||
|  | LuaSocket 2.0 license | ||||||
|  | Copyright <20> 2004-2005 Diego Nehab | ||||||
|  |  | ||||||
|  | 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. | ||||||
							
								
								
									
										149
									
								
								src/luasocket/auxiliar.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								src/luasocket/auxiliar.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,149 @@ | |||||||
|  | /*=========================================================================*\ | ||||||
|  | * Auxiliar routines for class hierarchy manipulation | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: auxiliar.c,v 1.14 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include <string.h> | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #include "auxiliar.h" | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Exported functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Initializes the module | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int auxiliar_open(lua_State *L) { | ||||||
|  |     (void) L; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Creates a new class with given methods | ||||||
|  | * Methods whose names start with __ are passed directly to the metatable. | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void auxiliar_newclass(lua_State *L, const char *classname, luaL_reg *func) { | ||||||
|  |     luaL_newmetatable(L, classname); /* mt */ | ||||||
|  |     /* create __index table to place methods */ | ||||||
|  |     lua_pushstring(L, "__index");    /* mt,"__index" */ | ||||||
|  |     lua_newtable(L);                 /* mt,"__index",it */  | ||||||
|  |     /* put class name into class metatable */ | ||||||
|  |     lua_pushstring(L, "class");      /* mt,"__index",it,"class" */ | ||||||
|  |     lua_pushstring(L, classname);    /* mt,"__index",it,"class",classname */ | ||||||
|  |     lua_rawset(L, -3);               /* mt,"__index",it */ | ||||||
|  |     /* pass all methods that start with _ to the metatable, and all others | ||||||
|  |      * to the index table */ | ||||||
|  |     for (; func->name; func++) {     /* mt,"__index",it */ | ||||||
|  |         lua_pushstring(L, func->name); | ||||||
|  |         lua_pushcfunction(L, func->func); | ||||||
|  |         lua_rawset(L, func->name[0] == '_' ? -5: -3); | ||||||
|  |     } | ||||||
|  |     lua_rawset(L, -3);               /* mt */ | ||||||
|  |     lua_pop(L, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Prints the value of a class in a nice way | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int auxiliar_tostring(lua_State *L) { | ||||||
|  |     char buf[32]; | ||||||
|  |     if (!lua_getmetatable(L, 1)) goto error; | ||||||
|  |     lua_pushstring(L, "__index"); | ||||||
|  |     lua_gettable(L, -2); | ||||||
|  |     if (!lua_istable(L, -1)) goto error; | ||||||
|  |     lua_pushstring(L, "class"); | ||||||
|  |     lua_gettable(L, -2); | ||||||
|  |     if (!lua_isstring(L, -1)) goto error; | ||||||
|  |     sprintf(buf, "%p", lua_touserdata(L, 1)); | ||||||
|  |     lua_pushfstring(L, "%s: %s", lua_tostring(L, -1), buf); | ||||||
|  |     return 1; | ||||||
|  | error: | ||||||
|  |     lua_pushstring(L, "invalid object passed to 'auxiliar.c:__tostring'"); | ||||||
|  |     lua_error(L); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Insert class into group | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void auxiliar_add2group(lua_State *L, const char *classname, const char *groupname) { | ||||||
|  |     luaL_getmetatable(L, classname); | ||||||
|  |     lua_pushstring(L, groupname); | ||||||
|  |     lua_pushboolean(L, 1); | ||||||
|  |     lua_rawset(L, -3); | ||||||
|  |     lua_pop(L, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Make sure argument is a boolean | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int auxiliar_checkboolean(lua_State *L, int objidx) { | ||||||
|  |     if (!lua_isboolean(L, objidx)) | ||||||
|  |         luaL_typerror(L, objidx, lua_typename(L, LUA_TBOOLEAN)); | ||||||
|  |     return lua_toboolean(L, objidx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Return userdata pointer if object belongs to a given class, abort with  | ||||||
|  | * error otherwise | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) { | ||||||
|  |     void *data = auxiliar_getclassudata(L, classname, objidx); | ||||||
|  |     if (!data) { | ||||||
|  |         char msg[45]; | ||||||
|  |         sprintf(msg, "%.35s expected", classname); | ||||||
|  |         luaL_argerror(L, objidx, msg); | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Return userdata pointer if object belongs to a given group, abort with  | ||||||
|  | * error otherwise | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx) { | ||||||
|  |     void *data = auxiliar_getgroupudata(L, groupname, objidx); | ||||||
|  |     if (!data) { | ||||||
|  |         char msg[45]; | ||||||
|  |         sprintf(msg, "%.35s expected", groupname); | ||||||
|  |         luaL_argerror(L, objidx, msg); | ||||||
|  |     } | ||||||
|  |     return data; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Set object class | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void auxiliar_setclass(lua_State *L, const char *classname, int objidx) { | ||||||
|  |     luaL_getmetatable(L, classname); | ||||||
|  |     if (objidx < 0) objidx--; | ||||||
|  |     lua_setmetatable(L, objidx); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Get a userdata pointer if object belongs to a given group. Return NULL  | ||||||
|  | * otherwise | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { | ||||||
|  |     if (!lua_getmetatable(L, objidx)) | ||||||
|  |         return NULL; | ||||||
|  |     lua_pushstring(L, groupname); | ||||||
|  |     lua_rawget(L, -2); | ||||||
|  |     if (lua_isnil(L, -1)) { | ||||||
|  |         lua_pop(L, 2); | ||||||
|  |         return NULL; | ||||||
|  |     } else { | ||||||
|  |         lua_pop(L, 2); | ||||||
|  |         return lua_touserdata(L, objidx); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Get a userdata pointer if object belongs to a given class. Return NULL  | ||||||
|  | * otherwise | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { | ||||||
|  |     return luaL_checkudata(L, objidx, classname); | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								src/luasocket/auxiliar.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/luasocket/auxiliar.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | |||||||
|  | #ifndef AUXILIAR_H | ||||||
|  | #define AUXILIAR_H | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Auxiliar routines for class hierarchy manipulation | ||||||
|  | * LuaSocket toolkit (but completely independent of other LuaSocket modules) | ||||||
|  | * | ||||||
|  | * A LuaSocket class is a name associated with Lua metatables. A LuaSocket  | ||||||
|  | * group is a name associated with a class. A class can belong to any number  | ||||||
|  | * of groups. This module provides the functionality to: | ||||||
|  | * | ||||||
|  | *   - create new classes  | ||||||
|  | *   - add classes to groups  | ||||||
|  | *   - set the class of objects | ||||||
|  | *   - check if an object belongs to a given class or group | ||||||
|  | *   - get the userdata associated to objects | ||||||
|  | *   - print objects in a pretty way | ||||||
|  | * | ||||||
|  | * LuaSocket class names follow the convention <module>{<class>}. Modules | ||||||
|  | * can define any number of classes and groups. The module tcp.c, for | ||||||
|  | * example, defines the classes tcp{master}, tcp{client} and tcp{server} and | ||||||
|  | * the groups tcp{client,server} and tcp{any}. Module functions can then | ||||||
|  | * perform type-checking on their arguments by either class or group. | ||||||
|  | * | ||||||
|  | * LuaSocket metatables define the __index metamethod as being a table. This | ||||||
|  | * table has one field for each method supported by the class, and a field | ||||||
|  | * "class" with the class name. | ||||||
|  | * | ||||||
|  | * The mapping from class name to the corresponding metatable and the | ||||||
|  | * reverse mapping are done using lauxlib.  | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: auxiliar.h,v 1.9 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  |  | ||||||
|  | int auxiliar_open(lua_State *L); | ||||||
|  | void auxiliar_newclass(lua_State *L, const char *classname, luaL_reg *func); | ||||||
|  | void auxiliar_add2group(lua_State *L, const char *classname, const char *group); | ||||||
|  | void auxiliar_setclass(lua_State *L, const char *classname, int objidx); | ||||||
|  | void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx); | ||||||
|  | void *auxiliar_checkgroup(lua_State *L, const char *groupname, int objidx); | ||||||
|  | void *auxiliar_getclassudata(lua_State *L, const char *groupname, int objidx); | ||||||
|  | void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx); | ||||||
|  | int auxiliar_checkboolean(lua_State *L, int objidx); | ||||||
|  | int auxiliar_tostring(lua_State *L); | ||||||
|  |  | ||||||
|  | #endif /* AUXILIAR_H */ | ||||||
							
								
								
									
										263
									
								
								src/luasocket/buffer.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										263
									
								
								src/luasocket/buffer.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,263 @@ | |||||||
|  | /*=========================================================================*\ | ||||||
|  | * Input/Output interface for Lua programs | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: buffer.c,v 1.27 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  |  | ||||||
|  | #include "buffer.h" | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internal function prototypes | ||||||
|  | \*=========================================================================*/ | ||||||
|  | static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b); | ||||||
|  | static int recvline(p_buffer buf, luaL_Buffer *b); | ||||||
|  | static int recvall(p_buffer buf, luaL_Buffer *b); | ||||||
|  | static int buffer_get(p_buffer buf, const char **data, size_t *count); | ||||||
|  | static void buffer_skip(p_buffer buf, size_t count); | ||||||
|  | static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent); | ||||||
|  |  | ||||||
|  | /* min and max macros */ | ||||||
|  | #ifndef MIN | ||||||
|  | #define MIN(x, y) ((x) < (y) ? x : y) | ||||||
|  | #endif | ||||||
|  | #ifndef MAX | ||||||
|  | #define MAX(x, y) ((x) > (y) ? x : y) | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Exported functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Initializes module | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_open(lua_State *L) { | ||||||
|  |     (void) L; | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Initializes C structure | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void buffer_init(p_buffer buf, p_io io, p_timeout tm) { | ||||||
|  | 	buf->first = buf->last = 0; | ||||||
|  |     buf->io = io; | ||||||
|  |     buf->tm = tm; | ||||||
|  |     buf->received = buf->sent = 0; | ||||||
|  |     buf->birthday = timeout_gettime(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * object:getstats() interface | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_meth_getstats(lua_State *L, p_buffer buf) { | ||||||
|  |     lua_pushnumber(L, buf->received); | ||||||
|  |     lua_pushnumber(L, buf->sent); | ||||||
|  |     lua_pushnumber(L, timeout_gettime() - buf->birthday); | ||||||
|  |     return 3; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * object:setstats() interface | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_meth_setstats(lua_State *L, p_buffer buf) { | ||||||
|  |     buf->received = (long) luaL_optnumber(L, 2, buf->received); | ||||||
|  |     buf->sent = (long) luaL_optnumber(L, 3, buf->sent); | ||||||
|  |     if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); | ||||||
|  |     lua_pushnumber(L, 1); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * object:send() interface | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_meth_send(lua_State *L, p_buffer buf) { | ||||||
|  |     int top = lua_gettop(L); | ||||||
|  |     /*p_timeout tm = timeout_markstart(buf->tm);*/ | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     size_t size = 0, sent = 0; | ||||||
|  |     const char *data = luaL_checklstring(L, 2, &size); | ||||||
|  |     long start = (long) luaL_optnumber(L, 3, 1); | ||||||
|  |     long end = (long) luaL_optnumber(L, 4, -1); | ||||||
|  |     if (start < 0) start = (long) (size+start+1); | ||||||
|  |     if (end < 0) end = (long) (size+end+1); | ||||||
|  |     if (start < 1) start = (long) 1; | ||||||
|  |     if (end > (long) size) end = (long) size; | ||||||
|  |     if (start <= end) err = sendraw(buf, data+start-1, end-start+1, &sent); | ||||||
|  |     /* check if there was an error */ | ||||||
|  |     if (err != IO_DONE) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, buf->io->error(buf->io->ctx, err)); | ||||||
|  |         lua_pushnumber(L, sent+start-1); | ||||||
|  |     } else { | ||||||
|  |         lua_pushnumber(L, sent+start-1); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |     } | ||||||
|  | #ifdef LUASOCKET_DEBUG | ||||||
|  |     /* push time elapsed during operation as the last return value */ | ||||||
|  |     lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); | ||||||
|  | #endif | ||||||
|  |     return lua_gettop(L) - top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * object:receive() interface | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_meth_receive(lua_State *L, p_buffer buf) { | ||||||
|  |     int err = IO_DONE, top = lua_gettop(L); | ||||||
|  |     /*p_timeout tm = timeout_markstart(buf->tm);*/ | ||||||
|  |     luaL_Buffer b; | ||||||
|  |     size_t size; | ||||||
|  |     const char *part = luaL_optlstring(L, 3, "", &size); | ||||||
|  |     /* initialize buffer with optional extra prefix | ||||||
|  |      * (useful for concatenating previous partial results) */ | ||||||
|  |     luaL_buffinit(L, &b); | ||||||
|  |     luaL_addlstring(&b, part, size); | ||||||
|  |     /* receive new patterns */ | ||||||
|  |     if (!lua_isnumber(L, 2)) { | ||||||
|  |         const char *p= luaL_optstring(L, 2, "*l"); | ||||||
|  |         if (p[0] == '*' && p[1] == 'l') err = recvline(buf, &b); | ||||||
|  |         else if (p[0] == '*' && p[1] == 'a') err = recvall(buf, &b); | ||||||
|  |         else luaL_argcheck(L, 0, 2, "invalid receive pattern"); | ||||||
|  |         /* get a fixed number of bytes (minus what was already partially | ||||||
|  |          * received) */ | ||||||
|  |     } else err = recvraw(buf, (size_t) lua_tonumber(L, 2)-size, &b); | ||||||
|  |     /* check if there was an error */ | ||||||
|  |     if (err != IO_DONE) { | ||||||
|  |         /* we can't push anyting in the stack before pushing the | ||||||
|  |          * contents of the buffer. this is the reason for the complication */ | ||||||
|  |         luaL_pushresult(&b); | ||||||
|  |         lua_pushstring(L, buf->io->error(buf->io->ctx, err)); | ||||||
|  |         lua_pushvalue(L, -2); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_replace(L, -4); | ||||||
|  |     } else { | ||||||
|  |         luaL_pushresult(&b); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |     } | ||||||
|  | #ifdef LUASOCKET_DEBUG | ||||||
|  |     /* push time elapsed during operation as the last return value */ | ||||||
|  |     lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); | ||||||
|  | #endif | ||||||
|  |     return lua_gettop(L) - top; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Determines if there is any data in the read buffer | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int buffer_isempty(p_buffer buf) { | ||||||
|  |     return buf->first >= buf->last; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internal functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Sends a block of data (unbuffered) | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | #define STEPSIZE 8192 | ||||||
|  | static int sendraw(p_buffer buf, const char *data, size_t count, size_t *sent) { | ||||||
|  |     p_io io = buf->io; | ||||||
|  |     p_timeout tm = buf->tm; | ||||||
|  |     size_t total = 0; | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     while (total < count && err == IO_DONE) { | ||||||
|  |         size_t done; | ||||||
|  |         size_t step = (count-total <= STEPSIZE)? count-total: STEPSIZE; | ||||||
|  |         err = io->send(io->ctx, data+total, step, &done, tm); | ||||||
|  |         total += done; | ||||||
|  |     } | ||||||
|  |     *sent = total; | ||||||
|  |     buf->sent += total; | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Reads a fixed number of bytes (buffered) | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int recvraw(p_buffer buf, size_t wanted, luaL_Buffer *b) { | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     size_t total = 0; | ||||||
|  |     while (total < wanted && err == IO_DONE) { | ||||||
|  |         size_t count; const char *data; | ||||||
|  |         err = buffer_get(buf, &data, &count); | ||||||
|  |         count = MIN(count, wanted - total); | ||||||
|  |         luaL_addlstring(b, data, count); | ||||||
|  |         buffer_skip(buf, count); | ||||||
|  |         total += count; | ||||||
|  |     } | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Reads everything until the connection is closed (buffered) | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int recvall(p_buffer buf, luaL_Buffer *b) { | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     while (err == IO_DONE) { | ||||||
|  |         const char *data; size_t count; | ||||||
|  |         err = buffer_get(buf, &data, &count); | ||||||
|  |         luaL_addlstring(b, data, count); | ||||||
|  |         buffer_skip(buf, count); | ||||||
|  |     } | ||||||
|  |     if (err == IO_CLOSED) return IO_DONE; | ||||||
|  |     else return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Reads a line terminated by a CR LF pair or just by a LF. The CR and LF | ||||||
|  | * are not returned by the function and are discarded from the buffer | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int recvline(p_buffer buf, luaL_Buffer *b) { | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     while (err == IO_DONE) { | ||||||
|  |         size_t count, pos; const char *data; | ||||||
|  |         err = buffer_get(buf, &data, &count); | ||||||
|  |         pos = 0; | ||||||
|  |         while (pos < count && data[pos] != '\n') { | ||||||
|  |             /* we ignore all \r's */ | ||||||
|  |             if (data[pos] != '\r') luaL_putchar(b, data[pos]); | ||||||
|  |             pos++; | ||||||
|  |         } | ||||||
|  |         if (pos < count) { /* found '\n' */ | ||||||
|  |             buffer_skip(buf, pos+1); /* skip '\n' too */ | ||||||
|  |             break; /* we are done */ | ||||||
|  |         } else /* reached the end of the buffer */ | ||||||
|  |             buffer_skip(buf, pos); | ||||||
|  |     } | ||||||
|  |     return err; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Skips a given number of bytes from read buffer. No data is read from the | ||||||
|  | * transport layer | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static void buffer_skip(p_buffer buf, size_t count) { | ||||||
|  |     buf->received += count; | ||||||
|  |     buf->first += count; | ||||||
|  |     if (buffer_isempty(buf)) | ||||||
|  |         buf->first = buf->last = 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Return any data available in buffer, or get more data from transport layer | ||||||
|  | * if buffer is empty | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int buffer_get(p_buffer buf, const char **data, size_t *count) { | ||||||
|  |     int err = IO_DONE; | ||||||
|  |     p_io io = buf->io; | ||||||
|  |     p_timeout tm = buf->tm; | ||||||
|  |     if (buffer_isempty(buf)) { | ||||||
|  |         size_t got; | ||||||
|  |         err = io->recv(io->ctx, buf->data, BUF_SIZE, &got, tm); | ||||||
|  |         buf->first = 0; | ||||||
|  |         buf->last = got; | ||||||
|  |     } | ||||||
|  |     *count = buf->last - buf->first; | ||||||
|  |     *data = buf->data + buf->first; | ||||||
|  |     return err; | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								src/luasocket/buffer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/luasocket/buffer.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | |||||||
|  | #ifndef BUF_H | ||||||
|  | #define BUF_H  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Input/Output interface for Lua programs | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * Line patterns require buffering. Reading one character at a time involves | ||||||
|  | * too many system calls and is very slow. This module implements the | ||||||
|  | * LuaSocket interface for input/output on connected objects, as seen by  | ||||||
|  | * Lua programs.  | ||||||
|  | * | ||||||
|  | * Input is buffered. Output is *not* buffered because there was no simple | ||||||
|  | * way of making sure the buffered output data would ever be sent. | ||||||
|  | * | ||||||
|  | * The module is built on top of the I/O abstraction defined in io.h and the | ||||||
|  | * timeout management is done with the timeout.h interface. | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: buffer.h,v 1.12 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "io.h" | ||||||
|  | #include "timeout.h" | ||||||
|  |  | ||||||
|  | /* buffer size in bytes */ | ||||||
|  | #define BUF_SIZE 8192 | ||||||
|  |  | ||||||
|  | /* buffer control structure */ | ||||||
|  | typedef struct t_buffer_ { | ||||||
|  |     double birthday;        /* throttle support info: creation time, */ | ||||||
|  |     size_t sent, received;  /* bytes sent, and bytes received */ | ||||||
|  |     p_io io;                /* IO driver used for this buffer */ | ||||||
|  |     p_timeout tm;           /* timeout management for this buffer */ | ||||||
|  | 	size_t first, last;     /* index of first and last bytes of stored data */ | ||||||
|  | 	char data[BUF_SIZE];    /* storage space for buffer data */ | ||||||
|  | } t_buffer; | ||||||
|  | typedef t_buffer *p_buffer; | ||||||
|  |  | ||||||
|  | int buffer_open(lua_State *L); | ||||||
|  | void buffer_init(p_buffer buf, p_io io, p_timeout tm); | ||||||
|  | int buffer_meth_send(lua_State *L, p_buffer buf); | ||||||
|  | int buffer_meth_receive(lua_State *L, p_buffer buf); | ||||||
|  | int buffer_meth_getstats(lua_State *L, p_buffer buf); | ||||||
|  | int buffer_meth_setstats(lua_State *L, p_buffer buf); | ||||||
|  | int buffer_isempty(p_buffer buf); | ||||||
|  |  | ||||||
|  | #endif /* BUF_H */ | ||||||
							
								
								
									
										97
									
								
								src/luasocket/compat51.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/luasocket/compat51.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,97 @@ | |||||||
|  | /* | ||||||
|  | ** Compat-5.1 | ||||||
|  | ** Copyright Kepler Project 2004-2005 (http://www.keplerproject.org/compat) | ||||||
|  | ** $Id: compat-5.1.c,v 1.12 2005/07/08 18:25:52 carregal Exp $ | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  | #include "compat51.h" | ||||||
|  |  | ||||||
|  | static void getfield(lua_State *L, int idx, const char *name) { | ||||||
|  |     const char *end = strchr(name, '.'); | ||||||
|  |     lua_pushvalue(L, idx); | ||||||
|  |     while (end) { | ||||||
|  |         lua_pushlstring(L, name, end - name); | ||||||
|  |         lua_gettable(L, -2); | ||||||
|  |         lua_remove(L, -2); | ||||||
|  |         if (lua_isnil(L, -1)) return; | ||||||
|  |         name = end+1; | ||||||
|  |         end = strchr(name, '.'); | ||||||
|  |     } | ||||||
|  |     lua_pushstring(L, name); | ||||||
|  |     lua_gettable(L, -2); | ||||||
|  |     lua_remove(L, -2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void setfield(lua_State *L, int idx, const char *name) { | ||||||
|  |     const char *end = strchr(name, '.'); | ||||||
|  |     lua_pushvalue(L, idx); | ||||||
|  |     while (end) { | ||||||
|  |         lua_pushlstring(L, name, end - name); | ||||||
|  |         lua_gettable(L, -2); | ||||||
|  |         /* create table if not found */ | ||||||
|  |         if (lua_isnil(L, -1)) { | ||||||
|  |             lua_pop(L, 1); | ||||||
|  |             lua_newtable(L); | ||||||
|  |             lua_pushlstring(L, name, end - name); | ||||||
|  |             lua_pushvalue(L, -2); | ||||||
|  |             lua_settable(L, -4); | ||||||
|  |         } | ||||||
|  |         lua_remove(L, -2); | ||||||
|  |         name = end+1; | ||||||
|  |         end = strchr(name, '.'); | ||||||
|  |     } | ||||||
|  |     lua_pushstring(L, name); | ||||||
|  |     lua_pushvalue(L, -3); | ||||||
|  |     lua_settable(L, -3); | ||||||
|  |     lua_pop(L, 2); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | LUALIB_API void luaL_module(lua_State *L, const char *libname, | ||||||
|  |                               const luaL_reg *l, int nup) { | ||||||
|  |   if (libname) { | ||||||
|  |     getfield(L, LUA_GLOBALSINDEX, libname);  /* check whether lib already exists */ | ||||||
|  |     if (lua_isnil(L, -1)) {  | ||||||
|  |       int env, ns; | ||||||
|  |       lua_pop(L, 1); /* get rid of nil */ | ||||||
|  |       lua_pushliteral(L, "require"); | ||||||
|  |       lua_gettable(L, LUA_GLOBALSINDEX); /* look for require */ | ||||||
|  |       lua_getfenv(L, -1); /* getfenv(require) */ | ||||||
|  |       lua_remove(L, -2); /* remove function require */ | ||||||
|  |       env = lua_gettop(L); | ||||||
|  |  | ||||||
|  |       lua_newtable(L); /* create namespace for lib */ | ||||||
|  |       ns = lua_gettop(L); | ||||||
|  |       getfield(L, env, "package.loaded"); /* get package.loaded table */ | ||||||
|  |       if (lua_isnil(L, -1)) { /* create package.loaded table */ | ||||||
|  |           lua_pop(L, 1); /* remove previous result */ | ||||||
|  |           lua_newtable(L); | ||||||
|  |           lua_pushvalue(L, -1); | ||||||
|  |           setfield(L, env, "package.loaded"); | ||||||
|  |       } | ||||||
|  |       else if (!lua_istable(L, -1)) | ||||||
|  |         luaL_error(L, "name conflict for library `%s'", libname); | ||||||
|  |       lua_pushstring(L, libname); | ||||||
|  |       lua_pushvalue(L, ns);  | ||||||
|  |       lua_settable(L, -3); /* package.loaded[libname] = ns */ | ||||||
|  |       lua_pop(L, 1); /* get rid of package.loaded table */ | ||||||
|  |       lua_pushvalue(L, ns); /* copy namespace */ | ||||||
|  |       setfield(L, LUA_GLOBALSINDEX, libname); | ||||||
|  |       lua_remove (L, env); /* remove env */ | ||||||
|  |     } | ||||||
|  |     lua_insert(L, -(nup+1));  /* move library table to below upvalues */ | ||||||
|  |   } | ||||||
|  |   for (; l->name; l++) { | ||||||
|  |     int i; | ||||||
|  |     lua_pushstring(L, l->name); | ||||||
|  |     for (i=0; i<nup; i++)  /* copy upvalues to the top */ | ||||||
|  |       lua_pushvalue(L, -(nup+1)); | ||||||
|  |     lua_pushcclosure(L, l->func, nup); | ||||||
|  |     lua_settable(L, -(nup+3)); | ||||||
|  |   } | ||||||
|  |   lua_pop(L, nup);  /* remove upvalues */ | ||||||
|  | } | ||||||
|  |  | ||||||
							
								
								
									
										13
									
								
								src/luasocket/compat51.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/luasocket/compat51.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | |||||||
|  | /* | ||||||
|  | ** Compat-5.1 | ||||||
|  | ** Copyright Kepler Project 2004-2005 (http://www.keplerproject.org/compat/) | ||||||
|  | ** $Id: compat-5.1.h,v 1.7 2005/07/08 18:25:52 carregal Exp $ | ||||||
|  | */ | ||||||
|  |  | ||||||
|  | #ifndef COMPAT_H | ||||||
|  |  | ||||||
|  | LUALIB_API void luaL_module(lua_State *L, const char *libname, | ||||||
|  |                                        const luaL_reg *l, int nup); | ||||||
|  | #define luaL_openlib luaL_module | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										235
									
								
								src/luasocket/compat51.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								src/luasocket/compat51.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | |||||||
|  | -- | ||||||
|  | -- Compat-5.1 | ||||||
|  | -- Copyright Kepler Project 2004-2005 (http://www.keplerproject.org/compat) | ||||||
|  | -- According to Lua 5.1 | ||||||
|  | -- $Id: compat-5.1.lua,v 1.19 2005/07/05 19:12:00 tomas Exp $ | ||||||
|  | -- | ||||||
|  |  | ||||||
|  | _COMPAT51 = "Compat-5.1 R4" | ||||||
|  |  | ||||||
|  | local LUA_DIRSEP = '/' | ||||||
|  | local LUA_OFSEP = '_' | ||||||
|  | local OLD_LUA_OFSEP = '' | ||||||
|  | local POF = 'luaopen_' | ||||||
|  |  | ||||||
|  | local assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type = assert, error, getfenv, ipairs, loadfile, loadlib, pairs, setfenv, setmetatable, type | ||||||
|  | local format, gfind, gsub = string.format, string.gfind, string.gsub | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- avoid overwriting the package table if it's already there | ||||||
|  | -- | ||||||
|  | package = package or {} | ||||||
|  |  | ||||||
|  | package.path = LUA_PATH or os.getenv("LUA_PATH") or | ||||||
|  |              ("./?.lua;" .. | ||||||
|  |               "/usr/local/share/lua/5.0/?.lua;" .. | ||||||
|  |               "/usr/local/share/lua/5.0/?/?.lua;" .. | ||||||
|  |               "/usr/local/share/lua/5.0/?/init.lua" ) | ||||||
|  |   | ||||||
|  | package.cpath = os.getenv("LUA_CPATH") or | ||||||
|  |              "./?.so;" .. | ||||||
|  |              "./l?.so;" .. | ||||||
|  |              "/usr/local/lib/lua/5.0/?.so;" .. | ||||||
|  |              "/usr/local/lib/lua/5.0/l?.so" | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- make sure require works with standard libraries | ||||||
|  | -- | ||||||
|  | package.loaded = package.loaded or {} | ||||||
|  | package.loaded.string = string | ||||||
|  | package.loaded.math = math | ||||||
|  | package.loaded.io = io | ||||||
|  | package.loaded.os = os | ||||||
|  | package.loaded.table = table  | ||||||
|  | package.loaded.base = _G | ||||||
|  | package.loaded.coroutine = coroutine | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- avoid overwriting the package.preload table if it's already there | ||||||
|  | -- | ||||||
|  | package.preload = package.preload or {} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- auxiliar function to read "nested globals" | ||||||
|  | -- | ||||||
|  | local function getfield (t, f) | ||||||
|  |   assert (type(f)=="string", "not a valid field name ("..tostring(f)..")") | ||||||
|  |   for w in gfind(f, "[%w_]+") do | ||||||
|  |     if not t then return nil end | ||||||
|  |     t = rawget(t, w) | ||||||
|  |   end | ||||||
|  |   return t | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- auxiliar function to write "nested globals" | ||||||
|  | -- | ||||||
|  | local function setfield (t, f, v) | ||||||
|  |   for w in gfind(f, "([%w_]+)%.") do | ||||||
|  |     t[w] = t[w] or {} -- create table if absent | ||||||
|  |     t = t[w]            -- get the table | ||||||
|  |   end | ||||||
|  |   local w = gsub(f, "[%w_]+%.", "")   -- get last field name | ||||||
|  |   t[w] = v            -- do the assignment | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- looks for a file `name' in given path | ||||||
|  | -- | ||||||
|  | local function search (path, name) | ||||||
|  |   for c in gfind(path, "[^;]+") do | ||||||
|  |     c = gsub(c, "%?", name) | ||||||
|  |     local f = io.open(c) | ||||||
|  |     if f then   -- file exist? | ||||||
|  |       f:close() | ||||||
|  |       return c | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return nil    -- file not found | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- check whether library is already loaded | ||||||
|  | -- | ||||||
|  | local function loader_preload (name) | ||||||
|  |   assert (type(name) == "string", format ( | ||||||
|  |     "bad argument #1 to `require' (string expected, got %s)", type(name))) | ||||||
|  |   if type(package.preload) ~= "table" then | ||||||
|  |     error ("`package.preload' must be a table") | ||||||
|  |   end | ||||||
|  |   return package.preload[name] | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- C library loader | ||||||
|  | -- | ||||||
|  | local function loader_C (name) | ||||||
|  |   assert (type(name) == "string", format ( | ||||||
|  |     "bad argument #1 to `require' (string expected, got %s)", type(name))) | ||||||
|  |   local fname = gsub (name, "%.", LUA_DIRSEP) | ||||||
|  |   fname = search (package.cpath, fname) | ||||||
|  |   if not fname then | ||||||
|  |     return false | ||||||
|  |   end | ||||||
|  |   local funcname = POF .. gsub (name, "%.", LUA_OFSEP) | ||||||
|  |   local f, err = loadlib (fname, funcname) | ||||||
|  |   if not f then | ||||||
|  |     funcname = POF .. gsub (name, "%.", OLD_LUA_OFSEP) | ||||||
|  |     f, err = loadlib (fname, funcname) | ||||||
|  |     if not f then | ||||||
|  |       error (format ("error loading package `%s' (%s)", name, err)) | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   return f | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- Lua library loader | ||||||
|  | -- | ||||||
|  | local function loader_Lua (name) | ||||||
|  |   assert (type(name) == "string", format ( | ||||||
|  |     "bad argument #1 to `require' (string expected, got %s)", type(name))) | ||||||
|  |   local path = LUA_PATH | ||||||
|  |   if not path then | ||||||
|  |     path = assert (package.path, "`package.path' must be a string") | ||||||
|  |   end | ||||||
|  |   local fname = gsub (name, "%.", LUA_DIRSEP) | ||||||
|  |   fname = search (path, fname) | ||||||
|  |   if not fname then | ||||||
|  |     return false | ||||||
|  |   end | ||||||
|  |   local f, err = loadfile (fname) | ||||||
|  |   if not f then | ||||||
|  |     error (format ("error loading package `%s' (%s)", name, err)) | ||||||
|  |   end | ||||||
|  |   return f | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- create `loaders' table | ||||||
|  | package.loaders = package.loaders or { loader_preload, loader_C, loader_Lua, } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- iterate over available loaders | ||||||
|  | -- | ||||||
|  | local function load (name, loaders) | ||||||
|  |   -- iterate over available loaders | ||||||
|  |   assert (type (loaders) == "table", "`package.loaders' must be a table") | ||||||
|  |   for i, loader in ipairs (loaders) do | ||||||
|  |     local f = loader (name) | ||||||
|  |     if f then | ||||||
|  |       return f | ||||||
|  |     end | ||||||
|  |   end | ||||||
|  |   error (format ("package `%s' not found", name)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- new require | ||||||
|  | -- | ||||||
|  | function _G.require (name) | ||||||
|  |   assert (type(name) == "string", format ( | ||||||
|  |     "bad argument #1 to `require' (string expected, got %s)", type(name))) | ||||||
|  |   local p = loaded[name] -- is it there? | ||||||
|  |   if p then | ||||||
|  |     return p | ||||||
|  |   end | ||||||
|  |   -- first mark it as loaded | ||||||
|  |   loaded[name] = true | ||||||
|  |   -- load and run init function | ||||||
|  |   local actual_arg = _G.arg | ||||||
|  |   _G.arg = { name } | ||||||
|  |   local res = load(name, loaders)(name) | ||||||
|  |   if res then  | ||||||
|  |     loaded[name] = res -- store result | ||||||
|  |   end | ||||||
|  |   _G.arg = actual_arg | ||||||
|  |   -- return value should be in loaded[name] | ||||||
|  |   return loaded[name] | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- new module function | ||||||
|  | -- | ||||||
|  | function _G.module (name) | ||||||
|  |   local _G = getfenv(0)       -- simulate C function environment | ||||||
|  |   local ns = getfield(_G, name)         -- search for namespace | ||||||
|  |   if not ns then | ||||||
|  |     ns = {}                             -- create new namespace | ||||||
|  |     setfield(_G, name, ns) | ||||||
|  |   elseif type(ns) ~= "table" then | ||||||
|  |     error("name conflict for module `"..name.."'") | ||||||
|  |   end | ||||||
|  |   if not ns._NAME then | ||||||
|  |     ns._NAME = name | ||||||
|  |     ns._M = ns | ||||||
|  |     ns._PACKAGE = gsub(name, "[^.]*$", "") | ||||||
|  |   end | ||||||
|  |   setmetatable(ns, {__index = _G}) | ||||||
|  |   loaded[name] = ns | ||||||
|  |   setfenv(2, ns) | ||||||
|  |   return ns | ||||||
|  | end | ||||||
|  |  | ||||||
|  |  | ||||||
|  | -- | ||||||
|  | -- define functions' environments | ||||||
|  | -- | ||||||
|  | local env = { | ||||||
|  | 	loaded = package.loaded, | ||||||
|  | 	loaders = package.loaders, | ||||||
|  | 	package = package, | ||||||
|  | 	_G = _G, | ||||||
|  | } | ||||||
|  | for i, f in ipairs { _G.module, _G.require, load, loader_preload, loader_C, loader_Lua, } do | ||||||
|  |   setfenv (f, env) | ||||||
|  | end | ||||||
							
								
								
									
										99
									
								
								src/luasocket/except.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								src/luasocket/except.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,99 @@ | |||||||
|  | /*=========================================================================*\ | ||||||
|  | * Simple exception support | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: except.c,v 1.8 2005/09/29 06:11:41 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include <stdio.h> | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  |  | ||||||
|  | #include "except.h" | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internal function prototypes. | ||||||
|  | \*=========================================================================*/ | ||||||
|  | static int global_protect(lua_State *L); | ||||||
|  | static int global_newtry(lua_State *L); | ||||||
|  | static int protected_(lua_State *L); | ||||||
|  | static int finalize(lua_State *L); | ||||||
|  | static int do_nothing(lua_State *L); | ||||||
|  |  | ||||||
|  | /* except functions */ | ||||||
|  | static luaL_reg func[] = { | ||||||
|  |     {"newtry",    global_newtry}, | ||||||
|  |     {"protect",   global_protect}, | ||||||
|  |     {NULL,        NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Try factory | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static void wrap(lua_State *L) { | ||||||
|  |     lua_newtable(L); | ||||||
|  |     lua_pushnumber(L, 1); | ||||||
|  |     lua_pushvalue(L, -3); | ||||||
|  |     lua_settable(L, -3); | ||||||
|  |     lua_insert(L, -2); | ||||||
|  |     lua_pop(L, 1); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int finalize(lua_State *L) { | ||||||
|  |     if (!lua_toboolean(L, 1)) { | ||||||
|  |         lua_pushvalue(L, lua_upvalueindex(1)); | ||||||
|  |         lua_pcall(L, 0, 0, 0); | ||||||
|  |         lua_settop(L, 2); | ||||||
|  |         wrap(L); | ||||||
|  |         lua_error(L); | ||||||
|  |         return 0; | ||||||
|  |     } else return lua_gettop(L); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int do_nothing(lua_State *L) {  | ||||||
|  |     (void) L; | ||||||
|  |     return 0;  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int global_newtry(lua_State *L) { | ||||||
|  |     lua_settop(L, 1); | ||||||
|  |     if (lua_isnil(L, 1)) lua_pushcfunction(L, do_nothing); | ||||||
|  |     lua_pushcclosure(L, finalize, 1); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Protect factory | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int unwrap(lua_State *L) { | ||||||
|  |     if (lua_istable(L, -1)) { | ||||||
|  |         lua_pushnumber(L, 1); | ||||||
|  |         lua_gettable(L, -2); | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_insert(L, -2); | ||||||
|  |         return 1; | ||||||
|  |     } else return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int protected_(lua_State *L) { | ||||||
|  |     lua_pushvalue(L, lua_upvalueindex(1)); | ||||||
|  |     lua_insert(L, 1); | ||||||
|  |     if (lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0) != 0) { | ||||||
|  |         if (unwrap(L)) return 2; | ||||||
|  |         else lua_error(L); | ||||||
|  |         return 0; | ||||||
|  |     } else return lua_gettop(L); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int global_protect(lua_State *L) { | ||||||
|  |     lua_pushcclosure(L, protected_, 1); | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Init module | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int except_open(lua_State *L) { | ||||||
|  |     luaL_openlib(L, NULL, func, 0); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										35
									
								
								src/luasocket/except.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/luasocket/except.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | #ifndef EXCEPT_H | ||||||
|  | #define EXCEPT_H | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Exception control | ||||||
|  | * LuaSocket toolkit (but completely independent from other modules) | ||||||
|  | * | ||||||
|  | * This provides support for simple exceptions in Lua. During the | ||||||
|  | * development of the HTTP/FTP/SMTP support, it became aparent that | ||||||
|  | * error checking was taking a substantial amount of the coding. These | ||||||
|  | * function greatly simplify the task of checking errors. | ||||||
|  | * | ||||||
|  | * The main idea is that functions should return nil as its first return | ||||||
|  | * value when it finds an error, and return an error message (or value) | ||||||
|  | * following nil. In case of success, as long as the first value is not nil, | ||||||
|  | * the other values don't matter. | ||||||
|  | * | ||||||
|  | * The idea is to nest function calls with the "try" function. This function | ||||||
|  | * checks the first value, and calls "error" on the second if the first is | ||||||
|  | * nil. Otherwise, it returns all values it received.  | ||||||
|  | * | ||||||
|  | * The protect function returns a new function that behaves exactly like the | ||||||
|  | * function it receives, but the new function doesn't throw exceptions: it | ||||||
|  | * returns nil followed by the error message instead. | ||||||
|  | * | ||||||
|  | * With these two function, it's easy to write functions that throw | ||||||
|  | * exceptions on error, but that don't interrupt the user script.  | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: except.h,v 1.2 2005/09/29 06:11:41 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | int except_open(lua_State *L); | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										280
									
								
								src/luasocket/ftp.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										280
									
								
								src/luasocket/ftp.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,280 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- FTP support for the Lua language | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: ftp.lua,v 1.42 2005/11/22 08:33:29 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local base = _G | ||||||
|  | local table = require("table") | ||||||
|  | local string = require("string") | ||||||
|  | local math = require("math") | ||||||
|  | local socket = require("socket") | ||||||
|  | local url = require("socket.url") | ||||||
|  | local tp = require("socket.tp") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | module("socket.ftp") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- timeout in seconds before the program gives up on a connection | ||||||
|  | TIMEOUT = 60 | ||||||
|  | -- default port for ftp service | ||||||
|  | PORT = 21 | ||||||
|  | -- this is the default anonymous password. used when no password is | ||||||
|  | -- provided in url. should be changed to your e-mail. | ||||||
|  | USER = "ftp" | ||||||
|  | PASSWORD = "anonymous@anonymous.org" | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Low level FTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function open(server, port, create) | ||||||
|  |     local tp = socket.try(tp.connect(server, port or PORT, create, TIMEOUT)) | ||||||
|  |     local f = base.setmetatable({ tp = tp }, metat) | ||||||
|  |     -- make sure everything gets closed in an exception | ||||||
|  |     f.try = socket.newtry(function() f:close() end) | ||||||
|  |     return f | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:portconnect() | ||||||
|  |     self.try(self.server:settimeout(TIMEOUT)) | ||||||
|  |     self.data = self.try(self.server:accept()) | ||||||
|  |     self.try(self.data:settimeout(TIMEOUT)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:pasvconnect() | ||||||
|  |     self.data = self.try(socket.tcp()) | ||||||
|  |     self.try(self.data:settimeout(TIMEOUT)) | ||||||
|  |     self.try(self.data:connect(self.pasvt.ip, self.pasvt.port)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:login(user, password) | ||||||
|  |     self.try(self.tp:command("user", user or USER)) | ||||||
|  |     local code, reply = self.try(self.tp:check{"2..", 331}) | ||||||
|  |     if code == 331 then | ||||||
|  |         self.try(self.tp:command("pass", password or PASSWORD)) | ||||||
|  |         self.try(self.tp:check("2..")) | ||||||
|  |     end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:pasv() | ||||||
|  |     self.try(self.tp:command("pasv")) | ||||||
|  |     local code, reply = self.try(self.tp:check("2..")) | ||||||
|  |     local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" | ||||||
|  |     local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) | ||||||
|  |     self.try(a and b and c and d and p1 and p2, reply) | ||||||
|  |     self.pasvt = { | ||||||
|  |         ip = string.format("%d.%d.%d.%d", a, b, c, d), | ||||||
|  |         port = p1*256 + p2 | ||||||
|  |     } | ||||||
|  |     if self.server then | ||||||
|  |         self.server:close() | ||||||
|  |         self.server = nil | ||||||
|  |     end | ||||||
|  |     return self.pasvt.ip, self.pasvt.port | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:port(ip, port) | ||||||
|  |     self.pasvt = nil | ||||||
|  |     if not ip then | ||||||
|  |         ip, port = self.try(self.tp:getcontrol():getsockname()) | ||||||
|  |         self.server = self.try(socket.bind(ip, 0)) | ||||||
|  |         ip, port = self.try(self.server:getsockname()) | ||||||
|  |         self.try(server:settimeout(TIMEOUT)) | ||||||
|  |     end | ||||||
|  |     local pl = math.mod(port, 256) | ||||||
|  |     local ph = (port - pl)/256 | ||||||
|  |     local arg = string.gsub(string.format("%s,%d,%d", ip, ph, pl), "%.", ",") | ||||||
|  |     self.try(self.tp:command("port", arg)) | ||||||
|  |     self.try(self.tp:check("2..")) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:send(sendt) | ||||||
|  |     self.try(self.pasvt or self.server, "need port or pasv first") | ||||||
|  |     -- if there is a pasvt table, we already sent a PASV command | ||||||
|  |     -- we just get the data connection into self.data | ||||||
|  |     if self.pasvt then self:pasvconnect() end | ||||||
|  |     -- get the transfer argument and command | ||||||
|  |     local argument = sendt.argument or | ||||||
|  |         url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) | ||||||
|  |     if argument == "" then argument = nil end | ||||||
|  |     local command = sendt.command or "stor" | ||||||
|  |     -- send the transfer command and check the reply | ||||||
|  |     self.try(self.tp:command(command, argument)) | ||||||
|  |     local code, reply = self.try(self.tp:check{"2..", "1.."}) | ||||||
|  |     -- if there is not a a pasvt table, then there is a server | ||||||
|  |     -- and we already sent a PORT command | ||||||
|  |     if not self.pasvt then self:portconnect() end | ||||||
|  |     -- get the sink, source and step for the transfer | ||||||
|  |     local step = sendt.step or ltn12.pump.step | ||||||
|  |     local checkstep = function(src, snk) | ||||||
|  |         -- check status in control connection while downloading | ||||||
|  |         local readyt = socket.select(readt, nil, 0) | ||||||
|  |         if readyt[tp] then self.try(self.tp:check("2..")) end | ||||||
|  |         return step(src, snk) | ||||||
|  |     end | ||||||
|  |     local sink = socket.sink("close-when-done", self.data) | ||||||
|  |     -- transfer all data and check error | ||||||
|  |     self.try(ltn12.pump.all(sendt.source, sink, checkstep)) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     -- done with data connection | ||||||
|  |     self.data:close() | ||||||
|  |     -- find out how many bytes were sent | ||||||
|  |     local sent = socket.skip(1, self.data:getstats()) | ||||||
|  |     self.data = nil | ||||||
|  |     return sent | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receive(recvt) | ||||||
|  |     self.try(self.pasvt or self.server, "need port or pasv first") | ||||||
|  |     if self.pasvt then self:pasvconnect() end | ||||||
|  |     local argument = recvt.argument or | ||||||
|  |         url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) | ||||||
|  |     if argument == "" then argument = nil end | ||||||
|  |     local command = recvt.command or "retr" | ||||||
|  |     self.try(self.tp:command(command, argument)) | ||||||
|  |     local code = self.try(self.tp:check{"1..", "2.."}) | ||||||
|  |     if not self.pasvt then self:portconnect() end | ||||||
|  |     local source = socket.source("until-closed", self.data) | ||||||
|  |     local step = recvt.step or ltn12.pump.step | ||||||
|  |     self.try(ltn12.pump.all(source, recvt.sink, step)) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     self.data:close() | ||||||
|  |     self.data = nil | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:cwd(dir) | ||||||
|  |     self.try(self.tp:command("cwd", dir)) | ||||||
|  |     self.try(self.tp:check(250)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:type(type) | ||||||
|  |     self.try(self.tp:command("type", type)) | ||||||
|  |     self.try(self.tp:check(200)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:greet() | ||||||
|  |     local code = self.try(self.tp:check{"1..", "2.."}) | ||||||
|  |     if string.find(code, "1..") then self.try(self.tp:check("2..")) end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:quit() | ||||||
|  |     self.try(self.tp:command("quit")) | ||||||
|  |     self.try(self.tp:check("2..")) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:close() | ||||||
|  |     if self.data then self.data:close() end | ||||||
|  |     if self.server then self.server:close() end | ||||||
|  |     return self.tp:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- High level FTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | function override(t) | ||||||
|  |     if t.url then | ||||||
|  |         u = url.parse(t.url) | ||||||
|  |         for i,v in base.pairs(t) do | ||||||
|  |             u[i] = v | ||||||
|  |         end | ||||||
|  |         return u | ||||||
|  |     else return t end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function tput(putt) | ||||||
|  |     putt = override(putt) | ||||||
|  |     socket.try(putt.host, "missing hostname") | ||||||
|  |     local f = open(putt.host, putt.port, putt.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(putt.user, putt.password) | ||||||
|  |     if putt.type then f:type(putt.type) end | ||||||
|  |     f:pasv() | ||||||
|  |     local sent = f:send(putt) | ||||||
|  |     f:quit() | ||||||
|  |     f:close() | ||||||
|  |     return sent | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local default = { | ||||||
|  | 	path = "/", | ||||||
|  | 	scheme = "ftp" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | local function parse(u) | ||||||
|  |     local t = socket.try(url.parse(u, default)) | ||||||
|  |     socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") | ||||||
|  |     socket.try(t.host, "missing hostname") | ||||||
|  |     local pat = "^type=(.)$" | ||||||
|  |     if t.params then | ||||||
|  |         t.type = socket.skip(2, string.find(t.params, pat)) | ||||||
|  |         socket.try(t.type == "a" or t.type == "i", | ||||||
|  |             "invalid type '" .. t.type .. "'") | ||||||
|  |     end | ||||||
|  |     return t | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function sput(u, body) | ||||||
|  |     local putt = parse(u) | ||||||
|  |     putt.source = ltn12.source.string(body) | ||||||
|  |     return tput(putt) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | put = socket.protect(function(putt, body) | ||||||
|  |     if base.type(putt) == "string" then return sput(putt, body) | ||||||
|  |     else return tput(putt) end | ||||||
|  | end) | ||||||
|  |  | ||||||
|  | local function tget(gett) | ||||||
|  |     gett = override(gett) | ||||||
|  |     socket.try(gett.host, "missing hostname") | ||||||
|  |     local f = open(gett.host, gett.port, gett.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(gett.user, gett.password) | ||||||
|  |     if gett.type then f:type(gett.type) end | ||||||
|  |     f:pasv() | ||||||
|  |     f:receive(gett) | ||||||
|  |     f:quit() | ||||||
|  |     return f:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function sget(u) | ||||||
|  |     local gett = parse(u) | ||||||
|  |     local t = {} | ||||||
|  |     gett.sink = ltn12.sink.table(t) | ||||||
|  |     tget(gett) | ||||||
|  |     return table.concat(t) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | command = socket.protect(function(cmdt) | ||||||
|  |     cmdt = override(cmdt) | ||||||
|  |     socket.try(cmdt.host, "missing hostname") | ||||||
|  |     socket.try(cmdt.command, "missing command") | ||||||
|  |     local f = open(cmdt.host, cmdt.port, cmdt.create) | ||||||
|  |     f:greet() | ||||||
|  |     f:login(cmdt.user, cmdt.password) | ||||||
|  |     f.try(f.tp:command(cmdt.command, cmdt.argument)) | ||||||
|  |     if cmdt.check then f.try(f.tp:check(cmdt.check)) end | ||||||
|  |     f:quit() | ||||||
|  |     return f:close() | ||||||
|  | end) | ||||||
|  |  | ||||||
|  | get = socket.protect(function(gett) | ||||||
|  |     if base.type(gett) == "string" then return sget(gett) | ||||||
|  |     else return tget(gett) end | ||||||
|  | end) | ||||||
|  |  | ||||||
							
								
								
									
										326
									
								
								src/luasocket/http.lua
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/luasocket/http.lua
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,326 @@ | |||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- HTTP/1.1 client support for the Lua language. | ||||||
|  | -- LuaSocket toolkit. | ||||||
|  | -- Author: Diego Nehab | ||||||
|  | -- RCS ID: $Id: http.lua,v 1.63 2005/11/22 08:33:29 diego Exp $ | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Declare module and import dependencies | ||||||
|  | ------------------------------------------------------------------------------- | ||||||
|  | local socket = require("socket") | ||||||
|  | local url = require("socket.url") | ||||||
|  | local ltn12 = require("ltn12") | ||||||
|  | local mime = require("mime") | ||||||
|  | local string = require("string") | ||||||
|  | local base = _G | ||||||
|  | local table = require("table") | ||||||
|  | module("socket.http") | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Program constants | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- connection timeout in seconds | ||||||
|  | TIMEOUT = 60 | ||||||
|  | -- default port for document retrieval | ||||||
|  | PORT = 80 | ||||||
|  | -- user agent field sent in request | ||||||
|  | USERAGENT = socket._VERSION | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Reads MIME headers from a connection, unfolding where needed | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function receiveheaders(sock, headers) | ||||||
|  |     local line, name, value, err | ||||||
|  |     headers = headers or {} | ||||||
|  |     -- get first line | ||||||
|  |     line, err = sock:receive() | ||||||
|  |     if err then return nil, err end | ||||||
|  |     -- headers go until a blank line is found | ||||||
|  |     while line ~= "" do | ||||||
|  |         -- get field-name and value | ||||||
|  |         name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) | ||||||
|  |         if not (name and value) then return nil, "malformed reponse headers" end | ||||||
|  |         name = string.lower(name) | ||||||
|  |         -- get next line (value might be folded) | ||||||
|  |         line, err  = sock:receive() | ||||||
|  |         if err then return nil, err end | ||||||
|  |         -- unfold any folded values | ||||||
|  |         while string.find(line, "^%s") do | ||||||
|  |             value = value .. line | ||||||
|  |             line = sock:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  |         end | ||||||
|  |         -- save pair in table | ||||||
|  |         if headers[name] then headers[name] = headers[name] .. ", " .. value | ||||||
|  |         else headers[name] = value end | ||||||
|  |     end | ||||||
|  |     return headers | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Extra sources and sinks | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | socket.sourcet["http-chunked"] = function(sock, headers) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function() | ||||||
|  |             -- get chunk size, skip extention | ||||||
|  |             local line, err = sock:receive() | ||||||
|  |             if err then return nil, err end | ||||||
|  |             local size = base.tonumber(string.gsub(line, ";.*", ""), 16) | ||||||
|  |             if not size then return nil, "invalid chunk size" end | ||||||
|  |             -- was it the last chunk? | ||||||
|  |             if size > 0 then | ||||||
|  |                 -- if not, get chunk and skip terminating CRLF | ||||||
|  |                 local chunk, err, part = sock:receive(size) | ||||||
|  |                 if chunk then sock:receive() end | ||||||
|  |                 return chunk, err | ||||||
|  |             else | ||||||
|  |                 -- if it was, read trailers into headers table | ||||||
|  |                 headers, err = receiveheaders(sock, headers) | ||||||
|  |                 if not headers then return nil, err end | ||||||
|  |             end | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | socket.sinkt["http-chunked"] = function(sock) | ||||||
|  |     return base.setmetatable({ | ||||||
|  |         getfd = function() return sock:getfd() end, | ||||||
|  |         dirty = function() return sock:dirty() end | ||||||
|  |     }, { | ||||||
|  |         __call = function(self, chunk, err) | ||||||
|  |             if not chunk then return sock:send("0\r\n\r\n") end | ||||||
|  |             local size = string.format("%X\r\n", string.len(chunk)) | ||||||
|  |             return sock:send(size ..  chunk .. "\r\n") | ||||||
|  |         end | ||||||
|  |     }) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- Low level HTTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local metat = { __index = {} } | ||||||
|  |  | ||||||
|  | function open(host, port, create) | ||||||
|  |     -- create socket with user connect function, or with default | ||||||
|  |     local c = socket.try(create or socket.tcp)() | ||||||
|  |     local h = base.setmetatable({ c = c }, metat) | ||||||
|  |     -- create finalized try | ||||||
|  |     h.try = socket.newtry(function() h:close() end) | ||||||
|  |     -- set timeout before connecting | ||||||
|  |     h.try(c:settimeout(TIMEOUT)) | ||||||
|  |     h.try(c:connect(host, port or PORT)) | ||||||
|  |     -- here everything worked | ||||||
|  |     return h | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendrequestline(method, uri) | ||||||
|  |     local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) | ||||||
|  |     return self.try(self.c:send(reqline)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendheaders(headers) | ||||||
|  |     local h = "\r\n" | ||||||
|  |     for i, v in base.pairs(headers) do | ||||||
|  |         h = i .. ": " .. v .. "\r\n" .. h | ||||||
|  |     end | ||||||
|  |     self.try(self.c:send(h)) | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:sendbody(headers, source, step) | ||||||
|  |     source = source or ltn12.source.empty() | ||||||
|  |     step = step or ltn12.pump.step | ||||||
|  |     -- if we don't know the size in advance, send chunked and hope for the best | ||||||
|  |     local mode = "http-chunked" | ||||||
|  |     if headers["content-length"] then mode = "keep-open" end | ||||||
|  |     return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receivestatusline() | ||||||
|  |     local status = self.try(self.c:receive()) | ||||||
|  |     local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) | ||||||
|  |     return self.try(base.tonumber(code), status) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receiveheaders() | ||||||
|  |     return self.try(receiveheaders(self.c)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:receivebody(headers, sink, step) | ||||||
|  |     sink = sink or ltn12.sink.null() | ||||||
|  |     step = step or ltn12.pump.step | ||||||
|  |     local length = base.tonumber(headers["content-length"]) | ||||||
|  |     local t = headers["transfer-encoding"] -- shortcut | ||||||
|  |     local mode = "default" -- connection close | ||||||
|  |     if t and t ~= "identity" then mode = "http-chunked" | ||||||
|  |     elseif base.tonumber(headers["content-length"]) then mode = "by-length" end | ||||||
|  |     return self.try(ltn12.pump.all(socket.source(mode, self.c, length), | ||||||
|  |         sink, step)) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function metat.__index:close() | ||||||
|  |     return self.c:close() | ||||||
|  | end | ||||||
|  |  | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | -- High level HTTP API | ||||||
|  | ----------------------------------------------------------------------------- | ||||||
|  | local function adjusturi(reqt) | ||||||
|  |     local u = reqt | ||||||
|  |     -- if there is a proxy, we need the full url. otherwise, just a part. | ||||||
|  |     if not reqt.proxy and not PROXY then | ||||||
|  |         u = { | ||||||
|  |            path = socket.try(reqt.path, "invalid path 'nil'"), | ||||||
|  |            params = reqt.params, | ||||||
|  |            query = reqt.query, | ||||||
|  |            fragment = reqt.fragment | ||||||
|  |         } | ||||||
|  |     end | ||||||
|  |     return url.build(u) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function adjustproxy(reqt) | ||||||
|  |     local proxy = reqt.proxy or PROXY | ||||||
|  |     if proxy then | ||||||
|  |         proxy = url.parse(proxy) | ||||||
|  |         return proxy.host, proxy.port or 3128 | ||||||
|  |     else | ||||||
|  |         return reqt.host, reqt.port | ||||||
|  |     end | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function adjustheaders(headers, host) | ||||||
|  |     -- default headers | ||||||
|  |     local lower = { | ||||||
|  |         ["user-agent"] = USERAGENT, | ||||||
|  |         ["host"] = host, | ||||||
|  |         ["connection"] = "close, TE", | ||||||
|  |         ["te"] = "trailers" | ||||||
|  |     } | ||||||
|  |     -- override with user headers | ||||||
|  |     for i,v in base.pairs(headers or lower) do | ||||||
|  |         lower[string.lower(i)] = v | ||||||
|  |     end | ||||||
|  |     return lower | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- default url parts | ||||||
|  | local default = { | ||||||
|  |     host = "", | ||||||
|  |     port = PORT, | ||||||
|  |     path ="/", | ||||||
|  |     scheme = "http" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | local function adjustrequest(reqt) | ||||||
|  |     -- parse url if provided | ||||||
|  |     local nreqt = reqt.url and url.parse(reqt.url, default) or {} | ||||||
|  |     local t = url.parse(reqt.url, default) | ||||||
|  |     -- explicit components override url | ||||||
|  |     for i,v in base.pairs(reqt) do nreqt[i] = v end | ||||||
|  |     socket.try(nreqt.host, "invalid host '" .. base.tostring(nreqt.host) .. "'") | ||||||
|  |     -- compute uri if user hasn't overriden | ||||||
|  |     nreqt.uri = reqt.uri or adjusturi(nreqt) | ||||||
|  |     -- ajust host and port if there is a proxy | ||||||
|  |     nreqt.host, nreqt.port = adjustproxy(nreqt) | ||||||
|  |     -- adjust headers in request | ||||||
|  |     nreqt.headers = adjustheaders(nreqt.headers, nreqt.host) | ||||||
|  |     return nreqt | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function shouldredirect(reqt, code, headers) | ||||||
|  |     return headers.location and | ||||||
|  |            string.gsub(headers.location, "%s", "") ~= "" and | ||||||
|  |            (reqt.redirect ~= false) and | ||||||
|  |            (code == 301 or code == 302) and | ||||||
|  |            (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") | ||||||
|  |            and (not reqt.nredirects or reqt.nredirects < 5) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function shouldauthorize(reqt, code) | ||||||
|  |     -- if there has been an authorization attempt, it must have failed | ||||||
|  |     if reqt.headers and reqt.headers["authorization"] then return nil end | ||||||
|  |     -- if last attempt didn't fail due to lack of authentication, | ||||||
|  |     -- or we don't have authorization information, we can't retry | ||||||
|  |     return code == 401 and reqt.user and reqt.password | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function shouldreceivebody(reqt, code) | ||||||
|  |     if reqt.method == "HEAD" then return nil end | ||||||
|  |     if code == 204 or code == 304 then return nil end | ||||||
|  |     if code >= 100 and code < 200 then return nil end | ||||||
|  |     return 1 | ||||||
|  | end | ||||||
|  |  | ||||||
|  | -- forward declarations | ||||||
|  | local trequest, tauthorize, tredirect | ||||||
|  |  | ||||||
|  | function tauthorize(reqt) | ||||||
|  |     local auth = "Basic " ..  (mime.b64(reqt.user .. ":" .. reqt.password)) | ||||||
|  |     reqt.headers["authorization"] = auth | ||||||
|  |     return trequest(reqt) | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function tredirect(reqt, location) | ||||||
|  |     local result, code, headers, status = trequest { | ||||||
|  |         -- the RFC says the redirect URL has to be absolute, but some | ||||||
|  |         -- servers do not respect that | ||||||
|  |         url = url.absolute(reqt, location), | ||||||
|  |         source = reqt.source, | ||||||
|  |         sink = reqt.sink, | ||||||
|  |         headers = reqt.headers, | ||||||
|  |         proxy = reqt.proxy, | ||||||
|  |         nredirects = (reqt.nredirects or 0) + 1, | ||||||
|  |         connect = reqt.connect | ||||||
|  |     } | ||||||
|  |     -- pass location header back as a hint we redirected | ||||||
|  |     headers.location = headers.location or location | ||||||
|  |     return result, code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | function trequest(reqt) | ||||||
|  |     reqt = adjustrequest(reqt) | ||||||
|  |     local h = open(reqt.host, reqt.port, reqt.create) | ||||||
|  |     h:sendrequestline(reqt.method, reqt.uri) | ||||||
|  |     h:sendheaders(reqt.headers) | ||||||
|  |     if reqt.source then h:sendbody(reqt.headers, reqt.source, reqt.step) end | ||||||
|  |     local code, headers, status | ||||||
|  |     code, status = h:receivestatusline() | ||||||
|  |     headers = h:receiveheaders() | ||||||
|  |     if shouldredirect(reqt, code, headers) then | ||||||
|  |         h:close() | ||||||
|  |         return tredirect(reqt, headers.location) | ||||||
|  |     elseif shouldauthorize(reqt, code) then | ||||||
|  |         h:close() | ||||||
|  |         return tauthorize(reqt) | ||||||
|  |     elseif shouldreceivebody(reqt, code) then | ||||||
|  |         h:receivebody(headers, reqt.sink, reqt.step) | ||||||
|  |     end | ||||||
|  |     h:close() | ||||||
|  |     return 1, code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | local function srequest(u, body) | ||||||
|  |     local t = {} | ||||||
|  |     local reqt = { | ||||||
|  |         url = u, | ||||||
|  |         sink = ltn12.sink.table(t) | ||||||
|  |     } | ||||||
|  |     if body then | ||||||
|  |         reqt.source = ltn12.source.string(body) | ||||||
|  |         reqt.headers = { ["content-length"] = string.len(body) } | ||||||
|  |         reqt.method = "POST" | ||||||
|  |     end | ||||||
|  |     local code, headers, status = socket.skip(1, trequest(reqt)) | ||||||
|  |     return table.concat(t), code, headers, status | ||||||
|  | end | ||||||
|  |  | ||||||
|  | request = socket.protect(function(reqt, body) | ||||||
|  |     if base.type(reqt) == "string" then return srequest(reqt, body) | ||||||
|  |     else return trequest(reqt) end | ||||||
|  | end) | ||||||
							
								
								
									
										281
									
								
								src/luasocket/inet.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								src/luasocket/inet.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,281 @@ | |||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internet domain functions | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: inet.c,v 1.28 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <string.h> | ||||||
|  |  | ||||||
|  | #include "lua.h" | ||||||
|  | #include "lauxlib.h" | ||||||
|  |  | ||||||
|  | #include "inet.h" | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internal function prototypes. | ||||||
|  | \*=========================================================================*/ | ||||||
|  | static int inet_global_toip(lua_State *L); | ||||||
|  | static int inet_global_tohostname(lua_State *L); | ||||||
|  | static void inet_pushresolved(lua_State *L, struct hostent *hp); | ||||||
|  | static int inet_global_gethostname(lua_State *L); | ||||||
|  |  | ||||||
|  | /* DNS functions */ | ||||||
|  | static luaL_reg func[] = { | ||||||
|  |     { "toip", inet_global_toip }, | ||||||
|  |     { "tohostname", inet_global_tohostname }, | ||||||
|  |     { "gethostname", inet_global_gethostname}, | ||||||
|  |     { NULL, NULL} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Exported functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Initializes module | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int inet_open(lua_State *L) | ||||||
|  | { | ||||||
|  |     lua_pushstring(L, "dns"); | ||||||
|  |     lua_newtable(L); | ||||||
|  |     luaL_openlib(L, NULL, func, 0); | ||||||
|  |     lua_settable(L, -3); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Global Lua functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Returns all information provided by the resolver given a host name | ||||||
|  | * or ip address | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int inet_gethost(const char *address, struct hostent **hp) { | ||||||
|  |     struct in_addr addr; | ||||||
|  |     if (inet_aton(address, &addr)) | ||||||
|  |         return socket_gethostbyaddr((char *) &addr, sizeof(addr), hp); | ||||||
|  |     else  | ||||||
|  |         return socket_gethostbyname(address, hp); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Returns all information provided by the resolver given a host name | ||||||
|  | * or ip address | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int inet_global_tohostname(lua_State *L) { | ||||||
|  |     const char *address = luaL_checkstring(L, 1); | ||||||
|  |     struct hostent *hp = NULL;  | ||||||
|  |     int err = inet_gethost(address, &hp); | ||||||
|  |     if (err != IO_DONE) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, socket_hoststrerror(err)); | ||||||
|  |         return 2; | ||||||
|  |     } | ||||||
|  |     lua_pushstring(L, hp->h_name); | ||||||
|  |     inet_pushresolved(L, hp); | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Returns all information provided by the resolver given a host name | ||||||
|  | * or ip address | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int inet_global_toip(lua_State *L) | ||||||
|  | { | ||||||
|  |     const char *address = luaL_checkstring(L, 1); | ||||||
|  |     struct hostent *hp = NULL;  | ||||||
|  |     int err = inet_gethost(address, &hp); | ||||||
|  |     if (err != IO_DONE) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, socket_hoststrerror(err)); | ||||||
|  |         return 2; | ||||||
|  |     } | ||||||
|  |     lua_pushstring(L, inet_ntoa(*((struct in_addr *) hp->h_addr))); | ||||||
|  |     inet_pushresolved(L, hp); | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Gets the host name | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static int inet_global_gethostname(lua_State *L) | ||||||
|  | { | ||||||
|  |     char name[257]; | ||||||
|  |     name[256] = '\0'; | ||||||
|  |     if (gethostname(name, 256) < 0) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, "gethostname failed"); | ||||||
|  |         return 2; | ||||||
|  |     } else { | ||||||
|  |         lua_pushstring(L, name); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Lua methods | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Retrieves socket peer name | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int inet_meth_getpeername(lua_State *L, p_socket ps) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in peer; | ||||||
|  |     socklen_t peer_len = sizeof(peer); | ||||||
|  |     if (getpeername(*ps, (SA *) &peer, &peer_len) < 0) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, "getpeername failed"); | ||||||
|  |     } else { | ||||||
|  |         lua_pushstring(L, inet_ntoa(peer.sin_addr)); | ||||||
|  |         lua_pushnumber(L, ntohs(peer.sin_port)); | ||||||
|  |     } | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Retrieves socket local name | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | int inet_meth_getsockname(lua_State *L, p_socket ps) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in local; | ||||||
|  |     socklen_t local_len = sizeof(local); | ||||||
|  |     if (getsockname(*ps, (SA *) &local, &local_len) < 0) { | ||||||
|  |         lua_pushnil(L); | ||||||
|  |         lua_pushstring(L, "getsockname failed"); | ||||||
|  |     } else { | ||||||
|  |         lua_pushstring(L, inet_ntoa(local.sin_addr)); | ||||||
|  |         lua_pushnumber(L, ntohs(local.sin_port)); | ||||||
|  |     } | ||||||
|  |     return 2; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internal functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Passes all resolver information to Lua as a table | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | static void inet_pushresolved(lua_State *L, struct hostent *hp) | ||||||
|  | { | ||||||
|  |     char **alias; | ||||||
|  |     struct in_addr **addr; | ||||||
|  |     int i, resolved; | ||||||
|  |     lua_newtable(L); resolved = lua_gettop(L); | ||||||
|  |     lua_pushstring(L, "name"); | ||||||
|  |     lua_pushstring(L, hp->h_name); | ||||||
|  |     lua_settable(L, resolved); | ||||||
|  |     lua_pushstring(L, "ip"); | ||||||
|  |     lua_pushstring(L, "alias"); | ||||||
|  |     i = 1; | ||||||
|  |     alias = hp->h_aliases; | ||||||
|  |     lua_newtable(L); | ||||||
|  |     if (alias) { | ||||||
|  |         while (*alias) { | ||||||
|  |             lua_pushnumber(L, i); | ||||||
|  |             lua_pushstring(L, *alias); | ||||||
|  |             lua_settable(L, -3); | ||||||
|  |             i++; alias++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lua_settable(L, resolved); | ||||||
|  |     i = 1; | ||||||
|  |     lua_newtable(L); | ||||||
|  |     addr = (struct in_addr **) hp->h_addr_list; | ||||||
|  |     if (addr) { | ||||||
|  |         while (*addr) { | ||||||
|  |             lua_pushnumber(L, i); | ||||||
|  |             lua_pushstring(L, inet_ntoa(**addr)); | ||||||
|  |             lua_settable(L, -3); | ||||||
|  |             i++; addr++; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     lua_settable(L, resolved); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Tries to create a new inet socket | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | const char *inet_trycreate(p_socket ps, int type) { | ||||||
|  |     return socket_strerror(socket_create(ps, AF_INET, type, 0)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Tries to connect to remote address (address, port) | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | const char *inet_tryconnect(p_socket ps, const char *address,  | ||||||
|  |         unsigned short port, p_timeout tm) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in remote; | ||||||
|  |     int err; | ||||||
|  |     memset(&remote, 0, sizeof(remote)); | ||||||
|  |     remote.sin_family = AF_INET; | ||||||
|  |     remote.sin_port = htons(port); | ||||||
|  | 	if (strcmp(address, "*")) { | ||||||
|  |         if (!inet_aton(address, &remote.sin_addr)) { | ||||||
|  |             struct hostent *hp = NULL; | ||||||
|  |             struct in_addr **addr; | ||||||
|  |             err = socket_gethostbyname(address, &hp); | ||||||
|  |             if (err != IO_DONE) return socket_hoststrerror(err); | ||||||
|  |             addr = (struct in_addr **) hp->h_addr_list; | ||||||
|  |             memcpy(&remote.sin_addr, *addr, sizeof(struct in_addr)); | ||||||
|  |         } | ||||||
|  |     } else remote.sin_family = AF_UNSPEC; | ||||||
|  |     err = socket_connect(ps, (SA *) &remote, sizeof(remote), tm); | ||||||
|  |     return socket_strerror(err); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Tries to bind socket to (address, port) | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | const char *inet_trybind(p_socket ps, const char *address, unsigned short port) | ||||||
|  | { | ||||||
|  |     struct sockaddr_in local; | ||||||
|  |     int err; | ||||||
|  |     memset(&local, 0, sizeof(local)); | ||||||
|  |     /* address is either wildcard or a valid ip address */ | ||||||
|  |     local.sin_addr.s_addr = htonl(INADDR_ANY); | ||||||
|  |     local.sin_port = htons(port); | ||||||
|  |     local.sin_family = AF_INET; | ||||||
|  |     if (strcmp(address, "*") && !inet_aton(address, &local.sin_addr)) { | ||||||
|  |         struct hostent *hp = NULL; | ||||||
|  |         struct in_addr **addr; | ||||||
|  |         err = socket_gethostbyname(address, &hp); | ||||||
|  |         if (err != IO_DONE) return socket_hoststrerror(err); | ||||||
|  |         addr = (struct in_addr **) hp->h_addr_list; | ||||||
|  |         memcpy(&local.sin_addr, *addr, sizeof(struct in_addr)); | ||||||
|  |     } | ||||||
|  |     err = socket_bind(ps, (SA *) &local, sizeof(local)); | ||||||
|  |     if (err != IO_DONE) socket_destroy(ps); | ||||||
|  |     return socket_strerror(err);  | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Some systems do not provide this so that we provide our own. It's not | ||||||
|  | * marvelously fast, but it works just fine. | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | #ifdef INET_ATON | ||||||
|  | int inet_aton(const char *cp, struct in_addr *inp) | ||||||
|  | { | ||||||
|  |     unsigned int a = 0, b = 0, c = 0, d = 0; | ||||||
|  |     int n = 0, r; | ||||||
|  |     unsigned long int addr = 0; | ||||||
|  |     r = sscanf(cp, "%u.%u.%u.%u%n", &a, &b, &c, &d, &n); | ||||||
|  |     if (r == 0 || n == 0) return 0; | ||||||
|  |     cp += n; | ||||||
|  |     if (*cp) return 0; | ||||||
|  |     if (a > 255 || b > 255 || c > 255 || d > 255) return 0; | ||||||
|  |     if (inp) { | ||||||
|  |         addr += a; addr <<= 8; | ||||||
|  |         addr += b; addr <<= 8; | ||||||
|  |         addr += c; addr <<= 8; | ||||||
|  |         addr += d; | ||||||
|  |         inp->s_addr = htonl(addr); | ||||||
|  |     } | ||||||
|  |     return 1; | ||||||
|  | } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
							
								
								
									
										42
									
								
								src/luasocket/inet.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/luasocket/inet.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | |||||||
|  | #ifndef INET_H  | ||||||
|  | #define INET_H  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Internet domain functions | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * This module implements the creation and connection of internet domain | ||||||
|  | * sockets, on top of the socket.h interface, and the interface of with the | ||||||
|  | * resolver.  | ||||||
|  | * | ||||||
|  | * The function inet_aton is provided for the platforms where it is not | ||||||
|  | * available. The module also implements the interface of the internet | ||||||
|  | * getpeername and getsockname functions as seen by Lua programs. | ||||||
|  | * | ||||||
|  | * The Lua functions toip and tohostname are also implemented here. | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: inet.h,v 1.16 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include "lua.h" | ||||||
|  | #include "socket.h" | ||||||
|  | #include "timeout.h" | ||||||
|  |  | ||||||
|  | #ifdef _WIN32 | ||||||
|  | #define INET_ATON | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | int inet_open(lua_State *L); | ||||||
|  |  | ||||||
|  | const char *inet_trycreate(p_socket ps, int type); | ||||||
|  | const char *inet_tryconnect(p_socket ps, const char *address,  | ||||||
|  |         unsigned short port, p_timeout tm); | ||||||
|  | const char *inet_trybind(p_socket ps, const char *address,  | ||||||
|  |         unsigned short port); | ||||||
|  |  | ||||||
|  | int inet_meth_getpeername(lua_State *L, p_socket ps); | ||||||
|  | int inet_meth_getsockname(lua_State *L, p_socket ps); | ||||||
|  |  | ||||||
|  | #ifdef INET_ATON | ||||||
|  | int inet_aton(const char *cp, struct in_addr *inp); | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* INET_H */ | ||||||
							
								
								
									
										32
									
								
								src/luasocket/io.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/luasocket/io.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | /*=========================================================================*\ | ||||||
|  | * Input/Output abstraction | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: io.c,v 1.6 2005/09/29 06:11:41 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include "io.h" | ||||||
|  |  | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Exported functions | ||||||
|  | \*=========================================================================*/ | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * Initializes C structure | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx) { | ||||||
|  |     io->send = send; | ||||||
|  |     io->recv = recv; | ||||||
|  |     io->error = error; | ||||||
|  |     io->ctx = ctx; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /*-------------------------------------------------------------------------*\ | ||||||
|  | * I/O error strings | ||||||
|  | \*-------------------------------------------------------------------------*/ | ||||||
|  | const char *io_strerror(int err) { | ||||||
|  |     switch (err) { | ||||||
|  |         case IO_DONE: return NULL; | ||||||
|  |         case IO_CLOSED: return "closed"; | ||||||
|  |         case IO_TIMEOUT: return "timeout"; | ||||||
|  |         default: return "unknown error";  | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										67
									
								
								src/luasocket/io.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/luasocket/io.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | |||||||
|  | #ifndef IO_H | ||||||
|  | #define IO_H | ||||||
|  | /*=========================================================================*\ | ||||||
|  | * Input/Output abstraction | ||||||
|  | * LuaSocket toolkit | ||||||
|  | * | ||||||
|  | * This module defines the interface that LuaSocket expects from the | ||||||
|  | * transport layer for streamed input/output. The idea is that if any | ||||||
|  | * transport implements this interface, then the buffer.c functions | ||||||
|  | * automatically work on it. | ||||||
|  | * | ||||||
|  | * The module socket.h implements this interface, and thus the module tcp.h | ||||||
|  | * is very simple. | ||||||
|  | * | ||||||
|  | * RCS ID: $Id: io.h,v 1.11 2005/10/07 04:40:59 diego Exp $ | ||||||
|  | \*=========================================================================*/ | ||||||
|  | #include <stdio.h> | ||||||
|  | #include "lua.h" | ||||||
|  |  | ||||||
|  | #include "timeout.h" | ||||||
|  |  | ||||||
|  | /* IO error codes */ | ||||||
|  | enum { | ||||||
|  |     IO_DONE = 0,        /* operation completed successfully */ | ||||||
|  |     IO_TIMEOUT = -1,    /* operation timed out */ | ||||||
|  |     IO_CLOSED = -2,     /* the connection has been closed */ | ||||||
|  | 	IO_UNKNOWN = -3 | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* interface to error message function */ | ||||||
|  | typedef const char *(*p_error) ( | ||||||
|  |     void *ctx,          /* context needed by send */ | ||||||
|  |     int err             /* error code */ | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | /* interface to send function */ | ||||||
|  | typedef int (*p_send) ( | ||||||
|  |     void *ctx,          /* context needed by send */ | ||||||
|  |     const char *data,   /* pointer to buffer with data to send */ | ||||||
|  |     size_t count,       /* number of bytes to send from buffer */ | ||||||
|  |     size_t *sent,       /* number of bytes sent uppon return */ | ||||||
|  |     p_timeout tm        /* timeout control */ | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | /* interface to recv function */ | ||||||
|  | typedef int (*p_recv) ( | ||||||
|  |     void *ctx,          /* context needed by recv */ | ||||||
|  |     char *data,         /* pointer to buffer where data will be writen */ | ||||||
|  |     size_t count,       /* number of bytes to receive into buffer */ | ||||||
|  |     size_t *got,        /* number of bytes received uppon return */ | ||||||
|  |     p_timeout tm        /* timeout control */ | ||||||
|  | ); | ||||||
|  |  | ||||||
|  | /* IO driver definition */ | ||||||
|  | typedef struct t_io_ { | ||||||
|  |     void *ctx;          /* context needed by send/recv */ | ||||||
|  |     p_send send;        /* send function pointer */ | ||||||
|  |     p_recv recv;        /* receive function pointer */ | ||||||
|  |     p_error error;      /* strerror function */ | ||||||
|  | } t_io; | ||||||
|  | typedef t_io *p_io; | ||||||
|  |  | ||||||
|  | void io_init(p_io io, p_send send, p_recv recv, p_error error, void *ctx); | ||||||
|  | const char *io_strerror(int err); | ||||||
|  |  | ||||||
|  | #endif /* IO_H */ | ||||||
|  |  | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user