diff options
Diffstat (limited to 'user')
-rw-r--r-- | user/lua-socket/0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch | 49 | ||||
-rw-r--r-- | user/lua-socket/APKBUILD | 41 | ||||
-rw-r--r-- | user/lua-socket/git.patch | 6609 | ||||
-rw-r--r-- | user/lua-socket/lua-cflags.patch | 22 |
4 files changed, 6721 insertions, 0 deletions
diff --git a/user/lua-socket/0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch b/user/lua-socket/0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch new file mode 100644 index 000000000..61bae6fbf --- /dev/null +++ b/user/lua-socket/0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch @@ -0,0 +1,49 @@ +From 3041a808c3797e3c87272d71666e7b2f7c7a9f46 Mon Sep 17 00:00:00 2001 +From: Natanael Copa <ncopa@alpinelinux.org> +Date: Wed, 25 Jan 2017 12:43:29 +0100 +Subject: [PATCH] Create socket on first sendto if family agnostic udp() was + used + +Create socket and set family on first sendto() if udp() was created +without address family. + +Signed-off-by: Natanael Copa <ncopa@alpinelinux.org> +--- + src/udp.c | 21 +++++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/src/udp.c b/src/udp.c +index ec97252..605c195 100644 +--- a/src/udp.c ++++ b/src/udp.c +@@ -189,6 +189,27 @@ static int meth_sendto(lua_State *L) { + lua_pushstring(L, gai_strerror(err)); + return 2; + } ++ ++ /* create socket if on first sendto if AF_UNSPEC was set */ ++ if (udp->family == AF_UNSPEC && udp->sock == SOCKET_INVALID) { ++ struct addrinfo *ap; ++ const char *errstr = NULL; ++ for (ap = ai; ap != NULL; ap = ap->ai_next) { ++ errstr = inet_trycreate(&udp->sock, ap->ai_family, SOCK_DGRAM, 0); ++ if (errstr == NULL) { ++ socket_setnonblocking(&udp->sock); ++ udp->family = ap->ai_family; ++ break; ++ } ++ } ++ if (errstr != NULL) { ++ lua_pushnil(L); ++ lua_pushstring(L, errstr); ++ freeaddrinfo(ai); ++ return 2; ++ } ++ } ++ + timeout_markstart(tm); + err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + (socklen_t) ai->ai_addrlen, tm); +-- +2.11.0 + diff --git a/user/lua-socket/APKBUILD b/user/lua-socket/APKBUILD new file mode 100644 index 000000000..16bfbcf40 --- /dev/null +++ b/user/lua-socket/APKBUILD @@ -0,0 +1,41 @@ +# Contributor: Mika Havela <mika.havela@gmail.com> +# Maintainer: Kiyoshi Aman <kiyoshi.aman+adelie@gmail.com> +pkgname=lua-socket +_name=luasocket +pkgver=3.0_rc1_git20160306 +pkgrel=0 +_ver=${pkgver%_git*} +_ver=${_ver/_rc/-rc} +pkgdesc="Networking library for Lua" +url="http://luaforge.net/projects/luasocket/" +arch="all" +license="MIT" +depends="lua5.3" +makedepends="lua5.3-dev" +source="luasocket-$_ver.tar.gz::https://github.com/diegonehab/luasocket/archive/v$_ver.tar.gz + git.patch + lua-cflags.patch + 0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch" +builddir="$srcdir/$_name-$_ver" + +build() { + cd "$builddir" + make LUAV="" +} + +check() { + cd "$builddir" + mkdir -p src/socket && cp src/socket-$_ver.so src/socket/core.so + mkdir -p src/mime && cp src/mime-1.0.3.so src/mime/core.so + LUA_CPATH=./src/?.so LUA_PATH="./src/?.lua;;" lua test/hello.lua +} + +package() { + cd "$builddir" + make prefix=/usr DESTDIR="$pkgdir" LUAV="5.3" install-unix +} + +sha512sums="f6efce259aaacaa11472911471f8a13b118fe009b8953a82c6aa18b9ec829cd1293180904e56935cb130d36d267e3f27c91db2d78e03f7488f3e100571ed0540 luasocket-3.0-rc1.tar.gz +45c80e488fedc879f0217bc8a654d80da003039f5d1ff21b0dea0eb769151787dbe793e44a3dfd72cb07ff2697eceaf4fc7b55b4634cd170fa71281f19f025a5 git.patch +61c15238a2f116b7239fdbdb8f617c82dbbecd0117c6e8389b12015bf07f3978299a8e8995e93a45a23530c747662b08d161073cdb6a8e07c4f449e45856e8cb lua-cflags.patch +c45a12e17771a1b3b71154b5415421f524cd10b7969b4649a5f37b652cdc826721e117edb8fe64758d3520e59946e2f755b814f72cbb39ff42bf59bbcf9a64e9 0001-Create-socket-on-first-sendto-if-family-agnostic-udp.patch" diff --git a/user/lua-socket/git.patch b/user/lua-socket/git.patch new file mode 100644 index 000000000..d665fc232 --- /dev/null +++ b/user/lua-socket/git.patch @@ -0,0 +1,6609 @@ +diff --git a/doc/http.html b/doc/http.html +index cd41c0d..3b7a8b1 100644 +--- a/doc/http.html ++++ b/doc/http.html +@@ -112,12 +112,15 @@ the HTTP module: + </p> + + <ul> +-<li> <tt>PORT</tt>: default port used for connections; +-<li> <tt>PROXY</tt>: default proxy used for connections; ++<li> <tt>PROXY</tt>: default proxy used for connections; + <li> <tt>TIMEOUT</tt>: sets the timeout for all I/O operations; + <li> <tt>USERAGENT</tt>: default user agent reported to server. + </ul> + ++<p class=note id="post"> ++Note: These constants are global. Changing them will also ++change the behavior other code that might be using LuaSocket. ++</p> + + <!-- http.request ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +diff --git a/doc/mime.html b/doc/mime.html +index ae136fd..8cb3507 100644 +--- a/doc/mime.html ++++ b/doc/mime.html +@@ -72,34 +72,6 @@ local mime = require("mime") + + <h3 id=high>High-level filters</h3> + +-<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id="normalize"> +-mime.<b>normalize(</b>[marker]<b>)</b> +-</p> +- +-<p class=description> +-Converts most common end-of-line markers to a specific given marker. +-</p> +- +-<p class=parameters> +-<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic +-end-of-line marker defined by the MIME standard. +-</p> +- +-<p class=return> +-The function returns a filter that performs the conversion. +-</p> +- +-<p class=note> +-Note: There is no perfect solution to this problem. Different end-of-line +-markers are an evil that will probably plague developers forever. +-This function, however, will work perfectly for text created with any of +-the most common end-of-line markers, i.e. the Mac OS (CR), the Unix (LF), +-or the DOS (CRLF) conventions. Even if the data has mixed end-of-line +-markers, the function will still work well, although it doesn't +-guarantee that the number of empty lines will be correct. +-</p> + + <!-- decode +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +@@ -159,6 +131,35 @@ base64 = ltn12.filter.chain( + ) + </pre> + ++<!-- normalize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="normalize"> ++mime.<b>normalize(</b>[marker]<b>)</b> ++</p> ++ ++<p class=description> ++Converts most common end-of-line markers to a specific given marker. ++</p> ++ ++<p class=parameters> ++<tt>Marker</tt> is the new marker. It defaults to CRLF, the canonic ++end-of-line marker defined by the MIME standard. ++</p> ++ ++<p class=return> ++The function returns a filter that performs the conversion. ++</p> ++ ++<p class=note> ++Note: There is no perfect solution to this problem. Different end-of-line ++markers are an evil that will probably plague developers forever. ++This function, however, will work perfectly for text created with any of ++the most common end-of-line markers, i.e. the Mac OS (CR), the Unix (LF), ++or the DOS (CRLF) conventions. Even if the data has mixed end-of-line ++markers, the function will still work well, although it doesn't ++guarantee that the number of empty lines will be correct. ++</p> ++ + <!-- stuff +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id="stuff"> +@@ -466,7 +467,7 @@ marker. + <p> + <small> + Last modified by Diego Nehab on <br> +-Thu Apr 20 00:25:44 EDT 2006 ++Fri Mar 4 15:19:17 BRT 2016 + </small> + </p> + </center> +diff --git a/doc/reference.css b/doc/reference.css +index b1dd25d..04e38cf 100644 +--- a/doc/reference.css ++++ b/doc/reference.css +@@ -2,6 +2,7 @@ body { + margin-left: 1em; + margin-right: 1em; + font-family: "Verdana", sans-serif; ++ background: #ffffff; + } + + tt { +diff --git a/doc/reference.html b/doc/reference.html +index e9bb5eb..287dc19 100644 +--- a/doc/reference.html ++++ b/doc/reference.html +@@ -147,6 +147,7 @@ Support, Manual"> + <a href="socket.html#connect">connect</a>, + <a href="socket.html#connect">connect4</a>, + <a href="socket.html#connect">connect6</a>, ++<a href="socket.html#datagramsize">_DATAGRAMSIZE</a>, + <a href="socket.html#debug">_DEBUG</a>, + <a href="dns.html#dns">dns</a>, + <a href="socket.html#gettime">gettime</a>, +@@ -158,11 +159,14 @@ Support, Manual"> + <a href="socket.html#skip">skip</a>, + <a href="socket.html#sleep">sleep</a>, + <a href="socket.html#setsize">_SETSIZE</a>, ++<a href="socket.html#socketinvalid">_SOCKETINVALID</a>, + <a href="socket.html#source">source</a>, + <a href="tcp.html#socket.tcp">tcp</a>, ++<a href="tcp.html#socket.tcp4">tcp4</a>, + <a href="tcp.html#socket.tcp6">tcp6</a>, + <a href="socket.html#try">try</a>, + <a href="udp.html#socket.udp">udp</a>, ++<a href="udp.html#socket.udp4">udp4</a>, + <a href="udp.html#socket.udp6">udp6</a>, + <a href="socket.html#version">_VERSION</a>. + </blockquote> +@@ -183,6 +187,7 @@ Support, Manual"> + <a href="tcp.html#getpeername">getpeername</a>, + <a href="tcp.html#getsockname">getsockname</a>, + <a href="tcp.html#getstats">getstats</a>, ++<a href="tcp.html#gettimeout">gettimeout</a>, + <a href="tcp.html#listen">listen</a>, + <a href="tcp.html#receive">receive</a>, + <a href="tcp.html#send">send</a>, +@@ -203,6 +208,7 @@ Support, Manual"> + <a href="udp.html#getoption">getoption</a>, + <a href="udp.html#getpeername">getpeername</a>, + <a href="udp.html#getsockname">getsockname</a>, ++<a href="udp.html#gettimeout">gettimeout</a>, + <a href="udp.html#receive">receive</a>, + <a href="udp.html#receivefrom">receivefrom</a>, + <a href="udp.html#send">send</a>, +diff --git a/doc/smtp.html b/doc/smtp.html +index bbbff80..600ec37 100644 +--- a/doc/smtp.html ++++ b/doc/smtp.html +@@ -114,6 +114,124 @@ the SMTP module: + <li> <tt>ZONE</tt>: default time zone. + </ul> + ++<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id=message> ++smtp.<b>message(</b>mesgt<b>)</b> ++</p> ++ ++<p class=description> ++Returns a <em>simple</em> ++<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> source that sends an SMTP message body, possibly multipart (arbitrarily deep). ++</p> ++ ++<p class=parameters> ++The only parameter of the function is a table describing the message. ++<tt>Mesgt</tt> has the following form (notice the recursive structure): ++</p> ++ ++<blockquote> ++<table summary="Mesgt table structure"> ++<tr><td><tt> ++mesgt = {<br> ++ headers = <i>header-table</i>,<br> ++ body = <i>LTN12 source</i> or <i>string</i> or ++<i>multipart-mesgt</i><br> ++}<br> ++ <br> ++multipart-mesgt = {<br> ++ [preamble = <i>string</i>,]<br> ++ [1] = <i>mesgt</i>,<br> ++ [2] = <i>mesgt</i>,<br> ++ ...<br> ++ [<i>n</i>] = <i>mesgt</i>,<br> ++ [epilogue = <i>string</i>,]<br> ++}<br> ++</tt></td></tr> ++</table> ++</blockquote> ++ ++<p class=parameters> ++For a simple message, all that is needed is a set of <tt>headers</tt> ++and the <tt>body</tt>. The message <tt>body</tt> can be given as a string ++or as a <em>simple</em> ++<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> ++source. For multipart messages, the body is a table that ++recursively defines each part as an independent message, plus an optional ++<tt>preamble</tt> and <tt>epilogue</tt>. ++</p> ++ ++<p class=return> ++The function returns a <em>simple</em> ++<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> ++source that produces the ++message contents as defined by <tt>mesgt</tt>, chunk by chunk. ++Hopefully, the following ++example will make things clear. When in doubt, refer to the appropriate RFC ++as listed in the introduction. </p> ++ ++<pre class=example> ++-- load the smtp support and its friends ++local smtp = require("socket.smtp") ++local mime = require("mime") ++local ltn12 = require("ltn12") ++ ++-- creates a source to send a message with two parts. The first part is ++-- plain text, the second part is a PNG image, encoded as base64. ++source = smtp.message{ ++ headers = { ++ -- Remember that headers are *ignored* by smtp.send. ++ from = "Sicrano de Oliveira <sicrano@example.com>", ++ to = "Fulano da Silva <fulano@example.com>", ++ subject = "Here is a message with attachments" ++ }, ++ body = { ++ preamble = "If your client doesn't understand attachments, \r\n" .. ++ "it will still display the preamble and the epilogue.\r\n" .. ++ "Preamble will probably appear even in a MIME enabled client.", ++ -- first part: no headers means plain text, us-ascii. ++ -- The mime.eol low-level filter normalizes end-of-line markers. ++ [1] = { ++ body = mime.eol(0, [[ ++ Lines in a message body should always end with CRLF. ++ The smtp module will *NOT* perform translation. However, the ++ send function *DOES* perform SMTP stuffing, whereas the message ++ function does *NOT*. ++ ]]) ++ }, ++ -- second part: headers describe content to be a png image, ++ -- sent under the base64 transfer content encoding. ++ -- notice that nothing happens until the message is actually sent. ++ -- small chunks are loaded into memory right before transmission and ++ -- translation happens on the fly. ++ [2] = { ++ headers = { ++ ["content-type"] = 'image/png; name="image.png"', ++ ["content-disposition"] = 'attachment; filename="image.png"', ++ ["content-description"] = 'a beautiful image', ++ ["content-transfer-encoding"] = "BASE64" ++ }, ++ body = ltn12.source.chain( ++ ltn12.source.file(io.open("image.png", "rb")), ++ ltn12.filter.chain( ++ mime.encode("base64"), ++ mime.wrap() ++ ) ++ ) ++ }, ++ epilogue = "This might also show up, but after the attachments" ++ } ++} ++ ++-- finally send it ++r, e = smtp.send{ ++ from = "<sicrano@example.com>", ++ rcpt = "<fulano@example.com>", ++ source = source, ++} ++</pre> ++ ++ + <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id=send> +@@ -275,123 +393,6 @@ r, e = smtp.send{ + } + </pre> + +-<!-- message ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id=message> +-smtp.<b>message(</b>mesgt<b>)</b> +-</p> +- +-<p class=description> +-Returns a <em>simple</em> +-<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> source that sends an SMTP message body, possibly multipart (arbitrarily deep). +-</p> +- +-<p class=parameters> +-The only parameter of the function is a table describing the message. +-<tt>Mesgt</tt> has the following form (notice the recursive structure): +-</p> +- +-<blockquote> +-<table summary="Mesgt table structure"> +-<tr><td><tt> +-mesgt = {<br> +- headers = <i>header-table</i>,<br> +- body = <i>LTN12 source</i> or <i>string</i> or +-<i>multipart-mesgt</i><br> +-}<br> +- <br> +-multipart-mesgt = {<br> +- [preamble = <i>string</i>,]<br> +- [1] = <i>mesgt</i>,<br> +- [2] = <i>mesgt</i>,<br> +- ...<br> +- [<i>n</i>] = <i>mesgt</i>,<br> +- [epilogue = <i>string</i>,]<br> +-}<br> +-</tt></td></tr> +-</table> +-</blockquote> +- +-<p class=parameters> +-For a simple message, all that is needed is a set of <tt>headers</tt> +-and the <tt>body</tt>. The message <tt>body</tt> can be given as a string +-or as a <em>simple</em> +-<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +-source. For multipart messages, the body is a table that +-recursively defines each part as an independent message, plus an optional +-<tt>preamble</tt> and <tt>epilogue</tt>. +-</p> +- +-<p class=return> +-The function returns a <em>simple</em> +-<a href="http://lua-users.org/wiki/FiltersSourcesAndSinks">LTN12</a> +-source that produces the +-message contents as defined by <tt>mesgt</tt>, chunk by chunk. +-Hopefully, the following +-example will make things clear. When in doubt, refer to the appropriate RFC +-as listed in the introduction. </p> +- +-<pre class=example> +--- load the smtp support and its friends +-local smtp = require("socket.smtp") +-local mime = require("mime") +-local ltn12 = require("ltn12") +- +--- creates a source to send a message with two parts. The first part is +--- plain text, the second part is a PNG image, encoded as base64. +-source = smtp.message{ +- headers = { +- -- Remember that headers are *ignored* by smtp.send. +- from = "Sicrano de Oliveira <sicrano@example.com>", +- to = "Fulano da Silva <fulano@example.com>", +- subject = "Here is a message with attachments" +- }, +- body = { +- preamble = "If your client doesn't understand attachments, \r\n" .. +- "it will still display the preamble and the epilogue.\r\n" .. +- "Preamble will probably appear even in a MIME enabled client.", +- -- first part: no headers means plain text, us-ascii. +- -- The mime.eol low-level filter normalizes end-of-line markers. +- [1] = { +- body = mime.eol(0, [[ +- Lines in a message body should always end with CRLF. +- The smtp module will *NOT* perform translation. However, the +- send function *DOES* perform SMTP stuffing, whereas the message +- function does *NOT*. +- ]]) +- }, +- -- second part: headers describe content to be a png image, +- -- sent under the base64 transfer content encoding. +- -- notice that nothing happens until the message is actually sent. +- -- small chunks are loaded into memory right before transmission and +- -- translation happens on the fly. +- [2] = { +- headers = { +- ["content-type"] = 'image/png; name="image.png"', +- ["content-disposition"] = 'attachment; filename="image.png"', +- ["content-description"] = 'a beautiful image', +- ["content-transfer-encoding"] = "BASE64" +- }, +- body = ltn12.source.chain( +- ltn12.source.file(io.open("image.png", "rb")), +- ltn12.filter.chain( +- mime.encode("base64"), +- mime.wrap() +- ) +- ) +- }, +- epilogue = "This might also show up, but after the attachments" +- } +-} +- +--- finally send it +-r, e = smtp.send{ +- from = "<sicrano@example.com>", +- rcpt = "<fulano@example.com>", +- source = source, +-} +-</pre> +- + <!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <div class=footer> +diff --git a/doc/socket.html b/doc/socket.html +index b9303cb..35f8391 100644 +--- a/doc/socket.html ++++ b/doc/socket.html +@@ -51,6 +51,30 @@ To obtain the <tt>socket</tt> namespace, run: + local socket = require("socket") + </pre> + ++<!-- headers.canonic ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="headers.canonic"> ++socket.headers.<b>canonic</b></p> ++ ++<p> The <tt>socket.headers.canonic</tt> table ++is used by the HTTP and SMTP modules to translate from ++lowercase field names back into their canonic ++capitalization. When a lowercase field name exists as a key ++in this table, the associated value is substituted in ++whenever the field name is sent out. ++</p> ++ ++<p> ++You can obtain the <tt>headers</tt> namespace if case run-time ++modifications are required by running: ++</p> ++ ++<pre class=example> ++-- loads the headers module ++local headers = require("headers") ++</pre> ++ ++ + <!-- bind ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id=bind> +@@ -90,7 +114,7 @@ of connect are defined as simple helper functions that restrict the + + <!-- debug ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id=debug> ++<p class=name id=debug> + socket.<b>_DEBUG</b> + </p> + +@@ -99,6 +123,19 @@ This constant is set to <tt><b>true</b></tt> if the library was compiled + with debug support. + </p> + ++<!-- datagramsize +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id=debug> ++socket.<b>_DATAGRAMSIZE</b> ++</p> ++ ++<p class=description> ++Default datagram size used by calls to ++<a href="udp.html#receive"<tt>receive</tt></a> and ++<a href="udp.html#receivefrom"><tt>receivefrom</tt></a>. ++(Unless changed in compile time, the value is 8192.) ++</p> ++ + <!-- get time +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id=gettime> +@@ -106,8 +143,7 @@ socket.<b>gettime()</b> + </p> + + <p class=description> +-Returns the time in seconds, relative to the origin of the +-universe. You should subtract the values returned by this function ++Returns the UNIX time in seconds. You should subtract the values returned by this function + to get meaningful values. + </p> + +@@ -117,29 +153,6 @@ t = socket.gettime() + print(socket.gettime() - t .. " seconds elapsed") + </pre> + +-<!-- socket.headers ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id="headers.canonic"> +-socket.headers.<b>canonic</b></p> +- +-<p> The <tt>socket.headers.canonic</tt> table +-is used by the HTTP and SMTP modules to translate from +-lowercase field names back into their canonic +-capitalization. When a lowercase field name exists as a key +-in this table, the associated value is substituted in +-whenever the field name is sent out. +-</p> +- +-<p> +-You can obtain the <tt>headers</tt> namespace if case run-time +-modifications are required by running: +-</p> +- +-<pre class=example> +--- loads the headers module +-local headers = require("headers") +-</pre> +- + <!-- newtry +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id=newtry> +@@ -155,8 +168,7 @@ is raised. + + <p class=parameters> + <tt>Finalizer</tt> is a function that will be called before +-<tt>try</tt> throws the exception. It will be called +-in <em>protected</em> mode. ++<tt>try</tt> throws the exception. + </p> + + <p class=return> +@@ -204,15 +216,9 @@ to throw exceptions. + </p> + + <p class=return> +-Returns an equivalent function that instead of throwing exceptions, +-returns <tt><b>nil</b></tt> followed by an error message. +-</p> +- +-<p class=note> +-Note: Beware that if your function performs some illegal operation that +-raises an error, the protected function will catch the error and return it +-as a string. This is because the <a href=#try><tt>try</tt></a> function +-uses errors as the mechanism to throw exceptions. ++Returns an equivalent function that instead of throwing exceptions in case of ++a failed <a href=#try><tt>try</tt></a> call, returns <tt><b>nil</b></tt> ++followed by an error message. + </p> + + <!-- select +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -238,7 +244,9 @@ non-numeric indices) in the arrays will be silently ignored. + + <p class=return> The function returns a list with the sockets ready for + reading, a list with the sockets ready for writing and an error message. +-The error message is "<tt>timeout</tt>" if a timeout condition was met and ++The error message is "<tt>timeout</tt>" if a timeout ++condition was met, "<tt>select failed</tt>" if the call ++to <tt>select</tt> failed, and + <tt><b>nil</b></tt> otherwise. The returned tables are + doubly keyed both by integers and also by the sockets + themselves, to simplify the test if a specific socket has +@@ -246,7 +254,7 @@ changed status. + </p> + + <p class=note> +-<b>Note: </b>: <tt>select</tt> can monitor a limited number ++<b>Note:</b> <tt>select</tt> can monitor a limited number + of sockets, as defined by the constant <tt>socket._SETSIZE</tt>. This + number may be as high as 1024 or as low as 64 by default, + depending on the system. It is usually possible to change this +@@ -276,6 +284,18 @@ it to <tt>select</tt>, it will be ignored. + <b>Using select with non-socket objects</b>: Any object that implements <tt>getfd</tt> and <tt>dirty</tt> can be used with <tt>select</tt>, allowing objects from other libraries to be used within a <tt>socket.select</tt> driven loop. + </p> + ++<!-- setsize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id=setsize> ++socket.<b>_SETSIZE</b> ++</p> ++ ++<p class=description> ++The maximum number of sockets that the <a ++href=#select><tt>select</tt></a> function can handle. ++</p> ++ ++ + <!-- sink ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id=sink> +@@ -383,15 +403,14 @@ side closes the connection. + The function returns a source with the appropriate behavior. + </p> + +-<!-- setsize ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++<!-- socketinvalid ++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id=setsize> +-socket.<b>_SETSIZE</b> ++<p class=name id=socketinvalid> ++socket.<b>_SOCKETINVALID</b> + </p> + + <p class=description> +-The maximum number of sockets that the <a +-href=#select><tt>select</tt></a> function can handle. ++The OS value for an invalid socket. + </p> + + <!-- try ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -401,9 +420,9 @@ socket.<b>try(</b>ret<sub>1</sub> [, ret<sub>2</sub> ... ret<sub>N</sub>]<b>)</b + </p> + + <p class=description> +-Throws an exception in case of error. The exception can only be caught +-by the <a href=#protect><tt>protect</tt></a> function. It does not explode +-into an error message. ++Throws an exception in case <tt>ret<sub>1</sub></tt> is falsy, using ++<tt>ret<sub>2</sub></tt> as the error message. The exception is supposed to be caught ++by a <a href=#protect><tt>protect</tt></a>ed function only. + </p> + + <p class=parameters> +@@ -414,7 +433,10 @@ nested with <tt>try</tt>. + + <p class=return> + The function returns <tt>ret</tt><sub>1</sub> to <tt>ret</tt><sub>N</sub> if +-<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt>. Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub>. ++<tt>ret</tt><sub>1</sub> is not <tt><b>nil</b></tt> or <tt><b>false</b></tt>. ++Otherwise, it calls <tt>error</tt> passing <tt>ret</tt><sub>2</sub> wrapped ++in a table with metatable used by <a href=#protect><tt>protect</tt></a> to ++distinguish exceptions from runtime errors. + </p> + + <pre class=example> +diff --git a/doc/tcp.html b/doc/tcp.html +index 4226d78..c6c6eb2 100644 +--- a/doc/tcp.html ++++ b/doc/tcp.html +@@ -1,10 +1,10 @@ +-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" ++<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + <html> + + <head> + <meta name="description" content="LuaSocket: The TCP/IP support"> +-<meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support"> ++<meta name="keywords" content="Lua, LuaSocket, Socket, TCP, Library, Network, Support"> + <title>LuaSocket: TCP/IP support</title> + <link rel="stylesheet" href="reference.css" type="text/css"> + </head> +@@ -28,7 +28,7 @@ + <a href="index.html#download">download</a> · + <a href="installation.html">installation</a> · + <a href="introduction.html">introduction</a> · +-<a href="reference.html">reference</a> ++<a href="reference.html">reference</a> + </p> + </center> + <hr> +@@ -36,56 +36,11 @@ + + <!-- tcp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<h2 id="tcp">TCP</h2> +- +-<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id="socket.tcp"> +-socket.<b>tcp()</b> +-</p> +- +-<p class=description> +-Creates and returns an IPv4 TCP master object. A master object can +-be transformed into a server object with the method +-<a href=#listen><tt>listen</tt></a> (after a call to <a +-href=#bind><tt>bind</tt></a>) or into a client object with +-the method <a href=#connect><tt>connect</tt></a>. The only other +-method supported by a master object is the +-<a href=#close><tt>close</tt></a> method.</p> +- +-<p class=return> +-In case of success, a new master object is returned. In case of error, +-<b><tt>nil</tt></b> is returned, followed by an error message. +-</p> +- +-<!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id="socket.tcp6"> +-socket.<b>tcp6()</b> +-</p> +- +-<p class=description> +-Creates and returns an IPv6 TCP master object. A master object can +-be transformed into a server object with the method +-<a href=#listen><tt>listen</tt></a> (after a call to <a +-href=#bind><tt>bind</tt></a>) or into a client object with +-the method <a href=#connect><tt>connect</tt></a>. The only other +-method supported by a master object is the +-<a href=#close><tt>close</tt></a> method.</p> +- +-<p class=return> +-In case of success, a new master object is returned. In case of error, +-<b><tt>nil</tt></b> is returned, followed by an error message. +-</p> +- +-<p class=note> +-Note: The TCP object returned will have the option +-"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>. +-</p> ++<h2 id="tcp">TCP</h2> + + <!-- accept +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="accept"> ++<p class=name id="accept"> + server:<b>accept()</b> + </p> + +@@ -95,9 +50,9 @@ object and returns a client object representing that connection. + </p> + + <p class=return> +-If a connection is successfully initiated, a client object is returned. ++If a connection is successfully initiated, a client object is returned. + If a timeout condition is met, the method returns <b><tt>nil</tt></b> +-followed by the error string '<tt>timeout</tt>'. Other errors are ++followed by the error string '<tt>timeout</tt>'. Other errors are + reported by <b><tt>nil</tt></b> followed by a message describing the error. + </p> + +@@ -107,28 +62,28 @@ with a server object in + the <tt>recvt</tt> parameter before a call to <tt>accept</tt> does + <em>not</em> guarantee <tt>accept</tt> will return immediately. Use the <a + href=#settimeout><tt>settimeout</tt></a> method or <tt>accept</tt> +-might block until <em>another</em> client shows up. ++might block until <em>another</em> client shows up. + </p> + + <!-- bind +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="bind"> ++<p class=name id="bind"> + master:<b>bind(</b>address, port<b>)</b> + </p> + + <p class=description> + Binds a master object to <tt>address</tt> and <tt>port</tt> on the +-local host. ++local host. + + <p class=parameters> +-<tt>Address</tt> can be an IP address or a host name. +-<tt>Port</tt> must be an integer number in the range [0..64K). ++<tt>Address</tt> can be an IP address or a host name. ++<tt>Port</tt> must be an integer number in the range [0..64K). + If <tt>address</tt> + is '<tt>*</tt>', the system binds to all local interfaces + using the <tt>INADDR_ANY</tt> constant or +-<tt>IN6ADDR_ANY_INIT</tt>, according to the family. ++<tt>IN6ADDR_ANY_INIT</tt>, according to the family. + If <tt>port</tt> is 0, the system automatically +-chooses an ephemeral port. ++chooses an ephemeral port. + </p> + + <p class=return> +@@ -137,13 +92,13 @@ method returns <b><tt>nil</tt></b> followed by an error message. + </p> + + <p class=note> +-Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a> ++Note: The function <a href=socket.html#bind><tt>socket.bind</tt></a> + is available and is a shortcut for the creation of server sockets. + </p> + + <!-- close ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="close"> ++<p class=name id="close"> + master:<b>close()</b><br> + client:<b>close()</b><br> + server:<b>close()</b> +@@ -154,14 +109,14 @@ Closes a TCP object. The internal socket used by the object is closed + and the local address to which the object was + bound is made available to other applications. No further operations + (except for further calls to the <tt>close</tt> method) are allowed on +-a closed socket. ++a closed socket. + </p> + + <p class=note> + Note: It is important to close all used sockets once they are not + needed, since, in many systems, each socket uses a file descriptor, + which are limited system resources. Garbage-collected objects are +-automatically closed before destruction, though. ++automatically closed before destruction, though. + </p> + + <!-- connect ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -172,19 +127,19 @@ master:<b>connect(</b>address, port<b>)</b> + + <p class=description> + Attempts to connect a master object to a remote host, transforming it into a +-client object. +-Client objects support methods ++client object. ++Client objects support methods + <a href=#send><tt>send</tt></a>, +-<a href=#receive><tt>receive</tt></a>, +-<a href=#getsockname><tt>getsockname</tt></a>, ++<a href=#receive><tt>receive</tt></a>, ++<a href=#getsockname><tt>getsockname</tt></a>, + <a href=#getpeername><tt>getpeername</tt></a>, +-<a href=#settimeout><tt>settimeout</tt></a>, ++<a href=#settimeout><tt>settimeout</tt></a>, + and <a href=#close><tt>close</tt></a>. + </p> + + <p class=parameters> +-<tt>Address</tt> can be an IP address or a host name. +-<tt>Port</tt> must be an integer number in the range [1..64K). ++<tt>Address</tt> can be an IP address or a host name. ++<tt>Port</tt> must be an integer number in the range [1..64K). + </p> + + <p class=return> +@@ -193,14 +148,14 @@ describing the error. In case of success, the method returns 1. + </p> + + <p class=note> +-Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a> ++Note: The function <a href=socket.html#connect><tt>socket.connect</tt></a> + is available and is a shortcut for the creation of client sockets. + </p> + + <p class=note> +-Note: Starting with LuaSocket 2.0, ++Note: Starting with LuaSocket 2.0, + the <a href=#settimeout><tt>settimeout</tt></a> +-method affects the behavior of <tt>connect</tt>, causing it to return ++method affects the behavior of <tt>connect</tt>, causing it to return + with an error in case of a timeout. If that happens, you can still call <a + href=socket.html#select><tt>socket.select</tt></a> with the socket in the + <tt>sendt</tt> table. The socket will be writable when the connection is +@@ -209,13 +164,88 @@ established. + + <p class=note> + Note: Starting with LuaSocket 3.0, the host name resolution +-depends on whether the socket was created by <a +-href=#socket.tcp><tt>socket.tcp</tt></a> or <a +-href=#socket.tcp6><tt>socket.tcp6</tt></a>. Addresses from +-the appropriate family are tried in succession until the +-first success or until the last failure. ++depends on whether the socket was created by ++<a href=#socket.tcp><tt>socket.tcp</tt></a>, ++<a href=#socket.tcp4><tt>socket.tcp4</tt></a> or ++<a href=#socket.tcp6><tt>socket.tcp6</tt></a>. Addresses from ++the appropriate family (or both) are tried in the order ++returned by the resolver until the ++first success or until the last failure. If the timeout was ++set to zero, only the first address is tried. ++</p> ++ ++<!-- dirty +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="dirty"> ++master:<b>dirty()</b><br> ++client:<b>dirty()</b><br> ++server:<b>dirty()</b> ++</p> ++ ++<p class=description> ++Check the read buffer status. ++</p> ++ ++<p class=return> ++Returns <tt>true</tt> if there is any data in the read buffer, <tt>false</tt> otherwise. ++</p> ++ ++<p class=note> ++Note: <b>This is an internal method, use at your own risk.</b> ++</p> ++ ++ ++<!-- getfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="getfd"> ++master:<b>getfd()</b><br> ++client:<b>getfd()</b><br> ++server:<b>getfd()</b> ++</p> ++ ++<p class=description> ++Returns the underling socket descriptor or handle associated to the object. ++</p> ++ ++<p class=return> ++The descriptor or handle. In case the object has been closed, the return will be -1. ++</p> ++ ++<p class=note> ++Note: <b>This is an internal method. Unlikely to be ++portable. Use at your own risk. </b> ++</p> ++ ++ ++<!-- getoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="getoption"> ++client:<b>getoption(</b>option)</b><br> ++server:<b>getoption(</b>option)</b> ++</p> ++ ++<p class=description> ++Gets options for the TCP object. ++See <a href=#setoption><tt>setoption</tt></a> for description of the ++option names and values. ++</p> ++ ++<p class=parameters> ++<tt>Option</tt> is a string with the option name. ++<ul> ++ ++<li> '<tt>keepalive</tt>' ++<li> '<tt>linger</tt>' ++<li> '<tt>reuseaddr</tt>' ++<li> '<tt>tcp-nodelay</tt>' ++</ul> ++ ++<p class=return> ++The method returns the option <tt>value</tt> in case of success, or ++<b><tt>nil</tt></b> followed by an error message otherwise. + </p> + ++ + <!-- getpeername ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id="getpeername"> +@@ -227,10 +257,10 @@ Returns information about the remote side of a connected client object. + </p> + + <p class=return> +-Returns a string with the IP address of the peer, the +-port number that peer is using for the connection, +-and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). +-In case of error, the method returns <b><tt>nil</tt></b>. ++Returns a string with the IP address of the peer, the ++port number that peer is using for the connection, ++and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). ++In case of error, the method returns <b><tt>nil</tt></b>. + </p> + + <p class=note> +@@ -246,13 +276,13 @@ server:<b>getsockname()</b> + </p> + + <p class=description> +-Returns the local address information associated to the object. ++Returns the local address information associated to the object. + </p> + + <p class=return> +-The method returns a string with local IP address, a number with +-the local port, +-and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). ++The method returns a string with local IP address, a number with ++the local port, ++and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). + In case of error, the method returns <b><tt>nil</tt></b>. + </p> + +@@ -266,32 +296,46 @@ server:<b>getstats()</b><br> + + <p class=description> + Returns accounting information on the socket, useful for throttling +-of bandwidth. ++of bandwidth. + </p> + + <p class=return> + The method returns the number of bytes received, the number of bytes sent, +-and the age of the socket object in seconds. ++and the age of the socket object in seconds. + </p> + ++<!-- gettimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="gettimeout"> ++master:<b>gettimeout()</b><br> ++client:<b>gettimeout()</b><br> ++server:<b>gettimeout()</b> ++</p> ++ ++<p class=description> ++Returns the current block timeout followed by the curent ++total timeout. ++</p> ++ ++ + <!-- listen ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="listen"> ++<p class=name id="listen"> + master:<b>listen(</b>backlog<b>)</b> + </p> + + <p class=description> + Specifies the socket is willing to receive connections, transforming the +-object into a server object. Server objects support the +-<a href=#accept><tt>accept</tt></a>, +-<a href=#getsockname><tt>getsockname</tt></a>, +-<a href=#setoption><tt>setoption</tt></a>, +-<a href=#settimeout><tt>settimeout</tt></a>, +-and <a href=#close><tt>close</tt></a> methods. ++object into a server object. Server objects support the ++<a href=#accept><tt>accept</tt></a>, ++<a href=#getsockname><tt>getsockname</tt></a>, ++<a href=#setoption><tt>setoption</tt></a>, ++<a href=#settimeout><tt>settimeout</tt></a>, ++and <a href=#close><tt>close</tt></a> methods. + </p> + + <p class=parameters> +-The parameter <tt>backlog</tt> specifies the number of client ++The parameter <tt>backlog</tt> specifies the number of client + connections that can + be queued waiting for service. If the queue is full and another client + attempts connection, the connection is refused. +@@ -310,11 +354,11 @@ client:<b>receive(</b>[pattern [, prefix]]<b>)</b> + + <p class=description> + Reads data from a client object, according to the specified <em>read +-pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. ++pattern</em>. Patterns follow the Lua file I/O format, and the difference in performance between all patterns is negligible. + </p> + + <p class=parameters> +-<tt>Pattern</tt> can be any of the following: ++<tt>Pattern</tt> can be any of the following: + </p> + + <ul> +@@ -325,7 +369,7 @@ terminated by a LF character (ASCII 10), optionally preceded by a + CR character (ASCII 13). The CR and LF characters are not included in + the returned line. In fact, <em>all</em> CR characters are + ignored by the pattern. This is the default pattern; +-<li> <tt>number</tt>: causes the method to read a specified <tt>number</tt> ++<li> <tt>number</tt>: causes the method to read a specified <tt>number</tt> + of bytes from the socket. + </ul> + +@@ -347,10 +391,10 @@ closed before the transmission was completed or the string + <p class=note> + <b>Important note</b>: This function was changed <em>severely</em>. It used + to support multiple patterns (but I have never seen this feature used) and +-now it doesn't anymore. Partial results used to be returned in the same ++now it doesn't anymore. Partial results used to be returned in the same + way as successful results. This last feature violated the idea that all + functions should return <tt><b>nil</b></tt> on error. Thus it was changed +-too. ++too. + </p> + + <!-- send +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -366,7 +410,7 @@ Sends <tt>data</tt> through client object. + <p class=parameters> + <tt>Data</tt> is the string to be sent. The optional arguments + <tt>i</tt> and <tt>j</tt> work exactly like the standard +-<tt>string.sub</tt> Lua function to allow the selection of a ++<tt>string.sub</tt> Lua function to allow the selection of a + substring to be sent. + </p> + +@@ -385,10 +429,10 @@ there was a timeout during the operation. + </p> + + <p class=note> +-Note: Output is <em>not</em> buffered. For small strings, +-it is always better to concatenate them in Lua +-(with the '<tt>..</tt>' operator) and send the result in one call +-instead of calling the method several times. ++Note: Output is <em>not</em> buffered. For small strings, ++it is always better to concatenate them in Lua ++(with the '<tt>..</tt>' operator) and send the result in one call ++instead of calling the method several times. + </p> + + <!-- setoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -400,12 +444,12 @@ server:<b>setoption(</b>option [, value]<b>)</b> + + <p class=description> + Sets options for the TCP object. Options are only needed by low-level or +-time-critical applications. You should only modify an option if you +-are sure you need it. ++time-critical applications. You should only modify an option if you ++are sure you need it. + </p> + + <p class=parameters> +-<tt>Option</tt> is a string with the option name, and <tt>value</tt> ++<tt>Option</tt> is a string with the option name, and <tt>value</tt> + depends on the option being set: + + <ul> +@@ -413,7 +457,7 @@ depends on the option being set: + <li> '<tt>keepalive</tt>': Setting this option to <tt>true</tt> enables + the periodic transmission of messages on a connected socket. Should the + connected party fail to respond to these messages, the connection is +-considered broken and processes using the socket are notified; ++considered broken and processes using the socket are notified; + + <li> '<tt>linger</tt>': Controls the action taken when unsent data are + queued on a socket and a close is performed. The value is a table with a +@@ -424,13 +468,13 @@ it is able to transmit the data or until '<tt>timeout</tt>' has passed. If + '<tt>on</tt>' is <tt>false</tt> and a close is issued, the system will + process the close in a manner that allows the process to continue as + quickly as possible. I do not advise you to set this to anything other than +-zero; ++zero; + + <li> '<tt>reuseaddr</tt>': Setting this option indicates that the rules +-used in validating addresses supplied in a call to ++used in validating addresses supplied in a call to + <a href=#bind><tt>bind</tt></a> should allow reuse of local addresses; + +-<li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt> ++<li> '<tt>tcp-nodelay</tt>': Setting this option to <tt>true</tt> + disables the Nagle's algorithm for the connection; + + <li> '<tt>ipv6-v6only</tt>': +@@ -447,34 +491,6 @@ followed by an error message otherwise. + Note: The descriptions above come from the man pages. + </p> + +-<!-- getoption ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class=name id="getoption"> +-client:<b>getoption(</b>option)</b><br> +-server:<b>getoption(</b>option)</b> +-</p> +- +-<p class=description> +-Gets options for the TCP object. +-See <a href=#setoption><tt>setoption</tt></a> for description of the +-option names and values. +-</p> +- +-<p class=parameters> +-<tt>Option</tt> is a string with the option name. +-<ul> +- +-<li> '<tt>keepalive</tt>' +-<li> '<tt>linger</tt>' +-<li> '<tt>reuseaddr</tt>' +-<li> '<tt>tcp-nodelay</tt>' +-</ul> +- +-<p class=return> +-The method returns the option <tt>value</tt> in case of success, or +-<b><tt>nil</tt></b> followed by an error message otherwise. +-</p> +- + <!-- setstats +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class=name id="setstats"> +@@ -485,7 +501,7 @@ server:<b>setstats(</b>received, sent, age<b>)</b><br> + + <p class=description> + Resets accounting information on the socket, useful for throttling +-of bandwidth. ++of bandwidth. + </p> + + <p class=parameters> +@@ -495,7 +511,7 @@ of bandwidth. + </p> + + <p class=return> +-The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise. ++The method returns 1 in case of success and <tt><b>nil</b></tt> otherwise. + </p> + + <!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +@@ -509,8 +525,8 @@ server:<b>settimeout(</b>value [, mode]<b>)</b> + <p class=description> + Changes the timeout values for the object. By default, + all I/O operations are blocking. That is, any call to the methods +-<a href=#send><tt>send</tt></a>, +-<a href=#receive><tt>receive</tt></a>, and ++<a href=#send><tt>send</tt></a>, ++<a href=#receive><tt>receive</tt></a>, and + <a href=#accept><tt>accept</tt></a> + will block indefinitely, until the operation completes. The + <tt>settimeout</tt> method defines a limit on the amount of time the +@@ -521,7 +537,7 @@ time has elapsed, the affected methods give up and fail with an error code. + <p class=parameters> + The amount of time to wait is specified as the + <tt>value</tt> parameter, in seconds. There are two timeout modes and +-both can be used together for fine tuning: ++both can be used together for fine tuning: + </p> + + <ul> +@@ -532,7 +548,7 @@ default mode;</li> + + <li> '<tt>t</tt>': <em>total</em> timeout. Specifies the upper limit on + the amount of time LuaSocket can block a Lua script before returning from +-a call.</li> ++a call.</li> + </ul> + + <p class=parameters> +@@ -562,7 +578,7 @@ client:<b>shutdown(</b>mode<b>)</b><br> + </p> + + <p class=description> +-Shuts down part of a full-duplex connection. ++Shuts down part of a full-duplex connection. + </p> + + <p class=parameters> +@@ -579,66 +595,107 @@ This is the default mode; + This function returns 1. + </p> + +-<!-- dirty +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++<!-- setfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="dirty"> +-master:<b>dirty()</b><br> +-client:<b>dirty()</b><br> +-server:<b>dirty()</b> ++<p class=name id="setfd"> ++master:<b>setfd(</b>fd<b>)</b><br> ++client:<b>setfd(</b>fd<b>)</b><br> ++server:<b>setfd(</b>fd<b>)</b> + </p> + + <p class=description> +-Check the read buffer status. ++Sets the underling socket descriptor or handle associated to the object. The current one is simply replaced, not closed, and no other change to the object state is made. + </p> + + <p class=return> +-Returns <tt>true</tt> if there is any data in the read buffer, <tt>false</tt> otherwise. ++No return value. + </p> + + <p class=note> +-Note: <b>This is an internal method, any use is unlikely to be portable.</b> ++Note: <b>This is an internal method. Unlikely to be ++portable. Use at your own risk. </b> + </p> + +-<!-- getfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<p class=name id="getfd"> +-master:<b>getfd()</b><br> +-client:<b>getfd()</b><br> +-server:<b>getfd()</b> ++<p class=name id="socket.tcp"> ++socket.<b>tcp()</b> + </p> + + <p class=description> +-Returns the underling socket descriptor or handle associated to the object. +-</p> ++Creates and returns an TCP master object. A master object can ++be transformed into a server object with the method ++<a href=#listen><tt>listen</tt></a> (after a call to <a ++href=#bind><tt>bind</tt></a>) or into a client object with ++the method <a href=#connect><tt>connect</tt></a>. The only other ++method supported by a master object is the ++<a href=#close><tt>close</tt></a> method.</p> + + <p class=return> +-The descriptor or handle. In case the object has been closed, the return will be -1. ++In case of success, a new master object is returned. In case of error, ++<b><tt>nil</tt></b> is returned, followed by an error message. + </p> + + <p class=note> +-Note: <b>This is an internal method, any use is unlikely to be portable.</b> ++Note: The choice between IPv4 and IPv6 happens during a call to ++<a href=#bind><tt>bind</tt></a> or <a ++href=#bind><tt>connect</tt></a>, depending on the address ++family obtained from the resolver. + </p> + +-<!-- setfd +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++<p class=note> ++Note: Before the choice between IPv4 and IPv6 happens, ++the internal socket object is invalid and therefore <a ++href=#setoption><tt>setoption</tt></a> will fail. ++</p> + +-<p class=name id="setfd"> +-master:<b>setfd(</b>fd<b>)</b><br> +-client:<b>setfd(</b>fd<b>)</b><br> +-server:<b>setfd(</b>fd<b>)</b> ++<!-- socket.tcp +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="socket.tcp4"> ++socket.<b>tcp4()</b> + </p> + + <p class=description> +-Sets the underling socket descriptor or handle associated to the object. The current one is simply replaced, not closed, and no other change to the object state is made. ++Creates and returns an IPv4 TCP master object. A master object can ++be transformed into a server object with the method ++<a href=#listen><tt>listen</tt></a> (after a call to <a ++href=#bind><tt>bind</tt></a>) or into a client object with ++the method <a href=#connect><tt>connect</tt></a>. The only other ++method supported by a master object is the ++<a href=#close><tt>close</tt></a> method.</p> ++ ++<p class=return> ++In case of success, a new master object is returned. In case of error, ++<b><tt>nil</tt></b> is returned, followed by an error message. ++</p> ++ ++<!-- socket.tcp6 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="socket.tcp6"> ++socket.<b>tcp6()</b> + </p> + ++<p class=description> ++Creates and returns an IPv6 TCP master object. A master object can ++be transformed into a server object with the method ++<a href=#listen><tt>listen</tt></a> (after a call to <a ++href=#bind><tt>bind</tt></a>) or into a client object with ++the method <a href=#connect><tt>connect</tt></a>. The only other ++method supported by a master object is the ++<a href=#close><tt>close</tt></a> method.</p> ++ + <p class=return> +-No return value. ++In case of success, a new master object is returned. In case of error, ++<b><tt>nil</tt></b> is returned, followed by an error message. + </p> + + <p class=note> +-Note: <b>This is an internal method, any use is unlikely to be portable.</b> ++Note: The TCP object returned will have the option ++"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>. + </p> + ++ ++ + <!-- footer +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <div class=footer> +diff --git a/doc/udp.html b/doc/udp.html +index e5b0ad0..4618aad 100644 +--- a/doc/udp.html ++++ b/doc/udp.html +@@ -4,7 +4,7 @@ + + <head> + <meta name="description" content="LuaSocket: The UDP support"> +-<meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support"> ++<meta name="keywords" content="Lua, LuaSocket, Socket, UDP, Library, Network, Support"> + <title>LuaSocket: UDP support</title> + <link rel="stylesheet" href="reference.css" type="text/css"> + </head> +@@ -28,7 +28,7 @@ + <a href="index.html#download">download</a> · + <a href="installation.html">installation</a> · + <a href="introduction.html">introduction</a> · +-<a href="reference.html">reference</a> ++<a href="reference.html">reference</a> + </p> + </center> + <hr> +@@ -37,74 +37,7 @@ + + <!-- udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +-<h2 id="udp">UDP</h2> +- +-<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class="name" id="socket.udp"> +-socket.<b>udp()</b> +-</p> +- +-<p class="description"> +-Creates and returns an unconnected IPv4 UDP object. +-Unconnected objects support the +-<a href="#sendto"><tt>sendto</tt></a>, +-<a href="#receive"><tt>receive</tt></a>, +-<a href="#receivefrom"><tt>receivefrom</tt></a>, +-<a href="#getoption"><tt>getoption</tt></a>, +-<a href="#getsockname"><tt>getsockname</tt></a>, +-<a href="#setoption"><tt>setoption</tt></a>, +-<a href="#settimeout"><tt>settimeout</tt></a>, +-<a href="#setpeername"><tt>setpeername</tt></a>, +-<a href="#setsockname"><tt>setsockname</tt></a>, and +-<a href="#close"><tt>close</tt></a>. +-The <a href="#setpeername"><tt>setpeername</tt></a> +-is used to connect the object. +-</p> +- +-<p class="return"> +-In case of success, a new unconnected UDP object +-returned. In case of error, <b><tt>nil</tt></b> is returned, followed by +-an error message. +-</p> +- +-<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class="name" id="socket.udp6"> +-socket.<b>udp6()</b> +-</p> +- +-<p class="description"> +-Creates and returns an unconnected IPv6 UDP object. +-Unconnected objects support the +-<a href="#sendto"><tt>sendto</tt></a>, +-<a href="#receive"><tt>receive</tt></a>, +-<a href="#receivefrom"><tt>receivefrom</tt></a>, +-<a href="#getoption"><tt>getoption</tt></a>, +-<a href="#getsockname"><tt>getsockname</tt></a>, +-<a href="#setoption"><tt>setoption</tt></a>, +-<a href="#settimeout"><tt>settimeout</tt></a>, +-<a href="#setpeername"><tt>setpeername</tt></a>, +-<a href="#setsockname"><tt>setsockname</tt></a>, and +-<a href="#close"><tt>close</tt></a>. +-The <a href="#setpeername"><tt>setpeername</tt></a> +-is used to connect the object. +-</p> +- +-<p class="return"> +-In case of success, a new unconnected UDP object +-returned. In case of error, <b><tt>nil</tt></b> is returned, followed by +-an error message. +-</p> +- +-<p class=note> +-Note: The TCP object returned will have the option +-"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>. +-</p> +- +- +- +-<!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++<h2 id="udp">UDP</h2> + + <!-- close +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + +@@ -129,6 +62,40 @@ Garbage-collected objects are automatically closed before + destruction, though. + </p> + ++<!-- getoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class="name" id="getoption"> ++connected:<b>getoption()</b><br> ++unconnected:<b>getoption()</b> ++</p> ++ ++<p class="description"> ++Gets an option value from the UDP object. ++See <a href=#setoption><tt>setoption</tt></a> for ++description of the option names and values. ++</p> ++ ++<p class="parameters"><tt>Option</tt> is a string with the option name. ++<ul> ++<li> '<tt>dontroute</tt>' ++<li> '<tt>broadcast</tt>' ++<li> '<tt>reuseaddr</tt>' ++<li> '<tt>reuseport</tt>' ++<li> '<tt>ip-multicast-loop</tt>' ++<li> '<tt>ipv6-v6only</tt>' ++<li> '<tt>ip-multicast-if</tt>' ++<li> '<tt>ip-multicast-ttl</tt>' ++<li> '<tt>ip-add-membership</tt>' ++<li> '<tt>ip-drop-membership</tt>' ++</ul> ++</p> ++ ++<p class=return> ++The method returns the option <tt>value</tt> in case of ++success, or ++<b><tt>nil</tt></b> followed by an error message otherwise. ++</p> ++ + <!-- getpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class="name" id="getpeername"> +@@ -142,10 +109,10 @@ associated with a connected UDP object. + + + <p class=return> +-Returns a string with the IP address of the peer, the +-port number that peer is using for the connection, +-and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). +-In case of error, the method returns <b><tt>nil</tt></b>. ++Returns a string with the IP address of the peer, the ++port number that peer is using for the connection, ++and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). ++In case of error, the method returns <b><tt>nil</tt></b>. + </p> + + <p class="note"> +@@ -165,9 +132,9 @@ Returns the local address information associated to the object. + + + <p class=return> +-The method returns a string with local IP address, a number with +-the local port, +-and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). ++The method returns a string with local IP address, a number with ++the local port, ++and a string with the family ("<tt>inet</tt>" or "<tt>inet6</tt>"). + In case of error, the method returns <b><tt>nil</tt></b>. + </p> + +@@ -179,6 +146,18 @@ first time (in which case it is bound to an ephemeral port and the + wild-card address). + </p> + ++<!-- gettimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class=name id="gettimeout"> ++connected:<b>settimeout(</b>value<b>)</b><br> ++unconnected:<b>settimeout(</b>value<b>)</b> ++</p> ++ ++<p class=description> ++Returns the current timeout value. ++</p> ++ ++ + <!-- receive +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class="name" id="receive"> +@@ -199,9 +178,12 @@ specifies the maximum size of the datagram to be retrieved. If + there are more than <tt>size</tt> bytes available in the datagram, + the excess bytes are discarded. If there are less then + <tt>size</tt> bytes available in the current datagram, the +-available bytes are returned. If <tt>size</tt> is omitted, the +-maximum datagram size is used (which is currently limited by the +-implementation to 8192 bytes). ++available bytes are returned. ++If <tt>size</tt> is omitted, the ++compile-time constant <a ++href=socket.html#datagramsize><tt>socket._DATAGRAMSIZE</tt></a> is used ++(it defaults to 8192 bytes). Larger sizes will cause a ++temporary buffer to be allocated for the operation. + </p> + + <p class="return"> +@@ -217,46 +199,12 @@ unconnected:<b>receivefrom(</b>[size]<b>)</b> + </p> + + <p class="description"> +-Works exactly as the <a href="#receive"><tt>receive</tt></a> ++Works exactly as the <a href="#receive"><tt>receive</tt></a> + method, except it returns the IP + address and port as extra return values (and is therefore slightly less + efficient). + </p> + +-<!-- getoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class="name" id="getoption"> +-connected:<b>getoption()</b><br> +-unconnected:<b>getoption()</b> +-</p> +- +-<p class="description"> +-Gets an option value from the UDP object. +-See <a href=#setoption><tt>setoption</tt></a> for +-description of the option names and values. +-</p> +- +-<p class="parameters"><tt>Option</tt> is a string with the option name. +-<ul> +-<li> '<tt>dontroute</tt>' +-<li> '<tt>broadcast</tt>' +-<li> '<tt>reuseaddr</tt>' +-<li> '<tt>reuseport</tt>' +-<li> '<tt>ip-multicast-loop</tt>' +-<li> '<tt>ipv6-v6only</tt>' +-<li> '<tt>ip-multicast-if</tt>' +-<li> '<tt>ip-multicast-ttl</tt>' +-<li> '<tt>ip-add-membership</tt>' +-<li> '<tt>ip-drop-membership</tt>' +-</ul> +-</p> +- +-<p class=return> +-The method returns the option <tt>value</tt> in case of +-success, or +-<b><tt>nil</tt></b> followed by an error message otherwise. +-</p> +- + <!-- send ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class="name" id="send"> +@@ -268,7 +216,7 @@ Sends a datagram to the UDP peer of a connected object. + </p> + + <p class="parameters"> +-<tt>Datagram</tt> is a string with the datagram contents. ++<tt>Datagram</tt> is a string with the datagram contents. + The maximum datagram size for UDP is 64K minus IP layer overhead. + However datagrams larger than the link layer packet size will be + fragmented, which may deteriorate performance and/or reliability. +@@ -298,11 +246,11 @@ Sends a datagram to the specified IP address and port number. + + <p class="parameters"> + <tt>Datagram</tt> is a string with the +-datagram contents. ++datagram contents. + The maximum datagram size for UDP is 64K minus IP layer overhead. + However datagrams larger than the link layer packet size will be + fragmented, which may deteriorate performance and/or reliability. +-<tt>Ip</tt> is the IP address of the recipient. ++<tt>Ip</tt> is the IP address of the recipient. + Host names are <em>not</em> allowed for performance reasons. + + <tt>Port</tt> is the port number at the recipient. +@@ -320,6 +268,75 @@ refuses to send a message to the specified address (i.e. no + interface accepts the address). + </p> + ++<!-- setoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class="name" id="setoption"> ++connected:<b>setoption(</b>option [, value]<b>)</b><br> ++unconnected:<b>setoption(</b>option [, value]<b>)</b> ++</p> ++ ++<p class="description"> ++Sets options for the UDP object. Options are ++only needed by low-level or time-critical applications. You should ++only modify an option if you are sure you need it.</p> ++<p class="parameters"><tt>Option</tt> is a string with the option ++name, and <tt>value</tt> depends on the option being set: ++</p> ++ ++<ul> ++<li> '<tt>dontroute</tt>': Indicates that outgoing ++messages should bypass the standard routing facilities. ++Receives a boolean value; ++<li> '<tt>broadcast</tt>': Requests permission to send ++broadcast datagrams on the socket. ++Receives a boolean value; ++<li> '<tt>reuseaddr</tt>': Indicates that the rules used in ++validating addresses supplied in a <tt>bind()</tt> call ++should allow reuse of local addresses. ++Receives a boolean value; ++<li> '<tt>reuseport</tt>': Allows completely duplicate ++bindings by multiple processes if they all set ++'<tt>reuseport</tt>' before binding the port. ++Receives a boolean value; ++<li> '<tt>ip-multicast-loop</tt>': ++Specifies whether or not a copy of an outgoing multicast ++datagram is delivered to the sending host as long as it is a ++member of the multicast group. ++Receives a boolean value; ++<li> '<tt>ipv6-v6only</tt>': ++Specifies whether to restrict <tt>inet6</tt> sockets to ++sending and receiving only IPv6 packets. ++Receive a boolean value; ++<li> '<tt>ip-multicast-if</tt>': ++Sets the interface over which outgoing multicast datagrams ++are sent. ++Receives an IP address; ++<li> '<tt>ip-multicast-ttl</tt>': ++Sets the Time To Live in the IP header for outgoing ++multicast datagrams. ++Receives a number; ++<li> '<tt>ip-add-membership</tt>': ++Joins the multicast group specified. ++Receives a table with fields ++<tt>multiaddr</tt> and <tt>interface</tt>, each containing an ++IP address; ++<li> '<tt>ip-drop-membership</tt>': Leaves the multicast ++group specified. ++Receives a table with fields ++<tt>multiaddr</tt> and <tt>interface</tt>, each containing an ++IP address. ++</ul> ++ ++<p class="return"> ++The method returns 1 in case of success, or ++<b><tt>nil</tt></b> followed by an error message otherwise. ++</p> ++ ++<p class=note> ++Note: The descriptions above come from the man pages. ++</p> ++ ++ + <!-- setpeername +++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class="name" id="setpeername"> +@@ -337,9 +354,9 @@ object or vice versa. + For connected objects, outgoing datagrams + will be sent to the specified peer, and datagrams received from + other peers will be discarded by the OS. Connected UDP objects must +-use the <a href="#send"><tt>send</tt></a> and +-<a href="#receive"><tt>receive</tt></a> methods instead of +-<a href="#sendto"><tt>sendto</tt></a> and ++use the <a href="#send"><tt>send</tt></a> and ++<a href="#receive"><tt>receive</tt></a> methods instead of ++<a href="#sendto"><tt>sendto</tt></a> and + <a href="#receivefrom"><tt>receivefrom</tt></a>. + </p> + +@@ -406,74 +423,6 @@ system or explicitly by <tt>setsockname</tt>, it cannot be + changed. + </p> + +-<!-- setoption +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> +- +-<p class="name" id="setoption"> +-connected:<b>setoption(</b>option [, value]<b>)</b><br> +-unconnected:<b>setoption(</b>option [, value]<b>)</b> +-</p> +- +-<p class="description"> +-Sets options for the UDP object. Options are +-only needed by low-level or time-critical applications. You should +-only modify an option if you are sure you need it.</p> +-<p class="parameters"><tt>Option</tt> is a string with the option +-name, and <tt>value</tt> depends on the option being set: +-</p> +- +-<ul> +-<li> '<tt>dontroute</tt>': Indicates that outgoing +-messages should bypass the standard routing facilities. +-Receives a boolean value; +-<li> '<tt>broadcast</tt>': Requests permission to send +-broadcast datagrams on the socket. +-Receives a boolean value; +-<li> '<tt>reuseaddr</tt>': Indicates that the rules used in +-validating addresses supplied in a <tt>bind()</tt> call +-should allow reuse of local addresses. +-Receives a boolean value; +-<li> '<tt>reuseport</tt>': Allows completely duplicate +-bindings by multiple processes if they all set +-'<tt>reuseport</tt>' before binding the port. +-Receives a boolean value; +-<li> '<tt>ip-multicast-loop</tt>': +-Specifies whether or not a copy of an outgoing multicast +-datagram is delivered to the sending host as long as it is a +-member of the multicast group. +-Receives a boolean value; +-<li> '<tt>ipv6-v6only</tt>': +-Specifies whether to restrict <tt>inet6</tt> sockets to +-sending and receiving only IPv6 packets. +-Receive a boolean value; +-<li> '<tt>ip-multicast-if</tt>': +-Sets the interface over which outgoing multicast datagrams +-are sent. +-Receives an IP address; +-<li> '<tt>ip-multicast-ttl</tt>': +-Sets the Time To Live in the IP header for outgoing +-multicast datagrams. +-Receives a number; +-<li> '<tt>ip-add-membership</tt>': +-Joins the multicast group specified. +-Receives a table with fields +-<tt>multiaddr</tt> and <tt>interface</tt>, each containing an +-IP address; +-<li> '<tt>ip-drop-membership</tt>': Leaves the multicast +-group specified. +-Receives a table with fields +-<tt>multiaddr</tt> and <tt>interface</tt>, each containing an +-IP address. +-</ul> +- +-<p class="return"> +-The method returns 1 in case of success, or +-<b><tt>nil</tt></b> followed by an error message otherwise. +-</p> +- +-<p class=note> +-Note: The descriptions above come from the man pages. +-</p> +- + <!-- settimeout +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <p class="name" id="settimeout"> +@@ -482,14 +431,14 @@ unconnected:<b>settimeout(</b>value<b>)</b> + </p> + + <p class="description"> +-Changes the timeout values for the object. By default, the +-<a href="#receive"><tt>receive</tt></a> and +-<a href="#receivefrom"><tt>receivefrom</tt></a> ++Changes the timeout values for the object. By default, the ++<a href="#receive"><tt>receive</tt></a> and ++<a href="#receivefrom"><tt>receivefrom</tt></a> + operations are blocking. That is, any call to the methods will block + indefinitely, until data arrives. The <tt>settimeout</tt> function defines + a limit on the amount of time the functions can block. When a timeout is + set and the specified amount of time has elapsed, the affected methods +-give up and fail with an error code. ++give up and fail with an error code. + </p> + + <p class="parameters"> +@@ -514,6 +463,114 @@ all other method names already contained verbs making their + imperative nature obvious. + </p> + ++<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class="name" id="socket.udp"> ++socket.<b>udp()</b> ++</p> ++ ++<p class="description"> ++Creates and returns an unconnected UDP object. ++Unconnected objects support the ++<a href="#sendto"><tt>sendto</tt></a>, ++<a href="#receive"><tt>receive</tt></a>, ++<a href="#receivefrom"><tt>receivefrom</tt></a>, ++<a href="#getoption"><tt>getoption</tt></a>, ++<a href="#getsockname"><tt>getsockname</tt></a>, ++<a href="#setoption"><tt>setoption</tt></a>, ++<a href="#settimeout"><tt>settimeout</tt></a>, ++<a href="#setpeername"><tt>setpeername</tt></a>, ++<a href="#setsockname"><tt>setsockname</tt></a>, and ++<a href="#close"><tt>close</tt></a>. ++The <a href="#setpeername"><tt>setpeername</tt></a> ++is used to connect the object. ++</p> ++ ++<p class="return"> ++In case of success, a new unconnected UDP object ++returned. In case of error, <b><tt>nil</tt></b> is returned, followed by ++an error message. ++</p> ++ ++<p class=note> ++Note: The choice between IPv4 and IPv6 happens during a call to ++<a href=#sendto><tt>sendto</tt></a>, <a ++href=#setpeername><tt>setpeername</tt></a>, or <a ++href=#setsockname><tt>sockname</tt></a>, depending on the address ++family obtained from the resolver. ++</p> ++ ++<p class=note> ++Note: Before the choice between IPv4 and IPv6 happens, ++the internal socket object is invalid and therefore <a ++href=#setoption><tt>setoption</tt></a> will fail. ++</p> ++ ++<!-- socket.udp4 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class="name" id="socket.udp"> ++socket.<b>udp4()</b> ++</p> ++ ++<p class="description"> ++Creates and returns an unconnected IPv4 UDP object. ++Unconnected objects support the ++<a href="#sendto"><tt>sendto</tt></a>, ++<a href="#receive"><tt>receive</tt></a>, ++<a href="#receivefrom"><tt>receivefrom</tt></a>, ++<a href="#getoption"><tt>getoption</tt></a>, ++<a href="#getsockname"><tt>getsockname</tt></a>, ++<a href="#setoption"><tt>setoption</tt></a>, ++<a href="#settimeout"><tt>settimeout</tt></a>, ++<a href="#setpeername"><tt>setpeername</tt></a>, ++<a href="#setsockname"><tt>setsockname</tt></a>, and ++<a href="#close"><tt>close</tt></a>. ++The <a href="#setpeername"><tt>setpeername</tt></a> ++is used to connect the object. ++</p> ++ ++<p class="return"> ++In case of success, a new unconnected UDP object ++returned. In case of error, <b><tt>nil</tt></b> is returned, followed by ++an error message. ++</p> ++ ++<!-- socket.udp ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> ++ ++<p class="name" id="socket.udp6"> ++socket.<b>udp6()</b> ++</p> ++ ++<p class="description"> ++Creates and returns an unconnected IPv6 UDP object. ++Unconnected objects support the ++<a href="#sendto"><tt>sendto</tt></a>, ++<a href="#receive"><tt>receive</tt></a>, ++<a href="#receivefrom"><tt>receivefrom</tt></a>, ++<a href="#getoption"><tt>getoption</tt></a>, ++<a href="#getsockname"><tt>getsockname</tt></a>, ++<a href="#setoption"><tt>setoption</tt></a>, ++<a href="#settimeout"><tt>settimeout</tt></a>, ++<a href="#setpeername"><tt>setpeername</tt></a>, ++<a href="#setsockname"><tt>setsockname</tt></a>, and ++<a href="#close"><tt>close</tt></a>. ++The <a href="#setpeername"><tt>setpeername</tt></a> ++is used to connect the object. ++</p> ++ ++<p class="return"> ++In case of success, a new unconnected UDP object ++returned. In case of error, <b><tt>nil</tt></b> is returned, followed by ++an error message. ++</p> ++ ++<p class=note> ++Note: The TCP object returned will have the option ++"<tt>ipv6-v6only</tt>" set to <tt><b>true</b></tt>. ++</p> ++ ++ ++ + <!-- footer ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ --> + + <div class=footer> +@@ -524,7 +581,7 @@ imperative nature obvious. + <a href="index.html#download">download</a> · + <a href="installation.html">installation</a> · + <a href="introduction.html">introduction</a> · +-<a href="reference.html">reference</a> ++<a href="reference.html">reference</a> + </p> + <p> + <small> +diff --git a/etc/dispatch.lua b/etc/dispatch.lua +index cab7f59..2485415 100644 +--- a/etc/dispatch.lua ++++ b/etc/dispatch.lua +@@ -5,6 +5,7 @@ + ----------------------------------------------------------------------------- + local base = _G + local table = require("table") ++local string = require("string") + local socket = require("socket") + local coroutine = require("coroutine") + module("dispatch") +@@ -43,26 +44,32 @@ end + ----------------------------------------------------------------------------- + -- Mega hack. Don't try to do this at home. + ----------------------------------------------------------------------------- +--- we can't yield across calls to protect, so we rewrite it with coxpcall ++-- we can't yield across calls to protect on Lua 5.1, so we rewrite it with ++-- coroutines + -- make sure you don't require any module that uses socket.protect before + -- loading our hack +-function socket.protect(f) +- return function(...) +- local co = coroutine.create(f) +- while true do +- local results = {coroutine.resume(co, ...)} +- local status = table.remove(results, 1) +- if not status then +- if base.type(results[1]) == 'table' then +- return nil, results[1][1] +- else base.error(results[1]) end +- end +- if coroutine.status(co) == "suspended" then +- arg = {coroutine.yield(base.unpack(results))} ++if string.sub(base._VERSION, -3) == "5.1" then ++ local function _protect(co, status, ...) ++ if not status then ++ local msg = ... ++ if base.type(msg) == 'table' then ++ return nil, msg[1] + else +- return base.unpack(results) ++ base.error(msg, 0) + end + end ++ if coroutine.status(co) == "suspended" then ++ return _protect(co, coroutine.resume(co, coroutine.yield(...))) ++ else ++ return ... ++ end ++ end ++ ++ function socket.protect(f) ++ return function(...) ++ local co = coroutine.create(f) ++ return _protect(co, coroutine.resume(co, ...)) ++ end + end + end + +diff --git a/linux.cmd b/linux.cmd +index bd59adc..6c6636b 100644 +--- a/linux.cmd ++++ b/linux.cmd +@@ -1 +1 @@ +-make PLAT=linux DEBUG=DEBUG LUAINC_linux_base=/home/diego/build/linux/include LUAPREFIX_linux=/home/diego/build/linux ++make PLAT=linux DEBUG=DEBUG LUAINC_linux_base=/home/diego/build/ubuntu/include LUAPREFIX_linux=/home/diego/build/ubuntu +diff --git a/luasocket-scm-0.rockspec b/luasocket-scm-0.rockspec +index f86567b..352a497 100644 +--- a/luasocket-scm-0.rockspec ++++ b/luasocket-scm-0.rockspec +@@ -50,13 +50,12 @@ local function make_plat(plat) + } + local modules = { + ["socket.core"] = { +- sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", +- "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c" }, ++ sources = { "src/luasocket.c", "src/timeout.c", "src/buffer.c", "src/io.c", "src/auxiliar.c", "src/options.c", "src/inet.c", "src/except.c", "src/select.c", "src/tcp.c", "src/udp.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, +- ["mime.core"] = { +- sources = { "src/mime.c" }, ++ ["mime.core"] = { ++ sources = { "src/mime.c", "src/compat.c" }, + defines = defines[plat], + incdir = "/src" + }, +@@ -73,14 +72,12 @@ local function make_plat(plat) + if plat == "unix" or plat == "macosx" then + modules["socket.core"].sources[#modules["socket.core"].sources+1] = "src/usocket.c" + modules["socket.unix"] = { +- sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", +- "src/usocket.c", "src/unix.c" }, ++ sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/unix.c" }, + defines = defines[plat], + incdir = "/src" + } + modules["socket.serial"] = { +- sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", +- "src/io.c", "src/usocket.c", "src/serial.c" }, ++ sources = { "src/buffer.c", "src/auxiliar.c", "src/options.c", "src/timeout.c", "src/io.c", "src/usocket.c", "src/serial.c" }, + defines = defines[plat], + incdir = "/src" + } +diff --git a/makefile b/makefile +index 04cd894..cc15b4e 100644 +--- a/makefile ++++ b/makefile +@@ -5,12 +5,12 @@ + # Targets: + # install install system independent support + # install-unix also install unix-only support +-# install-both install for both lua5.1 and lua5.2 +-# install-both-unix also install unix-only ++# install-both install for lua51 lua52 lua53 ++# install-both-unix also install unix-only + # print print the build settings + + PLAT?= linux +-PLATS= macosx linux win32 mingw ++PLATS= macosx linux win32 mingw freebsd solaris + + all: $(PLAT) + +@@ -24,20 +24,26 @@ test: + lua test/hello.lua + + install-both: +- $(MAKE) clean ++ $(MAKE) clean + @cd src; $(MAKE) $(PLAT) LUAV=5.1 + @cd src; $(MAKE) install LUAV=5.1 +- $(MAKE) clean ++ $(MAKE) clean + @cd src; $(MAKE) $(PLAT) LUAV=5.2 + @cd src; $(MAKE) install LUAV=5.2 ++ $(MAKE) clean ++ @cd src; $(MAKE) $(PLAT) LUAV=5.3 ++ @cd src; $(MAKE) install LUAV=5.3 + + install-both-unix: +- $(MAKE) clean ++ $(MAKE) clean + @cd src; $(MAKE) $(PLAT) LUAV=5.1 + @cd src; $(MAKE) install-unix LUAV=5.1 +- $(MAKE) clean ++ $(MAKE) clean + @cd src; $(MAKE) $(PLAT) LUAV=5.2 + @cd src; $(MAKE) install-unix LUAV=5.2 ++ $(MAKE) clean ++ @cd src; $(MAKE) $(PLAT) LUAV=5.3 ++ @cd src; $(MAKE) install-unix LUAV=5.3 + + .PHONY: test + +diff --git a/src/auxiliar.c b/src/auxiliar.c +index de625e9..18fa8e4 100644 +--- a/src/auxiliar.c ++++ b/src/auxiliar.c +@@ -26,7 +26,7 @@ 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 */ ++ 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 */ +@@ -84,7 +84,7 @@ int auxiliar_checkboolean(lua_State *L, int objidx) { + } + + /*-------------------------------------------------------------------------*\ +-* Return userdata pointer if object belongs to a given class, abort with ++* 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) { +@@ -98,7 +98,7 @@ void *auxiliar_checkclass(lua_State *L, const char *classname, int objidx) { + } + + /*-------------------------------------------------------------------------*\ +-* Return userdata pointer if object belongs to a given group, abort with ++* 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) { +@@ -121,7 +121,7 @@ void auxiliar_setclass(lua_State *L, const char *classname, int objidx) { + } + + /*-------------------------------------------------------------------------*\ +-* Get a userdata pointer if object belongs to a given group. Return NULL ++* 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) { +@@ -139,7 +139,7 @@ void *auxiliar_getgroupudata(lua_State *L, const char *groupname, int objidx) { + } + + /*-------------------------------------------------------------------------*\ +-* Get a userdata pointer if object belongs to a given class. Return NULL ++* 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) { +@@ -151,7 +151,7 @@ void *auxiliar_getclassudata(lua_State *L, const char *classname, int objidx) { + * Used to be part of lauxlib in Lua 5.1, was dropped from 5.2. + \*-------------------------------------------------------------------------*/ + int auxiliar_typeerror (lua_State *L, int narg, const char *tname) { +- const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, ++ const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, + luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); + } +diff --git a/src/auxiliar.h b/src/auxiliar.h +index ea99013..65511d4 100644 +--- a/src/auxiliar.h ++++ b/src/auxiliar.h +@@ -4,12 +4,12 @@ + * 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 ++* 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 ++* - 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 +@@ -26,11 +26,12 @@ + * "class" with the class name. + * + * The mapping from class name to the corresponding metatable and the +-* reverse mapping are done using lauxlib. ++* reverse mapping are done using lauxlib. + \*=========================================================================*/ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + int auxiliar_open(lua_State *L); + void auxiliar_newclass(lua_State *L, const char *classname, luaL_Reg *func); +diff --git a/src/buffer.c b/src/buffer.c +index 4ef4e8e..fff1634 100644 +--- a/src/buffer.c ++++ b/src/buffer.c +@@ -4,6 +4,7 @@ + \*=========================================================================*/ + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "buffer.h" + +@@ -37,7 +38,7 @@ int buffer_open(lua_State *L) { + } + + /*-------------------------------------------------------------------------*\ +-* Initializes C structure ++* Initializes C structure + \*-------------------------------------------------------------------------*/ + void buffer_init(p_buffer buf, p_io io, p_timeout tm) { + buf->first = buf->last = 0; +@@ -61,8 +62,8 @@ int buffer_meth_getstats(lua_State *L, p_buffer buf) { + * object:setstats() interface + \*-------------------------------------------------------------------------*/ + int buffer_meth_setstats(lua_State *L, p_buffer buf) { +- buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); +- buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); ++ buf->received = (long) luaL_optnumber(L, 2, (lua_Number) buf->received); ++ buf->sent = (long) luaL_optnumber(L, 3, (lua_Number) buf->sent); + if (lua_isnumber(L, 4)) buf->birthday = timeout_gettime() - lua_tonumber(L, 4); + lua_pushnumber(L, 1); + return 1; +@@ -78,9 +79,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { + const char *data = luaL_checklstring(L, 2, &size); + long start = (long) luaL_optnumber(L, 3, 1); + long end = (long) luaL_optnumber(L, 4, -1); +-#ifdef LUASOCKET_DEBUG +- p_timeout tm = timeout_markstart(buf->tm); +-#endif ++ timeout_markstart(buf->tm); + if (start < 0) start = (long) (size+start+1); + if (end < 0) end = (long) (size+end+1); + if (start < 1) start = (long) 1; +@@ -89,7 +88,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { + /* check if there was an error */ + if (err != IO_DONE) { + lua_pushnil(L); +- lua_pushstring(L, buf->io->error(buf->io->ctx, err)); ++ lua_pushstring(L, buf->io->error(buf->io->ctx, err)); + lua_pushnumber(L, (lua_Number) (sent+start-1)); + } else { + lua_pushnumber(L, (lua_Number) (sent+start-1)); +@@ -98,7 +97,7 @@ int buffer_meth_send(lua_State *L, p_buffer buf) { + } + #ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ +- lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); ++ lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); + #endif + return lua_gettop(L) - top; + } +@@ -111,10 +110,8 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { + luaL_Buffer b; + size_t size; + const char *part = luaL_optlstring(L, 3, "", &size); +-#ifdef LUASOCKET_DEBUG +- p_timeout tm = timeout_markstart(buf->tm); +-#endif +- /* initialize buffer with optional extra prefix ++ timeout_markstart(buf->tm); ++ /* initialize buffer with optional extra prefix + * (useful for concatenating previous partial results) */ + luaL_buffinit(L, &b); + luaL_addlstring(&b, part, size); +@@ -122,12 +119,12 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { + 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 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 ++ /* get a fixed number of bytes (minus what was already partially + * received) */ + } else { +- double n = lua_tonumber(L, 2); ++ double n = lua_tonumber(L, 2); + size_t wanted = (size_t) n; + luaL_argcheck(L, n >= 0, 2, "invalid receive pattern"); + if (size == 0 || wanted > size) +@@ -138,8 +135,8 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { + /* 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_pushstring(L, buf->io->error(buf->io->ctx, err)); ++ lua_pushvalue(L, -2); + lua_pushnil(L); + lua_replace(L, -4); + } else { +@@ -149,7 +146,7 @@ int buffer_meth_receive(lua_State *L, p_buffer buf) { + } + #ifdef LUASOCKET_DEBUG + /* push time elapsed during operation as the last return value */ +- lua_pushnumber(L, timeout_gettime() - timeout_getstart(tm)); ++ lua_pushnumber(L, timeout_gettime() - timeout_getstart(buf->tm)); + #endif + return lua_gettop(L) - top; + } +@@ -222,7 +219,7 @@ static int recvall(p_buffer buf, luaL_Buffer *b) { + } + + /*-------------------------------------------------------------------------*\ +-* Reads a line terminated by a CR LF pair or just by a LF. The CR and LF ++* 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) { +@@ -252,7 +249,7 @@ static int recvline(p_buffer buf, luaL_Buffer *b) { + static void buffer_skip(p_buffer buf, size_t count) { + buf->received += count; + buf->first += count; +- if (buffer_isempty(buf)) ++ if (buffer_isempty(buf)) + buf->first = buf->last = 0; + } + +diff --git a/src/compat.c b/src/compat.c +new file mode 100644 +index 0000000..c2d99cb +--- /dev/null ++++ b/src/compat.c +@@ -0,0 +1,19 @@ ++#include "compat.h" ++ ++#if LUA_VERSION_NUM==501 ++/* ++** Adapted from Lua 5.2 ++*/ ++void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { ++ luaL_checkstack(L, nup+1, "too many upvalues"); ++ for (; l->name != NULL; l++) { /* fill the table with given functions */ ++ 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); /* closure with those upvalues */ ++ lua_settable(L, -(nup + 3)); ++ } ++ lua_pop(L, nup); /* remove upvalues */ ++} ++#endif +diff --git a/src/compat.h b/src/compat.h +new file mode 100644 +index 0000000..7bf8010 +--- /dev/null ++++ b/src/compat.h +@@ -0,0 +1,11 @@ ++#ifndef COMPAT_H ++#define COMPAT_H ++ ++#include "lua.h" ++#include "lauxlib.h" ++ ++#if LUA_VERSION_NUM==501 ++void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); ++#endif ++ ++#endif +diff --git a/src/except.c b/src/except.c +index 002e701..60b5005 100644 +--- a/src/except.c ++++ b/src/except.c +@@ -6,9 +6,19 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "except.h" + ++#if LUA_VERSION_NUM < 502 ++#define lua_pcallk(L, na, nr, err, ctx, cont) \ ++ (((void)ctx),((void)cont),lua_pcall(L, na, nr, err)) ++#endif ++ ++#if LUA_VERSION_NUM < 503 ++typedef int lua_KContext; ++#endif ++ + /*=========================================================================*\ + * Internal function prototypes. + \*=========================================================================*/ +@@ -29,18 +39,17 @@ static luaL_Reg func[] = { + * 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); ++ lua_createtable(L, 1, 0); ++ lua_pushvalue(L, -2); ++ lua_rawseti(L, -2, 1); ++ lua_pushvalue(L, lua_upvalueindex(1)); ++ lua_setmetatable(L, -2); + } + + static int finalize(lua_State *L) { + if (!lua_toboolean(L, 1)) { +- lua_pushvalue(L, lua_upvalueindex(1)); +- lua_pcall(L, 0, 0, 0); ++ lua_pushvalue(L, lua_upvalueindex(2)); ++ lua_call(L, 0, 0); + lua_settop(L, 2); + wrap(L); + lua_error(L); +@@ -48,15 +57,17 @@ static int finalize(lua_State *L) { + } else return lua_gettop(L); + } + +-static int do_nothing(lua_State *L) { ++static int do_nothing(lua_State *L) { + (void) L; +- return 0; ++ 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); ++ lua_pushvalue(L, lua_upvalueindex(1)); ++ lua_insert(L, -2); ++ lua_pushcclosure(L, finalize, 2); + return 1; + } + +@@ -64,27 +75,49 @@ static int global_newtry(lua_State *L) { + * 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; ++ if (lua_istable(L, -1) && lua_getmetatable(L, -1)) { ++ int r = lua_rawequal(L, -1, lua_upvalueindex(1)); ++ lua_pop(L, 1); ++ if (r) { ++ lua_pushnil(L); ++ lua_rawgeti(L, -2, 1); ++ return 1; ++ } ++ } ++ 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) { ++static int protected_finish(lua_State *L, int status, lua_KContext ctx) { ++ (void)ctx; ++ if (status != 0 && status != LUA_YIELD) { + if (unwrap(L)) return 2; +- else lua_error(L); +- return 0; ++ else return lua_error(L); + } else return lua_gettop(L); + } + ++#if LUA_VERSION_NUM == 502 ++static int protected_cont(lua_State *L) { ++ int ctx = 0; ++ int status = lua_getctx(L, &ctx); ++ return protected_finish(L, status, ctx); ++} ++#else ++#define protected_cont protected_finish ++#endif ++ ++static int protected_(lua_State *L) { ++ int status; ++ lua_pushvalue(L, lua_upvalueindex(2)); ++ lua_insert(L, 1); ++ status = lua_pcallk(L, lua_gettop(L) - 1, LUA_MULTRET, 0, 0, protected_cont); ++ return protected_finish(L, status, 0); ++} ++ + static int global_protect(lua_State *L) { +- lua_pushcclosure(L, protected_, 1); ++ lua_settop(L, 1); ++ lua_pushvalue(L, lua_upvalueindex(1)); ++ lua_insert(L, 1); ++ lua_pushcclosure(L, protected_, 2); + return 1; + } + +@@ -92,10 +125,9 @@ static int global_protect(lua_State *L) { + * Init module + \*-------------------------------------------------------------------------*/ + int except_open(lua_State *L) { +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) +- luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif ++ lua_newtable(L); /* metatable for wrapped exceptions */ ++ lua_pushboolean(L, 0); ++ lua_setfield(L, -2, "__metatable"); ++ luaL_setfuncs(L, func, 1); + return 0; + } +diff --git a/src/except.h b/src/except.h +index 1e7a245..2497c05 100644 +--- a/src/except.h ++++ b/src/except.h +@@ -9,21 +9,26 @@ + * 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) ++* The main idea is that functions should return nil as their first return ++* values when they find 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. ++* checks the first value, and, if it's falsy, wraps the second value in a ++* table with metatable and calls "error" on it. Otherwise, it returns all ++* values it received. Basically, it works like the Lua "assert" function, ++* but it creates errors targeted specifically at "protect". + * +-* 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. ++* The "newtry" function is a factory for "try" functions that call a ++* finalizer in protected mode before calling "error". + * +-* With these two function, it's easy to write functions that throw +-* exceptions on error, but that don't interrupt the user script. ++* The "protect" function returns a new function that behaves exactly like ++* the function it receives, but the new function catches exceptions thrown ++* by "try" functions and returns nil followed by the error message instead. ++* ++* With these three functions, it's easy to write functions that throw ++* exceptions on error, but that don't interrupt the user script. + \*=========================================================================*/ + + #include "lua.h" +diff --git a/src/ftp.lua b/src/ftp.lua +index ea1145b..e0c3cae 100644 +--- a/src/ftp.lua ++++ b/src/ftp.lua +@@ -268,11 +268,20 @@ _M.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) ++ local f = _M.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 ++ if type(cmdt.command) == "table" then ++ local argument = cmdt.argument or {} ++ local check = cmdt.check or {} ++ for i,cmd in ipairs(cmdt.command) do ++ f.try(f.tp:command(cmd, argument[i])) ++ if check[i] then f.try(f.tp:check(check[i])) end ++ end ++ else ++ f.try(f.tp:command(cmdt.command, cmdt.argument)) ++ if cmdt.check then f.try(f.tp:check(cmdt.check)) end ++ end + f:quit() + return f:close() + end) +@@ -282,4 +291,4 @@ _M.get = socket.protect(function(gett) + else return tget(gett) end + end) + +-return _M +\ No newline at end of file ++return _M +diff --git a/src/http.lua b/src/http.lua +index ac4b2d6..d6bcc91 100644 +--- a/src/http.lua ++++ b/src/http.lua +@@ -22,12 +22,15 @@ local _M = socket.http + -- Program constants + ----------------------------------------------------------------------------- + -- connection timeout in seconds +-TIMEOUT = 60 +--- default port for document retrieval +-_M.PORT = 80 ++_M.TIMEOUT = 60 + -- user agent field sent in request + _M.USERAGENT = socket._VERSION + ++-- supported schemes ++local SCHEMES = { ["http"] = true } ++-- default port for document retrieval ++local PORT = 80 ++ + ----------------------------------------------------------------------------- + -- Reads MIME headers from a connection, unfolding where needed + ----------------------------------------------------------------------------- +@@ -114,7 +117,7 @@ function _M.open(host, port, create) + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(_M.TIMEOUT)) +- h.try(c:connect(host, port or _M.PORT)) ++ h.try(c:connect(host, port or PORT)) + -- here everything worked + return h + end +@@ -186,7 +189,7 @@ end + 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 ++ if not reqt.proxy and not _M.PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, +@@ -198,7 +201,7 @@ local function adjusturi(reqt) + end + + local function adjustproxy(reqt) +- local proxy = reqt.proxy or PROXY ++ local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 +@@ -209,17 +212,27 @@ end + + local function adjustheaders(reqt) + -- default headers ++ local host = string.gsub(reqt.authority, "^.-@", "") + local lower = { + ["user-agent"] = _M.USERAGENT, +- ["host"] = reqt.host, ++ ["host"] = host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then +- lower["authorization"] = ++ lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. reqt.password)) + end ++ -- if we have proxy authentication information, pass it along ++ local proxy = reqt.proxy or _M.PROXY ++ if proxy then ++ proxy = url.parse(proxy) ++ if proxy.user and proxy.password then ++ lower["proxy-authorization"] = ++ "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password)) ++ end ++ end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v +@@ -230,7 +243,7 @@ end + -- default url parts + local default = { + host = "", +- port = _M.PORT, ++ port = PORT, + path ="/", + scheme = "http" + } +@@ -240,22 +253,27 @@ local function adjustrequest(reqt) + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end +- if nreqt.port == "" then nreqt.port = 80 end +- socket.try(nreqt.host and nreqt.host ~= "", +- "invalid host '" .. base.tostring(nreqt.host) .. "'") ++ if nreqt.port == "" then nreqt.port = PORT end ++ if not (nreqt.host and nreqt.host ~= "") then ++ socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") ++ end + -- 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) ++ -- ajust host and port if there is a proxy ++ nreqt.host, nreqt.port = adjustproxy(nreqt) + return nreqt + end + + local function shouldredirect(reqt, code, headers) +- return headers.location and +- string.gsub(headers.location, "%s", "") ~= "" and +- (reqt.redirect ~= false) and ++ local location = headers.location ++ if not location then return false end ++ location = string.gsub(location, "%s", "") ++ if location == "" then return false end ++ local scheme = string.match(location, "^([%w][%w%+%-%.]*)%:") ++ if scheme and not SCHEMES[scheme] then return false end ++ return (reqt.redirect ~= false) and + (code == 301 or code == 302 or code == 303 or code == 307) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and (not reqt.nredirects or reqt.nredirects < 5) +@@ -279,10 +297,10 @@ local trequest, tredirect + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, +- proxy = reqt.proxy, ++ proxy = reqt.proxy, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create +- } ++ } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location +@@ -299,7 +317,7 @@ end + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then +- h:sendbody(nreqt.headers, nreqt.source, nreqt.step) ++ h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done +@@ -309,13 +327,13 @@ end + end + local headers + -- ignore any 100-continue messages +- while code == 100 do ++ while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server +- -- we can't redirect if we already used the source, so we report the error ++ -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) +@@ -351,4 +369,4 @@ _M.request = socket.protect(function(reqt, body) + else return trequest(reqt) end + end) + +-return _M +\ No newline at end of file ++return _M +diff --git a/src/inet.c b/src/inet.c +index 1a411f6..f4c8404 100644 +--- a/src/inet.c ++++ b/src/inet.c +@@ -8,6 +8,7 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "inet.h" + +@@ -41,11 +42,7 @@ int inet_open(lua_State *L) + { + lua_pushstring(L, "dns"); + lua_newtable(L); +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif + lua_settable(L, -3); + return 0; + } +@@ -97,7 +94,7 @@ static int inet_global_getnameinfo(lua_State *L) { + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; +- hints.ai_family = PF_UNSPEC; ++ hints.ai_family = AF_UNSPEC; + + ret = getaddrinfo(host, serv, &hints, &resolved); + if (ret != 0) { +@@ -108,8 +105,8 @@ static int inet_global_getnameinfo(lua_State *L) { + + lua_newtable(L); + for (i = 1, iter = resolved; iter; i++, iter = iter->ai_next) { +- getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, +- hbuf, host? (socklen_t) sizeof(hbuf): 0, ++ getnameinfo(iter->ai_addr, (socklen_t) iter->ai_addrlen, ++ hbuf, host? (socklen_t) sizeof(hbuf): 0, + sbuf, serv? (socklen_t) sizeof(sbuf): 0, 0); + if (host) { + lua_pushnumber(L, i); +@@ -149,7 +146,7 @@ static int inet_global_toip(lua_State *L) + int inet_optfamily(lua_State* L, int narg, const char* def) + { + static const char* optname[] = { "unspec", "inet", "inet6", NULL }; +- static int optvalue[] = { PF_UNSPEC, PF_INET, PF_INET6, 0 }; ++ static int optvalue[] = { AF_UNSPEC, AF_INET, AF_INET6, 0 }; + + return optvalue[luaL_checkoption(L, narg, def, optname)]; + } +@@ -170,7 +167,7 @@ static int inet_global_getaddrinfo(lua_State *L) + int i = 1, ret = 0; + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; +- hints.ai_family = PF_UNSPEC; ++ hints.ai_family = AF_UNSPEC; + ret = getaddrinfo(hostname, NULL, &hints, &resolved); + if (ret != 0) { + lua_pushnil(L); +@@ -180,9 +177,10 @@ static int inet_global_getaddrinfo(lua_State *L) + lua_newtable(L); + for (iterator = resolved; iterator; iterator = iterator->ai_next) { + char hbuf[NI_MAXHOST]; +- ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, ++ ret = getnameinfo(iterator->ai_addr, (socklen_t) iterator->ai_addrlen, + hbuf, (socklen_t) sizeof(hbuf), NULL, 0, NI_NUMERICHOST); + if (ret){ ++ freeaddrinfo(resolved); + lua_pushnil(L); + lua_pushstring(L, socket_gaistrerror(ret)); + return 2; +@@ -200,6 +198,16 @@ static int inet_global_getaddrinfo(lua_State *L) + lua_pushliteral(L, "inet6"); + lua_settable(L, -3); + break; ++ case AF_UNSPEC: ++ lua_pushliteral(L, "family"); ++ lua_pushliteral(L, "unspec"); ++ lua_settable(L, -3); ++ break; ++ default: ++ lua_pushliteral(L, "family"); ++ lua_pushliteral(L, "unknown"); ++ lua_settable(L, -3); ++ break; + } + lua_pushliteral(L, "addr"); + lua_pushstring(L, hbuf); +@@ -256,12 +264,11 @@ int inet_meth_getpeername(lua_State *L, p_socket ps, int family) + } + lua_pushstring(L, name); + lua_pushinteger(L, (int) strtol(port, (char **) NULL, 10)); +- if (family == PF_INET) { +- lua_pushliteral(L, "inet"); +- } else if (family == PF_INET6) { +- lua_pushliteral(L, "inet6"); +- } else { +- lua_pushliteral(L, "uknown family"); ++ switch (family) { ++ case AF_INET: lua_pushliteral(L, "inet"); break; ++ case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; ++ default: lua_pushliteral(L, "unknown"); break; + } + return 3; + } +@@ -281,7 +288,7 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + lua_pushstring(L, socket_strerror(errno)); + return 2; + } +- err=getnameinfo((struct sockaddr *)&peer, peer_len, ++ err=getnameinfo((struct sockaddr *)&peer, peer_len, + name, INET6_ADDRSTRLEN, port, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); +@@ -290,12 +297,11 @@ int inet_meth_getsockname(lua_State *L, p_socket ps, int family) + } + lua_pushstring(L, name); + lua_pushstring(L, port); +- if (family == PF_INET) { +- lua_pushliteral(L, "inet"); +- } else if (family == PF_INET6) { +- lua_pushliteral(L, "inet6"); +- } else { +- lua_pushliteral(L, "uknown family"); ++ switch (family) { ++ case AF_INET: lua_pushliteral(L, "inet"); break; ++ case AF_INET6: lua_pushliteral(L, "inet6"); break; ++ case AF_UNSPEC: lua_pushliteral(L, "unspec"); break; ++ default: lua_pushliteral(L, "unknown"); break; + } + return 3; + } +@@ -346,8 +352,13 @@ static void inet_pushresolved(lua_State *L, struct hostent *hp) + /*-------------------------------------------------------------------------*\ + * Tries to create a new inet socket + \*-------------------------------------------------------------------------*/ +-const char *inet_trycreate(p_socket ps, int family, int type) { +- return socket_strerror(socket_create(ps, family, type, 0)); ++const char *inet_trycreate(p_socket ps, int family, int type, int protocol) { ++ const char *err = socket_strerror(socket_create(ps, family, type, protocol)); ++ if (err == NULL && family == AF_INET6) { ++ int yes = 1; ++ setsockopt(*ps, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&yes, sizeof(yes)); ++ } ++ return err; + } + + /*-------------------------------------------------------------------------*\ +@@ -356,21 +367,21 @@ const char *inet_trycreate(p_socket ps, int family, int type) { + const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm) + { + switch (family) { +- case PF_INET: { ++ case AF_INET: { + struct sockaddr_in sin; + memset((char *) &sin, 0, sizeof(sin)); + sin.sin_family = AF_UNSPEC; + sin.sin_addr.s_addr = INADDR_ANY; +- return socket_strerror(socket_connect(ps, (SA *) &sin, ++ return socket_strerror(socket_connect(ps, (SA *) &sin, + sizeof(sin), tm)); + } +- case PF_INET6: { ++ case AF_INET6: { + struct sockaddr_in6 sin6; +- struct in6_addr addrany = IN6ADDR_ANY_INIT; ++ struct in6_addr addrany = IN6ADDR_ANY_INIT; + memset((char *) &sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_UNSPEC; + sin6.sin6_addr = addrany; +- return socket_strerror(socket_connect(ps, (SA *) &sin6, ++ return socket_strerror(socket_connect(ps, (SA *) &sin6, + sizeof(sin6), tm)); + } + } +@@ -385,6 +396,7 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, + { + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; ++ int current_family = *family; + /* try resolving */ + err = socket_gaistrerror(getaddrinfo(address, serv, + connecthints, &resolved)); +@@ -399,23 +411,23 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, + * that shows up while iterating. if there was a + * bind, all families will be the same and we will + * not enter this branch. */ +- if (*family != iterator->ai_family) { ++ if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { + socket_destroy(ps); +- err = socket_strerror(socket_create(ps, iterator->ai_family, +- iterator->ai_socktype, iterator->ai_protocol)); +- if (err != NULL) { +- freeaddrinfo(resolved); +- return err; +- } +- *family = iterator->ai_family; +- /* all sockets initially non-blocking */ ++ err = inet_trycreate(ps, iterator->ai_family, ++ iterator->ai_socktype, iterator->ai_protocol); ++ if (err) continue; ++ current_family = iterator->ai_family; ++ /* set non-blocking before connect */ + socket_setnonblocking(ps); + } + /* try connecting to remote address */ +- err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, ++ err = socket_strerror(socket_connect(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen, tm)); +- /* if success, break out of loop */ +- if (err == NULL) break; ++ /* if success or timeout is zero, break out of loop */ ++ if (err == NULL || timeout_iszero(tm)) { ++ *family = current_family; ++ break; ++ } + } + freeaddrinfo(resolved); + /* here, if err is set, we failed */ +@@ -425,29 +437,27 @@ const char *inet_tryconnect(p_socket ps, int *family, const char *address, + /*-------------------------------------------------------------------------*\ + * Tries to accept a socket + \*-------------------------------------------------------------------------*/ +-const char *inet_tryaccept(p_socket server, int family, p_socket client, +- p_timeout tm) +-{ ++const char *inet_tryaccept(p_socket server, int family, p_socket client, ++ p_timeout tm) { + socklen_t len; + t_sockaddr_storage addr; +- if (family == PF_INET6) { +- len = sizeof(struct sockaddr_in6); +- } else { +- len = sizeof(struct sockaddr_in); +- } +- return socket_strerror(socket_accept(server, client, (SA *) &addr, ++ switch (family) { ++ case AF_INET6: len = sizeof(struct sockaddr_in6); break; ++ case AF_INET: len = sizeof(struct sockaddr_in); break; ++ default: len = sizeof(addr); break; ++ } ++ return socket_strerror(socket_accept(server, client, (SA *) &addr, + &len, tm)); + } + + /*-------------------------------------------------------------------------*\ + * Tries to bind socket to (address, port) + \*-------------------------------------------------------------------------*/ +-const char *inet_trybind(p_socket ps, const char *address, const char *serv, +- struct addrinfo *bindhints) +-{ ++const char *inet_trybind(p_socket ps, int *family, const char *address, ++ const char *serv, struct addrinfo *bindhints) { + struct addrinfo *iterator = NULL, *resolved = NULL; + const char *err = NULL; +- t_socket sock = *ps; ++ int current_family = *family; + /* translate luasocket special values to C */ + if (strcmp(address, "*") == 0) address = NULL; + if (!serv) serv = "0"; +@@ -459,35 +469,32 @@ const char *inet_trybind(p_socket ps, const char *address, const char *serv, + } + /* iterate over resolved addresses until one is good */ + for (iterator = resolved; iterator; iterator = iterator->ai_next) { +- if(sock == SOCKET_INVALID) { +- err = socket_strerror(socket_create(&sock, iterator->ai_family, +- iterator->ai_socktype, iterator->ai_protocol)); +- if(err) +- continue; ++ if (current_family != iterator->ai_family || *ps == SOCKET_INVALID) { ++ socket_destroy(ps); ++ err = inet_trycreate(ps, iterator->ai_family, ++ iterator->ai_socktype, iterator->ai_protocol); ++ if (err) continue; ++ current_family = iterator->ai_family; + } + /* try binding to local address */ +- err = socket_strerror(socket_bind(&sock, +- (SA *) iterator->ai_addr, ++ err = socket_strerror(socket_bind(ps, (SA *) iterator->ai_addr, + (socklen_t) iterator->ai_addrlen)); +- + /* keep trying unless bind succeeded */ +- if (err) { +- if(sock != *ps) +- socket_destroy(&sock); +- } else { +- /* remember what we connected to, particularly the family */ +- *bindhints = *iterator; ++ if (err == NULL) { ++ *family = current_family; ++ /* set to non-blocking after bind */ ++ socket_setnonblocking(ps); + break; + } + } + /* cleanup and return error */ + freeaddrinfo(resolved); +- *ps = sock; ++ /* here, if err is set, we failed */ + return err; + } + + /*-------------------------------------------------------------------------*\ +-* Some systems do not provide these so that we provide our own. ++* Some systems do not provide these so that we provide our own. + \*-------------------------------------------------------------------------*/ + #ifdef LUASOCKET_INET_ATON + int inet_aton(const char *cp, struct in_addr *inp) +@@ -512,7 +519,7 @@ int inet_aton(const char *cp, struct in_addr *inp) + #endif + + #ifdef LUASOCKET_INET_PTON +-int inet_pton(int af, const char *src, void *dst) ++int inet_pton(int af, const char *src, void *dst) + { + struct addrinfo hints, *res; + int ret = 1; +@@ -529,7 +536,7 @@ int inet_pton(int af, const char *src, void *dst) + } else { + ret = -1; + } +- freeaddrinfo(res); ++ freeaddrinfo(res); + return ret; + } + +diff --git a/src/inet.h b/src/inet.h +index 1f1a96a..feb3541 100644 +--- a/src/inet.h ++++ b/src/inet.h +@@ -1,12 +1,12 @@ +-#ifndef INET_H +-#define INET_H ++#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. ++* resolver. + * + * The function inet_aton is provided for the platforms where it is not + * available. The module also implements the interface of the internet +@@ -24,11 +24,11 @@ + + int inet_open(lua_State *L); + +-const char *inet_trycreate(p_socket ps, int family, int type); ++const char *inet_trycreate(p_socket ps, int family, int type, int protocol); + const char *inet_tryconnect(p_socket ps, int *family, const char *address, + const char *serv, p_timeout tm, struct addrinfo *connecthints); +-const char *inet_trybind(p_socket ps, const char *address, const char *serv, +- struct addrinfo *bindhints); ++const char *inet_trybind(p_socket ps, int *family, const char *address, ++ const char *serv, struct addrinfo *bindhints); + const char *inet_trydisconnect(p_socket ps, int family, p_timeout tm); + const char *inet_tryaccept(p_socket server, int family, p_socket client, p_timeout tm); + +diff --git a/src/io.c b/src/io.c +index 35f46f7..a4230ce 100644 +--- a/src/io.c ++++ b/src/io.c +@@ -25,6 +25,6 @@ const char *io_strerror(int err) { + case IO_DONE: return NULL; + case IO_CLOSED: return "closed"; + case IO_TIMEOUT: return "timeout"; +- default: return "unknown error"; ++ default: return "unknown error"; + } + } +diff --git a/src/io.h b/src/io.h +index 76a3e58..8cca08a 100644 +--- a/src/io.h ++++ b/src/io.h +@@ -22,7 +22,7 @@ enum { + IO_DONE = 0, /* operation completed successfully */ + IO_TIMEOUT = -1, /* operation timed out */ + IO_CLOSED = -2, /* the connection has been closed */ +- IO_UNKNOWN = -3 ++ IO_UNKNOWN = -3 + }; + + /* interface to error message function */ +diff --git a/src/ltn12.lua b/src/ltn12.lua +index 5b10f56..575c5a7 100644 +--- a/src/ltn12.lua ++++ b/src/ltn12.lua +@@ -9,6 +9,7 @@ + ----------------------------------------------------------------------------- + local string = require("string") + local table = require("table") ++local unpack = unpack or table.unpack + local base = _G + local _M = {} + if module then -- heuristic for exporting a global package table +@@ -21,6 +22,9 @@ _M.source = source + _M.sink = sink + _M.pump = pump + ++local unpack = unpack or table.unpack ++local select = base.select ++ + -- 2048 seems to be better in windows... + _M.BLOCKSIZE = 2048 + _M._VERSION = "LTN12 1.0.3" +@@ -42,7 +46,7 @@ end + -- (thanks to Wim Couwenberg) + function filter.chain(...) + local arg = {...} +- local n = select('#',...) ++ local n = base.select('#',...) + local top, index = 1, 1 + local retry = "" + return function(chunk) +@@ -139,7 +143,9 @@ function source.rewind(src) + end + end + +-function source.chain(src, f) ++-- chains a source with one or several filter(s) ++function source.chain(src, f, ...) ++ if ... then f=filter.chain(f, ...) end + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" +@@ -254,8 +260,13 @@ function sink.error(err) + end + end + +--- chains a sink with a filter +-function sink.chain(f, snk) ++-- chains a sink with one or several filter(s) ++function sink.chain(f, snk, ...) ++ if ... then ++ local args = { f, snk, ... } ++ snk = table.remove(args, #args) ++ f = filter.chain(unpack(args)) ++ end + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then +diff --git a/src/luasocket.c b/src/luasocket.c +index e6ee747..7d9c802 100644 +--- a/src/luasocket.c ++++ b/src/luasocket.c +@@ -17,7 +17,7 @@ + \*=========================================================================*/ + #include "lua.h" + #include "lauxlib.h" +- ++#include "compat.h" + + /*=========================================================================*\ + * LuaSocket includes +@@ -64,7 +64,7 @@ static luaL_Reg func[] = { + * Skip a few arguments + \*-------------------------------------------------------------------------*/ + static int global_skip(lua_State *L) { +- int amount = luaL_checkint(L, 1); ++ int amount = luaL_checkinteger(L, 1); + int ret = lua_gettop(L) - amount - 1; + return ret >= 0 ? ret : 0; + } +@@ -78,26 +78,14 @@ static int global_unload(lua_State *L) { + return 0; + } + +-#if LUA_VERSION_NUM > 501 +-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); +-} +-#endif +- + /*-------------------------------------------------------------------------*\ + * Setup basic stuff. + \*-------------------------------------------------------------------------*/ + static int base_open(lua_State *L) { + if (socket_open()) { + /* export functions (and leave namespace table on top of stack) */ +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + lua_newtable(L); + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, "socket", func, 0); +-#endif + #ifdef LUASOCKET_DEBUG + lua_pushstring(L, "_DEBUG"); + lua_pushboolean(L, 1); +diff --git a/src/makefile b/src/makefile +index c24e61b..adf687f 100644 +--- a/src/makefile ++++ b/src/makefile +@@ -20,15 +20,17 @@ PLAT?=linux + # lua version to build against + LUAV?=5.1 + ++# MYCFLAGS: to be set by user if needed ++MYCFLAGS= ++ ++# MYLDFLAGS: to be set by user if needed ++MYLDFLAGS= ++ + # DEBUG: NODEBUG DEBUG + # debug mode causes luasocket to collect and returns timing information useful + # for testing and debugging luasocket itself + DEBUG?=NODEBUG + +-# COMPAT: COMPAT NOCOMPAT +-# when compiling for 5.2, use LUA_COMPAT_MODULE +-COMPAT?=NOCOMPAT +- + # where lua headers are found for macosx builds + # LUAINC_macosx: + # /opt/local/include +@@ -40,7 +42,6 @@ LUAPREFIX_macosx?=/opt/local + CDIR_macosx?=lib/lua/$(LUAV) + LDIR_macosx?=share/lua/$(LUAV) + +- + # LUAINC_linux: + # /usr/include/lua$(LUAV) + # /usr/local/include +@@ -52,8 +53,17 @@ LUAPREFIX_linux?=/usr/local + CDIR_linux?=lib/lua/$(LUAV) + LDIR_linux?=share/lua/$(LUAV) + ++# LUAINC_freebsd: ++# /usr/local/include/lua$(LUAV) ++# where lua headers are found for freebsd builds ++LUAINC_freebsd_base?=/usr/local/include/ ++LUAINC_freebsd?=$(LUAINC_freebsd_base)/lua$(LUAV) ++LUAPREFIX_freebsd?=/usr/local/ ++CDIR_freebsd?=lib/lua/$(LUAV) ++LDIR_freebsd?=share/lua/$(LUAV) ++ + # where lua headers are found for mingw builds +-# LUAINC_mingw: ++# LUAINC_mingw: + # /opt/local/include + LUAINC_mingw_base?=/usr/include + LUAINC_mingw?=$(LUAINC_mingw_base)/lua/$(LUAV) +@@ -67,13 +77,21 @@ LDIR_mingw?=lua/$(LUAV)/lua + # LUAINC_win32: + # LUALIB_win32: + # where lua headers and libraries are found for win32 builds +-LUAINC_win32_base?= +-LUAINC_win32?=$(LUAINC_win32_base)/lua/$(LUAV) +-PLATFORM_win32?=Release + LUAPREFIX_win32?= +-CDIR_win32?=lua/$(LUAV)/$(PLATFORM_win32) +-LDIR_win32?=lua/$(LUAV)/$(PLATFORM_win32)/lua +-LUALIB_win32?=$(LUAPREFIX_win32)/lua/$(LUAV)/$(PLATFORM_win32) ++LUAINC_win32?=$(LUAPREFIX_win32)/include/lua/$(LUAV) ++PLATFORM_win32?=Release ++CDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32) ++LDIR_win32?=bin/lua/$(LUAV)/$(PLATFORM_win32)/lua ++LUALIB_win32?=$(LUAPREFIX_win32)/lib/lua/$(LUAV)/$(PLATFORM_win32) ++LUALIBNAME_win32?=lua$(subst .,,$(LUAV)).lib ++ ++ ++# LUAINC_solaris: ++LUAINC_solaris_base?=/usr/include ++LUAINC_solaris?=$(LUAINC_solaris_base)/lua/$(LUAV) ++LUAPREFIX_solaris?=/usr/local ++CDIR_solaris?=lib/lua/$(LUAV) ++LDIR_solaris?=share/lua/$(LUAV) + + # prefix: /usr/local /usr /opt/local /sw + # the top of the default install tree +@@ -121,7 +139,7 @@ print: + #------ + # Supported platforms + # +-PLATS= macosx linux win32 mingw ++PLATS= macosx linux win32 mingw solaris + + #------ + # Compiler and linker settings +@@ -129,11 +147,11 @@ PLATS= macosx linux win32 mingw + SO_macosx=so + O_macosx=o + CC_macosx=gcc +-DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN -DLUA_$(COMPAT)_MODULE \ ++DEF_macosx= -DLUASOCKET_$(DEBUG) -DUNIX_HAS_SUN_LEN \ + -DLUASOCKET_API='__attribute__((visibility("default")))' \ + -DUNIX_API='__attribute__((visibility("default")))' \ + -DMIME_API='__attribute__((visibility("default")))' +-CFLAGS_macosx= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \ ++CFLAGS_macosx= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \ + -fvisibility=hidden + LDFLAGS_macosx= -bundle -undefined dynamic_lookup -o + LD_macosx= export MACOSX_DEPLOYMENT_TARGET="10.3"; gcc +@@ -145,11 +163,11 @@ SOCKET_macosx=usocket.o + SO_linux=so + O_linux=o + CC_linux=gcc +-DEF_linux=-DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \ ++DEF_linux=-DLUASOCKET_$(DEBUG) \ + -DLUASOCKET_API='__attribute__((visibility("default")))' \ + -DUNIX_API='__attribute__((visibility("default")))' \ + -DMIME_API='__attribute__((visibility("default")))' +-CFLAGS_linux= -I$(LUAINC) $(DEF) -pedantic -Wall -Wshadow -Wextra \ ++CFLAGS_linux= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden + LDFLAGS_linux=-O -shared -fpic -o + LD_linux=gcc +@@ -157,14 +175,46 @@ SOCKET_linux=usocket.o + + #------ + # Compiler and linker settings ++# for FreeBSD ++SO_freebsd=so ++O_freebsd=o ++CC_freebsd=gcc ++DEF_freebsd=-DLUASOCKET_$(DEBUG) \ ++ -DLUASOCKET_API='__attribute__((visibility("default")))' \ ++ -DUNIX_API='__attribute__((visibility("default")))' \ ++ -DMIME_API='__attribute__((visibility("default")))' ++CFLAGS_freebsd= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ ++ -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden ++LDFLAGS_freebsd=-O -shared -fpic -o ++LD_freebsd=gcc ++SOCKET_freebsd=usocket.o ++ ++#------ ++# Compiler and linker settings ++# for Solaris ++SO_solaris=so ++O_solaris=o ++CC_solaris=gcc ++DEF_solaris=-DLUASOCKET_$(DEBUG) \ ++ -DLUASOCKET_API='__attribute__((visibility("default")))' \ ++ -DUNIX_API='__attribute__((visibility("default")))' \ ++ -DMIME_API='__attribute__((visibility("default")))' ++CFLAGS_solaris=-I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ ++ -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden ++LDFLAGS_solaris=-lnsl -lsocket -lresolv -O -shared -fpic -o ++LD_solaris=gcc ++SOCKET_solaris=usocket.o ++ ++#------ ++# Compiler and linker settings + # for MingW + SO_mingw=dll + O_mingw=o + CC_mingw=gcc +-DEF_mingw= -DLUASOCKET_INET_PTON -DLUASOCKET_$(DEBUG) -DLUA_$(COMPAT)_MODULE \ ++DEF_mingw= -DLUASOCKET_INET_PTON -DLUASOCKET_$(DEBUG) \ + -DWINVER=0x0501 -DLUASOCKET_API='__declspec(dllexport)' \ + -DMIME_API='__declspec(dllexport)' +-CFLAGS_mingw= -I$(LUAINC) $(DEF) -pedantic -Wall -O2 -fno-common \ ++CFLAGS_mingw= -I$(LUAINC) $(DEF) -Wall -O2 -fno-common \ + -fvisibility=hidden + LDFLAGS_mingw= $(LUALIB) -shared -Wl,-s -lws2_32 -o + LD_mingw=gcc +@@ -179,8 +229,7 @@ O_win32=obj + CC_win32=cl + DEF_win32= //D "WIN32" //D "NDEBUG" //D "_WINDOWS" //D "_USRDLL" \ + //D "LUASOCKET_API=__declspec(dllexport)" //D "_CRT_SECURE_NO_WARNINGS" \ +- //D "_WINDLL" //D "LUA_$(COMPAT)_MODULE" \ +- //D "MIME_API=__declspec(dllexport)" \ ++ //D "_WINDLL" //D "MIME_API=__declspec(dllexport)" \ + //D "LUASOCKET_$(DEBUG)" + CFLAGS_win32=//I "$(LUAINC)" $(DEF) //O2 //Ot //MD //W3 //nologo + LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ +@@ -188,7 +237,7 @@ LDFLAGS_win32= //nologo //link //NOLOGO //DLL //INCREMENTAL:NO \ + //MANIFESTUAC:"level='asInvoker' uiAccess='false'" \ + //SUBSYSTEM:WINDOWS //OPT:REF //OPT:ICF //DYNAMICBASE:NO \ + //MACHINE:X86 /LIBPATH:"$(shell cmd //c echo $(LUALIB))" \ +- lua$(subst .,,$(LUAV)).lib ws2_32.lib //OUT: ++ $(LUALIBNAME_win32) ws2_32.lib //OUT: + LD_win32=cl + SOCKET_win32=wsocket.obj + +@@ -204,8 +253,8 @@ SO=$(SO_$(PLAT)) + O=$(O_$(PLAT)) + SOCKET_V=3.0-rc1 + MIME_V=1.0.3 +-SOCKET_SO=socket.$(SO).$(SOCKET_V) +-MIME_SO=mime.$(SO).$(MIME_V) ++SOCKET_SO=socket-$(SOCKET_V).$(SO) ++MIME_SO=mime-$(MIME_V).$(SO) + UNIX_SO=unix.$(SO) + SERIAL_SO=serial.$(SO) + SOCKET=$(SOCKET_$(PLAT)) +@@ -215,8 +264,8 @@ SOCKET=$(SOCKET_$(PLAT)) + # + CC=$(CC_$(PLAT)) + DEF=$(DEF_$(PLAT)) +-CFLAGS=$(CFLAGS_$(PLAT)) +-LDFLAGS=$(LDFLAGS_$(PLAT)) ++CFLAGS=$(MYCFLAGS) $(CFLAGS_$(PLAT)) ++LDFLAGS=$(MYLDFLAGS) $(LDFLAGS_$(PLAT)) + LD=$(LD_$(PLAT)) + LUAINC= $(LUAINC_$(PLAT)) + LUALIB= $(LUALIB_$(PLAT)) +@@ -230,6 +279,7 @@ SOCKET_OBJS= \ + buffer.$(O) \ + io.$(O) \ + auxiliar.$(O) \ ++ compat.$(O) \ + options.$(O) \ + inet.$(O) \ + $(SOCKET) \ +@@ -242,7 +292,8 @@ SOCKET_OBJS= \ + # Modules belonging mime-core + # + MIME_OBJS= \ +- mime.$(O) ++ mime.$(O) \ ++ compat.$(O) + + #------ + # Modules belonging unix (local domain sockets) +@@ -259,7 +310,7 @@ UNIX_OBJS=\ + #------ + # Modules belonging to serial (device streams) + # +-SERIAL_OBJS:=\ ++SERIAL_OBJS=\ + buffer.$(O) \ + auxiliar.$(O) \ + options.$(O) \ +@@ -289,6 +340,10 @@ TO_TOP_LDIR= \ + # + default: $(PLAT) + ++ ++freebsd: ++ $(MAKE) all-unix PLAT=freebsd ++ + macosx: + $(MAKE) all-unix PLAT=macosx + +@@ -300,6 +355,9 @@ linux: + + mingw: + $(MAKE) all PLAT=mingw ++ ++solaris: ++ $(MAKE) all-unix PLAT=solaris + + none: + @echo "Please run" +@@ -349,6 +407,7 @@ clean: + #------ + # List of dependencies + # ++compat.$(O): compat.c compat.h + auxiliar.$(O): auxiliar.c auxiliar.h + buffer.$(O): buffer.c buffer.h io.h timeout.h + except.$(O): except.c except.h +diff --git a/src/mbox.lua b/src/mbox.lua +index 7724ae2..ed9e781 100644 +--- a/src/mbox.lua ++++ b/src/mbox.lua +@@ -61,7 +61,7 @@ function _M.parse_from(from) + end + + function _M.split_mbox(mbox_s) +- mbox = {} ++ local mbox = {} + mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" + local nj, i, j = 1, 1, 1 + while 1 do +diff --git a/src/mime.c b/src/mime.c +index dd37dcf..ed44104 100644 +--- a/src/mime.c ++++ b/src/mime.c +@@ -6,10 +6,7 @@ + + #include "lua.h" + #include "lauxlib.h" +- +-#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501) +-#include "compat-5.1.h" +-#endif ++#include "compat.h" + + #include "mime.h" + +@@ -41,7 +38,7 @@ static size_t b64decode(UC c, UC *input, size_t size, luaL_Buffer *buffer); + static void qpsetup(UC *class, UC *unbase); + static void qpquote(UC c, luaL_Buffer *buffer); + static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer); +-static size_t qpencode(UC c, UC *input, size_t size, ++static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer); + static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer); + +@@ -81,12 +78,8 @@ static UC b64unbase[256]; + \*-------------------------------------------------------------------------*/ + MIME_API int luaopen_mime_core(lua_State *L) + { +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + lua_newtable(L); + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, "mime", func, 0); +-#endif + /* make version string available to scripts */ + lua_pushstring(L, "_VERSION"); + lua_pushstring(L, MIME_VERSION); +@@ -103,15 +96,15 @@ MIME_API int luaopen_mime_core(lua_State *L) + /*-------------------------------------------------------------------------*\ + * Incrementaly breaks a string into lines. The string can have CRLF breaks. + * A, n = wrp(l, B, length) +-* A is a copy of B, broken into lines of at most 'length' bytes. +-* 'l' is how many bytes are left for the first line of B. +-* 'n' is the number of bytes left in the last line of A. ++* A is a copy of B, broken into lines of at most 'length' bytes. ++* 'l' is how many bytes are left for the first line of B. ++* 'n' is the number of bytes left in the last line of A. + \*-------------------------------------------------------------------------*/ + static int mime_global_wrp(lua_State *L) + { + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); +- const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); ++ const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; +@@ -123,7 +116,7 @@ static int mime_global_wrp(lua_State *L) + else lua_pushnil(L); + lua_pushnumber(L, length); + return 2; +- } ++ } + luaL_buffinit(L, &buffer); + while (input < last) { + switch (*input) { +@@ -150,9 +143,9 @@ static int mime_global_wrp(lua_State *L) + } + + /*-------------------------------------------------------------------------*\ +-* Fill base64 decode map. ++* Fill base64 decode map. + \*-------------------------------------------------------------------------*/ +-static void b64setup(UC *unbase) ++static void b64setup(UC *unbase) + { + int i; + for (i = 0; i <= 255; i++) unbase[i] = (UC) 255; +@@ -161,11 +154,11 @@ static void b64setup(UC *unbase) + } + + /*-------------------------------------------------------------------------*\ +-* Acumulates bytes in input buffer until 3 bytes are available. ++* Acumulates bytes in input buffer until 3 bytes are available. + * Translate the 3 bytes into Base64 form and append to buffer. + * Returns new number of bytes in buffer. + \*-------------------------------------------------------------------------*/ +-static size_t b64encode(UC c, UC *input, size_t size, ++static size_t b64encode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) + { + input[size++] = c; +@@ -174,7 +167,7 @@ static size_t b64encode(UC c, UC *input, size_t size, + unsigned long value = 0; + value += input[0]; value <<= 8; + value += input[1]; value <<= 8; +- value += input[2]; ++ value += input[2]; + code[3] = b64base[value & 0x3f]; value >>= 6; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; +@@ -186,11 +179,11 @@ static size_t b64encode(UC c, UC *input, size_t size, + } + + /*-------------------------------------------------------------------------*\ +-* Encodes the Base64 last 1 or 2 bytes and adds padding '=' ++* Encodes the Base64 last 1 or 2 bytes and adds padding '=' + * Result, if any, is appended to buffer. + * Returns 0. + \*-------------------------------------------------------------------------*/ +-static size_t b64pad(const UC *input, size_t size, ++static size_t b64pad(const UC *input, size_t size, + luaL_Buffer *buffer) + { + unsigned long value = 0; +@@ -203,7 +196,7 @@ static size_t b64pad(const UC *input, size_t size, + luaL_addlstring(buffer, (char *) code, 4); + break; + case 2: +- value = input[0]; value <<= 8; ++ value = input[0]; value <<= 8; + value |= input[1]; value <<= 2; + code[2] = b64base[value & 0x3f]; value >>= 6; + code[1] = b64base[value & 0x3f]; value >>= 6; +@@ -217,11 +210,11 @@ static size_t b64pad(const UC *input, size_t size, + } + + /*-------------------------------------------------------------------------*\ +-* Acumulates bytes in input buffer until 4 bytes are available. ++* Acumulates bytes in input buffer until 4 bytes are available. + * Translate the 4 bytes from Base64 form and append to buffer. + * Returns new number of bytes in buffer. + \*-------------------------------------------------------------------------*/ +-static size_t b64decode(UC c, UC *input, size_t size, ++static size_t b64decode(UC c, UC *input, size_t size, + luaL_Buffer *buffer) + { + /* ignore invalid characters */ +@@ -239,7 +232,7 @@ static size_t b64decode(UC c, UC *input, size_t size, + decoded[1] = (UC) (value & 0xff); value >>= 8; + decoded[0] = (UC) value; + /* take care of paddding */ +- valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; ++ valid = (input[2] == '=') ? 1 : (input[3] == '=') ? 2 : 3; + luaL_addlstring(buffer, (char *) decoded, valid); + return 0; + /* need more data */ +@@ -251,7 +244,7 @@ static size_t b64decode(UC c, UC *input, size_t size, + * A, B = b64(C, D) + * A is the encoded version of the largest prefix of C .. D that is + * divisible by 3. B has the remaining bytes of C .. D, *without* encoding. +-* The easiest thing would be to concatenate the two strings and ++* The easiest thing would be to concatenate the two strings and + * encode the result, but we can't afford that or Lua would dupplicate + * every chunk we received. + \*-------------------------------------------------------------------------*/ +@@ -259,7 +252,7 @@ static int mime_global_b64(lua_State *L) + { + UC atom[3]; + size_t isize = 0, asize = 0; +- const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); ++ const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ +@@ -272,9 +265,9 @@ static int mime_global_b64(lua_State *L) + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); +- while (input < last) ++ while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); +- input = (UC *) luaL_optlstring(L, 2, NULL, &isize); ++ input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + size_t osize = 0; +@@ -288,7 +281,7 @@ static int mime_global_b64(lua_State *L) + } + /* otherwise process the second part */ + last = input + isize; +- while (input < last) ++ while (input < last) + asize = b64encode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); +@@ -305,7 +298,7 @@ static int mime_global_unb64(lua_State *L) + { + UC atom[4]; + size_t isize = 0, asize = 0; +- const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); ++ const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ +@@ -318,9 +311,9 @@ static int mime_global_unb64(lua_State *L) + lua_settop(L, 2); + /* process first part of the input */ + luaL_buffinit(L, &buffer); +- while (input < last) ++ while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); +- input = (UC *) luaL_optlstring(L, 2, NULL, &isize); ++ input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second is nil, we are done */ + if (!input) { + size_t osize = 0; +@@ -333,7 +326,7 @@ static int mime_global_unb64(lua_State *L) + } + /* otherwise, process the rest of the input */ + last = input + isize; +- while (input < last) ++ while (input < last) + asize = b64decode(*input++, atom, asize, &buffer); + luaL_pushresult(&buffer); + lua_pushlstring(L, (char *) atom, asize); +@@ -349,7 +342,7 @@ static int mime_global_unb64(lua_State *L) + * 9 and 32 can be plain, unless in the end of a line, where must be =XX + * encoded lines must be no longer than 76 not counting CRLF + * soft line-break are =CRLF +-* To encode one byte, we need to see the next two. ++* To encode one byte, we need to see the next two. + * Worst case is when we see a space, and wonder if a CRLF is comming + \*-------------------------------------------------------------------------*/ + /*-------------------------------------------------------------------------*\ +@@ -362,7 +355,7 @@ static void qpsetup(UC *cl, UC *unbase) + for (i = 0; i < 256; i++) cl[i] = QP_QUOTED; + for (i = 33; i <= 60; i++) cl[i] = QP_PLAIN; + for (i = 62; i <= 126; i++) cl[i] = QP_PLAIN; +- cl['\t'] = QP_IF_LAST; ++ cl['\t'] = QP_IF_LAST; + cl[' '] = QP_IF_LAST; + cl['\r'] = QP_CR; + for (i = 0; i < 256; i++) unbase[i] = 255; +@@ -388,9 +381,9 @@ static void qpquote(UC c, luaL_Buffer *buffer) + + /*-------------------------------------------------------------------------*\ + * Accumulate characters until we are sure about how to deal with them. +-* Once we are sure, output to the buffer, in the correct form. ++* Once we are sure, output to the buffer, in the correct form. + \*-------------------------------------------------------------------------*/ +-static size_t qpencode(UC c, UC *input, size_t size, ++static size_t qpencode(UC c, UC *input, size_t size, + const char *marker, luaL_Buffer *buffer) + { + input[size++] = c; +@@ -431,7 +424,7 @@ static size_t qpencode(UC c, UC *input, size_t size, + } + + /*-------------------------------------------------------------------------*\ +-* Deal with the final characters ++* Deal with the final characters + \*-------------------------------------------------------------------------*/ + static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) + { +@@ -448,8 +441,8 @@ static size_t qppad(UC *input, size_t size, luaL_Buffer *buffer) + * Incrementally converts a string to quoted-printable + * A, B = qp(C, D, marker) + * Marker is the text to be used to replace CRLF sequences found in A. +-* A is the encoded version of the largest prefix of C .. D that +-* can be encoded without doubts. ++* A is the encoded version of the largest prefix of C .. D that ++* can be encoded without doubts. + * B has the remaining bytes of C .. D, *without* encoding. + \*-------------------------------------------------------------------------*/ + static int mime_global_qp(lua_State *L) +@@ -457,7 +450,7 @@ static int mime_global_qp(lua_State *L) + + size_t asize = 0, isize = 0; + UC atom[3]; +- const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); ++ const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + const char *marker = luaL_optstring(L, 3, CRLF); + luaL_Buffer buffer; +@@ -473,7 +466,7 @@ static int mime_global_qp(lua_State *L) + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpencode(*input++, atom, asize, marker, &buffer); +- input = (UC *) luaL_optlstring(L, 2, NULL, &isize); ++ input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + asize = qppad(atom, asize, &buffer); +@@ -493,7 +486,7 @@ static int mime_global_qp(lua_State *L) + + /*-------------------------------------------------------------------------*\ + * Accumulate characters until we are sure about how to deal with them. +-* Once we are sure, output the to the buffer, in the correct form. ++* Once we are sure, output the to the buffer, in the correct form. + \*-------------------------------------------------------------------------*/ + static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + int d; +@@ -501,8 +494,8 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + /* deal with all characters we can deal */ + switch (input[0]) { + /* if we have an escape character */ +- case '=': +- if (size < 3) return size; ++ case '=': ++ if (size < 3) return size; + /* eliminate soft line break */ + if (input[1] == '\r' && input[2] == '\n') return 0; + /* decode quoted representation */ +@@ -512,7 +505,7 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + else luaL_addchar(buffer, (char) ((c << 4) + d)); + return 0; + case '\r': +- if (size < 2) return size; ++ if (size < 2) return size; + if (input[1] == '\n') luaL_addlstring(buffer, (char *)input, 2); + return 0; + default: +@@ -525,15 +518,15 @@ static size_t qpdecode(UC c, UC *input, size_t size, luaL_Buffer *buffer) { + /*-------------------------------------------------------------------------*\ + * Incrementally decodes a string in quoted-printable + * A, B = qp(C, D) +-* A is the decoded version of the largest prefix of C .. D that +-* can be decoded without doubts. ++* A is the decoded version of the largest prefix of C .. D that ++* can be decoded without doubts. + * B has the remaining bytes of C .. D, *without* decoding. + \*-------------------------------------------------------------------------*/ + static int mime_global_unqp(lua_State *L) + { + size_t asize = 0, isize = 0; + UC atom[3]; +- const UC *input = (UC *) luaL_optlstring(L, 1, NULL, &isize); ++ const UC *input = (const UC *) luaL_optlstring(L, 1, NULL, &isize); + const UC *last = input + isize; + luaL_Buffer buffer; + /* end-of-input blackhole */ +@@ -548,14 +541,14 @@ static int mime_global_unqp(lua_State *L) + luaL_buffinit(L, &buffer); + while (input < last) + asize = qpdecode(*input++, atom, asize, &buffer); +- input = (UC *) luaL_optlstring(L, 2, NULL, &isize); ++ input = (const UC *) luaL_optlstring(L, 2, NULL, &isize); + /* if second part is nil, we are done */ + if (!input) { + luaL_pushresult(&buffer); + if (!(*lua_tostring(L, -1))) lua_pushnil(L); + lua_pushnil(L); + return 2; +- } ++ } + /* otherwise process rest of input */ + last = input + isize; + while (input < last) +@@ -568,9 +561,9 @@ static int mime_global_unqp(lua_State *L) + /*-------------------------------------------------------------------------*\ + * Incrementally breaks a quoted-printed string into lines + * A, n = qpwrp(l, B, length) +-* A is a copy of B, broken into lines of at most 'length' bytes. +-* 'l' is how many bytes are left for the first line of B. +-* 'n' is the number of bytes left in the last line of A. ++* A is a copy of B, broken into lines of at most 'length' bytes. ++* 'l' is how many bytes are left for the first line of B. ++* 'n' is the number of bytes left in the last line of A. + * There are two complications: lines can't be broken in the middle + * of an encoded =XX, and there might be line breaks already + \*-------------------------------------------------------------------------*/ +@@ -578,7 +571,7 @@ static int mime_global_qpwrp(lua_State *L) + { + size_t size = 0; + int left = (int) luaL_checknumber(L, 1); +- const UC *input = (UC *) luaL_optlstring(L, 2, NULL, &size); ++ const UC *input = (const UC *) luaL_optlstring(L, 2, NULL, &size); + const UC *last = input + size; + int length = (int) luaL_optnumber(L, 3, 76); + luaL_Buffer buffer; +@@ -603,11 +596,11 @@ static int mime_global_qpwrp(lua_State *L) + if (left <= 3) { + left = length; + luaL_addstring(&buffer, EQCRLF); +- } ++ } + luaL_addchar(&buffer, *input); + left--; + break; +- default: ++ default: + if (left <= 1) { + left = length; + luaL_addstring(&buffer, EQCRLF); +@@ -635,7 +628,7 @@ static int mime_global_qpwrp(lua_State *L) + * last is the previous character + \*-------------------------------------------------------------------------*/ + #define eolcandidate(c) (c == '\r' || c == '\n') +-static int eolprocess(int c, int last, const char *marker, ++static int eolprocess(int c, int last, const char *marker, + luaL_Buffer *buffer) + { + if (eolcandidate(c)) { +@@ -653,15 +646,15 @@ static int eolprocess(int c, int last, const char *marker, + } + + /*-------------------------------------------------------------------------*\ +-* Converts a string to uniform EOL convention. ++* Converts a string to uniform EOL convention. + * A, n = eol(o, B, marker) + * A is the converted version of the largest prefix of B that can be +-* converted unambiguously. 'o' is the context returned by the previous ++* converted unambiguously. 'o' is the context returned by the previous + * call. 'n' is the new context. + \*-------------------------------------------------------------------------*/ + static int mime_global_eol(lua_State *L) + { +- int ctx = luaL_checkint(L, 1); ++ int ctx = luaL_checkinteger(L, 1); + size_t isize = 0; + const char *input = luaL_optlstring(L, 2, NULL, &isize); + const char *last = input + isize; +@@ -683,18 +676,18 @@ static int mime_global_eol(lua_State *L) + } + + /*-------------------------------------------------------------------------*\ +-* Takes one byte and stuff it if needed. ++* Takes one byte and stuff it if needed. + \*-------------------------------------------------------------------------*/ + static size_t dot(int c, size_t state, luaL_Buffer *buffer) + { + luaL_addchar(buffer, (char) c); + switch (c) { +- case '\r': ++ case '\r': + return 1; +- case '\n': +- return (state == 1)? 2: 0; +- case '.': +- if (state == 2) ++ case '\n': ++ return (state == 1)? 2: 0; ++ case '.': ++ if (state == 2) + luaL_addchar(buffer, '.'); + default: + return 0; +@@ -719,7 +712,7 @@ static int mime_global_dot(lua_State *L) + } + /* process all input */ + luaL_buffinit(L, &buffer); +- while (input < last) ++ while (input < last) + state = dot(*input++, state, &buffer); + luaL_pushresult(&buffer); + lua_pushnumber(L, (lua_Number) state); +diff --git a/src/options.c b/src/options.c +index 8ac2a14..20f4c28 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -1,8 +1,8 @@ + /*=========================================================================*\ +-* Common option interface ++* Common option interface + * LuaSocket toolkit + \*=========================================================================*/ +-#include <string.h> ++#include <string.h> + + #include "lauxlib.h" + +@@ -20,9 +20,9 @@ static int opt_setboolean(lua_State *L, p_socket ps, int level, int name); + static int opt_getboolean(lua_State *L, p_socket ps, int level, int name); + static int opt_setint(lua_State *L, p_socket ps, int level, int name); + static int opt_getint(lua_State *L, p_socket ps, int level, int name); +-static int opt_set(lua_State *L, p_socket ps, int level, int name, ++static int opt_set(lua_State *L, p_socket ps, int level, int name, + void *val, int len); +-static int opt_get(lua_State *L, p_socket ps, int level, int name, ++static int opt_get(lua_State *L, p_socket ps, int level, int name, + void *val, int* len); + + /*=========================================================================*\ +@@ -60,29 +60,29 @@ int opt_meth_getoption(lua_State *L, p_opt opt, p_socket ps) + /* enables reuse of local address */ + int opt_set_reuseaddr(lua_State *L, p_socket ps) + { +- return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); ++ return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); + } + + int opt_get_reuseaddr(lua_State *L, p_socket ps) + { +- return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); ++ return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEADDR); + } + + /* enables reuse of local port */ + int opt_set_reuseport(lua_State *L, p_socket ps) + { +- return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); ++ return opt_setboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); + } + + int opt_get_reuseport(lua_State *L, p_socket ps) + { +- return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); ++ return opt_getboolean(L, ps, SOL_SOCKET, SO_REUSEPORT); + } + + /* disables the Naggle algorithm */ + int opt_set_tcp_nodelay(lua_State *L, p_socket ps) + { +- return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); ++ return opt_setboolean(L, ps, IPPROTO_TCP, TCP_NODELAY); + } + + int opt_get_tcp_nodelay(lua_State *L, p_socket ps) +@@ -92,12 +92,12 @@ int opt_get_tcp_nodelay(lua_State *L, p_socket ps) + + int opt_set_keepalive(lua_State *L, p_socket ps) + { +- return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); ++ return opt_setboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); + } + + int opt_get_keepalive(lua_State *L, p_socket ps) + { +- return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); ++ return opt_getboolean(L, ps, SOL_SOCKET, SO_KEEPALIVE); + } + + int opt_set_dontroute(lua_State *L, p_socket ps) +@@ -105,11 +105,21 @@ int opt_set_dontroute(lua_State *L, p_socket ps) + return opt_setboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); + } + ++int opt_get_dontroute(lua_State *L, p_socket ps) ++{ ++ return opt_getboolean(L, ps, SOL_SOCKET, SO_DONTROUTE); ++} ++ + int opt_set_broadcast(lua_State *L, p_socket ps) + { + return opt_setboolean(L, ps, SOL_SOCKET, SO_BROADCAST); + } + ++int opt_get_broadcast(lua_State *L, p_socket ps) ++{ ++ return opt_getboolean(L, ps, SOL_SOCKET, SO_BROADCAST); ++} ++ + int opt_set_ip6_unicast_hops(lua_State *L, p_socket ps) + { + return opt_setint(L, ps, IPPROTO_IPV6, IPV6_UNICAST_HOPS); +@@ -156,12 +166,12 @@ int opt_set_linger(lua_State *L, p_socket ps) + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "on"); + lua_gettable(L, 3); +- if (!lua_isboolean(L, -1)) ++ if (!lua_isboolean(L, -1)) + luaL_argerror(L, 3, "boolean 'on' field expected"); + li.l_onoff = (u_short) lua_toboolean(L, -1); + lua_pushstring(L, "timeout"); + lua_gettable(L, 3); +- if (!lua_isnumber(L, -1)) ++ if (!lua_isnumber(L, -1)) + luaL_argerror(L, 3, "number 'timeout' field expected"); + li.l_linger = (u_short) lua_tonumber(L, -1); + return opt_set(L, ps, SOL_SOCKET, SO_LINGER, (char *) &li, sizeof(li)); +@@ -194,7 +204,7 @@ int opt_set_ip_multicast_if(lua_State *L, p_socket ps) + val.s_addr = htonl(INADDR_ANY); + if (strcmp(address, "*") && !inet_aton(address, &val)) + luaL_argerror(L, 3, "ip expected"); +- return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, ++ return opt_set(L, ps, IPPROTO_IP, IP_MULTICAST_IF, + (char *) &val, sizeof(val)); + } + +@@ -250,17 +260,17 @@ static int opt_setmembership(lua_State *L, p_socket ps, int level, int name) + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); +- if (!lua_isstring(L, -1)) ++ if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); +- if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) ++ if (!inet_aton(lua_tostring(L, -1), &val.imr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); +- if (!lua_isstring(L, -1)) ++ if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'interface' field expected"); + val.imr_interface.s_addr = htonl(INADDR_ANY); + if (strcmp(lua_tostring(L, -1), "*") && +- !inet_aton(lua_tostring(L, -1), &val.imr_interface)) ++ !inet_aton(lua_tostring(L, -1), &val.imr_interface)) + luaL_argerror(L, 3, "invalid 'interface' ip address"); + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } +@@ -272,14 +282,14 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + if (!lua_istable(L, 3)) auxiliar_typeerror(L,3,lua_typename(L, LUA_TTABLE)); + lua_pushstring(L, "multiaddr"); + lua_gettable(L, 3); +- if (!lua_isstring(L, -1)) ++ if (!lua_isstring(L, -1)) + luaL_argerror(L, 3, "string 'multiaddr' field expected"); +- if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) ++ if (!inet_pton(AF_INET6, lua_tostring(L, -1), &val.ipv6mr_multiaddr)) + luaL_argerror(L, 3, "invalid 'multiaddr' ip address"); + lua_pushstring(L, "interface"); + lua_gettable(L, 3); + /* By default we listen to interface on default route +- * (sigh). However, interface= can override it. We should ++ * (sigh). However, interface= can override it. We should + * support either number, or name for it. Waiting for + * windows port of if_nametoindex */ + if (!lua_isnil(L, -1)) { +@@ -291,7 +301,7 @@ static int opt_ip6_setmembership(lua_State *L, p_socket ps, int level, int name) + return opt_set(L, ps, level, name, (char *) &val, sizeof(val)); + } + +-static ++static + int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) + { + socklen_t socklen = *len; +@@ -304,7 +314,7 @@ int opt_get(lua_State *L, p_socket ps, int level, int name, void *val, int* len) + return 0; + } + +-static ++static + int opt_set(lua_State *L, p_socket ps, int level, int name, void *val, int len) + { + if (setsockopt(*ps, level, name, (char *) val, len) < 0) { +diff --git a/src/options.h b/src/options.h +index 5657a06..19ba0df 100644 +--- a/src/options.h ++++ b/src/options.h +@@ -21,7 +21,6 @@ typedef t_opt *p_opt; + /* supported options for setoption */ + int opt_set_dontroute(lua_State *L, p_socket ps); + int opt_set_broadcast(lua_State *L, p_socket ps); +-int opt_set_reuseaddr(lua_State *L, p_socket ps); + int opt_set_tcp_nodelay(lua_State *L, p_socket ps); + int opt_set_keepalive(lua_State *L, p_socket ps); + int opt_set_linger(lua_State *L, p_socket ps); +@@ -40,18 +39,21 @@ int opt_set_ip6_drop_membersip(lua_State *L, p_socket ps); + int opt_set_ip6_v6only(lua_State *L, p_socket ps); + + /* supported options for getoption */ ++int opt_get_dontroute(lua_State *L, p_socket ps); ++int opt_get_broadcast(lua_State *L, p_socket ps); + int opt_get_reuseaddr(lua_State *L, p_socket ps); ++int opt_get_reuseport(lua_State *L, p_socket ps); + int opt_get_tcp_nodelay(lua_State *L, p_socket ps); + int opt_get_keepalive(lua_State *L, p_socket ps); + int opt_get_linger(lua_State *L, p_socket ps); +-int opt_get_reuseaddr(lua_State *L, p_socket ps); + int opt_get_ip_multicast_loop(lua_State *L, p_socket ps); + int opt_get_ip_multicast_if(lua_State *L, p_socket ps); + int opt_get_error(lua_State *L, p_socket ps); + int opt_get_ip6_multicast_loop(lua_State *L, p_socket ps); + int opt_get_ip6_multicast_hops(lua_State *L, p_socket ps); + int opt_get_ip6_unicast_hops(lua_State *L, p_socket ps); +-int opt_get_ip6_v6only(lua_State *L, p_socket ps); ++int opt_get_ip6_v6only(lua_State *L, p_socket ps); ++int opt_get_reuseport(lua_State *L, p_socket ps); + + /* invokes the appropriate option handler */ + int opt_meth_setoption(lua_State *L, p_opt opt, p_socket ps); +diff --git a/src/pierror.h b/src/pierror.h +new file mode 100644 +index 0000000..cb773ab +--- /dev/null ++++ b/src/pierror.h +@@ -0,0 +1,28 @@ ++#ifndef PIERROR_H ++#define PIERROR_H ++/*=========================================================================*\ ++* Error messages ++* Defines platform independent error messages ++\*=========================================================================*/ ++ ++#define PIE_HOST_NOT_FOUND "host not found" ++#define PIE_ADDRINUSE "address already in use" ++#define PIE_ISCONN "already connected" ++#define PIE_ACCESS "permission denied" ++#define PIE_CONNREFUSED "connection refused" ++#define PIE_CONNABORTED "closed" ++#define PIE_CONNRESET "closed" ++#define PIE_TIMEDOUT "timeout" ++#define PIE_AGAIN "temporary failure in name resolution" ++#define PIE_BADFLAGS "invalid value for ai_flags" ++#define PIE_BADHINTS "invalid value for hints" ++#define PIE_FAIL "non-recoverable failure in name resolution" ++#define PIE_FAMILY "ai_family not supported" ++#define PIE_MEMORY "memory allocation failure" ++#define PIE_NONAME "host or service not provided, or not known" ++#define PIE_OVERFLOW "argument buffer overflow" ++#define PIE_PROTOCOL "resolved protocol is unknown" ++#define PIE_SERVICE "service not supported for socket type" ++#define PIE_SOCKTYPE "ai_socktype not supported" ++ ++#endif +diff --git a/src/select.c b/src/select.c +index fafaa62..9d133b7 100644 +--- a/src/select.c ++++ b/src/select.c +@@ -6,6 +6,7 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "socket.h" + #include "timeout.h" +@@ -16,10 +17,10 @@ + \*=========================================================================*/ + static t_socket getfd(lua_State *L); + static int dirty(lua_State *L); +-static void collect_fd(lua_State *L, int tab, int itab, ++static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd); + static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set); +-static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, ++static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start); + static void make_assoc(lua_State *L, int tab); + static int global_select(lua_State *L); +@@ -38,13 +39,12 @@ static luaL_Reg func[] = { + \*-------------------------------------------------------------------------*/ + int select_open(lua_State *L) { + lua_pushstring(L, "_SETSIZE"); +- lua_pushnumber(L, FD_SETSIZE); ++ lua_pushinteger(L, FD_SETSIZE); ++ lua_rawset(L, -3); ++ lua_pushstring(L, "_SOCKETINVALID"); ++ lua_pushinteger(L, SOCKET_INVALID); + lua_rawset(L, -3); +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif + return 0; + } + +@@ -98,10 +98,10 @@ static t_socket getfd(lua_State *L) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + if (lua_isnumber(L, -1)) { +- double numfd = lua_tonumber(L, -1); ++ double numfd = lua_tonumber(L, -1); + fd = (numfd >= 0.0)? (t_socket) numfd: SOCKET_INVALID; + } +- } ++ } + lua_pop(L, 1); + return fd; + } +@@ -114,12 +114,12 @@ static int dirty(lua_State *L) { + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + is = lua_toboolean(L, -1); +- } ++ } + lua_pop(L, 1); + return is; + } + +-static void collect_fd(lua_State *L, int tab, int itab, ++static void collect_fd(lua_State *L, int tab, int itab, + fd_set *set, t_socket *max_fd) { + int i = 1, n = 0; + /* nil is the same as an empty table */ +@@ -139,16 +139,16 @@ static void collect_fd(lua_State *L, int tab, int itab, + if (fd != SOCKET_INVALID) { + /* make sure we don't overflow the fd_set */ + #ifdef _WIN32 +- if (n >= FD_SETSIZE) ++ if (n >= FD_SETSIZE) + luaL_argerror(L, tab, "too many sockets"); + #else +- if (fd >= FD_SETSIZE) ++ if (fd >= FD_SETSIZE) + luaL_argerror(L, tab, "descriptor too large for set size"); + #endif + FD_SET(fd, set); + n++; + /* keep track of the largest descriptor so far */ +- if (*max_fd == SOCKET_INVALID || *max_fd < fd) ++ if (*max_fd == SOCKET_INVALID || *max_fd < fd) + *max_fd = fd; + /* make sure we can map back from descriptor to the object */ + lua_pushnumber(L, (lua_Number) fd); +@@ -162,9 +162,9 @@ static void collect_fd(lua_State *L, int tab, int itab, + + static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { + int ndirty = 0, i = 1; +- if (lua_isnil(L, tab)) ++ if (lua_isnil(L, tab)) + return 0; +- for ( ;; ) { ++ for ( ;; ) { + t_socket fd; + lua_pushnumber(L, i); + lua_gettable(L, tab); +@@ -185,7 +185,7 @@ static int check_dirty(lua_State *L, int tab, int dtab, fd_set *set) { + return ndirty; + } + +-static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, ++static void return_fd(lua_State *L, fd_set *set, t_socket max_fd, + int itab, int tab, int start) { + t_socket fd; + for (fd = 0; fd < max_fd; fd++) { +diff --git a/src/serial.c b/src/serial.c +index 583d4e5..7bdb21c 100644 +--- a/src/serial.c ++++ b/src/serial.c +@@ -2,7 +2,7 @@ + * Serial stream + * LuaSocket toolkit + \*=========================================================================*/ +-#include <string.h> ++#include <string.h> + + #include "lua.h" + #include "lauxlib.h" +@@ -11,7 +11,7 @@ + #include "socket.h" + #include "options.h" + #include "unix.h" +-#include <sys/un.h> ++#include <sys/un.h> + + /* + Reuses userdata definition from unix.h, since it is useful for all +@@ -54,15 +54,6 @@ static luaL_Reg serial_methods[] = { + {NULL, NULL} + }; + +-/* our socket creation function */ +-/* this is an ad-hoc module that returns a single function +- * as such, do not include other functions in this array. */ +-static luaL_Reg func[] = { +- {"serial", global_create}, +- {NULL, NULL} +-}; +- +- + /*-------------------------------------------------------------------------*\ + * Initializes module + \*-------------------------------------------------------------------------*/ +@@ -71,14 +62,7 @@ LUASOCKET_API int luaopen_socket_serial(lua_State *L) { + auxiliar_newclass(L, "serial{client}", serial_methods); + /* create class groups */ + auxiliar_add2group(L, "serial{client}", "serial{any}"); +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) +- lua_pushcfunction(L, global_create); +- (void) func; +-#else +- /* set function into socket namespace */ +- luaL_openlib(L, "socket", func, 0); + lua_pushcfunction(L, global_create); +-#endif + return 1; + } + +@@ -120,7 +104,7 @@ static int meth_getfd(lua_State *L) { + /* this is very dangerous, but can be handy for those that are brave enough */ + static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "serial{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); ++ un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; + } + +@@ -131,7 +115,7 @@ static int meth_dirty(lua_State *L) { + } + + /*-------------------------------------------------------------------------*\ +-* Closes socket used by object ++* Closes socket used by object + \*-------------------------------------------------------------------------*/ + static int meth_close(lua_State *L) + { +@@ -156,7 +140,7 @@ static int meth_settimeout(lua_State *L) { + + + /*-------------------------------------------------------------------------*\ +-* Creates a serial object ++* Creates a serial object + \*-------------------------------------------------------------------------*/ + static int global_create(lua_State *L) { + const char* path = luaL_checkstring(L, 1); +@@ -180,7 +164,7 @@ static int global_create(lua_State *L) { + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; +- io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, ++ io_init(&un->io, (p_send) socket_write, (p_recv) socket_read, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); +diff --git a/src/socket.lua b/src/socket.lua +index 3913e6f..d1c0b16 100644 +--- a/src/socket.lua ++++ b/src/socket.lua +@@ -32,23 +32,23 @@ function _M.bind(host, port, backlog) + err = "no info on address" + for i, alt in base.ipairs(addrinfo) do + if alt.family == "inet" then +- sock, err = socket.tcp() ++ sock, err = socket.tcp4() + else + sock, err = socket.tcp6() + end + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + res, err = sock:bind(alt.addr, port) +- if not res then ++ if not res then + sock:close() +- else ++ else + res, err = sock:listen(backlog) +- if not res then ++ if not res then + sock:close() + else + return sock + end +- end ++ end + end + return nil, err + end +diff --git a/src/tcp.c b/src/tcp.c +index 6594bda..ef9ee6f 100644 +--- a/src/tcp.c ++++ b/src/tcp.c +@@ -6,6 +6,7 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "auxiliar.h" + #include "socket.h" +@@ -17,6 +18,7 @@ + * Internal function prototypes + \*=========================================================================*/ + static int global_create(lua_State *L); ++static int global_create4(lua_State *L); + static int global_create6(lua_State *L); + static int global_connect(lua_State *L); + static int meth_connect(lua_State *L); +@@ -34,6 +36,7 @@ static int meth_accept(lua_State *L); + static int meth_close(lua_State *L); + static int meth_getoption(lua_State *L); + static int meth_setoption(lua_State *L); ++static int meth_gettimeout(lua_State *L); + static int meth_settimeout(lua_State *L); + static int meth_getfd(lua_State *L); + static int meth_setfd(lua_State *L); +@@ -63,6 +66,7 @@ static luaL_Reg tcp_methods[] = { + {"setpeername", meth_connect}, + {"setsockname", meth_bind}, + {"settimeout", meth_settimeout}, ++ {"gettimeout", meth_gettimeout}, + {"shutdown", meth_shutdown}, + {NULL, NULL} + }; +@@ -71,6 +75,7 @@ static luaL_Reg tcp_methods[] = { + static t_opt optget[] = { + {"keepalive", opt_get_keepalive}, + {"reuseaddr", opt_get_reuseaddr}, ++ {"reuseport", opt_get_reuseport}, + {"tcp-nodelay", opt_get_tcp_nodelay}, + {"linger", opt_get_linger}, + {"error", opt_get_error}, +@@ -80,6 +85,7 @@ static t_opt optget[] = { + static t_opt optset[] = { + {"keepalive", opt_set_keepalive}, + {"reuseaddr", opt_set_reuseaddr}, ++ {"reuseport", opt_set_reuseport}, + {"tcp-nodelay", opt_set_tcp_nodelay}, + {"ipv6-v6only", opt_set_ip6_v6only}, + {"linger", opt_set_linger}, +@@ -89,6 +95,7 @@ static t_opt optset[] = { + /* functions in library namespace */ + static luaL_Reg func[] = { + {"tcp", global_create}, ++ {"tcp4", global_create4}, + {"tcp6", global_create6}, + {"connect", global_connect}, + {NULL, NULL} +@@ -108,11 +115,7 @@ int tcp_open(lua_State *L) + auxiliar_add2group(L, "tcp{client}", "tcp{any}"); + auxiliar_add2group(L, "tcp{server}", "tcp{any}"); + /* define library functions */ +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif + return 0; + } + +@@ -216,8 +219,7 @@ static int meth_accept(lua_State *L) + /*-------------------------------------------------------------------------*\ + * Binds an object to an address + \*-------------------------------------------------------------------------*/ +-static int meth_bind(lua_State *L) +-{ ++static int meth_bind(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkclass(L, "tcp{master}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); +@@ -227,7 +229,7 @@ static int meth_bind(lua_State *L) + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = tcp->family; + bindhints.ai_flags = AI_PASSIVE; +- err = inet_trybind(&tcp->sock, address, port, &bindhints); ++ err = inet_trybind(&tcp->sock, &tcp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); +@@ -240,8 +242,7 @@ static int meth_bind(lua_State *L) + /*-------------------------------------------------------------------------*\ + * Turns a master tcp object into a client object. + \*-------------------------------------------------------------------------*/ +-static int meth_connect(lua_State *L) +-{ ++static int meth_connect(lua_State *L) { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); + const char *address = luaL_checkstring(L, 2); + const char *port = luaL_checkstring(L, 3); +@@ -252,7 +253,7 @@ static int meth_connect(lua_State *L) + /* make sure we try to connect only to the same family */ + connecthints.ai_family = tcp->family; + timeout_markstart(&tcp->tm); +- err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, ++ err = inet_tryconnect(&tcp->sock, &tcp->family, address, port, + &tcp->tm, &connecthints); + /* have to set the class even if it failed due to non-blocking connects */ + auxiliar_setclass(L, "tcp{client}", 1); +@@ -282,9 +283,12 @@ static int meth_close(lua_State *L) + static int meth_getfamily(lua_State *L) + { + p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); +- if (tcp->family == PF_INET6) { ++ if (tcp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; ++ } else if (tcp->family == AF_INET) { ++ lua_pushliteral(L, "inet4"); ++ return 1; + } else { + lua_pushliteral(L, "inet4"); + return 1; +@@ -348,6 +352,12 @@ static int meth_settimeout(lua_State *L) + return timeout_meth_settimeout(L, &tcp->tm); + } + ++static int meth_gettimeout(lua_State *L) ++{ ++ p_tcp tcp = (p_tcp) auxiliar_checkgroup(L, "tcp{any}", 1); ++ return timeout_meth_gettimeout(L, &tcp->tm); ++} ++ + /*=========================================================================*\ + * Library functions + \*=========================================================================*/ +@@ -355,37 +365,36 @@ static int meth_settimeout(lua_State *L) + * Creates a master tcp object + \*-------------------------------------------------------------------------*/ + static int tcp_create(lua_State *L, int family) { +- t_socket sock; +- const char *err = inet_trycreate(&sock, family, SOCK_STREAM); +- /* try to allocate a system socket */ +- if (!err) { +- /* allocate tcp object */ +- p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); +- memset(tcp, 0, sizeof(t_tcp)); +- /* set its type as master object */ +- auxiliar_setclass(L, "tcp{master}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- if (family == PF_INET6) { +- int yes = 1; +- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, +- (void *)&yes, sizeof(yes)); ++ p_tcp tcp = (p_tcp) lua_newuserdata(L, sizeof(t_tcp)); ++ memset(tcp, 0, sizeof(t_tcp)); ++ /* set its type as master object */ ++ auxiliar_setclass(L, "tcp{master}", -1); ++ /* if family is AF_UNSPEC, we leave the socket invalid and ++ * store AF_UNSPEC into family. This will allow it to later be ++ * replaced with an AF_INET6 or AF_INET socket upon first use. */ ++ tcp->sock = SOCKET_INVALID; ++ tcp->family = family; ++ io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, ++ (p_error) socket_ioerror, &tcp->sock); ++ timeout_init(&tcp->tm, -1, -1); ++ buffer_init(&tcp->buf, &tcp->io, &tcp->tm); ++ if (family != AF_UNSPEC) { ++ const char *err = inet_trycreate(&tcp->sock, family, SOCK_STREAM, 0); ++ if (err != NULL) { ++ lua_pushnil(L); ++ lua_pushstring(L, err); ++ return 2; + } +- tcp->sock = sock; +- io_init(&tcp->io, (p_send) socket_send, (p_recv) socket_recv, +- (p_error) socket_ioerror, &tcp->sock); +- timeout_init(&tcp->tm, -1, -1); +- buffer_init(&tcp->buf, &tcp->io, &tcp->tm); +- tcp->family = family; +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; ++ socket_setnonblocking(&tcp->sock); + } ++ return 1; + } + + static int global_create(lua_State *L) { ++ return tcp_create(L, AF_UNSPEC); ++} ++ ++static int global_create4(lua_State *L) { + return tcp_create(L, AF_INET); + } + +@@ -393,53 +402,6 @@ static int global_create6(lua_State *L) { + return tcp_create(L, AF_INET6); + } + +-#if 0 +-static const char *tryconnect6(const char *remoteaddr, const char *remoteserv, +- struct addrinfo *connecthints, p_tcp tcp) { +- struct addrinfo *iterator = NULL, *resolved = NULL; +- const char *err = NULL; +- /* try resolving */ +- err = socket_gaistrerror(getaddrinfo(remoteaddr, remoteserv, +- connecthints, &resolved)); +- if (err != NULL) { +- if (resolved) freeaddrinfo(resolved); +- return err; +- } +- /* iterate over all returned addresses trying to connect */ +- for (iterator = resolved; iterator; iterator = iterator->ai_next) { +- p_timeout tm = timeout_markstart(&tcp->tm); +- /* create new socket if necessary. if there was no +- * bind, we need to create one for every new family +- * that shows up while iterating. if there was a +- * bind, all families will be the same and we will +- * not enter this branch. */ +- if (tcp->family != iterator->ai_family) { +- socket_destroy(&tcp->sock); +- err = socket_strerror(socket_create(&tcp->sock, +- iterator->ai_family, iterator->ai_socktype, +- iterator->ai_protocol)); +- if (err != NULL) { +- freeaddrinfo(resolved); +- return err; +- } +- tcp->family = iterator->ai_family; +- /* all sockets initially non-blocking */ +- socket_setnonblocking(&tcp->sock); +- } +- /* finally try connecting to remote address */ +- err = socket_strerror(socket_connect(&tcp->sock, +- (SA *) iterator->ai_addr, +- (socklen_t) iterator->ai_addrlen, tm)); +- /* if success, break out of loop */ +- if (err == NULL) break; +- } +- +- freeaddrinfo(resolved); +- /* here, if err is set, we failed */ +- return err; +-} +-#endif +- + static int global_connect(lua_State *L) { + const char *remoteaddr = luaL_checkstring(L, 1); + const char *remoteserv = luaL_checkstring(L, 2); +@@ -456,26 +418,26 @@ static int global_connect(lua_State *L) { + timeout_init(&tcp->tm, -1, -1); + buffer_init(&tcp->buf, &tcp->io, &tcp->tm); + tcp->sock = SOCKET_INVALID; +- tcp->family = PF_UNSPEC; ++ tcp->family = AF_UNSPEC; + /* allow user to pick local address and port */ + memset(&bindhints, 0, sizeof(bindhints)); + bindhints.ai_socktype = SOCK_STREAM; + bindhints.ai_family = family; + bindhints.ai_flags = AI_PASSIVE; + if (localaddr) { +- err = inet_trybind(&tcp->sock, localaddr, localserv, &bindhints); ++ err = inet_trybind(&tcp->sock, &tcp->family, localaddr, ++ localserv, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); + return 2; + } +- tcp->family = bindhints.ai_family; + } + /* try to connect to remote address and port */ + memset(&connecthints, 0, sizeof(connecthints)); + connecthints.ai_socktype = SOCK_STREAM; + /* make sure we try to connect only to the same family */ +- connecthints.ai_family = bindhints.ai_family; ++ connecthints.ai_family = tcp->family; + err = inet_tryconnect(&tcp->sock, &tcp->family, remoteaddr, remoteserv, + &tcp->tm, &connecthints); + if (err) { +diff --git a/src/timeout.c b/src/timeout.c +index bdd5e1c..5a601d5 100644 +--- a/src/timeout.c ++++ b/src/timeout.c +@@ -8,6 +8,7 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "auxiliar.h" + #include "timeout.h" +@@ -52,7 +53,7 @@ void timeout_init(p_timeout tm, double block, double total) { + + /*-------------------------------------------------------------------------*\ + * Determines how much time we have left for the next system call, +-* if the previous call was successful ++* if the previous call was successful + * Input + * tm: timeout control structure + * Returns +@@ -107,7 +108,7 @@ double timeout_getretry(p_timeout tm) { + } + + /*-------------------------------------------------------------------------*\ +-* Marks the operation start time in structure ++* Marks the operation start time in structure + * Input + * tm: timeout control structure + \*-------------------------------------------------------------------------*/ +@@ -117,7 +118,7 @@ p_timeout timeout_markstart(p_timeout tm) { + } + + /*-------------------------------------------------------------------------*\ +-* Gets time in s, relative to January 1, 1970 (UTC) ++* Gets time in s, relative to January 1, 1970 (UTC) + * Returns + * time in s. + \*-------------------------------------------------------------------------*/ +@@ -144,11 +145,7 @@ double timeout_gettime(void) { + * Initializes module + \*-------------------------------------------------------------------------*/ + int timeout_open(lua_State *L) { +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif + return 0; + } + +@@ -163,7 +160,7 @@ int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + const char *mode = luaL_optstring(L, 3, "b"); + switch (*mode) { + case 'b': +- tm->block = t; ++ tm->block = t; + break; + case 'r': case 't': + tm->total = t; +@@ -176,6 +173,16 @@ int timeout_meth_settimeout(lua_State *L, p_timeout tm) { + return 1; + } + ++/*-------------------------------------------------------------------------*\ ++* Gets timeout values for IO operations ++* Lua Output: block, total ++\*-------------------------------------------------------------------------*/ ++int timeout_meth_gettimeout(lua_State *L, p_timeout tm) { ++ lua_pushnumber(L, tm->block); ++ lua_pushnumber(L, tm->total); ++ return 2; ++} ++ + /*=========================================================================*\ + * Test support functions + \*=========================================================================*/ +diff --git a/src/timeout.h b/src/timeout.h +index 6715ca7..af90231 100644 +--- a/src/timeout.h ++++ b/src/timeout.h +@@ -22,6 +22,7 @@ p_timeout timeout_markstart(p_timeout tm); + double timeout_getstart(p_timeout tm); + double timeout_gettime(void); + int timeout_meth_settimeout(lua_State *L, p_timeout tm); ++int timeout_meth_gettimeout(lua_State *L, p_timeout tm); + + #define timeout_iszero(tm) ((tm)->block == 0.0) + +diff --git a/src/tp.lua b/src/tp.lua +index cbeff56..328cbab 100644 +--- a/src/tp.lua ++++ b/src/tp.lua +@@ -74,7 +74,7 @@ function metat.__index:command(cmd, arg) + end + + function metat.__index:sink(snk, pat) +- local chunk, err = c:receive(pat) ++ local chunk, err = self.c:receive(pat) + return snk(chunk, err) + end + +diff --git a/src/udp.c b/src/udp.c +index a9f2393..ec97252 100644 +--- a/src/udp.c ++++ b/src/udp.c +@@ -7,6 +7,7 @@ + + #include "lua.h" + #include "lauxlib.h" ++#include "compat.h" + + #include "auxiliar.h" + #include "socket.h" +@@ -26,6 +27,7 @@ + * Internal function prototypes + \*=========================================================================*/ + static int global_create(lua_State *L); ++static int global_create4(lua_State *L); + static int global_create6(lua_State *L); + static int meth_send(lua_State *L); + static int meth_sendto(lua_State *L); +@@ -34,6 +36,7 @@ static int meth_receivefrom(lua_State *L); + static int meth_getfamily(lua_State *L); + static int meth_getsockname(lua_State *L); + static int meth_getpeername(lua_State *L); ++static int meth_gettimeout(lua_State *L); + static int meth_setsockname(lua_State *L); + static int meth_setpeername(lua_State *L); + static int meth_close(lua_State *L); +@@ -64,6 +67,7 @@ static luaL_Reg udp_methods[] = { + {"setpeername", meth_setpeername}, + {"setsockname", meth_setsockname}, + {"settimeout", meth_settimeout}, ++ {"gettimeout", meth_gettimeout}, + {NULL, NULL} + }; + +@@ -89,6 +93,10 @@ static t_opt optset[] = { + + /* socket options for getoption */ + static t_opt optget[] = { ++ {"dontroute", opt_get_dontroute}, ++ {"broadcast", opt_get_broadcast}, ++ {"reuseaddr", opt_get_reuseaddr}, ++ {"reuseport", opt_get_reuseport}, + {"ip-multicast-if", opt_get_ip_multicast_if}, + {"ip-multicast-loop", opt_get_ip_multicast_loop}, + {"error", opt_get_error}, +@@ -102,6 +110,7 @@ static t_opt optget[] = { + /* functions in library namespace */ + static luaL_Reg func[] = { + {"udp", global_create}, ++ {"udp4", global_create4}, + {"udp6", global_create6}, + {NULL, NULL} + }; +@@ -109,8 +118,7 @@ static luaL_Reg func[] = { + /*-------------------------------------------------------------------------*\ + * Initializes module + \*-------------------------------------------------------------------------*/ +-int udp_open(lua_State *L) +-{ ++int udp_open(lua_State *L) { + /* create classes */ + auxiliar_newclass(L, "udp{connected}", udp_methods); + auxiliar_newclass(L, "udp{unconnected}", udp_methods); +@@ -120,18 +128,18 @@ int udp_open(lua_State *L) + auxiliar_add2group(L, "udp{connected}", "select{able}"); + auxiliar_add2group(L, "udp{unconnected}", "select{able}"); + /* define library functions */ +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) + luaL_setfuncs(L, func, 0); +-#else +- luaL_openlib(L, NULL, func, 0); +-#endif ++ /* export default UDP size */ ++ lua_pushliteral(L, "_DATAGRAMSIZE"); ++ lua_pushinteger(L, UDP_DATAGRAMSIZE); ++ lua_rawset(L, -3); + return 0; + } + + /*=========================================================================*\ + * Lua methods + \*=========================================================================*/ +-const char *udp_strerror(int err) { ++static const char *udp_strerror(int err) { + /* a 'closed' error on an unconnected means the target address was not + * accepted by the transport layer */ + if (err == IO_CLOSED) return "refused"; +@@ -182,7 +190,7 @@ static int meth_sendto(lua_State *L) { + return 2; + } + timeout_markstart(tm); +- err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, ++ err = socket_sendto(&udp->sock, data, count, &sent, ai->ai_addr, + (socklen_t) ai->ai_addrlen, tm); + freeaddrinfo(ai); + if (err != IO_DONE) { +@@ -199,71 +207,80 @@ static int meth_sendto(lua_State *L) { + \*-------------------------------------------------------------------------*/ + static int meth_receive(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); +- char buffer[UDP_DATAGRAMSIZE]; +- size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); ++ char buf[UDP_DATAGRAMSIZE]; ++ size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); ++ char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + int err; + p_timeout tm = &udp->tm; +- count = MIN(count, sizeof(buffer)); + timeout_markstart(tm); +- err = socket_recv(&udp->sock, buffer, count, &got, tm); ++ if (!dgram) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "out of memory"); ++ return 2; ++ } ++ err = socket_recv(&udp->sock, dgram, wanted, &got, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ +- if (err == IO_CLOSED) +- err = IO_DONE; +- if (err != IO_DONE) { ++ if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); ++ if (wanted > sizeof(buf)) free(dgram); + return 2; + } +- lua_pushlstring(L, buffer, got); ++ lua_pushlstring(L, dgram, got); ++ if (wanted > sizeof(buf)) free(dgram); + return 1; + } + + /*-------------------------------------------------------------------------*\ + * Receives data and sender from a UDP socket + \*-------------------------------------------------------------------------*/ +-static int meth_receivefrom(lua_State *L) +-{ ++static int meth_receivefrom(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkclass(L, "udp{unconnected}", 1); +- char buffer[UDP_DATAGRAMSIZE]; +- size_t got, count = (size_t) luaL_optnumber(L, 2, sizeof(buffer)); +- int err; +- p_timeout tm = &udp->tm; ++ char buf[UDP_DATAGRAMSIZE]; ++ size_t got, wanted = (size_t) luaL_optnumber(L, 2, sizeof(buf)); ++ char *dgram = wanted > sizeof(buf)? (char *) malloc(wanted): buf; + struct sockaddr_storage addr; + socklen_t addr_len = sizeof(addr); + char addrstr[INET6_ADDRSTRLEN]; + char portstr[6]; ++ int err; ++ p_timeout tm = &udp->tm; + timeout_markstart(tm); +- count = MIN(count, sizeof(buffer)); +- err = socket_recvfrom(&udp->sock, buffer, count, &got, (SA *) &addr, ++ if (!dgram) { ++ lua_pushnil(L); ++ lua_pushliteral(L, "out of memory"); ++ return 2; ++ } ++ err = socket_recvfrom(&udp->sock, dgram, wanted, &got, (SA *) &addr, + &addr_len, tm); + /* Unlike TCP, recv() of zero is not closed, but a zero-length packet. */ +- if (err == IO_CLOSED) +- err = IO_DONE; +- if (err != IO_DONE) { ++ if (err != IO_DONE && err != IO_CLOSED) { + lua_pushnil(L); + lua_pushstring(L, udp_strerror(err)); ++ if (wanted > sizeof(buf)) free(dgram); + return 2; + } +- err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, ++ err = getnameinfo((struct sockaddr *)&addr, addr_len, addrstr, + INET6_ADDRSTRLEN, portstr, 6, NI_NUMERICHOST | NI_NUMERICSERV); + if (err) { + lua_pushnil(L); + lua_pushstring(L, gai_strerror(err)); ++ if (wanted > sizeof(buf)) free(dgram); + return 2; + } +- lua_pushlstring(L, buffer, got); ++ lua_pushlstring(L, dgram, got); + lua_pushstring(L, addrstr); + lua_pushinteger(L, (int) strtol(portstr, (char **) NULL, 10)); ++ if (wanted > sizeof(buf)) free(dgram); + return 3; + } + + /*-------------------------------------------------------------------------*\ + * Returns family as string + \*-------------------------------------------------------------------------*/ +-static int meth_getfamily(lua_State *L) +-{ ++static int meth_getfamily(lua_State *L) { + p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); +- if (udp->family == PF_INET6) { ++ if (udp->family == AF_INET6) { + lua_pushliteral(L, "inet6"); + return 1; + } else { +@@ -332,6 +349,11 @@ static int meth_settimeout(lua_State *L) { + return timeout_meth_settimeout(L, &udp->tm); + } + ++static int meth_gettimeout(lua_State *L) { ++ p_udp udp = (p_udp) auxiliar_checkgroup(L, "udp{any}", 1); ++ return timeout_meth_gettimeout(L, &udp->tm); ++} ++ + /*-------------------------------------------------------------------------*\ + * Turns a master udp object into a client object. + \*-------------------------------------------------------------------------*/ +@@ -348,7 +370,7 @@ static int meth_setpeername(lua_State *L) { + /* make sure we try to connect only to the same family */ + connecthints.ai_family = udp->family; + if (connecting) { +- err = inet_tryconnect(&udp->sock, &udp->family, address, ++ err = inet_tryconnect(&udp->sock, &udp->family, address, + port, tm, &connecthints); + if (err) { + lua_pushnil(L); +@@ -362,7 +384,6 @@ static int meth_setpeername(lua_State *L) { + inet_trydisconnect(&udp->sock, udp->family, tm); + auxiliar_setclass(L, "udp{unconnected}", 1); + } +- /* change class to connected or unconnected depending on address */ + lua_pushnumber(L, 1); + return 1; + } +@@ -390,7 +411,7 @@ static int meth_setsockname(lua_State *L) { + bindhints.ai_socktype = SOCK_DGRAM; + bindhints.ai_family = udp->family; + bindhints.ai_flags = AI_PASSIVE; +- err = inet_trybind(&udp->sock, address, port, &bindhints); ++ err = inet_trybind(&udp->sock, &udp->family, address, port, &bindhints); + if (err) { + lua_pushnil(L); + lua_pushstring(L, err); +@@ -407,32 +428,32 @@ static int meth_setsockname(lua_State *L) { + * Creates a master udp object + \*-------------------------------------------------------------------------*/ + static int udp_create(lua_State *L, int family) { +- t_socket sock; +- const char *err = inet_trycreate(&sock, family, SOCK_DGRAM); +- /* try to allocate a system socket */ +- if (!err) { +- /* allocate udp object */ +- p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); +- auxiliar_setclass(L, "udp{unconnected}", -1); +- /* initialize remaining structure fields */ +- socket_setnonblocking(&sock); +- if (family == PF_INET6) { +- int yes = 1; +- setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, +- (void *)&yes, sizeof(yes)); ++ /* allocate udp object */ ++ p_udp udp = (p_udp) lua_newuserdata(L, sizeof(t_udp)); ++ auxiliar_setclass(L, "udp{unconnected}", -1); ++ /* if family is AF_UNSPEC, we leave the socket invalid and ++ * store AF_UNSPEC into family. This will allow it to later be ++ * replaced with an AF_INET6 or AF_INET socket upon first use. */ ++ udp->sock = SOCKET_INVALID; ++ timeout_init(&udp->tm, -1, -1); ++ udp->family = family; ++ if (family != AF_UNSPEC) { ++ const char *err = inet_trycreate(&udp->sock, family, SOCK_DGRAM, 0); ++ if (err != NULL) { ++ lua_pushnil(L); ++ lua_pushstring(L, err); ++ return 2; + } +- udp->sock = sock; +- timeout_init(&udp->tm, -1, -1); +- udp->family = family; +- return 1; +- } else { +- lua_pushnil(L); +- lua_pushstring(L, err); +- return 2; ++ socket_setnonblocking(&udp->sock); + } ++ return 1; + } + + static int global_create(lua_State *L) { ++ return udp_create(L, AF_UNSPEC); ++} ++ ++static int global_create4(lua_State *L) { + return udp_create(L, AF_INET); + } + +diff --git a/src/udp.h b/src/udp.h +index 2b831a5..be9b6a5 100644 +--- a/src/udp.h ++++ b/src/udp.h +@@ -8,7 +8,7 @@ + * (AF_INET, SOCK_DGRAM). + * + * Two classes are defined: connected and unconnected. UDP objects are +-* originally unconnected. They can be "connected" to a given address ++* originally unconnected. They can be "connected" to a given address + * with a call to the setpeername function. The same function can be used to + * break the connection. + \*=========================================================================*/ +@@ -17,7 +17,6 @@ + #include "timeout.h" + #include "socket.h" + +-/* can't be larger than wsocket.c MAXCHUNK!!! */ + #define UDP_DATAGRAMSIZE 8192 + + typedef struct t_udp_ { +diff --git a/src/unix.c b/src/unix.c +index 91aaaf8..5bc3148 100644 +--- a/src/unix.c ++++ b/src/unix.c +@@ -1,8 +1,8 @@ + /*=========================================================================*\ +-* Unix domain socket ++* Unix domain socket + * LuaSocket toolkit + \*=========================================================================*/ +-#include <string.h> ++#include <string.h> + + #include "lua.h" + #include "lauxlib.h" +@@ -11,7 +11,7 @@ + #include "socket.h" + #include "options.h" + #include "unix.h" +-#include <sys/un.h> ++#include <sys/un.h> + + /*=========================================================================*\ + * Internal function prototypes +@@ -68,15 +68,6 @@ static t_opt optset[] = { + {NULL, NULL} + }; + +-/* our socket creation function */ +-/* this is an ad-hoc module that returns a single function +- * as such, do not include other functions in this array. */ +-static luaL_Reg func[] = { +- {"unix", global_create}, +- {NULL, NULL} +-}; +- +- + /*-------------------------------------------------------------------------*\ + * Initializes module + \*-------------------------------------------------------------------------*/ +@@ -89,15 +80,8 @@ int luaopen_socket_unix(lua_State *L) { + auxiliar_add2group(L, "unix{master}", "unix{any}"); + auxiliar_add2group(L, "unix{client}", "unix{any}"); + auxiliar_add2group(L, "unix{server}", "unix{any}"); +-#if LUA_VERSION_NUM > 501 && !defined(LUA_COMPAT_MODULE) +- lua_pushcfunction(L, global_create); +- (void) func; +-#else +- /* set function into socket namespace */ +- luaL_openlib(L, "socket", func, 0); +- lua_pushcfunction(L, global_create); +-#endif + /* return the function instead of the 'socket' table */ ++ lua_pushcfunction(L, global_create); + return 1; + } + +@@ -147,7 +131,7 @@ static int meth_getfd(lua_State *L) { + /* this is very dangerous, but can be handy for those that are brave enough */ + static int meth_setfd(lua_State *L) { + p_unix un = (p_unix) auxiliar_checkgroup(L, "unix{any}", 1); +- un->sock = (t_socket) luaL_checknumber(L, 2); ++ un->sock = (t_socket) luaL_checknumber(L, 2); + return 0; + } + +@@ -158,8 +142,8 @@ static int meth_dirty(lua_State *L) { + } + + /*-------------------------------------------------------------------------*\ +-* Waits for and returns a client object attempting connection to the +-* server object ++* Waits for and returns a client object attempting connection to the ++* server object + \*-------------------------------------------------------------------------*/ + static int meth_accept(lua_State *L) { + p_unix server = (p_unix) auxiliar_checkclass(L, "unix{server}", 1); +@@ -173,20 +157,20 @@ static int meth_accept(lua_State *L) { + /* initialize structure fields */ + socket_setnonblocking(&sock); + clnt->sock = sock; +- io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, ++ io_init(&clnt->io, (p_send)socket_send, (p_recv)socket_recv, + (p_error) socket_ioerror, &clnt->sock); + timeout_init(&clnt->tm, -1, -1); + buffer_init(&clnt->buf, &clnt->io, &clnt->tm); + return 1; + } else { +- lua_pushnil(L); ++ lua_pushnil(L); + lua_pushstring(L, socket_strerror(err)); + return 2; + } + } + + /*-------------------------------------------------------------------------*\ +-* Binds an object to an address ++* Binds an object to an address + \*-------------------------------------------------------------------------*/ + static const char *unix_trybind(p_unix un, const char *path) { + struct sockaddr_un local; +@@ -197,16 +181,16 @@ static const char *unix_trybind(p_unix un, const char *path) { + strcpy(local.sun_path, path); + local.sun_family = AF_UNIX; + #ifdef UNIX_HAS_SUN_LEN +- local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) ++ local.sun_len = sizeof(local.sun_family) + sizeof(local.sun_len) + + len + 1; + err = socket_bind(&un->sock, (SA *) &local, local.sun_len); + +-#else +- err = socket_bind(&un->sock, (SA *) &local, ++#else ++ err = socket_bind(&un->sock, (SA *) &local, + sizeof(local.sun_family) + len); + #endif + if (err != IO_DONE) socket_destroy(&un->sock); +- return socket_strerror(err); ++ return socket_strerror(err); + } + + static int meth_bind(lua_State *L) { +@@ -236,11 +220,11 @@ static const char *unix_tryconnect(p_unix un, const char *path) + remote.sun_family = AF_UNIX; + timeout_markstart(&un->tm); + #ifdef UNIX_HAS_SUN_LEN +- remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) ++ remote.sun_len = sizeof(remote.sun_family) + sizeof(remote.sun_len) + + len + 1; + err = socket_connect(&un->sock, (SA *) &remote, remote.sun_len, &un->tm); + #else +- err = socket_connect(&un->sock, (SA *) &remote, ++ err = socket_connect(&un->sock, (SA *) &remote, + sizeof(remote.sun_family) + len, &un->tm); + #endif + if (err != IO_DONE) socket_destroy(&un->sock); +@@ -264,7 +248,7 @@ static int meth_connect(lua_State *L) + } + + /*-------------------------------------------------------------------------*\ +-* Closes socket used by object ++* Closes socket used by object + \*-------------------------------------------------------------------------*/ + static int meth_close(lua_State *L) + { +@@ -319,13 +303,13 @@ static int meth_settimeout(lua_State *L) { + * Library functions + \*=========================================================================*/ + /*-------------------------------------------------------------------------*\ +-* Creates a master unix object ++* Creates a master unix object + \*-------------------------------------------------------------------------*/ + static int global_create(lua_State *L) { + t_socket sock; + int err = socket_create(&sock, AF_UNIX, SOCK_STREAM, 0); + /* try to allocate a system socket */ +- if (err == IO_DONE) { ++ if (err == IO_DONE) { + /* allocate unix object */ + p_unix un = (p_unix) lua_newuserdata(L, sizeof(t_unix)); + /* set its type as master object */ +@@ -333,7 +317,7 @@ static int global_create(lua_State *L) { + /* initialize remaining structure fields */ + socket_setnonblocking(&sock); + un->sock = sock; +- io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, ++ io_init(&un->io, (p_send) socket_send, (p_recv) socket_recv, + (p_error) socket_ioerror, &un->sock); + timeout_init(&un->tm, -1, -1); + buffer_init(&un->buf, &un->io, &un->tm); +diff --git a/src/url.lua b/src/url.lua +index 7809535..fbd93d1 100644 +--- a/src/url.lua ++++ b/src/url.lua +@@ -219,6 +219,7 @@ end + -- corresponding absolute url + ----------------------------------------------------------------------------- + function _M.absolute(base_url, relative_url) ++ local base_parsed + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = _M.build(base_parsed) +diff --git a/src/usocket.c b/src/usocket.c +index 096ecd0..8adc573 100644 +--- a/src/usocket.c ++++ b/src/usocket.c +@@ -4,12 +4,13 @@ + * + * The code is now interrupt-safe. + * The penalty of calling select to avoid busy-wait is only paid when +-* the I/O call fail in the first place. ++* the I/O call fail in the first place. + \*=========================================================================*/ +-#include <string.h> ++#include <string.h> + #include <signal.h> + + #include "socket.h" ++#include "pierror.h" + + /*-------------------------------------------------------------------------*\ + * Wait for readable/writable/connected socket with timeout +@@ -72,7 +73,7 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + + + /*-------------------------------------------------------------------------*\ +-* Initializes module ++* Initializes module + \*-------------------------------------------------------------------------*/ + int socket_open(void) { + /* instals a handler to ignore sigpipe or it will crash us */ +@@ -81,7 +82,7 @@ int socket_open(void) { + } + + /*-------------------------------------------------------------------------*\ +-* Close module ++* Close module + \*-------------------------------------------------------------------------*/ + int socket_close(void) { + return 1; +@@ -92,7 +93,6 @@ int socket_close(void) { + \*-------------------------------------------------------------------------*/ + void socket_destroy(p_socket ps) { + if (*ps != SOCKET_INVALID) { +- socket_setblocking(ps); + close(*ps); + *ps = SOCKET_INVALID; + } +@@ -101,7 +101,7 @@ void socket_destroy(p_socket ps) { + /*-------------------------------------------------------------------------*\ + * Select with timeout control + \*-------------------------------------------------------------------------*/ +-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, ++int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { + int ret; + do { +@@ -120,8 +120,8 @@ int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + \*-------------------------------------------------------------------------*/ + int socket_create(p_socket ps, int domain, int type, int protocol) { + *ps = socket(domain, type, protocol); +- if (*ps != SOCKET_INVALID) return IO_DONE; +- else return errno; ++ if (*ps != SOCKET_INVALID) return IO_DONE; ++ else return errno; + } + + /*-------------------------------------------------------------------------*\ +@@ -130,29 +130,25 @@ int socket_create(p_socket ps, int domain, int type, int protocol) { + int socket_bind(p_socket ps, SA *addr, socklen_t len) { + int err = IO_DONE; + socket_setblocking(ps); +- if (bind(*ps, addr, len) < 0) err = errno; ++ if (bind(*ps, addr, len) < 0) err = errno; + socket_setnonblocking(ps); + return err; + } + + /*-------------------------------------------------------------------------*\ +-* ++* + \*-------------------------------------------------------------------------*/ + int socket_listen(p_socket ps, int backlog) { +- int err = IO_DONE; +- socket_setblocking(ps); +- if (listen(*ps, backlog)) err = errno; +- socket_setnonblocking(ps); ++ int err = IO_DONE; ++ if (listen(*ps, backlog)) err = errno; + return err; + } + + /*-------------------------------------------------------------------------*\ +-* ++* + \*-------------------------------------------------------------------------*/ + void socket_shutdown(p_socket ps, int how) { +- socket_setblocking(ps); + shutdown(*ps, how); +- socket_setnonblocking(ps); + } + + /*-------------------------------------------------------------------------*\ +@@ -166,7 +162,7 @@ int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + do if (connect(*ps, addr, len) == 0) return IO_DONE; + while ((err = errno) == EINTR); + /* if connection failed immediately, return error code */ +- if (err != EINPROGRESS && err != EAGAIN) return err; ++ if (err != EINPROGRESS && err != EAGAIN) return err; + /* zero timeout case optimization */ + if (timeout_iszero(tm)) return IO_TIMEOUT; + /* wait until we have the result of the connection attempt or timeout */ +@@ -181,7 +177,7 @@ int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + * Accept with timeout + \*-------------------------------------------------------------------------*/ + int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout tm) { +- if (*ps == SOCKET_INVALID) return IO_CLOSED; ++ if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { + int err; + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; +@@ -197,7 +193,7 @@ int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, p_timeout + /*-------------------------------------------------------------------------*\ + * Send with timeout + \*-------------------------------------------------------------------------*/ +-int socket_send(p_socket ps, const char *data, size_t count, ++int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) + { + int err; +@@ -215,6 +211,8 @@ int socket_send(p_socket ps, const char *data, size_t count, + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; ++ /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ ++ if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ +@@ -229,20 +227,21 @@ int socket_send(p_socket ps, const char *data, size_t count, + /*-------------------------------------------------------------------------*\ + * Sendto with timeout + \*-------------------------------------------------------------------------*/ +-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, ++int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) + { + int err; + *sent = 0; + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { +- long put = (long) sendto(*ps, data, count, 0, addr, len); ++ long put = (long) sendto(*ps, data, count, 0, addr, len); + if (put >= 0) { + *sent = put; + return IO_DONE; + } + err = errno; + if (err == EPIPE) return IO_CLOSED; ++ if (err == EPROTOTYPE) continue; + if (err == EINTR) continue; + if (err != EAGAIN) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +@@ -266,8 +265,8 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; +- if (err != EAGAIN) return err; +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; ++ if (err != EAGAIN) return err; ++ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; + } +@@ -275,7 +274,7 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm + /*-------------------------------------------------------------------------*\ + * Recvfrom with timeout + \*-------------------------------------------------------------------------*/ +-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, ++int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + SA *addr, socklen_t *len, p_timeout tm) { + int err; + *got = 0; +@@ -289,8 +288,8 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; +- if (err != EAGAIN) return err; +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; ++ if (err != EAGAIN) return err; ++ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; + } +@@ -303,7 +302,7 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + * with send/recv replaced with write/read. We can't just use write/read + * in the socket version, because behaviour when size is zero is different. + \*-------------------------------------------------------------------------*/ +-int socket_write(p_socket ps, const char *data, size_t count, ++int socket_write(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) + { + int err; +@@ -321,6 +320,8 @@ int socket_write(p_socket ps, const char *data, size_t count, + err = errno; + /* EPIPE means the connection was closed */ + if (err == EPIPE) return IO_CLOSED; ++ /* EPROTOTYPE means the connection is being closed (on Yosemite!)*/ ++ if (err == EPROTOTYPE) continue; + /* we call was interrupted, just try again */ + if (err == EINTR) continue; + /* if failed fatal reason, report error */ +@@ -349,8 +350,8 @@ int socket_read(p_socket ps, char *data, size_t count, size_t *got, p_timeout tm + err = errno; + if (taken == 0) return IO_CLOSED; + if (err == EINTR) continue; +- if (err != EAGAIN) return err; +- if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; ++ if (err != EAGAIN) return err; ++ if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; + } + return IO_UNKNOWN; + } +@@ -374,7 +375,7 @@ void socket_setnonblocking(p_socket ps) { + } + + /*-------------------------------------------------------------------------*\ +-* DNS helpers ++* DNS helpers + \*-------------------------------------------------------------------------*/ + int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); +@@ -399,7 +400,7 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) { + const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { +- case HOST_NOT_FOUND: return "host not found"; ++ case HOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; + default: return hstrerror(err); + } + } +@@ -407,42 +408,43 @@ const char *socket_hoststrerror(int err) { + const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { +- case EADDRINUSE: return "address already in use"; +- case EISCONN: return "already connected"; +- case EACCES: return "permission denied"; +- case ECONNREFUSED: return "connection refused"; +- case ECONNABORTED: return "closed"; +- case ECONNRESET: return "closed"; +- case ETIMEDOUT: return "timeout"; +- default: return strerror(err); ++ case EADDRINUSE: return PIE_ADDRINUSE; ++ case EISCONN: return PIE_ISCONN; ++ case EACCES: return PIE_ACCESS; ++ case ECONNREFUSED: return PIE_CONNREFUSED; ++ case ECONNABORTED: return PIE_CONNABORTED; ++ case ECONNRESET: return PIE_CONNRESET; ++ case ETIMEDOUT: return PIE_TIMEDOUT; ++ default: { ++ return strerror(err); ++ } + } + } + + const char *socket_ioerror(p_socket ps, int err) { + (void) ps; + return socket_strerror(err); +-} ++} + + const char *socket_gaistrerror(int err) { +- if (err == 0) return NULL; ++ if (err == 0) return NULL; + switch (err) { +- case EAI_AGAIN: return "temporary failure in name resolution"; +- case EAI_BADFLAGS: return "invalid value for ai_flags"; ++ case EAI_AGAIN: return PIE_AGAIN; ++ case EAI_BADFLAGS: return PIE_BADFLAGS; + #ifdef EAI_BADHINTS +- case EAI_BADHINTS: return "invalid value for hints"; ++ case EAI_BADHINTS: return PIE_BADHINTS; + #endif +- case EAI_FAIL: return "non-recoverable failure in name resolution"; +- case EAI_FAMILY: return "ai_family not supported"; +- case EAI_MEMORY: return "memory allocation failure"; +- case EAI_NONAME: +- return "host or service not provided, or not known"; +- case EAI_OVERFLOW: return "argument buffer overflow"; ++ case EAI_FAIL: return PIE_FAIL; ++ case EAI_FAMILY: return PIE_FAMILY; ++ case EAI_MEMORY: return PIE_MEMORY; ++ case EAI_NONAME: return PIE_NONAME; ++ case EAI_OVERFLOW: return PIE_OVERFLOW; + #ifdef EAI_PROTOCOL +- case EAI_PROTOCOL: return "resolved protocol is unknown"; ++ case EAI_PROTOCOL: return PIE_PROTOCOL; + #endif +- case EAI_SERVICE: return "service not supported for socket type"; +- case EAI_SOCKTYPE: return "ai_socktype not supported"; +- case EAI_SYSTEM: return strerror(errno); ++ case EAI_SERVICE: return PIE_SERVICE; ++ case EAI_SOCKTYPE: return PIE_SOCKTYPE; ++ case EAI_SYSTEM: return strerror(errno); + default: return gai_strerror(err); + } + } +diff --git a/src/wsocket.c b/src/wsocket.c +index b4a4384..8ecb0fc 100644 +--- a/src/wsocket.c ++++ b/src/wsocket.c +@@ -3,33 +3,34 @@ + * LuaSocket toolkit + * + * The penalty of calling select to avoid busy-wait is only paid when +-* the I/O call fail in the first place. ++* the I/O call fail in the first place. + \*=========================================================================*/ + #include <string.h> + + #include "socket.h" ++#include "pierror.h" + + /* WinSock doesn't have a strerror... */ + static const char *wstrerror(int err); + + /*-------------------------------------------------------------------------*\ +-* Initializes module ++* Initializes module + \*-------------------------------------------------------------------------*/ + int socket_open(void) { + WSADATA wsaData; +- WORD wVersionRequested = MAKEWORD(2, 0); ++ WORD wVersionRequested = MAKEWORD(2, 0); + int err = WSAStartup(wVersionRequested, &wsaData ); + if (err != 0) return 0; + if ((LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 0) && + (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)) { + WSACleanup(); +- return 0; ++ return 0; + } + return 1; + } + + /*-------------------------------------------------------------------------*\ +-* Close module ++* Close module + \*-------------------------------------------------------------------------*/ + int socket_close(void) { + WSACleanup(); +@@ -50,10 +51,10 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + struct timeval tv, *tp = NULL; + double t; + if (timeout_iszero(tm)) return IO_TIMEOUT; /* optimize timeout == 0 case */ +- if (sw & WAITFD_R) { +- FD_ZERO(&rfds); ++ if (sw & WAITFD_R) { ++ FD_ZERO(&rfds); + FD_SET(*ps, &rfds); +- rp = &rfds; ++ rp = &rfds; + } + if (sw & WAITFD_W) { FD_ZERO(&wfds); FD_SET(*ps, &wfds); wp = &wfds; } + if (sw & WAITFD_C) { FD_ZERO(&efds); FD_SET(*ps, &efds); ep = &efds; } +@@ -72,9 +73,9 @@ int socket_waitfd(p_socket ps, int sw, p_timeout tm) { + /*-------------------------------------------------------------------------*\ + * Select with int timeout in ms + \*-------------------------------------------------------------------------*/ +-int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, ++int socket_select(t_socket n, fd_set *rfds, fd_set *wfds, fd_set *efds, + p_timeout tm) { +- struct timeval tv; ++ struct timeval tv; + double t = timeout_get(tm); + tv.tv_sec = (int) t; + tv.tv_usec = (int) ((t - tv.tv_sec) * 1.0e6); +@@ -96,7 +97,7 @@ void socket_destroy(p_socket ps) { + } + + /*-------------------------------------------------------------------------*\ +-* ++* + \*-------------------------------------------------------------------------*/ + void socket_shutdown(p_socket ps, int how) { + socket_setblocking(ps); +@@ -134,10 +135,10 @@ int socket_connect(p_socket ps, SA *addr, socklen_t len, p_timeout tm) { + /* give windows time to set the error (yes, disgusting) */ + Sleep(10); + /* find out why we failed */ +- getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); ++ getsockopt(*ps, SOL_SOCKET, SO_ERROR, (char *)&err, &len); + /* we KNOW there was an error. if 'why' is 0, we will return + * "unknown error", but it's not really our fault */ +- return err > 0? err: IO_UNKNOWN; ++ return err > 0? err: IO_UNKNOWN; + } else return err; + + } +@@ -154,7 +155,7 @@ int socket_bind(p_socket ps, SA *addr, socklen_t len) { + } + + /*-------------------------------------------------------------------------*\ +-* ++* + \*-------------------------------------------------------------------------*/ + int socket_listen(p_socket ps, int backlog) { + int err = IO_DONE; +@@ -167,7 +168,7 @@ int socket_listen(p_socket ps, int backlog) { + /*-------------------------------------------------------------------------*\ + * Accept with timeout + \*-------------------------------------------------------------------------*/ +-int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, ++int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + p_timeout tm) { + if (*ps == SOCKET_INVALID) return IO_CLOSED; + for ( ;; ) { +@@ -175,21 +176,21 @@ int socket_accept(p_socket ps, p_socket pa, SA *addr, socklen_t *len, + /* try to get client socket */ + if ((*pa = accept(*ps, addr, len)) != SOCKET_INVALID) return IO_DONE; + /* find out why we failed */ +- err = WSAGetLastError(); ++ err = WSAGetLastError(); + /* if we failed because there was no connectoin, keep trying */ + if (err != WSAEWOULDBLOCK && err != WSAECONNABORTED) return err; + /* call select to avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_R, tm)) != IO_DONE) return err; +- } ++ } + } + + /*-------------------------------------------------------------------------*\ + * Send with timeout +-* On windows, if you try to send 10MB, the OS will buffer EVERYTHING +-* this can take an awful lot of time and we will end up blocked. ++* On windows, if you try to send 10MB, the OS will buffer EVERYTHING ++* this can take an awful lot of time and we will end up blocked. + * Therefore, whoever calls this function should not pass a huge buffer. + \*-------------------------------------------------------------------------*/ +-int socket_send(p_socket ps, const char *data, size_t count, ++int socket_send(p_socket ps, const char *data, size_t count, + size_t *sent, p_timeout tm) + { + int err; +@@ -206,18 +207,18 @@ int socket_send(p_socket ps, const char *data, size_t count, + return IO_DONE; + } + /* deal with failure */ +- err = WSAGetLastError(); ++ err = WSAGetLastError(); + /* we can only proceed if there was no serious error */ + if (err != WSAEWOULDBLOCK) return err; + /* avoid busy wait */ + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } ++ } + } + + /*-------------------------------------------------------------------------*\ + * Sendto with timeout + \*-------------------------------------------------------------------------*/ +-int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, ++int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + SA *addr, socklen_t len, p_timeout tm) + { + int err; +@@ -229,17 +230,17 @@ int socket_sendto(p_socket ps, const char *data, size_t count, size_t *sent, + *sent = put; + return IO_DONE; + } +- err = WSAGetLastError(); ++ err = WSAGetLastError(); + if (err != WSAEWOULDBLOCK) return err; + if ((err = socket_waitfd(ps, WAITFD_W, tm)) != IO_DONE) return err; +- } ++ } + } + + /*-------------------------------------------------------------------------*\ + * Receive with timeout + \*-------------------------------------------------------------------------*/ +-int socket_recv(p_socket ps, char *data, size_t count, size_t *got, +- p_timeout tm) ++int socket_recv(p_socket ps, char *data, size_t count, size_t *got, ++ p_timeout tm) + { + int err, prev = IO_DONE; + *got = 0; +@@ -252,9 +253,9 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. +- * On TCP, it means our socket is now useless, so the error passes. ++ /* On UDP, a connreset simply means the previous send failed. ++ * So we try again. ++ * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { + if (err != WSAECONNRESET || prev == WSAECONNRESET) return err; +@@ -267,8 +268,8 @@ int socket_recv(p_socket ps, char *data, size_t count, size_t *got, + /*-------------------------------------------------------------------------*\ + * Recvfrom with timeout + \*-------------------------------------------------------------------------*/ +-int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, +- SA *addr, socklen_t *len, p_timeout tm) ++int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, ++ SA *addr, socklen_t *len, p_timeout tm) + { + int err, prev = IO_DONE; + *got = 0; +@@ -281,8 +282,8 @@ int socket_recvfrom(p_socket ps, char *data, size_t count, size_t *got, + } + if (taken == 0) return IO_CLOSED; + err = WSAGetLastError(); +- /* On UDP, a connreset simply means the previous send failed. +- * So we try again. ++ /* On UDP, a connreset simply means the previous send failed. ++ * So we try again. + * On TCP, it means our socket is now useless, so the error passes. + * (We will loop again, exiting because the same error will happen) */ + if (err != WSAEWOULDBLOCK) { +@@ -310,7 +311,7 @@ void socket_setnonblocking(p_socket ps) { + } + + /*-------------------------------------------------------------------------*\ +-* DNS helpers ++* DNS helpers + \*-------------------------------------------------------------------------*/ + int socket_gethostbyaddr(const char *addr, socklen_t len, struct hostent **hp) { + *hp = gethostbyaddr(addr, len, AF_INET); +@@ -330,21 +331,21 @@ int socket_gethostbyname(const char *addr, struct hostent **hp) { + const char *socket_hoststrerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { +- case WSAHOST_NOT_FOUND: return "host not found"; +- default: return wstrerror(err); ++ case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; ++ default: return wstrerror(err); + } + } + + const char *socket_strerror(int err) { + if (err <= 0) return io_strerror(err); + switch (err) { +- case WSAEADDRINUSE: return "address already in use"; +- case WSAECONNREFUSED: return "connection refused"; +- case WSAEISCONN: return "already connected"; +- case WSAEACCES: return "permission denied"; +- case WSAECONNABORTED: return "closed"; +- case WSAECONNRESET: return "closed"; +- case WSAETIMEDOUT: return "timeout"; ++ case WSAEADDRINUSE: return PIE_ADDRINUSE; ++ case WSAECONNREFUSED : return PIE_CONNREFUSED; ++ case WSAEISCONN: return PIE_ISCONN; ++ case WSAEACCES: return PIE_ACCESS; ++ case WSAECONNABORTED: return PIE_CONNABORTED; ++ case WSAECONNRESET: return PIE_CONNRESET; ++ case WSAETIMEDOUT: return PIE_TIMEDOUT; + default: return wstrerror(err); + } + } +@@ -357,7 +358,7 @@ const char *socket_ioerror(p_socket ps, int err) { + static const char *wstrerror(int err) { + switch (err) { + case WSAEINTR: return "Interrupted function call"; +- case WSAEACCES: return "Permission denied"; ++ case WSAEACCES: return PIE_ACCESS; // "Permission denied"; + case WSAEFAULT: return "Bad address"; + case WSAEINVAL: return "Invalid argument"; + case WSAEMFILE: return "Too many open files"; +@@ -370,63 +371,61 @@ static const char *wstrerror(int err) { + case WSAEPROTOTYPE: return "Protocol wrong type for socket"; + case WSAENOPROTOOPT: return "Bad protocol option"; + case WSAEPROTONOSUPPORT: return "Protocol not supported"; +- case WSAESOCKTNOSUPPORT: return "Socket type not supported"; ++ case WSAESOCKTNOSUPPORT: return PIE_SOCKTYPE; // "Socket type not supported"; + case WSAEOPNOTSUPP: return "Operation not supported"; + case WSAEPFNOSUPPORT: return "Protocol family not supported"; +- case WSAEAFNOSUPPORT: +- return "Address family not supported by protocol family"; +- case WSAEADDRINUSE: return "Address already in use"; ++ case WSAEAFNOSUPPORT: return PIE_FAMILY; // "Address family not supported by protocol family"; ++ case WSAEADDRINUSE: return PIE_ADDRINUSE; // "Address already in use"; + case WSAEADDRNOTAVAIL: return "Cannot assign requested address"; + case WSAENETDOWN: return "Network is down"; + case WSAENETUNREACH: return "Network is unreachable"; + case WSAENETRESET: return "Network dropped connection on reset"; + case WSAECONNABORTED: return "Software caused connection abort"; +- case WSAECONNRESET: return "Connection reset by peer"; ++ case WSAECONNRESET: return PIE_CONNRESET; // "Connection reset by peer"; + case WSAENOBUFS: return "No buffer space available"; +- case WSAEISCONN: return "Socket is already connected"; ++ case WSAEISCONN: return PIE_ISCONN; // "Socket is already connected"; + case WSAENOTCONN: return "Socket is not connected"; + case WSAESHUTDOWN: return "Cannot send after socket shutdown"; +- case WSAETIMEDOUT: return "Connection timed out"; +- case WSAECONNREFUSED: return "Connection refused"; ++ case WSAETIMEDOUT: return PIE_TIMEDOUT; // "Connection timed out"; ++ case WSAECONNREFUSED: return PIE_CONNREFUSED; // "Connection refused"; + case WSAEHOSTDOWN: return "Host is down"; + case WSAEHOSTUNREACH: return "No route to host"; + case WSAEPROCLIM: return "Too many processes"; + case WSASYSNOTREADY: return "Network subsystem is unavailable"; + case WSAVERNOTSUPPORTED: return "Winsock.dll version out of range"; +- case WSANOTINITIALISED: ++ case WSANOTINITIALISED: + return "Successful WSAStartup not yet performed"; + case WSAEDISCON: return "Graceful shutdown in progress"; +- case WSAHOST_NOT_FOUND: return "Host not found"; ++ case WSAHOST_NOT_FOUND: return PIE_HOST_NOT_FOUND; // "Host not found"; + case WSATRY_AGAIN: return "Nonauthoritative host not found"; +- case WSANO_RECOVERY: return "Nonrecoverable name lookup error"; ++ case WSANO_RECOVERY: return PIE_FAIL; // "Nonrecoverable name lookup error"; + case WSANO_DATA: return "Valid name, no data record of requested type"; + default: return "Unknown error"; + } + } + + const char *socket_gaistrerror(int err) { +- if (err == 0) return NULL; ++ if (err == 0) return NULL; + switch (err) { +- case EAI_AGAIN: return "temporary failure in name resolution"; +- case EAI_BADFLAGS: return "invalid value for ai_flags"; ++ case EAI_AGAIN: return PIE_AGAIN; ++ case EAI_BADFLAGS: return PIE_BADFLAGS; + #ifdef EAI_BADHINTS +- case EAI_BADHINTS: return "invalid value for hints"; ++ case EAI_BADHINTS: return PIE_BADHINTS; + #endif +- case EAI_FAIL: return "non-recoverable failure in name resolution"; +- case EAI_FAMILY: return "ai_family not supported"; +- case EAI_MEMORY: return "memory allocation failure"; +- case EAI_NONAME: +- return "host or service not provided, or not known"; ++ case EAI_FAIL: return PIE_FAIL; ++ case EAI_FAMILY: return PIE_FAMILY; ++ case EAI_MEMORY: return PIE_MEMORY; ++ case EAI_NONAME: return PIE_NONAME; + #ifdef EAI_OVERFLOW +- case EAI_OVERFLOW: return "argument buffer overflow"; ++ case EAI_OVERFLOW: return PIE_OVERFLOW; + #endif + #ifdef EAI_PROTOCOL +- case EAI_PROTOCOL: return "resolved protocol is unknown"; ++ case EAI_PROTOCOL: return PIE_PROTOCOL; + #endif +- case EAI_SERVICE: return "service not supported for socket type"; +- case EAI_SOCKTYPE: return "ai_socktype not supported"; ++ case EAI_SERVICE: return PIE_SERVICE; ++ case EAI_SOCKTYPE: return PIE_SOCKTYPE; + #ifdef EAI_SYSTEM +- case EAI_SYSTEM: return strerror(errno); ++ case EAI_SYSTEM: return strerror(errno); + #endif + default: return gai_strerror(err); + } +diff --git a/test/auth/.htaccess b/test/auth/.htaccess +new file mode 100644 +index 0000000..bb2794a +--- /dev/null ++++ b/test/auth/.htaccess +@@ -0,0 +1,4 @@ ++AuthName "test-auth" ++ AuthType Basic ++ AuthUserFile /Users/diego/impa/luasocket/test/auth/.htpasswd ++ Require valid-user +diff --git a/test/auth/.htpasswd b/test/auth/.htpasswd +index fd9002b..cfb2603 100644 +--- a/test/auth/.htpasswd ++++ b/test/auth/.htpasswd +@@ -1 +1 @@ +-luasocket:l8n2npozPB.sQ ++luasocket:$apr1$47u2O.Me$.m/5BWAtt7GVoxsouIPBR1 +diff --git a/test/excepttest.lua b/test/excepttest.lua +index ce9f197..80c9cb8 100644 +--- a/test/excepttest.lua ++++ b/test/excepttest.lua +@@ -1,6 +1,30 @@ + local socket = require("socket") +-try = socket.newtry(function() +- print("finalized!!!") ++ ++local finalizer_called ++ ++local func = socket.protect(function(err, ...) ++ local try = socket.newtry(function() ++ finalizer_called = true ++ end) ++ ++ if err then ++ return error(err, 0) ++ else ++ return try(...) ++ end + end) +-try = socket.protect(try) +-print(try(nil, "it works")) ++ ++local ret1, ret2, ret3 = func(false, 1, 2, 3) ++assert(not finalizer_called, "unexpected finalizer call") ++assert(ret1 == 1 and ret2 == 2 and ret3 == 3, "incorrect return values") ++ ++ret1, ret2, ret3 = func(false, false, "error message") ++assert(finalizer_called, "finalizer not called") ++assert(ret1 == nil and ret2 == "error message" and ret3 == nil, "incorrect return values") ++ ++local err = {key = "value"} ++ret1, ret2 = pcall(func, err) ++assert(not ret1, "error not rethrown") ++assert(ret2 == err, "incorrect error rethrown") ++ ++print("OK") +diff --git a/test/httptest.lua b/test/httptest.lua +index d5fbb37..63ff921 100644 +--- a/test/httptest.lua ++++ b/test/httptest.lua +@@ -1,4 +1,4 @@ +--- needs Alias from /home/c/diego/tec/luasocket/test to ++-- needs Alias from /home/c/diego/tec/luasocket/test to + -- "/luasocket-test" and "/luasocket-test/" + -- needs ScriptAlias from /home/c/diego/tec/luasocket/test/cgi + -- to "/luasocket-test-cgi" and "/luasocket-test-cgi/" +@@ -36,22 +36,22 @@ index = readfile(index_file) + local check_result = function(response, expect, ignore) + for i,v in pairs(response) do + if not ignore[i] then +- if v ~= expect[i] then ++ if v ~= expect[i] then + local f = io.open("err", "w") + f:write(tostring(v), "\n\n versus\n\n", tostring(expect[i])) + f:close() +- fail(i .. " differs!") ++ fail(i .. " differs!") + end + end + end + for i,v in pairs(expect) do + if not ignore[i] then +- if v ~= response[i] then ++ if v ~= response[i] then + local f = io.open("err", "w") + f:write(tostring(response[i]), "\n\n versus\n\n", tostring(v)) + v = string.sub(type(v) == "string" and v or "", 1, 70) + f:close() +- fail(i .. " differs!") ++ fail(i .. " differs!") + end + end + end +@@ -61,10 +61,10 @@ end + local check_request = function(request, expect, ignore) + local t + if not request.sink then request.sink, t = ltn12.sink.table() end +- request.source = request.source or ++ request.source = request.source or + (request.body and ltn12.source.string(request.body)) + local response = {} +- response.code, response.headers, response.status = ++ response.code, response.headers, response.status = + socket.skip(1, http.request(request)) + if t and #t > 0 then response.body = table.concat(t) end + check_result(response, expect, ignore) +@@ -82,7 +82,7 @@ else fail(back.query) end + ------------------------------------------------------------------------ + io.write("testing query string correctness: ") + forth = "this+is+the+query+string" +-back = http.request("http://" .. host .. cgiprefix .. ++back = http.request("http://" .. host .. cgiprefix .. + "/query-string?" .. forth) + if similar(back, forth) then print("ok") + else fail("failed!") end +@@ -120,10 +120,10 @@ check_request(request, expect, ignore) + ------------------------------------------------------------------------ + io.write("testing invalid url: ") + local r, e = http.request{url = host .. prefix} +-assert(r == nil and e == "invalid host ''") ++assert(r == nil and e == "invalid host ''") + r, re = http.request(host .. prefix) +-assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) .. +- " vs " .. tostring(e)) ++assert(r == nil and e == re, tostring(r) ..", " .. tostring(re) .. ++ " vs " .. tostring(e)) + print("ok") + + io.write("testing invalid empty port: ") +@@ -212,7 +212,7 @@ os.remove(index_file .. "-back") + io.write("testing ltn12.(sink|source).chain and mime.(encode|decode): ") + + local function b64length(len) +- local a = math.ceil(len/3)*4 ++ local a = math.ceil(len/3)*4 + local l = math.ceil(a/76) + return a + l*2 + end +@@ -313,7 +313,7 @@ ignore = { + headers = 1 + } + check_request(request, expect, ignore) +- ++ + ------------------------------------------------------------------------ + io.write("testing document not found: ") + request = { +@@ -429,9 +429,9 @@ print("ok") + io.write("testing host not found: ") + local c, e = socket.connect("example.invalid", 80) + local r, re = http.request{url = "http://example.invalid/does/not/exist"} +-assert(r == nil and e == re, tostring(r) .. " " .. tostring(re)) ++assert(r == nil and e == re, tostring(r) .. " " .. tostring(re)) + r, re = http.request("http://example.invalid/does/not/exist") +-assert(r == nil and e == re) ++assert(r == nil and e == re) + print("ok") + + ------------------------------------------------------------------------ +diff --git a/test/ltn12test.lua b/test/ltn12test.lua +index 74a45e8..e3f85fb 100644 +--- a/test/ltn12test.lua ++++ b/test/ltn12test.lua +@@ -192,6 +192,21 @@ assert(filter(nil, 1), "filter not empty") + print("ok") + + -------------------------------- ++io.write("testing source.chain (with several filters): ") ++local function double(x) -- filter turning "ABC" into "AABBCC" ++ if not x then return end ++ local b={} ++ for k in x:gmatch'.' do table.insert(b, k..k) end ++ return table.concat(b) ++end ++source = ltn12.source.string(s) ++source = ltn12.source.chain(source, double, double, double) ++sink, t = ltn12.sink.table() ++assert(ltn12.pump.all(source, sink), "returned error") ++assert(table.concat(t) == double(double(double(s))), "mismatch") ++print("ok") ++ ++-------------------------------- + io.write("testing source.chain (with split) and sink.chain (with merge): ") + source = ltn12.source.string(s) + filter = split(5) +@@ -206,6 +221,15 @@ assert(filter2(nil, 1), "filter2 not empty") + print("ok") + + -------------------------------- ++io.write("testing sink.chain (with several filters): ") ++source = ltn12.source.string(s) ++sink, t = ltn12.sink.table() ++sink = ltn12.sink.chain(double, double, double, sink) ++assert(ltn12.pump.all(source, sink), "returned error") ++assert(table.concat(t) == double(double(double(s))), "mismatch") ++print("ok") ++ ++-------------------------------- + io.write("testing filter.chain (and sink.chain, with split, merge): ") + source = ltn12.source.string(s) + filter = split(5) +@@ -272,3 +296,4 @@ assert(filter3(nil, 1), "filter3 not empty") + assert(filter4(nil, 1), "filter4 not empty") + assert(filter5(nil, 1), "filter5 not empty") + print("ok") ++ +diff --git a/test/testclnt.lua b/test/testclnt.lua +index 315783b..170e187 100644 +--- a/test/testclnt.lua ++++ b/test/testclnt.lua +@@ -8,7 +8,7 @@ function printf(...) + end + + function pass(...) +- printf(...) ++ printf(...) + io.stderr:write("\n") + end + +@@ -45,30 +45,30 @@ function check_timeout(tm, sl, elapsed, err, opp, mode, alldone) + if not err then warn("must be buffered") + elseif err == "timeout" then pass("proper timeout") + else fail("unexpected error '%s'", err) end +- else +- if err ~= "timeout" then fail("should have timed out") ++ else ++ if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + end + else + if mode == "total" then +- if elapsed > tm then ++ if elapsed > tm then + if err ~= "timeout" then fail("should have timed out") + else pass("proper timeout") end + elseif elapsed < tm then +- if err then fail(err) ++ if err then fail(err) + else pass("ok") end +- else +- if alldone then +- if err then fail("unexpected error '%s'", err) ++ else ++ if alldone then ++ if err then fail("unexpected error '%s'", err) + else pass("ok") end + else +- if err ~= "timeout" then fail(err) ++ if err ~= "timeout" then fail(err) + else pass("proper timeoutk") end + end + end +- else +- if err then fail(err) +- else pass("ok") end ++ else ++ if err then fail(err) ++ else pass("ok") end + end + end + end +@@ -104,8 +104,8 @@ control:setoption("tcp-nodelay", true) + ------------------------------------------------------------------------ + function test_methods(sock, methods) + for _, v in pairs(methods) do +- if type(sock[v]) ~= "function" then +- fail(sock.class .. " method '" .. v .. "' not registered") ++ if type(sock[v]) ~= "function" then ++ fail(sock.class .. " method '" .. v .. "' not registered") + end + end + pass(sock.class .. " methods are ok") +@@ -121,7 +121,7 @@ function test_mixed(len) + local p3 = "raw " .. string.rep("z", inter) .. "bytes" + local p4 = "end" .. string.rep("w", inter) .. "bytes" + local bp1, bp2, bp3, bp4 +-remote (string.format("str = data:receive(%d)", ++remote (string.format("str = data:receive(%d)", + string.len(p1)+string.len(p2)+string.len(p3)+string.len(p4))) + sent, err = data:send(p1..p2..p3..p4) + if err then fail(err) end +@@ -166,7 +166,7 @@ function test_rawline(len) + io.stderr:write("length " .. len .. ": ") + local str, str10, back, err + str = string.rep(string.char(47), math.mod(len, 10)) +- str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), ++ str10 = string.rep(string.char(120,21,77,4,5,0,7,36,44,100), + math.floor(len/10)) + str = str .. str10 + remote "str = data:receive()" +@@ -216,7 +216,7 @@ function test_totaltimeoutreceive(len, tm, sl) + data:settimeout(tm, "total") + local t = socket.gettime() + str, err, partial, elapsed = data:receive(2*len) +- check_timeout(tm, sl, elapsed, err, "receive", "total", ++ check_timeout(tm, sl, elapsed, err, "receive", "total", + string.len(str or partial) == 2*len) + end + +@@ -236,7 +236,7 @@ function test_totaltimeoutsend(len, tm, sl) + data:settimeout(tm, "total") + str = string.rep("a", 2*len) + total, err, partial, elapsed = data:send(str) +- check_timeout(tm, sl, elapsed, err, "send", "total", ++ check_timeout(tm, sl, elapsed, err, "send", "total", + total == 2*len) + end + +@@ -256,7 +256,7 @@ function test_blockingtimeoutreceive(len, tm, sl) + ]], 2*tm, len, sl, sl)) + data:settimeout(tm) + str, err, partial, elapsed = data:receive(2*len) +- check_timeout(tm, sl, elapsed, err, "receive", "blocking", ++ check_timeout(tm, sl, elapsed, err, "receive", "blocking", + string.len(str or partial) == 2*len) + end + +@@ -290,10 +290,10 @@ function empty_connect() + data = server:accept() + ]] + data, err = socket.connect("", port) +- if not data then ++ if not data then + pass("ok") + data = socket.connect(host, port) +- else ++ else + pass("gethostbyname returns localhost on empty string...") + end + end +@@ -304,15 +304,20 @@ function isclosed(c) + end + + function active_close() +- reconnect() +- if isclosed(data) then fail("should not be closed") end +- data:close() +- if not isclosed(data) then fail("should be closed") end +- data = nil +- local udp = socket.udp() ++ local tcp = socket.tcp4() ++ if isclosed(tcp) then fail("should not be closed") end ++ tcp:close() ++ if not isclosed(tcp) then fail("should be closed") end ++ tcp = socket.tcp() ++ if not isclosed(tcp) then fail("should be closed") end ++ tcp = nil ++ local udp = socket.udp4() + if isclosed(udp) then fail("should not be closed") end + udp:close() + if not isclosed(udp) then fail("should be closed") end ++ udp = socket.udp() ++ if not isclosed(udp) then fail("should be closed") end ++ udp = nil + pass("ok") + end + +@@ -327,7 +332,7 @@ function test_closed() + data:close() + data = nil + ]], str)) +- -- try to get a line ++ -- try to get a line + back, err, partial = data:receive() + if not err then fail("should have gotten 'closed'.") + elseif err ~= "closed" then fail("got '"..err.."' instead of 'closed'.") +@@ -340,25 +345,25 @@ function test_closed() + data = nil + ]] + total, err, partial = data:send(string.rep("ugauga", 100000)) +- if not err then ++ if not err then + pass("failed: output buffer is at least %d bytes long!", total) +- elseif err ~= "closed" then ++ elseif err ~= "closed" then + fail("got '"..err.."' instead of 'closed'.") +- else +- pass("graceful 'closed' received after %d bytes were sent", partial) ++ else ++ pass("graceful 'closed' received after %d bytes were sent", partial) + end + end + + ------------------------------------------------------------------------ + function test_selectbugs() + local r, s, e = socket.select(nil, nil, 0.1) +- assert(type(r) == "table" and type(s) == "table" and ++ assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("both nil: ok") + local udp = socket.udp() + udp:close() + r, s, e = socket.select({ udp }, { udp }, 0.1) +- assert(type(r) == "table" and type(s) == "table" and ++ assert(type(r) == "table" and type(s) == "table" and + (e == "timeout" or e == "error")) + pass("closed sockets: ok") + e = pcall(socket.select, "wrong", 1, 0.1) +@@ -368,7 +373,7 @@ function test_selectbugs() + pass("invalid input: ok") + local toomany = {} + for i = 1, socket._SETSIZE+1 do +- toomany[#toomany+1] = socket.udp() ++ toomany[#toomany+1] = socket.udp4() + end + if #toomany > socket._SETSIZE then + local e = pcall(socket.select, toomany, nil, 0.1) +@@ -389,7 +394,7 @@ function accept_timeout() + local t = socket.gettime() + s:settimeout(1) + local c, e = s:accept() +- assert(not c, "should not accept") ++ assert(not c, "should not accept") + assert(e == "timeout", string.format("wrong error message (%s)", e)) + t = socket.gettime() - t + assert(t < 2, string.format("took to long to give up (%gs)", t)) +@@ -407,9 +412,9 @@ function connect_timeout() + local t = socket.gettime() + local r, e = c:connect("10.0.0.1", 81) + assert(not r, "should not connect") +- assert(socket.gettime() - t < 2, "took too long to give up.") ++ assert(socket.gettime() - t < 2, "took too long to give up.") + c:close() +- pass("ok") ++ pass("ok") + end + + ------------------------------------------------------------------------ +@@ -447,16 +452,14 @@ end + + ------------------------------------------------------------------------ + function rebind_test() +- --local c ,c1 = socket.bind("localhost", 0) + local c ,c1 = socket.bind("127.0.0.1", 0) + if not c then pass ("failed to bind! " .. tostring(c) .. ' ' .. tostring(c1)) return end + assert(c,c1) +- + local i, p = c:getsockname() + local s, e = socket.tcp() + assert(s, e) + s:setoption("reuseaddr", false) +- r, e = s:bind("localhost", p) ++ r, e = s:bind(i, p) + assert(not r, "managed to rebind!") + assert(e) + pass("ok") +@@ -476,9 +479,9 @@ function getstats_test() + data:receive(c) + t = t + c + local r, s, a = data:getstats() +- assert(r == t, "received count failed" .. tostring(r) ++ assert(r == t, "received count failed" .. tostring(r) + .. "/" .. tostring(t)) +- assert(s == t, "sent count failed" .. tostring(s) ++ assert(s == t, "sent count failed" .. tostring(s) + .. "/" .. tostring(t)) + end + pass("ok") +@@ -486,7 +489,7 @@ end + + + ------------------------------------------------------------------------ +-function test_nonblocking(size) ++function test_nonblocking(size) + reconnect() + printf("testing " .. 2*size .. " bytes: ") + remote(string.format([[ +@@ -545,7 +548,7 @@ function test_readafterclose() + data:close() + data = nil + ]])) +- data:close() ++ data:close() + back, err, partial = data:receive("*a") + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") +@@ -555,7 +558,7 @@ function test_readafterclose() + data:close() + data = nil + ]])) +- data:close() ++ data:close() + back, err, partial = data:receive() + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") +@@ -565,7 +568,7 @@ function test_readafterclose() + data:close() + data = nil + ]])) +- data:close() ++ data:close() + back, err, partial = data:receive(1) + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") +@@ -575,7 +578,7 @@ function test_readafterclose() + data:close() + data = nil + ]])) +- data:close() ++ data:close() + back, err, partial = data:receive(0) + assert(back == nil and err == "closed", "should have returned 'closed'") + pass("ok") +@@ -590,10 +593,10 @@ function test_writeafterclose() + data = nil + ]])) + local sent, err, errsent +- while not err do ++ while not err do + sent, err, errsent, time = data:send(str) + end +- assert(err == "closed", "should have returned 'closed'") ++ assert(err == "closed", "got " .. err .. " instead of 'closed'") + pass("ok") + end + +@@ -648,25 +651,24 @@ else io.stderr:write("Warning! IPv6 does not support!\n") end + end + + local udp_methods = { +- "close", ++ "close", + "dirty", + "getfamily", + "getfd", + "getoption", + "getpeername", + "getsockname", +- "receive", +- "receivefrom", +- "send", +- "sendto", +- "setfd", ++ "receive", ++ "receivefrom", ++ "send", ++ "sendto", ++ "setfd", + "setoption", + "setpeername", + "setsockname", + "settimeout" + } + +- + ------------------------------------------------------------------------ + test_methods(socket.udp(), udp_methods) + do local sock = socket.tcp6() +@@ -674,6 +676,9 @@ if sock then test_methods(socket.udp6(), udp_methods) + else io.stderr:write("Warning! IPv6 does not support!\n") end + end + ++test("closed connection detection: ") ++test_closed() ++ + test("partial receive") + test_partialrecv() + +@@ -697,9 +702,6 @@ rebind_test() + test("active close: ") + active_close() + +-test("closed connection detection: ") +-test_closed() +- + test("accept function: ") + accept_timeout() + accept_errors() +diff --git a/test/testsrvr.lua b/test/testsrvr.lua +index 72b93ab..1eb2d5b 100644 +--- a/test/testsrvr.lua ++++ b/test/testsrvr.lua +@@ -6,7 +6,7 @@ ack = "\n"; + while 1 do + print("server: waiting for client connection..."); + control = assert(server:accept()); +- while 1 do ++ while 1 do + command, emsg = control:receive(); + if emsg == "closed" then + control:close() +diff --git a/test/udpconnectclnt.lua b/test/udpconnectclnt.lua +index effe13a..ad6ab6a 100644 +--- a/test/udpconnectclnt.lua ++++ b/test/udpconnectclnt.lua +@@ -1,7 +1,7 @@ + local socket = require"socket" + local udp = socket.udp + local localhost = "127.0.0.1" +-local port = arg[1] ++local port = assert(arg[1], "missing port argument") + + se = udp(); se:setoption("reuseaddr", true) + se:setsockname(localhost, 5062) +diff --git a/win32.cmd b/win32.cmd +index 48522f0..3045721 100644 +--- a/win32.cmd ++++ b/win32.cmd +@@ -1,12 +1 @@ +-make PLAT=win32 LUAV=5.2 LUAINC_win32='c:\cygwin\home\diego\build\include' LUALIB_win32='c:\cygwin\home\diego\build\bin\release' +- +-#!/bin/sh +-for p in Release Debug x64/Release x64/Debug; do +- for el in mime socket; do +- for e in dll lib; do +- cp $p/$el/core.$e ../bin/$p/$el/ +- done; +- done; +- cp src/ltn12.lua src/socket.lua src/mime.lua ../bin/$p/ +- cp src/http.lua src/url.lua src/tp.lua src/ftp.lua src/headers.lua src/smtp.lua ../bin/$p/socket/ +-done; ++make LUAPREFIX_win32='c:\cygwin\home\diego\vc12' LUAV=5.1 PLAT=win32 LUALIBNAME_win32=lualib.lib PLATFORM_win32=Debug install-both diff --git a/user/lua-socket/lua-cflags.patch b/user/lua-socket/lua-cflags.patch new file mode 100644 index 000000000..c1da89644 --- /dev/null +++ b/user/lua-socket/lua-cflags.patch @@ -0,0 +1,22 @@ +diff --git a/src/makefile b/src/makefile +index adf687f..c2abddc 100644 +--- a/src/makefile ++++ b/src/makefile +@@ -160,6 +160,8 @@ SOCKET_macosx=usocket.o + #------ + # Compiler and linker settings + # for Linux ++LUAPC=lua ++LUA_CFLAGS=$(shell pkg-config --cflags $(LUAPC)) + SO_linux=so + O_linux=o + CC_linux=gcc +@@ -167,7 +169,7 @@ DEF_linux=-DLUASOCKET_$(DEBUG) \ + -DLUASOCKET_API='__attribute__((visibility("default")))' \ + -DUNIX_API='__attribute__((visibility("default")))' \ + -DMIME_API='__attribute__((visibility("default")))' +-CFLAGS_linux= -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ ++CFLAGS_linux= $(LUA_CFLAGS) -I$(LUAINC) $(DEF) -Wall -Wshadow -Wextra \ + -Wimplicit -O2 -ggdb3 -fpic -fvisibility=hidden + LDFLAGS_linux=-O -shared -fpic -o + LD_linux=gcc |