Browse Source

Merge branch 'master' of https://github.com/bel2125/civetweb

Maarten Fremouw 10 years ago
parent
commit
b3819c9008

+ 5 - 0
.gitignore

@@ -237,3 +237,8 @@ requests.db
 ##########################
 ##########################
 tags
 tags
 
 
+##########################
+## Files created by autotools
+##########################
+*.lo
+.libs

+ 1 - 0
CREDITS.md

@@ -6,6 +6,7 @@
 * celeron55
 * celeron55
 * Daniel Oaks
 * Daniel Oaks
 * Danny Al-Gaaf
 * Danny Al-Gaaf
+* Dialga
 * F-Secure Corporation
 * F-Secure Corporation
 * HariKamath Kamath
 * HariKamath Kamath
 * Jordan Shelley
 * Jordan Shelley

+ 29 - 17
Makefile

@@ -25,7 +25,10 @@ DOCDIR = $(DATAROOTDIR)/doc/$(CPROG)
 SYSCONFDIR = $(PREFIX)/etc
 SYSCONFDIR = $(PREFIX)/etc
 HTMLDIR = $(DOCDIR)
 HTMLDIR = $(DOCDIR)
 
 
-UNAME := $(shell uname)
+# build tools
+MKDIR = mkdir -p
+RMF = rm -f
+RMRF = rm -rf
 
 
 # desired configuration of the document root
 # desired configuration of the document root
 # never assume that the document_root actually
 # never assume that the document_root actually
@@ -35,15 +38,17 @@ UNAME := $(shell uname)
 DOCUMENT_ROOT = $(HTMLDIR)
 DOCUMENT_ROOT = $(HTMLDIR)
 PORTS = 8080
 PORTS = 8080
 
 
-BUILD_DIRS += $(BUILD_DIR) $(BUILD_DIR)/src
+BUILD_DIRS = $(BUILD_DIR) $(BUILD_DIR)/src $(BUILD_DIR)/resources
 
 
 LIB_SOURCES = src/civetweb.c
 LIB_SOURCES = src/civetweb.c
 LIB_INLINE  = src/mod_lua.inl src/md5.inl
 LIB_INLINE  = src/mod_lua.inl src/md5.inl
 APP_SOURCES = src/main.c
 APP_SOURCES = src/main.c
+WINDOWS_RESOURCES = resources/res.rc
 UNIT_TEST_SOURCES = test/unit_test.c
 UNIT_TEST_SOURCES = test/unit_test.c
 SOURCE_DIRS =
 SOURCE_DIRS =
 
 
 OBJECTS = $(LIB_SOURCES:.c=.o) $(APP_SOURCES:.c=.o)
 OBJECTS = $(LIB_SOURCES:.c=.o) $(APP_SOURCES:.c=.o)
+BUILD_RESOURCES =
 
 
 # The unit tests include the source files directly to get visibility to the
 # The unit tests include the source files directly to get visibility to the
 # static functions.  So we clear OBJECTS so that we don't try to build or link
 # static functions.  So we clear OBJECTS so that we don't try to build or link
@@ -115,21 +120,25 @@ LIB_OBJECTS = $(filter-out $(MAIN_OBJECTS), $(BUILD_OBJECTS))
 
 
 ifeq ($(TARGET_OS),LINUX)
 ifeq ($(TARGET_OS),LINUX)
   LIBS += -ldl
   LIBS += -ldl
+  CAN_INSTALL = 1
 endif
 endif
 
 
-ifeq ($(TARGET_OS),LINUX)
-  CAN_INSTALL = 1
+ifeq ($(TARGET_OS),WIN32)
+  MKDIR = mkdir
+  RMF = del /q
+  RMRF = rmdir /s /q
 endif
 endif
 
 
 ifdef WITH_LUA_SHARED
 ifdef WITH_LUA_SHARED
   LIBS += -llua5.2
   LIBS += -llua5.2
 endif
 endif
 
 
-ifneq (, $(findstring MINGW32, $(UNAME)))
-  LIBS += -lws2_32 -lcomdlg32
-  SHARED_LIB=dll
+ifneq (, $(findstring mingw32, $(shell gcc -dumpmachine)))
+  BUILD_RESOURCES = $(BUILD_DIR)/$(WINDOWS_RESOURCES:.rc=.o)
+  LIBS := $(filter-out -lrt, $(LIBS)) -lws2_32 -lcomdlg32 -mwindows
+  SHARED_LIB = dll
 else
 else
-  SHARED_LIB=so
+  SHARED_LIB = so
 endif
 endif
 
 
 all: build
 all: build
@@ -214,16 +223,16 @@ lib: lib$(CPROG).a
 slib: lib$(CPROG).$(SHARED_LIB)
 slib: lib$(CPROG).$(SHARED_LIB)
 
 
 clean:
 clean:
-	rm -rf $(BUILD_DIR)
+	$(RMRF) $(BUILD_DIR)
 
 
 distclean: clean
 distclean: clean
-	@rm -rf VS2012/Debug VS2012/*/Debug  VS2012/*/*/Debug
-	@rm -rf VS2012/Release VS2012/*/Release  VS2012/*/*/Release
-	rm -f $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe lib$(CPROG).dll lib$(CPROG).dll.a
-	rm -f $(UNIT_TEST_PROG)
+	@$(RMRF) VS2012/Debug VS2012/*/Debug  VS2012/*/*/Debug
+	@$(RMRF) VS2012/Release VS2012/*/Release  VS2012/*/*/Release
+	$(RMF) $(CPROG) lib$(CPROG).so lib$(CPROG).a *.dmg *.msi *.exe lib$(CPROG).dll lib$(CPROG).dll.a
+	$(RMF) $(UNIT_TEST_PROG)
 
 
 lib$(CPROG).a: $(LIB_OBJECTS)
 lib$(CPROG).a: $(LIB_OBJECTS)
-	@rm -f $@
+	@$(RMF) $@
 	ar cq $@ $(LIB_OBJECTS)
 	ar cq $@ $(LIB_OBJECTS)
 
 
 lib$(CPROG).so: CFLAGS += -fPIC
 lib$(CPROG).so: CFLAGS += -fPIC
@@ -238,8 +247,8 @@ $(UNIT_TEST_PROG): CFLAGS += -Isrc
 $(UNIT_TEST_PROG): $(LIB_SOURCES) $(LIB_INLINE) $(UNIT_TEST_SOURCES) $(BUILD_OBJECTS)
 $(UNIT_TEST_PROG): $(LIB_SOURCES) $(LIB_INLINE) $(UNIT_TEST_SOURCES) $(BUILD_OBJECTS)
 	$(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(UNIT_TEST_SOURCES) $(BUILD_OBJECTS) $(LIBS)
 	$(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(UNIT_TEST_SOURCES) $(BUILD_OBJECTS) $(LIBS)
 
 
-$(CPROG): $(BUILD_OBJECTS)
-	$(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(LIBS)
+$(CPROG): $(BUILD_OBJECTS) $(BUILD_RESOURCES)
+	$(LCC) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(BUILD_RESOURCES) $(LIBS)
 
 
 $(CXXPROG): $(BUILD_OBJECTS)
 $(CXXPROG): $(BUILD_OBJECTS)
 	$(CXX) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(LIBS)
 	$(CXX) -o $@ $(CFLAGS) $(LDFLAGS) $(BUILD_OBJECTS) $(LIBS)
@@ -247,7 +256,7 @@ $(CXXPROG): $(BUILD_OBJECTS)
 $(BUILD_OBJECTS): $(BUILD_DIRS)
 $(BUILD_OBJECTS): $(BUILD_DIRS)
 
 
 $(BUILD_DIRS):
 $(BUILD_DIRS):
-	-@mkdir -p "$@"
+	-@$(MKDIR) "$@"
 
 
 $(BUILD_DIR)/%.o : %.cpp
 $(BUILD_DIR)/%.o : %.cpp
 	$(CXX) -c $(CFLAGS) $(CXXFLAGS) $< -o $@
 	$(CXX) -c $(CFLAGS) $(CXXFLAGS) $< -o $@
@@ -255,6 +264,9 @@ $(BUILD_DIR)/%.o : %.cpp
 $(BUILD_DIR)/%.o : %.c
 $(BUILD_DIR)/%.o : %.c
 	$(CC) -c $(CFLAGS) $< -o $@
 	$(CC) -c $(CFLAGS) $< -o $@
 
 
+$(BUILD_RESOURCES) : $(WINDOWS_RESOURCES)
+	windres $< $@
+
 # This rules is used to keep the code formatted in a reasonable manor
 # This rules is used to keep the code formatted in a reasonable manor
 # For this to work astyle must be installed and in the path
 # For this to work astyle must be installed and in the path
 # http://sourceforge.net/projects/astyle
 # http://sourceforge.net/projects/astyle

+ 5 - 0
RELEASE_NOTES.md

@@ -5,6 +5,11 @@ Release Notes v1.7 (Under Development)
 Changes
 Changes
 -------
 -------
 
 
+- Fix build for MinGW-x64 and TDM-GCC
+- Update SQLite to 3.8.8.3
+- Fix CGI variables SCRIPT_NAME and PATH_TRANSLATED
+- Set TCP_USER_TIMEOUT to deal faster with broken connections
+- Add a Lua form handling example
 - Return more differentiated HTTP error codes
 - Return more differentiated HTTP error codes
 - Add log_access callback
 - Add log_access callback
 - Rewrite and comment request handling function
 - Rewrite and comment request handling function

+ 1 - 1
VS2012/lua_lib/lua_lib.vcxproj

@@ -82,7 +82,7 @@
     <ClCompile>
     <ClCompile>
       <PrecompiledHeader>
       <PrecompiledHeader>
       </PrecompiledHeader>
       </PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
+      <WarningLevel>TurnOffAllWarnings</WarningLevel>
       <Optimization>Disabled</Optimization>
       <Optimization>Disabled</Optimization>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <PreprocessorDefinitions>LUA_COMPAT_ALL;THREADSAFE=1;SQLITE_ENABLE_FTS3;SQLITE_ENABLE_FTS3_PARENTHESIS;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
       <AdditionalIncludeDirectories>$(ProjectDir)..\..\src\third_party\lua-5.2.3\src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

+ 7 - 0
docs/Building.md

@@ -11,10 +11,17 @@ https://github.com/bel2125/civetweb
 Building for Windows
 Building for Windows
 ---------
 ---------
 
 
+#### Using Visual Studio
 Open the *VS2012/civetweb.sln* in Visual Studio.
 Open the *VS2012/civetweb.sln* in Visual Studio.
 To include SSL support, you may have to use yaSSL.  However, it is GPL licensed.
 To include SSL support, you may have to use yaSSL.  However, it is GPL licensed.
 See [yaSSL.md](https://github.com/bel2125/civetweb/blob/master/docs/yaSSL.md) for more information.
 See [yaSSL.md](https://github.com/bel2125/civetweb/blob/master/docs/yaSSL.md) for more information.
 
 
+#### Using MinGW-w64 or TDM-GCC
+In the start menu locate and run the "Run terminal" batch file. For TDM-GCC this is named "MinGW Command Prompt".
+Navigate to the civetweb sources directory and run:
+```
+mingw32-make CC=gcc
+```
 
 
 Building for Linux, BSD, and OSX
 Building for Linux, BSD, and OSX
 ---------
 ---------

+ 186 - 187
examples/websocket/WebSockCallbacks.c

@@ -1,187 +1,186 @@
-#include <assert.h>
-#include <stdlib.h>
-#include <time.h>
-#include <string.h>
-#include "WebSockCallbacks.h"
-
-#ifdef _WIN32
-#include <Windows.h>
-#define mg_sleep(x) Sleep(x)
-#else
-#include <unistd.h>
-#include <pthread.h>
-#define mg_sleep(x) usleep((x) * 1000)
-#endif
-
-
-static void send_to_all_websockets(struct mg_context *ctx, const char * data, int data_len) {
-
-    int i;
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-
-    mg_lock_context(ctx);
-    for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
-        if (ws_ctx->socketList[i] && (ws_ctx->socketList[i]->webSockState==2)) {
-            mg_websocket_write(ws_ctx->socketList[i]->conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
-        }
-    }
-    mg_unlock_context(ctx);
-}
-
-
-void websocket_ready_handler(struct mg_connection *conn) {
-
-    int i;
-    struct mg_request_info * rq = mg_get_request_info(conn);
-    struct mg_context * ctx = mg_get_context(conn);
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-    tWebSockInfo * wsock = malloc(sizeof(tWebSockInfo));
-    assert(wsock);
-    wsock->webSockState = 0;
-    rq->conn_data = wsock;
-
-    mg_lock_context(ctx);
-    for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
-        if (0==ws_ctx->socketList[i]) {
-            ws_ctx->socketList[i] = wsock;
-            wsock->conn = conn;
-            wsock->webSockState = 1;
-            break;
-        }
-    }
-    printf("\nNew websocket attached: %s:%u\n", rq->remote_addr, rq->remote_port);
-    mg_unlock_context(ctx);
-}
-
-
-static void websocket_done(tWebSockContext *ws_ctx, tWebSockInfo * wsock) {
-
-    int i;
-
-    if (wsock) {
-        wsock->webSockState = 99;
-        for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
-            if (wsock==ws_ctx->socketList[i]) {
-                ws_ctx->socketList[i] = 0;
-                break;
-            }
-        }
-        printf("\nClose websocket attached: %s:%u\n", mg_get_request_info(wsock->conn)->remote_addr, mg_get_request_info(wsock->conn)->remote_port);
-        free(wsock);
-    }
-}
-
-
-int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len) {
-
-    struct mg_request_info * rq = mg_get_request_info(conn);
-    tWebSockInfo * wsock = (tWebSockInfo*)rq->conn_data;
-    struct mg_context * ctx = mg_get_context(conn);
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-    char msg[128];
-
-    mg_lock_context(ctx);
-    if (flags==136) {
-        // close websock
-        websocket_done(ws_ctx, wsock);
-        rq->conn_data = 0;
-        mg_unlock_context(ctx);
-        return 1;
-    }
-    if (((data_len>=5) && (data_len<100) && (flags==129)) || (flags==130)) {
-
-        // init command
-        if ((wsock->webSockState==1) && (!memcmp(data,"init ",5))) {
-            char * chk;
-            unsigned long gid;
-            memcpy(msg,data+5,data_len-5);
-            msg[data_len-5]=0;
-            gid = strtoul(msg,&chk,10);
-            wsock->initId = gid;
-            if (gid>0 && chk!=NULL && *chk==0) {
-                wsock->webSockState = 2;
-            }
-            mg_unlock_context(ctx);
-            return 1;
-        }
-
-        // chat message
-        if ((wsock->webSockState==2) && (!memcmp(data,"msg ",4))) {
-            send_to_all_websockets(ctx, data, data_len);
-            mg_unlock_context(ctx);
-            return 1;
-        }
-    }
-
-    // keep alive
-    if ((data_len==4) && !memcmp(data,"ping",4)) {
-        mg_unlock_context(ctx);
-        return 1;
-    }
-
-    mg_unlock_context(ctx);
-    return 0;
-}
-
-
-void connection_close_handler(struct mg_connection *conn) {
-
-    struct mg_request_info * rq = mg_get_request_info(conn);
-    tWebSockInfo * wsock = (tWebSockInfo*)rq->conn_data;
-    struct mg_context * ctx = mg_get_context(conn);
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-
-    mg_lock_context(ctx);
-    websocket_done(ws_ctx, wsock);
-    rq->conn_data = 0;
-    mg_unlock_context(ctx);
-}
-
-
-static void * eventMain(void * arg) {
-
-    char msg[256];
-    struct mg_context *ctx = (struct mg_context *)arg;
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-
-    ws_ctx->runLoop = 1;
-    while (ws_ctx->runLoop) {
-        time_t t = time(0);
-        struct tm * timestr = localtime(&t);
-        sprintf(msg,"title %s",asctime(timestr));
-
-        send_to_all_websockets(ctx, msg, strlen(msg));
-
-        mg_sleep(1000);
-    }
-
-    return NULL;
-}
-
-void websock_send_broadcast(struct mg_context *ctx, const char * data, int data_len) {
-
-    char buffer[260];
-
-    if (data_len<=256) {
-        strcpy(buffer, "msg ");
-        memcpy(buffer+4, data, data_len);
-
-        send_to_all_websockets(ctx, buffer, data_len+4);
-    }
-}
-
-void websock_init_lib(struct mg_context *ctx) {
-
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-    memset(ws_ctx,0,sizeof(*ws_ctx));
-    /* todo: use mg_start_thread_id instead of mg_start_thread */
-    mg_start_thread(eventMain, ctx);
-}
-
-void websock_exit_lib(struct mg_context *ctx) {
-
-    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
-    ws_ctx->runLoop = 0;
-    /* todo: wait for the thread instead of a timeout */
-    mg_sleep(2000);
-}
+#include <assert.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "WebSockCallbacks.h"
+
+#ifdef _WIN32
+#include <Windows.h>
+#define mg_sleep(x) Sleep(x)
+#else
+#include <unistd.h>
+#include <pthread.h>
+#define mg_sleep(x) usleep((x) * 1000)
+#endif
+
+
+static void send_to_all_websockets(struct mg_context *ctx, const char * data, int data_len) {
+
+    int i;
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+
+    mg_lock_context(ctx);
+    for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
+        if (ws_ctx->socketList[i] && (ws_ctx->socketList[i]->webSockState==2)) {
+            mg_websocket_write(ws_ctx->socketList[i]->conn, WEBSOCKET_OPCODE_TEXT, data, data_len);
+        }
+    }
+    mg_unlock_context(ctx);
+}
+
+
+void websocket_ready_handler(struct mg_connection *conn) {
+
+    int i;
+    struct mg_request_info * rq = mg_get_request_info(conn);
+    struct mg_context * ctx = mg_get_context(conn);
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+    tWebSockInfo * wsock = malloc(sizeof(tWebSockInfo));
+    assert(wsock);
+    wsock->webSockState = 0;
+    rq->conn_data = wsock;
+
+    mg_lock_context(ctx);
+    for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
+        if (0==ws_ctx->socketList[i]) {
+            ws_ctx->socketList[i] = wsock;
+            wsock->conn = conn;
+            wsock->webSockState = 1;
+            break;
+        }
+    }
+    printf("\nNew websocket attached: %s:%u\n", rq->remote_addr, rq->remote_port);
+    mg_unlock_context(ctx);
+}
+
+
+static void websocket_done(tWebSockContext *ws_ctx, tWebSockInfo * wsock) {
+
+    int i;
+
+    if (wsock) {
+        wsock->webSockState = 99;
+        for (i=0;i<MAX_NUM_OF_WEBSOCKS;i++) {
+            if (wsock==ws_ctx->socketList[i]) {
+                ws_ctx->socketList[i] = 0;
+                break;
+            }
+        }
+        printf("\nClose websocket attached: %s:%u\n", mg_get_request_info(wsock->conn)->remote_addr, mg_get_request_info(wsock->conn)->remote_port);
+        free(wsock);
+    }
+}
+
+
+int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len) {
+
+    struct mg_request_info * rq = mg_get_request_info(conn);
+    tWebSockInfo * wsock = (tWebSockInfo*)rq->conn_data;
+    struct mg_context * ctx = mg_get_context(conn);
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+    char msg[128];
+
+    mg_lock_context(ctx);
+    if (flags==136) {
+        // close websock
+        websocket_done(ws_ctx, wsock);
+        rq->conn_data = 0;
+        mg_unlock_context(ctx);
+        return 1;
+    }
+    if (((data_len>=5) && (data_len<100) && (flags==129)) || (flags==130)) {
+
+        // init command
+        if ((wsock->webSockState==1) && (!memcmp(data,"init ",5))) {
+            char * chk;
+            unsigned long gid;
+            memcpy(msg,data+5,data_len-5);
+            msg[data_len-5]=0;
+            gid = strtoul(msg,&chk,10);
+            wsock->initId = gid;
+            if (gid>0 && chk!=NULL && *chk==0) {
+                wsock->webSockState = 2;
+            }
+            mg_unlock_context(ctx);
+            return 1;
+        }
+
+        // chat message
+        if ((wsock->webSockState==2) && (!memcmp(data,"msg ",4))) {
+            send_to_all_websockets(ctx, data, data_len);
+            mg_unlock_context(ctx);
+            return 1;
+        }
+    }
+
+    // keep alive
+    if ((data_len==4) && !memcmp(data,"ping",4)) {
+        mg_unlock_context(ctx);
+        return 1;
+    }
+
+    mg_unlock_context(ctx);
+    return 0;
+}
+
+
+void connection_close_handler(struct mg_connection *conn) {
+
+    struct mg_request_info * rq = mg_get_request_info(conn);
+    tWebSockInfo * wsock = (tWebSockInfo*)rq->conn_data;
+    struct mg_context * ctx = mg_get_context(conn);
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+
+    mg_lock_context(ctx);
+    websocket_done(ws_ctx, wsock);
+    rq->conn_data = 0;
+    mg_unlock_context(ctx);
+}
+
+
+static void * eventMain(void * arg) {
+
+    char msg[256];
+    struct mg_context *ctx = (struct mg_context *)arg;
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+
+    ws_ctx->runLoop = 1;
+    while (ws_ctx->runLoop) {
+        time_t t = time(0);
+        struct tm * timestr = localtime(&t);
+        strftime(msg, sizeof(msg), "title %c", timestr);
+        send_to_all_websockets(ctx, msg, strlen(msg));
+
+        mg_sleep(1000);
+    }
+
+    return NULL;
+}
+
+void websock_send_broadcast(struct mg_context *ctx, const char * data, int data_len) {
+
+    char buffer[260];
+
+    if (data_len<=256) {
+        strcpy(buffer, "msg ");
+        memcpy(buffer+4, data, data_len);
+
+        send_to_all_websockets(ctx, buffer, data_len+4);
+    }
+}
+
+void websock_init_lib(struct mg_context *ctx) {
+
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+    memset(ws_ctx,0,sizeof(*ws_ctx));
+    /* todo: use mg_start_thread_id instead of mg_start_thread */
+    mg_start_thread(eventMain, ctx);
+}
+
+void websock_exit_lib(struct mg_context *ctx) {
+
+    tWebSockContext *ws_ctx = (tWebSockContext*) mg_get_user_data(ctx);
+    ws_ctx->runLoop = 0;
+    /* todo: wait for the thread instead of a timeout */
+    mg_sleep(2000);
+}

+ 38 - 38
examples/websocket/WebSockCallbacks.h

@@ -1,39 +1,39 @@
-
-#ifndef WEBSOCKCALLBACKS_H_INCLUDED
-#define WEBSOCKCALLBACKS_H_INCLUDED
-
-#include "civetweb.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct tWebSockInfo {
-    int webSockState;
-    unsigned long initId;
-    struct mg_connection *conn;
-} tWebSockInfo;
-
-#define MAX_NUM_OF_WEBSOCKS (256)
-typedef struct tWebSockContext {
-    int runLoop;
-    void * thread_id;
-    tWebSockInfo *socketList[MAX_NUM_OF_WEBSOCKS];
-} tWebSockContext;
-
-
-void websock_init_lib(struct mg_context *ctx);
-void websock_exit_lib(struct mg_context *ctx);
-
-void websock_send_broadcast(struct mg_context *ctx, const char * data, int data_len);
-
-void websocket_ready_handler(struct mg_connection *conn);
-int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len);
-void connection_close_handler(struct mg_connection *conn);
-
-
-#ifdef __cplusplus
-}
-#endif
-
+
+#ifndef WEBSOCKCALLBACKS_H_INCLUDED
+#define WEBSOCKCALLBACKS_H_INCLUDED
+
+#include "civetweb.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct tWebSockInfo {
+    int webSockState;
+    unsigned long initId;
+    struct mg_connection *conn;
+} tWebSockInfo;
+
+#define MAX_NUM_OF_WEBSOCKS (256)
+typedef struct tWebSockContext {
+    int runLoop;
+    void * thread_id;
+    tWebSockInfo *socketList[MAX_NUM_OF_WEBSOCKS];
+} tWebSockContext;
+
+
+void websock_init_lib(struct mg_context *ctx);
+void websock_exit_lib(struct mg_context *ctx);
+
+void websock_send_broadcast(struct mg_context *ctx, const char * data, int data_len);
+
+void websocket_ready_handler(struct mg_connection *conn);
+int websocket_data_handler(struct mg_connection *conn, int flags, char *data, size_t data_len);
+void connection_close_handler(struct mg_connection *conn);
+
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif
 #endif

+ 52 - 18
src/civetweb.c

@@ -25,6 +25,9 @@
 #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
 #define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005 */
 #endif
 #endif
 #else
 #else
+#if defined(__GNUC__) && !defined(_GNU_SOURCE)
+# define _GNU_SOURCE          /* for setgroups() */
+#endif
 #ifdef __linux__
 #ifdef __linux__
 #define _XOPEN_SOURCE 600     /* For flockfile() on Linux */
 #define _XOPEN_SOURCE 600     /* For flockfile() on Linux */
 #endif
 #endif
@@ -284,6 +287,7 @@ typedef unsigned short int in_port_t;
 
 
 #include <pwd.h>
 #include <pwd.h>
 #include <unistd.h>
 #include <unistd.h>
+#include <grp.h> 
 #include <dirent.h>
 #include <dirent.h>
 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
 #if !defined(NO_SSL_DL) && !defined(NO_SSL)
 #include <dlfcn.h>
 #include <dlfcn.h>
@@ -1638,11 +1642,14 @@ static void send_http_error(struct mg_connection *conn, int status, const char *
         }
         }
         DEBUG_TRACE("[%s]", buf);
         DEBUG_TRACE("[%s]", buf);
 
 
-        mg_printf(conn, "HTTP/1.1 %d %s\r\n"
-                        "Content-Length: %d\r\n"
+        mg_printf(conn, "HTTP/1.1 %d %s\r\n", status, status_text);
+        if (len>0) {
+            mg_printf(conn, "Content-Type: text/plain\r\n");
+        }
+        mg_printf(conn, "Content-Length: %d\r\n"
                         "Date: %s\r\n"
                         "Date: %s\r\n"
                         "Connection: %s\r\n\r\n",
                         "Connection: %s\r\n\r\n",
-                        status, status_text, len, date,
+                        len, date,
                         suggest_connection_header(conn));
                         suggest_connection_header(conn));
         conn->num_bytes_sent += mg_printf(conn, "%s", buf);
         conn->num_bytes_sent += mg_printf(conn, "%s", buf);
     }
     }
@@ -1818,13 +1825,19 @@ static void change_slashes_to_backslashes(char *path)
     int i;
     int i;
 
 
     for (i = 0; path[i] != '\0'; i++) {
     for (i = 0; path[i] != '\0'; i++) {
-        if (path[i] == '/')
+
+        if (path[i] == '/') {
             path[i] = '\\';
             path[i] = '\\';
-        /* i > 0 check is to preserve UNC paths, like \\server\file.txt */
-        if (path[i] == '\\' && i > 0)
-            while (path[i + 1] == '\\' || path[i + 1] == '/')
+        }
+
+        /* remove double backslash (check i > 0 to preserve UNC paths, like \\server\file.txt) */
+        if ((path[i] == '\\') && (i > 0)) {
+
+            while (path[i + 1] == '\\' || path[i + 1] == '/') {
                 (void) memmove(path + i + 1,
                 (void) memmove(path + i + 1,
                                path + i + 2, strlen(path + i + 1));
                                path + i + 2, strlen(path + i + 1));
+            }
+        }
     }
     }
 }
 }
 
 
@@ -3753,7 +3766,7 @@ int mg_url_encode(const char *src, char *dst, size_t dst_len)
             pos[2] = hex[(* (const unsigned char *) src) & 0xf];
             pos[2] = hex[(* (const unsigned char *) src) & 0xf];
             pos += 2;
             pos += 2;
         } else {
         } else {
-            return -1;
+            break;
         }
         }
     }
     }
 
 
@@ -4495,7 +4508,7 @@ static void prepare_cgi_environment(struct mg_connection *conn,
                                     const char *prog,
                                     const char *prog,
                                     struct cgi_env_block *blk)
                                     struct cgi_env_block *blk)
 {
 {
-    const char *s, *slash;
+    const char *s;
     struct vec var_vec;
     struct vec var_vec;
     char *p, src_addr[IP_ADDR_STR_LEN];
     char *p, src_addr[IP_ADDR_STR_LEN];
     int  i;
     int  i;
@@ -4523,15 +4536,17 @@ static void prepare_cgi_environment(struct mg_connection *conn,
     addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
     addenv(blk, "REQUEST_URI=%s", conn->request_info.uri);
 
 
     /* SCRIPT_NAME */
     /* SCRIPT_NAME */
-    assert(conn->request_info.uri[0] == '/');
-    slash = strrchr(conn->request_info.uri, '/');
-    if ((s = strrchr(prog, '/')) == NULL)
-        s = prog;
-    addenv(blk, "SCRIPT_NAME=%.*s%s", (int) (slash - conn->request_info.uri),
-           conn->request_info.uri, s);
+    addenv(blk, "SCRIPT_NAME=%.*s",
+           (int)strlen(conn->request_info.uri) - ((conn->path_info == NULL) ? 0 : (int)strlen(conn->path_info)),
+           conn->request_info.uri);
 
 
     addenv(blk, "SCRIPT_FILENAME=%s", prog);
     addenv(blk, "SCRIPT_FILENAME=%s", prog);
-    addenv(blk, "PATH_TRANSLATED=%s", prog);
+    if (conn->path_info == NULL) {
+        addenv(blk, "PATH_TRANSLATED=%s", conn->ctx->config[DOCUMENT_ROOT]);
+    } else {
+        addenv(blk, "PATH_TRANSLATED=%s%s", conn->ctx->config[DOCUMENT_ROOT], conn->path_info);
+    }
+
     addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
     addenv(blk, "HTTPS=%s", conn->ssl == NULL ? "off" : "on");
 
 
     if ((s = mg_get_header(conn, "Content-Type")) != NULL)
     if ((s = mg_get_header(conn, "Content-Type")) != NULL)
@@ -7499,15 +7514,34 @@ static void produce_socket(struct mg_context *ctx, const struct socket *sp)
 
 
 static int set_sock_timeout(SOCKET sock, int milliseconds)
 static int set_sock_timeout(SOCKET sock, int milliseconds)
 {
 {
+    int r1, r2;
 #ifdef _WIN32
 #ifdef _WIN32
     DWORD t = milliseconds;
     DWORD t = milliseconds;
 #else
 #else
+#if defined(TCP_USER_TIMEOUT)
+    unsigned int uto = (unsigned int)milliseconds;
+#endif
     struct timeval t;
     struct timeval t;
     t.tv_sec = milliseconds / 1000;
     t.tv_sec = milliseconds / 1000;
     t.tv_usec = (milliseconds * 1000) % 1000000;
     t.tv_usec = (milliseconds * 1000) % 1000000;
+
+    /* TCP_USER_TIMEOUT/RFC5482 (http://tools.ietf.org/html/rfc5482):
+       max. time waiting for the acknowledged of TCP data before the connection
+       will be forcefully closed and ETIMEDOUT is returned to the application.
+       If this option is not set, the default timeout of 20-30 minutes is used.
+    */
+    /* #define TCP_USER_TIMEOUT (18) */
+
+#if defined(TCP_USER_TIMEOUT)
+    setsockopt(sock, 6, TCP_USER_TIMEOUT, (const void *)&uto, sizeof(uto));
+#endif
+
 #endif
 #endif
-    return setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t)) ||
-           setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
+
+    r1 = setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
+    r2 = setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (SOCK_OPT_TYPE) &t, sizeof(t));
+
+    return r1 || r2;
 }
 }
 
 
 static void accept_new_connection(const struct socket *listener,
 static void accept_new_connection(const struct socket *listener,

+ 21 - 20
src/main.c

@@ -45,9 +45,13 @@
 #include "civetweb.h"
 #include "civetweb.h"
 
 
 #ifdef _WIN32
 #ifdef _WIN32
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0501 /* Target Windows XP or higher */
+#endif
 #include <windows.h>
 #include <windows.h>
 #include <winsvc.h>
 #include <winsvc.h>
 #include <shlobj.h>
 #include <shlobj.h>
+#include <io.h>
 
 
 #define getcwd(a,b) _getcwd(a,b)
 #define getcwd(a,b) _getcwd(a,b)
 #if !defined(__MINGW32__)
 #if !defined(__MINGW32__)
@@ -1500,34 +1504,31 @@ static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam,
     return DefWindowProc(hWnd, msg, wParam, lParam);
     return DefWindowProc(hWnd, msg, wParam, lParam);
 }
 }
 
 
-#include <fcntl.h>
-#include <io.h>
-
 static int MakeConsole() {
 static int MakeConsole() {
     DWORD err;
     DWORD err;
     int ok = (GetConsoleWindow() != NULL);
     int ok = (GetConsoleWindow() != NULL);
     if (!ok) {
     if (!ok) {
         if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
         if (!AttachConsole(ATTACH_PARENT_PROCESS)) {
             FreeConsole();
             FreeConsole();
-            if (!AllocConsole()) {
-                err = GetLastError();
-                if (err==ERROR_ACCESS_DENIED) {
-                    MessageBox(NULL, "Insufficient rights to create a console window", "Error", MB_ICONERROR);
-                }
-            }
-            AttachConsole(GetCurrentProcessId());
-        }
+            if (!AllocConsole()) {
+                err = GetLastError();
+                if (err==ERROR_ACCESS_DENIED) {
+                    MessageBox(NULL, "Insufficient rights to create a console window", "Error", MB_ICONERROR);
+                }
+            }
+            AttachConsole(GetCurrentProcessId());
+        }
 
 
         ok = (GetConsoleWindow() != NULL);
         ok = (GetConsoleWindow() != NULL);
-        if (ok) {
-            freopen("CONIN$", "r", stdin);
-            freopen("CONOUT$", "w", stdout);
-            freopen("CONOUT$", "w", stderr);
-        }
-    }
-
-    if (ok) {
-        SetConsoleTitle(server_name);
+        if (ok) {
+            freopen("CONIN$", "r", stdin);
+            freopen("CONOUT$", "w", stdout);
+            freopen("CONOUT$", "w", stderr);
+        }
+    }
+
+    if (ok) {
+        SetConsoleTitle(server_name);
     }
     }
 
 
     return ok;
     return ok;

+ 895 - 895
src/third_party/lfs.c

@@ -1,895 +1,895 @@
-/*
-** LuaFileSystem
-** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)
-**
-** File system manipulation library.
-** This library offers these functions:
-**   lfs.attributes (filepath [, attributename])
-**   lfs.chdir (path)
-**   lfs.currentdir ()
-**   lfs.dir (path)
-**   lfs.lock (fh, mode)
-**   lfs.lock_dir (path)
-**   lfs.mkdir (path)
-**   lfs.rmdir (path)
-**   lfs.setmode (filepath, mode)
-**   lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts
-**   lfs.touch (filepath [, atime [, mtime]])
-**   lfs.unlock (fh)
-**
-** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $
-*/
-
-#ifdef _MSC_VER
-#ifndef _CRT_SECURE_NO_WARNINGS
-#define _CRT_SECURE_NO_WARNINGS /* disable Visual Studio compiler warnings for using standard C functions */
-#endif
-#endif
-
-#ifndef _WIN32
-#ifndef _AIX
-#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */
-#else
-#define _LARGE_FILES 1 /* AIX */
-#endif
-#endif
-
-#define _LARGEFILE64_SOURCE
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <time.h>
-#include <sys/stat.h>
-
-#ifdef _WIN32
-#include <direct.h>
-#include <windows.h>
-#include <io.h>
-#include <sys/locking.h>
-#ifdef __BORLANDC__
- #include <utime.h>
-#else
- #include <sys/utime.h>
-#endif
-#include <fcntl.h>
-#else
-#include <unistd.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <sys/types.h>
-#include <utime.h>
-#endif
-
-#include <lua.h>
-#include <lauxlib.h>
-#include <lualib.h>
-
-#include "lfs.h"
-
-#define LFS_VERSION "1.6.2"
-#define LFS_LIBNAME "lfs"
-
-#if LUA_VERSION_NUM < 502
-#  define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l))
-#endif
-
-/* Define 'strerror' for systems that do not implement it */
-#ifdef NO_STRERROR
-#define strerror(_)     "System unable to describe the error"
-#endif
-
-/* Define 'getcwd' for systems that do not implement it */
-#ifdef NO_GETCWD
-#define getcwd(p,s)     NULL
-#define getcwd_error    "Function 'getcwd' not provided by system"
-#else
-#define getcwd_error    strerror(errno)
-  #ifdef _WIN32
-	 /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
-    #define LFS_MAXPATHLEN MAX_PATH
-  #else
-	/* For MAXPATHLEN: */
-    #include <sys/param.h>
-    #define LFS_MAXPATHLEN MAXPATHLEN
-  #endif
-#endif
-
-#define DIR_METATABLE "directory metatable"
-typedef struct dir_data {
-        int  closed;
-#ifdef _WIN32
-        long hFile;
-        char pattern[MAX_PATH+1];
-#else
-        DIR *dir;
-#endif
-} dir_data;
-
-#define LOCK_METATABLE "lock metatable"
-
-#ifdef _WIN32
- #ifdef __BORLANDC__
-  #define lfs_setmode(L,file,m)   ((void)L, setmode(_fileno(file), m))
-  #define STAT_STRUCT struct stati64
- #else
-  #define lfs_setmode(L,file,m)   ((void)L, _setmode(_fileno(file), m))
-  #define STAT_STRUCT struct _stati64
- #endif
-#define STAT_FUNC _stati64
-#define LSTAT_FUNC STAT_FUNC
-#else
-#define _O_TEXT               0
-#define _O_BINARY             0
-#define lfs_setmode(L,file,m)   ((void)L, (void)file, (void)m, 0)
-#define STAT_STRUCT struct stat
-#define STAT_FUNC stat
-#define LSTAT_FUNC lstat
-#endif
-
-/*
-** Utility functions
-*/
-static int pusherror(lua_State *L, const char *info)
-{
-        lua_pushnil(L);
-        if (info==NULL)
-                lua_pushstring(L, strerror(errno));
-        else
-                lua_pushfstring(L, "%s: %s", info, strerror(errno));
-        lua_pushinteger(L, errno);
-        return 3;
-}
-
-static int pushresult(lua_State *L, int i, const char *info)
-{
-        if (i==-1)
-                return pusherror(L, info);
-        lua_pushinteger(L, i);
-        return 1;
-}
-
-
-/*
-** This function changes the working (current) directory
-*/
-static int change_dir (lua_State *L) {
-        const char *path = luaL_checkstring(L, 1);
-        if (chdir(path)) {
-                lua_pushnil (L);
-                lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n",
-                                path, chdir_error);
-                return 2;
-        } else {
-                lua_pushboolean (L, 1);
-                return 1;
-        }
-}
-
-/*
-** This function returns the current directory
-** If unable to get the current directory, it returns nil
-**  and a string describing the error
-*/
-static int get_dir (lua_State *L) {
-  char *path;
-  /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */
-  char buf[LFS_MAXPATHLEN];
-  if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) {
-    lua_pushnil(L);
-    lua_pushstring(L, getcwd_error);
-    return 2;
-  }
-  else {
-    lua_pushstring(L, path);
-    return 1;
-  }
-}
-
-/*
-** Check if the given element on the stack is a file and returns it.
-*/
-static FILE *check_file (lua_State *L, int idx, const char *funcname) {
-        FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*");
-        if (fh == NULL) {
-                luaL_error (L, "%s: not a file", funcname);
-                return 0;
-        } else if (*fh == NULL) {
-                luaL_error (L, "%s: closed file", funcname);
-                return 0;
-        } else
-                return *fh;
-}
-
-
-/*
-**
-*/
-static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) {
-        int code;
-#ifdef _WIN32
-        /* lkmode valid values are:
-           LK_LOCK    Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error.
-           LK_NBLCK   Locks the specified bytes. If the bytes cannot be locked, the constant returns an error.
-           LK_NBRLCK  Same as _LK_NBLCK.
-           LK_RLCK    Same as _LK_LOCK.
-           LK_UNLCK   Unlocks the specified bytes, which must have been previously locked.
-
-           Regions should be locked only briefly and should be unlocked before closing a file or exiting the program.
-
-           http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp
-        */
-        int lkmode;
-        switch (*mode) {
-                case 'r': lkmode = LK_NBLCK; break;
-                case 'w': lkmode = LK_NBLCK; break;
-                case 'u': lkmode = LK_UNLCK; break;
-                default : return luaL_error (L, "%s: invalid mode", funcname);
-        }
-        if (!len) {
-                fseek (fh, 0L, SEEK_END);
-                len = ftell (fh);
-        }
-        fseek (fh, start, SEEK_SET);
-#ifdef __BORLANDC__
-        code = locking (fileno(fh), lkmode, len);
-#else
-        code = _locking (fileno(fh), lkmode, len);
-#endif
-#else
-        struct flock f;
-        switch (*mode) {
-                case 'w': f.l_type = F_WRLCK; break;
-                case 'r': f.l_type = F_RDLCK; break;
-                case 'u': f.l_type = F_UNLCK; break;
-                default : return luaL_error (L, "%s: invalid mode", funcname);
-        }
-        f.l_whence = SEEK_SET;
-        f.l_start = (off_t)start;
-        f.l_len = (off_t)len;
-        code = fcntl (fileno(fh), F_SETLK, &f);
-#endif
-        return (code != -1);
-}
-
-#ifdef _WIN32
-typedef struct lfs_Lock {
-  HANDLE fd;
-} lfs_Lock;
-static int lfs_lock_dir(lua_State *L) {
-  size_t pathl; HANDLE fd;
-  lfs_Lock *lock;
-  char *ln;
-  const char *lockfile = "/lockfile.lfs";
-  const char *path = luaL_checklstring(L, 1, &pathl);
-  ln = (char*)malloc(pathl + strlen(lockfile) + 1);
-  if(!ln) {
-    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
-  }
-  strcpy(ln, path); strcat(ln, lockfile);
-  /* Use "CreateFileA" to use the Multi-Byte-Character version, even if the rest of the project uses the Unicode (UTF16) version */
-  if((fd = CreateFileA(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW,
-                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
-        int en = GetLastError();
-        free(ln); lua_pushnil(L);
-        if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION)
-                lua_pushstring(L, "File exists");
-        else
-                lua_pushstring(L, strerror(en));
-        return 2;
-  }
-  free(ln);
-  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
-  lock->fd = fd;
-  luaL_getmetatable (L, LOCK_METATABLE);
-  lua_setmetatable (L, -2);
-  return 1;
-}
-static int lfs_unlock_dir(lua_State *L) {
-  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);
-  CloseHandle(lock->fd);
-  return 0;
-}
-#else
-typedef struct lfs_Lock {
-  char *ln;
-} lfs_Lock;
-static int lfs_lock_dir(lua_State *L) {
-  lfs_Lock *lock;
-  size_t pathl;
-  char *ln;
-  const char *lockfile = "/lockfile.lfs";
-  const char *path = luaL_checklstring(L, 1, &pathl);
-  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
-  ln = (char*)malloc(pathl + strlen(lockfile) + 1);
-  if(!ln) {
-    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
-  }
-  strcpy(ln, path); strcat(ln, lockfile);
-  if(symlink("lock", ln) == -1) {
-    free(ln); lua_pushnil(L);
-    lua_pushstring(L, strerror(errno)); return 2;
-  }
-  lock->ln = ln;
-  luaL_getmetatable (L, LOCK_METATABLE);
-  lua_setmetatable (L, -2);
-  return 1;
-}
-static int lfs_unlock_dir(lua_State *L) {
-  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);
-  if(lock->ln) {
-    unlink(lock->ln);
-    free(lock->ln);
-    lock->ln = NULL;
-  }
-  return 0;
-}
-#endif
-
-static int lfs_g_setmode (lua_State *L, FILE *f, int arg) {
-  static const int mode[] = {_O_BINARY, _O_TEXT};
-  static const char *const modenames[] = {"binary", "text", NULL};
-  int op = luaL_checkoption(L, arg, NULL, modenames);
-  int res = lfs_setmode(L, f, mode[op]);
-  if (res != -1) {
-    int i;
-    lua_pushboolean(L, 1);
-    for (i = 0; modenames[i] != NULL; i++) {
-      if (mode[i] == res) {
-        lua_pushstring(L, modenames[i]);
-        goto exit;
-      }
-    }
-    lua_pushnil(L);
-  exit:
-    return 2;
-  } else {
-    int en = errno;
-    lua_pushnil(L);
-    lua_pushfstring(L, "%s", strerror(en));
-    lua_pushinteger(L, en);
-    return 3;
-  }
-}
-
-static int lfs_f_setmode(lua_State *L) {
-  return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2);
-}
-
-/*
-** Locks a file.
-** @param #1 File handle.
-** @param #2 String with lock mode ('w'rite, 'r'ead).
-** @param #3 Number with start position (optional).
-** @param #4 Number with length (optional).
-*/
-static int file_lock (lua_State *L) {
-        FILE *fh = check_file (L, 1, "lock");
-        const char *mode = luaL_checkstring (L, 2);
-        const long start = luaL_optlong (L, 3, 0);
-        long len = luaL_optlong (L, 4, 0);
-        if (_file_lock (L, fh, mode, start, len, "lock")) {
-                lua_pushboolean (L, 1);
-                return 1;
-        } else {
-                lua_pushnil (L);
-                lua_pushfstring (L, "%s", strerror(errno));
-                return 2;
-        }
-}
-
-
-/*
-** Unlocks a file.
-** @param #1 File handle.
-** @param #2 Number with start position (optional).
-** @param #3 Number with length (optional).
-*/
-static int file_unlock (lua_State *L) {
-        FILE *fh = check_file (L, 1, "unlock");
-        const long start = luaL_optlong (L, 2, 0);
-        long len = luaL_optlong (L, 3, 0);
-        if (_file_lock (L, fh, "u", start, len, "unlock")) {
-                lua_pushboolean (L, 1);
-                return 1;
-        } else {
-                lua_pushnil (L);
-                lua_pushfstring (L, "%s", strerror(errno));
-                return 2;
-        }
-}
-
-
-/*
-** Creates a link.
-** @param #1 Object to link to.
-** @param #2 Name of link.
-** @param #3 True if link is symbolic (optional).
-*/
-static int make_link(lua_State *L)
-{
-#ifndef _WIN32
-        const char *oldpath = luaL_checkstring(L, 1);
-        const char *newpath = luaL_checkstring(L, 2);
-        return pushresult(L,
-                (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL);
-#else
-        return pusherror(L, "make_link is not supported on Windows");
-#endif
-}
-
-
-/*
-** Creates a directory.
-** @param #1 Directory path.
-*/
-static int make_dir (lua_State *L) {
-        const char *path = luaL_checkstring (L, 1);
-        int fail;
-#ifdef _WIN32
-        fail = _mkdir (path);
-#else
-        fail =  mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
-                             S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH );
-#endif
-        if (fail) {
-                lua_pushnil (L);
-        lua_pushfstring (L, "%s", strerror(errno));
-                return 2;
-        }
-        lua_pushboolean (L, 1);
-        return 1;
-}
-
-/*
-** Removes a directory.
-** @param #1 Directory path.
-*/
-static int remove_dir (lua_State *L) {
-        const char *path = luaL_checkstring (L, 1);
-        int fail;
-
-        fail = rmdir (path);
-
-        if (fail) {
-                lua_pushnil (L);
-                lua_pushfstring (L, "%s", strerror(errno));
-                return 2;
-        }
-        lua_pushboolean (L, 1);
-        return 1;
-}
-
-/*
-** Directory iterator
-*/
-static int dir_iter (lua_State *L) {
-#ifdef _WIN32
-        struct _finddata_t c_file;
-#else
-        struct dirent *entry;
-#endif
-        dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE);
-        luaL_argcheck (L, d->closed == 0, 1, "closed directory");
-#ifdef _WIN32
-        if (d->hFile == 0L) { /* first entry */
-                if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) {
-                        lua_pushnil (L);
-                        lua_pushstring (L, strerror (errno));
-                        d->closed = 1;
-                        return 2;
-                } else {
-                        lua_pushstring (L, c_file.name);
-                        return 1;
-                }
-        } else { /* next entry */
-                if (_findnext (d->hFile, &c_file) == -1L) {
-                        /* no more entries => close directory */
-                        _findclose (d->hFile);
-                        d->closed = 1;
-                        return 0;
-                } else {
-                        lua_pushstring (L, c_file.name);
-                        return 1;
-                }
-        }
-#else
-        if ((entry = readdir (d->dir)) != NULL) {
-                lua_pushstring (L, entry->d_name);
-                return 1;
-        } else {
-                /* no more entries => close directory */
-                closedir (d->dir);
-                d->closed = 1;
-                return 0;
-        }
-#endif
-}
-
-
-/*
-** Closes directory iterators
-*/
-static int dir_close (lua_State *L) {
-        dir_data *d = (dir_data *)lua_touserdata (L, 1);
-#ifdef _WIN32
-        if (!d->closed && d->hFile) {
-                _findclose (d->hFile);
-        }
-#else
-        if (!d->closed && d->dir) {
-                closedir (d->dir);
-        }
-#endif
-        d->closed = 1;
-        return 0;
-}
-
-
-/*
-** Factory of directory iterators
-*/
-static int dir_iter_factory (lua_State *L) {
-        const char *path = luaL_checkstring (L, 1);
-        dir_data *d;
-        lua_pushcfunction (L, dir_iter);
-        d = (dir_data *) lua_newuserdata (L, sizeof(dir_data));
-        luaL_getmetatable (L, DIR_METATABLE);
-        lua_setmetatable (L, -2);
-        d->closed = 0;
-#ifdef _WIN32
-        d->hFile = 0L;
-        if (strlen(path) > MAX_PATH-2)
-          luaL_error (L, "path too long: %s", path);
-        else
-          sprintf (d->pattern, "%s/*", path);
-#else
-        d->dir = opendir (path);
-        if (d->dir == NULL)
-          luaL_error (L, "cannot open %s: %s", path, strerror (errno));
-#endif
-        return 2;
-}
-
-
-/*
-** Creates directory metatable.
-*/
-static int dir_create_meta (lua_State *L) {
-        luaL_newmetatable (L, DIR_METATABLE);
-
-        /* Method table */
-        lua_newtable(L);
-        lua_pushcfunction (L, dir_iter);
-        lua_setfield(L, -2, "next");
-        lua_pushcfunction (L, dir_close);
-        lua_setfield(L, -2, "close");
-
-        /* Metamethods */
-        lua_setfield(L, -2, "__index");
-        lua_pushcfunction (L, dir_close);
-        lua_setfield (L, -2, "__gc");
-        return 1;
-}
-
-/*
-** Creates lock metatable.
-*/
-static int lock_create_meta (lua_State *L) {
-        luaL_newmetatable (L, LOCK_METATABLE);
-
-        /* Method table */
-        lua_newtable(L);
-        lua_pushcfunction(L, lfs_unlock_dir);
-        lua_setfield(L, -2, "free");
-
-        /* Metamethods */
-        lua_setfield(L, -2, "__index");
-        lua_pushcfunction(L, lfs_unlock_dir);
-        lua_setfield(L, -2, "__gc");
-        return 1;
-}
-
-
-#ifdef _WIN32
- #ifndef S_ISDIR
-   #define S_ISDIR(mode)  (mode&_S_IFDIR)
- #endif
- #ifndef S_ISREG
-   #define S_ISREG(mode)  (mode&_S_IFREG)
- #endif
- #ifndef S_ISLNK
-   #define S_ISLNK(mode)  (0)
- #endif
- #ifndef S_ISSOCK
-   #define S_ISSOCK(mode)  (0)
- #endif
- #ifndef S_ISFIFO
-   #define S_ISFIFO(mode)  (0)
- #endif
- #ifndef S_ISCHR
-   #define S_ISCHR(mode)  (mode&_S_IFCHR)
- #endif
- #ifndef S_ISBLK
-   #define S_ISBLK(mode)  (0)
- #endif
-#endif
-/*
-** Convert the inode protection mode to a string.
-*/
-#ifdef _WIN32
-static const char *mode2string (unsigned short mode) {
-#else
-static const char *mode2string (mode_t mode) {
-#endif
-  if ( S_ISREG(mode) )
-    return "file";
-  else if ( S_ISDIR(mode) )
-    return "directory";
-  else if ( S_ISLNK(mode) )
-        return "link";
-  else if ( S_ISSOCK(mode) )
-    return "socket";
-  else if ( S_ISFIFO(mode) )
-        return "named pipe";
-  else if ( S_ISCHR(mode) )
-        return "char device";
-  else if ( S_ISBLK(mode) )
-        return "block device";
-  else
-        return "other";
-}
-
-
-/*
-** Set access time and modification values for file
-*/
-static int file_utime (lua_State *L) {
-        const char *file = luaL_checkstring (L, 1);
-        struct utimbuf utb, *buf;
-
-        if (lua_gettop (L) == 1) /* set to current date/time */
-                buf = NULL;
-        else {
-                utb.actime = (time_t)luaL_optnumber (L, 2, 0);
-                utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime);
-                buf = &utb;
-        }
-        if (utime (file, buf)) {
-                lua_pushnil (L);
-                lua_pushfstring (L, "%s", strerror (errno));
-                return 2;
-        }
-        lua_pushboolean (L, 1);
-        return 1;
-}
-
-
-/* inode protection mode */
-static void push_st_mode (lua_State *L, STAT_STRUCT *info) {
-        lua_pushstring (L, mode2string (info->st_mode));
-}
-/* device inode resides on */
-static void push_st_dev (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_dev);
-}
-/* inode's number */
-static void push_st_ino (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_ino);
-}
-/* number of hard links to the file */
-static void push_st_nlink (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_nlink);
-}
-/* user-id of owner */
-static void push_st_uid (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_uid);
-}
-/* group-id of owner */
-static void push_st_gid (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_gid);
-}
-/* device type, for special file inode */
-static void push_st_rdev (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_rdev);
-}
-/* time of last access */
-static void push_st_atime (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, info->st_atime);
-}
-/* time of last data modification */
-static void push_st_mtime (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, info->st_mtime);
-}
-/* time of last file status change */
-static void push_st_ctime (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, info->st_ctime);
-}
-/* file size, in bytes */
-static void push_st_size (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_size);
-}
-#ifndef _WIN32
-/* blocks allocated for file */
-static void push_st_blocks (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_blocks);
-}
-/* optimal file system I/O blocksize */
-static void push_st_blksize (lua_State *L, STAT_STRUCT *info) {
-        lua_pushnumber (L, (lua_Number)info->st_blksize);
-}
-#endif
-static void push_invalid (lua_State *L, STAT_STRUCT *info) {
-  luaL_error(L, "invalid attribute name");
-#ifndef _WIN32
-  info->st_blksize = 0; /* never reached */
-#endif
-}
-
- /*
-** Convert the inode protection mode to a permission list.
-*/
-
-#ifdef _WIN32
-static const char *perm2string (unsigned short mode) {
-  static char perms[10] = "---------\0";
-  int i;
-  for (i=0;i<9;i++) perms[i]='-';
-  if (mode  & _S_IREAD)
-   { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; }
-  if (mode  & _S_IWRITE)
-   { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; }
-  if (mode  & _S_IEXEC)
-   { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; }
-  return perms;
-}
-#else
-static const char *perm2string (mode_t mode) {
-  static char perms[10] = "---------\0";
-  int i;
-  for (i=0;i<9;i++) perms[i]='-';
-  if (mode & S_IRUSR) perms[0] = 'r';
-  if (mode & S_IWUSR) perms[1] = 'w';
-  if (mode & S_IXUSR) perms[2] = 'x';
-  if (mode & S_IRGRP) perms[3] = 'r';
-  if (mode & S_IWGRP) perms[4] = 'w';
-  if (mode & S_IXGRP) perms[5] = 'x';
-  if (mode & S_IROTH) perms[6] = 'r';
-  if (mode & S_IWOTH) perms[7] = 'w';
-  if (mode & S_IXOTH) perms[8] = 'x';
-  return perms;
-}
-#endif
-
-/* permssions string */
-static void push_st_perm (lua_State *L, STAT_STRUCT *info) {
-    lua_pushstring (L, perm2string (info->st_mode));
-}
-
-typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info);
-
-struct _stat_members {
-        const char *name;
-        _push_function push;
-};
-
-struct _stat_members members[] = {
-        { "mode",         push_st_mode },
-        { "dev",          push_st_dev },
-        { "ino",          push_st_ino },
-        { "nlink",        push_st_nlink },
-        { "uid",          push_st_uid },
-        { "gid",          push_st_gid },
-        { "rdev",         push_st_rdev },
-        { "access",       push_st_atime },
-        { "modification", push_st_mtime },
-        { "change",       push_st_ctime },
-        { "size",         push_st_size },
-        { "permissions",  push_st_perm },
-#ifndef _WIN32
-        { "blocks",       push_st_blocks },
-        { "blksize",      push_st_blksize },
-#endif
-        { NULL, push_invalid }
-};
-
-/*
-** Get file or symbolic link information
-*/
-static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
-        int i;
-        STAT_STRUCT info;
-        const char *file = luaL_checkstring (L, 1);
-
-        if (st(file, &info)) {
-                lua_pushnil (L);
-                lua_pushfstring (L, "cannot obtain information from file `%s'", file);
-                return 2;
-        }
-        if (lua_isstring (L, 2)) {
-                int v;
-                const char *member = lua_tostring (L, 2);
-                if (strcmp (member, "mode") == 0) v = 0;
-#ifndef _WIN32
-                else if (strcmp (member, "blocks")  == 0) v = 11;
-                else if (strcmp (member, "blksize") == 0) v = 12;
-#endif
-                else /* look for member */
-                        for (v = 1; members[v].name; v++)
-                                if (*members[v].name == *member)
-                                        break;
-                /* push member value and return */
-                members[v].push (L, &info);
-                return 1;
-        } else if (!lua_istable (L, 2))
-                /* creates a table if none is given */
-                lua_newtable (L);
-        /* stores all members in table on top of the stack */
-        for (i = 0; members[i].name; i++) {
-                lua_pushstring (L, members[i].name);
-                members[i].push (L, &info);
-                lua_rawset (L, -3);
-        }
-        return 1;
-}
-
-
-/*
-** Get file information using stat.
-*/
-static int file_info (lua_State *L) {
-        return _file_info_ (L, STAT_FUNC);
-}
-
-
-/*
-** Get symbolic link information using lstat.
-*/
-static int link_info (lua_State *L) {
-        return _file_info_ (L, LSTAT_FUNC);
-}
-
-
-/*
-** Assumes the table is on top of the stack.
-*/
-static void set_info (lua_State *L) {
-        lua_pushliteral (L, "_COPYRIGHT");
-        lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project");
-        lua_settable (L, -3);
-        lua_pushliteral (L, "_DESCRIPTION");
-        lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution");
-        lua_settable (L, -3);
-        lua_pushliteral (L, "_VERSION");
-        lua_pushliteral (L, "LuaFileSystem "LFS_VERSION);
-        lua_settable (L, -3);
-}
-
-
-static const struct luaL_Reg fslib[] = {
-        {"attributes", file_info},
-        {"chdir", change_dir},
-        {"currentdir", get_dir},
-        {"dir", dir_iter_factory},
-        {"link", make_link},
-        {"lock", file_lock},
-        {"mkdir", make_dir},
-        {"rmdir", remove_dir},
-        {"symlinkattributes", link_info},
-        {"setmode", lfs_f_setmode},
-        {"touch", file_utime},
-        {"unlock", file_unlock},
-        {"lock_dir", lfs_lock_dir},
-        {NULL, NULL},
-};
-
-int luaopen_lfs (lua_State *L) {
-        dir_create_meta (L);
-        lock_create_meta (L);
-        luaL_newlib (L, fslib);
-        lua_pushvalue(L, -1);
-        lua_setglobal(L, LFS_LIBNAME);
-        set_info (L);
-        return 1;
-}
+/*
+** LuaFileSystem
+** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)
+**
+** File system manipulation library.
+** This library offers these functions:
+**   lfs.attributes (filepath [, attributename])
+**   lfs.chdir (path)
+**   lfs.currentdir ()
+**   lfs.dir (path)
+**   lfs.lock (fh, mode)
+**   lfs.lock_dir (path)
+**   lfs.mkdir (path)
+**   lfs.rmdir (path)
+**   lfs.setmode (filepath, mode)
+**   lfs.symlinkattributes (filepath [, attributename]) -- thanks to Sam Roberts
+**   lfs.touch (filepath [, atime [, mtime]])
+**   lfs.unlock (fh)
+**
+** $Id: lfs.c,v 1.61 2009/07/04 02:10:16 mascarenhas Exp $
+*/
+
+#ifdef _MSC_VER
+#ifndef _CRT_SECURE_NO_WARNINGS
+#define _CRT_SECURE_NO_WARNINGS /* disable Visual Studio compiler warnings for using standard C functions */
+#endif
+#endif
+
+#ifndef _WIN32
+#ifndef _AIX
+#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */
+#else
+#define _LARGE_FILES 1 /* AIX */
+#endif
+#endif
+
+#define _LARGEFILE64_SOURCE
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#include <windows.h>
+#include <io.h>
+#include <sys/locking.h>
+#ifdef __BORLANDC__
+ #include <utime.h>
+#else
+ #include <sys/utime.h>
+#endif
+#include <fcntl.h>
+#else
+#include <unistd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <utime.h>
+#endif
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#include "lfs.h"
+
+#define LFS_VERSION "1.6.2"
+#define LFS_LIBNAME "lfs"
+
+#if LUA_VERSION_NUM < 502
+#  define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l))
+#endif
+
+/* Define 'strerror' for systems that do not implement it */
+#ifdef NO_STRERROR
+#define strerror(_)     "System unable to describe the error"
+#endif
+
+/* Define 'getcwd' for systems that do not implement it */
+#ifdef NO_GETCWD
+#define getcwd(p,s)     NULL
+#define getcwd_error    "Function 'getcwd' not provided by system"
+#else
+#define getcwd_error    strerror(errno)
+  #ifdef _WIN32
+	 /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */
+    #define LFS_MAXPATHLEN MAX_PATH
+  #else
+	/* For MAXPATHLEN: */
+    #include <sys/param.h>
+    #define LFS_MAXPATHLEN MAXPATHLEN
+  #endif
+#endif
+
+#define DIR_METATABLE "directory metatable"
+typedef struct dir_data {
+        int  closed;
+#ifdef _WIN32
+        long hFile;
+        char pattern[MAX_PATH+1];
+#else
+        DIR *dir;
+#endif
+} dir_data;
+
+#define LOCK_METATABLE "lock metatable"
+
+#ifdef _WIN32
+ #ifdef __BORLANDC__
+  #define lfs_setmode(L,file,m)   ((void)L, setmode(_fileno(file), m))
+  #define STAT_STRUCT struct stati64
+ #else
+  #define lfs_setmode(L,file,m)   ((void)L, _setmode(_fileno(file), m))
+  #define STAT_STRUCT struct _stati64
+ #endif
+#define STAT_FUNC _stati64
+#define LSTAT_FUNC STAT_FUNC
+#else
+#define _O_TEXT               0
+#define _O_BINARY             0
+#define lfs_setmode(L,file,m)   ((void)L, (void)file, (void)m, 0)
+#define STAT_STRUCT struct stat
+#define STAT_FUNC stat
+#define LSTAT_FUNC lstat
+#endif
+
+/*
+** Utility functions
+*/
+static int pusherror(lua_State *L, const char *info)
+{
+        lua_pushnil(L);
+        if (info==NULL)
+                lua_pushstring(L, strerror(errno));
+        else
+                lua_pushfstring(L, "%s: %s", info, strerror(errno));
+        lua_pushinteger(L, errno);
+        return 3;
+}
+
+static int pushresult(lua_State *L, int i, const char *info)
+{
+        if (i==-1)
+                return pusherror(L, info);
+        lua_pushinteger(L, i);
+        return 1;
+}
+
+
+/*
+** This function changes the working (current) directory
+*/
+static int change_dir (lua_State *L) {
+        const char *path = luaL_checkstring(L, 1);
+        if (chdir(path)) {
+                lua_pushnil (L);
+                lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n",
+                                path, chdir_error);
+                return 2;
+        } else {
+                lua_pushboolean (L, 1);
+                return 1;
+        }
+}
+
+/*
+** This function returns the current directory
+** If unable to get the current directory, it returns nil
+**  and a string describing the error
+*/
+static int get_dir (lua_State *L) {
+  char *path;
+  /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */
+  char buf[LFS_MAXPATHLEN];
+  if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) {
+    lua_pushnil(L);
+    lua_pushstring(L, getcwd_error);
+    return 2;
+  }
+  else {
+    lua_pushstring(L, path);
+    return 1;
+  }
+}
+
+/*
+** Check if the given element on the stack is a file and returns it.
+*/
+static FILE *check_file (lua_State *L, int idx, const char *funcname) {
+        FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*");
+        if (fh == NULL) {
+                luaL_error (L, "%s: not a file", funcname);
+                return 0;
+        } else if (*fh == NULL) {
+                luaL_error (L, "%s: closed file", funcname);
+                return 0;
+        } else
+                return *fh;
+}
+
+
+/*
+**
+*/
+static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) {
+        int code;
+#ifdef _WIN32
+        /* lkmode valid values are:
+           LK_LOCK    Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error.
+           LK_NBLCK   Locks the specified bytes. If the bytes cannot be locked, the constant returns an error.
+           LK_NBRLCK  Same as _LK_NBLCK.
+           LK_RLCK    Same as _LK_LOCK.
+           LK_UNLCK   Unlocks the specified bytes, which must have been previously locked.
+
+           Regions should be locked only briefly and should be unlocked before closing a file or exiting the program.
+
+           http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp
+        */
+        int lkmode;
+        switch (*mode) {
+                case 'r': lkmode = LK_NBLCK; break;
+                case 'w': lkmode = LK_NBLCK; break;
+                case 'u': lkmode = LK_UNLCK; break;
+                default : return luaL_error (L, "%s: invalid mode", funcname);
+        }
+        if (!len) {
+                fseek (fh, 0L, SEEK_END);
+                len = ftell (fh);
+        }
+        fseek (fh, start, SEEK_SET);
+#ifdef __BORLANDC__
+        code = locking (fileno(fh), lkmode, len);
+#else
+        code = _locking (fileno(fh), lkmode, len);
+#endif
+#else
+        struct flock f;
+        switch (*mode) {
+                case 'w': f.l_type = F_WRLCK; break;
+                case 'r': f.l_type = F_RDLCK; break;
+                case 'u': f.l_type = F_UNLCK; break;
+                default : return luaL_error (L, "%s: invalid mode", funcname);
+        }
+        f.l_whence = SEEK_SET;
+        f.l_start = (off_t)start;
+        f.l_len = (off_t)len;
+        code = fcntl (fileno(fh), F_SETLK, &f);
+#endif
+        return (code != -1);
+}
+
+#ifdef _WIN32
+typedef struct lfs_Lock {
+  HANDLE fd;
+} lfs_Lock;
+static int lfs_lock_dir(lua_State *L) {
+  size_t pathl; HANDLE fd;
+  lfs_Lock *lock;
+  char *ln;
+  const char *lockfile = "/lockfile.lfs";
+  const char *path = luaL_checklstring(L, 1, &pathl);
+  ln = (char*)malloc(pathl + strlen(lockfile) + 1);
+  if(!ln) {
+    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
+  }
+  strcpy(ln, path); strcat(ln, lockfile);
+  /* Use "CreateFileA" to use the Multi-Byte-Character version, even if the rest of the project uses the Unicode (UTF16) version */
+  if((fd = CreateFileA(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW,
+                FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) {
+        int en = GetLastError();
+        free(ln); lua_pushnil(L);
+        if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION)
+                lua_pushstring(L, "File exists");
+        else
+                lua_pushstring(L, strerror(en));
+        return 2;
+  }
+  free(ln);
+  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
+  lock->fd = fd;
+  luaL_getmetatable (L, LOCK_METATABLE);
+  lua_setmetatable (L, -2);
+  return 1;
+}
+static int lfs_unlock_dir(lua_State *L) {
+  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);
+  CloseHandle(lock->fd);
+  return 0;
+}
+#else
+typedef struct lfs_Lock {
+  char *ln;
+} lfs_Lock;
+static int lfs_lock_dir(lua_State *L) {
+  lfs_Lock *lock;
+  size_t pathl;
+  char *ln;
+  const char *lockfile = "/lockfile.lfs";
+  const char *path = luaL_checklstring(L, 1, &pathl);
+  lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock));
+  ln = (char*)malloc(pathl + strlen(lockfile) + 1);
+  if(!ln) {
+    lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2;
+  }
+  strcpy(ln, path); strcat(ln, lockfile);
+  if(symlink("lock", ln) == -1) {
+    free(ln); lua_pushnil(L);
+    lua_pushstring(L, strerror(errno)); return 2;
+  }
+  lock->ln = ln;
+  luaL_getmetatable (L, LOCK_METATABLE);
+  lua_setmetatable (L, -2);
+  return 1;
+}
+static int lfs_unlock_dir(lua_State *L) {
+  lfs_Lock *lock = luaL_checkudata(L, 1, LOCK_METATABLE);
+  if(lock->ln) {
+    unlink(lock->ln);
+    free(lock->ln);
+    lock->ln = NULL;
+  }
+  return 0;
+}
+#endif
+
+static int lfs_g_setmode (lua_State *L, FILE *f, int arg) {
+  static const int mode[] = {_O_BINARY, _O_TEXT};
+  static const char *const modenames[] = {"binary", "text", NULL};
+  int op = luaL_checkoption(L, arg, NULL, modenames);
+  int res = lfs_setmode(L, f, mode[op]);
+  if (res != -1) {
+    int i;
+    lua_pushboolean(L, 1);
+    for (i = 0; modenames[i] != NULL; i++) {
+      if (mode[i] == res) {
+        lua_pushstring(L, modenames[i]);
+        goto exit;
+      }
+    }
+    lua_pushnil(L);
+  exit:
+    return 2;
+  } else {
+    int en = errno;
+    lua_pushnil(L);
+    lua_pushfstring(L, "%s", strerror(en));
+    lua_pushinteger(L, en);
+    return 3;
+  }
+}
+
+static int lfs_f_setmode(lua_State *L) {
+  return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2);
+}
+
+/*
+** Locks a file.
+** @param #1 File handle.
+** @param #2 String with lock mode ('w'rite, 'r'ead).
+** @param #3 Number with start position (optional).
+** @param #4 Number with length (optional).
+*/
+static int file_lock (lua_State *L) {
+        FILE *fh = check_file (L, 1, "lock");
+        const char *mode = luaL_checkstring (L, 2);
+        const long start = luaL_optlong (L, 3, 0);
+        long len = luaL_optlong (L, 4, 0);
+        if (_file_lock (L, fh, mode, start, len, "lock")) {
+                lua_pushboolean (L, 1);
+                return 1;
+        } else {
+                lua_pushnil (L);
+                lua_pushfstring (L, "%s", strerror(errno));
+                return 2;
+        }
+}
+
+
+/*
+** Unlocks a file.
+** @param #1 File handle.
+** @param #2 Number with start position (optional).
+** @param #3 Number with length (optional).
+*/
+static int file_unlock (lua_State *L) {
+        FILE *fh = check_file (L, 1, "unlock");
+        const long start = luaL_optlong (L, 2, 0);
+        long len = luaL_optlong (L, 3, 0);
+        if (_file_lock (L, fh, "u", start, len, "unlock")) {
+                lua_pushboolean (L, 1);
+                return 1;
+        } else {
+                lua_pushnil (L);
+                lua_pushfstring (L, "%s", strerror(errno));
+                return 2;
+        }
+}
+
+
+/*
+** Creates a link.
+** @param #1 Object to link to.
+** @param #2 Name of link.
+** @param #3 True if link is symbolic (optional).
+*/
+static int make_link(lua_State *L)
+{
+#ifndef _WIN32
+        const char *oldpath = luaL_checkstring(L, 1);
+        const char *newpath = luaL_checkstring(L, 2);
+        return pushresult(L,
+                (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL);
+#else
+        return pusherror(L, "make_link is not supported on Windows");
+#endif
+}
+
+
+/*
+** Creates a directory.
+** @param #1 Directory path.
+*/
+static int make_dir (lua_State *L) {
+        const char *path = luaL_checkstring (L, 1);
+        int fail;
+#ifdef _WIN32
+        fail = _mkdir (path);
+#else
+        fail =  mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
+                             S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH );
+#endif
+        if (fail) {
+                lua_pushnil (L);
+        lua_pushfstring (L, "%s", strerror(errno));
+                return 2;
+        }
+        lua_pushboolean (L, 1);
+        return 1;
+}
+
+/*
+** Removes a directory.
+** @param #1 Directory path.
+*/
+static int remove_dir (lua_State *L) {
+        const char *path = luaL_checkstring (L, 1);
+        int fail;
+
+        fail = rmdir (path);
+
+        if (fail) {
+                lua_pushnil (L);
+                lua_pushfstring (L, "%s", strerror(errno));
+                return 2;
+        }
+        lua_pushboolean (L, 1);
+        return 1;
+}
+
+/*
+** Directory iterator
+*/
+static int dir_iter (lua_State *L) {
+#ifdef _WIN32
+        struct _finddata_t c_file;
+#else
+        struct dirent *entry;
+#endif
+        dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE);
+        luaL_argcheck (L, d->closed == 0, 1, "closed directory");
+#ifdef _WIN32
+        if (d->hFile == 0L) { /* first entry */
+                if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) {
+                        lua_pushnil (L);
+                        lua_pushstring (L, strerror (errno));
+                        d->closed = 1;
+                        return 2;
+                } else {
+                        lua_pushstring (L, c_file.name);
+                        return 1;
+                }
+        } else { /* next entry */
+                if (_findnext (d->hFile, &c_file) == -1L) {
+                        /* no more entries => close directory */
+                        _findclose (d->hFile);
+                        d->closed = 1;
+                        return 0;
+                } else {
+                        lua_pushstring (L, c_file.name);
+                        return 1;
+                }
+        }
+#else
+        if ((entry = readdir (d->dir)) != NULL) {
+                lua_pushstring (L, entry->d_name);
+                return 1;
+        } else {
+                /* no more entries => close directory */
+                closedir (d->dir);
+                d->closed = 1;
+                return 0;
+        }
+#endif
+}
+
+
+/*
+** Closes directory iterators
+*/
+static int dir_close (lua_State *L) {
+        dir_data *d = (dir_data *)lua_touserdata (L, 1);
+#ifdef _WIN32
+        if (!d->closed && d->hFile) {
+                _findclose (d->hFile);
+        }
+#else
+        if (!d->closed && d->dir) {
+                closedir (d->dir);
+        }
+#endif
+        d->closed = 1;
+        return 0;
+}
+
+
+/*
+** Factory of directory iterators
+*/
+static int dir_iter_factory (lua_State *L) {
+        const char *path = luaL_checkstring (L, 1);
+        dir_data *d;
+        lua_pushcfunction (L, dir_iter);
+        d = (dir_data *) lua_newuserdata (L, sizeof(dir_data));
+        luaL_getmetatable (L, DIR_METATABLE);
+        lua_setmetatable (L, -2);
+        d->closed = 0;
+#ifdef _WIN32
+        d->hFile = 0L;
+        if (strlen(path) > MAX_PATH-2)
+          luaL_error (L, "path too long: %s", path);
+        else
+          sprintf (d->pattern, "%s/*", path);
+#else
+        d->dir = opendir (path);
+        if (d->dir == NULL)
+          luaL_error (L, "cannot open %s: %s", path, strerror (errno));
+#endif
+        return 2;
+}
+
+
+/*
+** Creates directory metatable.
+*/
+static int dir_create_meta (lua_State *L) {
+        luaL_newmetatable (L, DIR_METATABLE);
+
+        /* Method table */
+        lua_newtable(L);
+        lua_pushcfunction (L, dir_iter);
+        lua_setfield(L, -2, "next");
+        lua_pushcfunction (L, dir_close);
+        lua_setfield(L, -2, "close");
+
+        /* Metamethods */
+        lua_setfield(L, -2, "__index");
+        lua_pushcfunction (L, dir_close);
+        lua_setfield (L, -2, "__gc");
+        return 1;
+}
+
+/*
+** Creates lock metatable.
+*/
+static int lock_create_meta (lua_State *L) {
+        luaL_newmetatable (L, LOCK_METATABLE);
+
+        /* Method table */
+        lua_newtable(L);
+        lua_pushcfunction(L, lfs_unlock_dir);
+        lua_setfield(L, -2, "free");
+
+        /* Metamethods */
+        lua_setfield(L, -2, "__index");
+        lua_pushcfunction(L, lfs_unlock_dir);
+        lua_setfield(L, -2, "__gc");
+        return 1;
+}
+
+
+#ifdef _WIN32
+ #ifndef S_ISDIR
+   #define S_ISDIR(mode)  (mode&_S_IFDIR)
+ #endif
+ #ifndef S_ISREG
+   #define S_ISREG(mode)  (mode&_S_IFREG)
+ #endif
+ #ifndef S_ISLNK
+   #define S_ISLNK(mode)  (0)
+ #endif
+ #ifndef S_ISSOCK
+   #define S_ISSOCK(mode)  (0)
+ #endif
+ #ifndef S_ISFIFO
+   #define S_ISFIFO(mode)  (0)
+ #endif
+ #ifndef S_ISCHR
+   #define S_ISCHR(mode)  (mode&_S_IFCHR)
+ #endif
+ #ifndef S_ISBLK
+   #define S_ISBLK(mode)  (0)
+ #endif
+#endif
+/*
+** Convert the inode protection mode to a string.
+*/
+#ifdef _WIN32
+static const char *mode2string (unsigned short mode) {
+#else
+static const char *mode2string (mode_t mode) {
+#endif
+  if ( S_ISREG(mode) )
+    return "file";
+  else if ( S_ISDIR(mode) )
+    return "directory";
+  else if ( S_ISLNK(mode) )
+        return "link";
+  else if ( S_ISSOCK(mode) )
+    return "socket";
+  else if ( S_ISFIFO(mode) )
+        return "named pipe";
+  else if ( S_ISCHR(mode) )
+        return "char device";
+  else if ( S_ISBLK(mode) )
+        return "block device";
+  else
+        return "other";
+}
+
+
+/*
+** Set access time and modification values for file
+*/
+static int file_utime (lua_State *L) {
+        const char *file = luaL_checkstring (L, 1);
+        struct utimbuf utb, *buf;
+
+        if (lua_gettop (L) == 1) /* set to current date/time */
+                buf = NULL;
+        else {
+                utb.actime = (time_t)luaL_optnumber (L, 2, 0);
+                utb.modtime = (time_t)luaL_optnumber (L, 3, utb.actime);
+                buf = &utb;
+        }
+        if (utime (file, buf)) {
+                lua_pushnil (L);
+                lua_pushfstring (L, "%s", strerror (errno));
+                return 2;
+        }
+        lua_pushboolean (L, 1);
+        return 1;
+}
+
+
+/* inode protection mode */
+static void push_st_mode (lua_State *L, STAT_STRUCT *info) {
+        lua_pushstring (L, mode2string (info->st_mode));
+}
+/* device inode resides on */
+static void push_st_dev (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_dev);
+}
+/* inode's number */
+static void push_st_ino (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_ino);
+}
+/* number of hard links to the file */
+static void push_st_nlink (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_nlink);
+}
+/* user-id of owner */
+static void push_st_uid (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_uid);
+}
+/* group-id of owner */
+static void push_st_gid (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_gid);
+}
+/* device type, for special file inode */
+static void push_st_rdev (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_rdev);
+}
+/* time of last access */
+static void push_st_atime (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, info->st_atime);
+}
+/* time of last data modification */
+static void push_st_mtime (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, info->st_mtime);
+}
+/* time of last file status change */
+static void push_st_ctime (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, info->st_ctime);
+}
+/* file size, in bytes */
+static void push_st_size (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_size);
+}
+#ifndef _WIN32
+/* blocks allocated for file */
+static void push_st_blocks (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_blocks);
+}
+/* optimal file system I/O blocksize */
+static void push_st_blksize (lua_State *L, STAT_STRUCT *info) {
+        lua_pushnumber (L, (lua_Number)info->st_blksize);
+}
+#endif
+static void push_invalid (lua_State *L, STAT_STRUCT *info) {
+  luaL_error(L, "invalid attribute name");
+#ifndef _WIN32
+  info->st_blksize = 0; /* never reached */
+#endif
+}
+
+ /*
+** Convert the inode protection mode to a permission list.
+*/
+
+#ifdef _WIN32
+static const char *perm2string (unsigned short mode) {
+  static char perms[10] = "---------\0";
+  int i;
+  for (i=0;i<9;i++) perms[i]='-';
+  if (mode  & _S_IREAD)
+   { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; }
+  if (mode  & _S_IWRITE)
+   { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; }
+  if (mode  & _S_IEXEC)
+   { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; }
+  return perms;
+}
+#else
+static const char *perm2string (mode_t mode) {
+  static char perms[10] = "---------\0";
+  int i;
+  for (i=0;i<9;i++) perms[i]='-';
+  if (mode & S_IRUSR) perms[0] = 'r';
+  if (mode & S_IWUSR) perms[1] = 'w';
+  if (mode & S_IXUSR) perms[2] = 'x';
+  if (mode & S_IRGRP) perms[3] = 'r';
+  if (mode & S_IWGRP) perms[4] = 'w';
+  if (mode & S_IXGRP) perms[5] = 'x';
+  if (mode & S_IROTH) perms[6] = 'r';
+  if (mode & S_IWOTH) perms[7] = 'w';
+  if (mode & S_IXOTH) perms[8] = 'x';
+  return perms;
+}
+#endif
+
+/* permssions string */
+static void push_st_perm (lua_State *L, STAT_STRUCT *info) {
+    lua_pushstring (L, perm2string (info->st_mode));
+}
+
+typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info);
+
+struct _stat_members {
+        const char *name;
+        _push_function push;
+};
+
+struct _stat_members members[] = {
+        { "mode",         push_st_mode },
+        { "dev",          push_st_dev },
+        { "ino",          push_st_ino },
+        { "nlink",        push_st_nlink },
+        { "uid",          push_st_uid },
+        { "gid",          push_st_gid },
+        { "rdev",         push_st_rdev },
+        { "access",       push_st_atime },
+        { "modification", push_st_mtime },
+        { "change",       push_st_ctime },
+        { "size",         push_st_size },
+        { "permissions",  push_st_perm },
+#ifndef _WIN32
+        { "blocks",       push_st_blocks },
+        { "blksize",      push_st_blksize },
+#endif
+        { NULL, push_invalid }
+};
+
+/*
+** Get file or symbolic link information
+*/
+static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) {
+        int i;
+        STAT_STRUCT info;
+        const char *file = luaL_checkstring (L, 1);
+
+        if (st(file, &info)) {
+                lua_pushnil (L);
+                lua_pushfstring (L, "cannot obtain information from file `%s'", file);
+                return 2;
+        }
+        if (lua_isstring (L, 2)) {
+                int v;
+                const char *member = lua_tostring (L, 2);
+                if (strcmp (member, "mode") == 0) v = 0;
+#ifndef _WIN32
+                else if (strcmp (member, "blocks")  == 0) v = 11;
+                else if (strcmp (member, "blksize") == 0) v = 12;
+#endif
+                else /* look for member */
+                        for (v = 1; members[v].name; v++)
+                                if (*members[v].name == *member)
+                                        break;
+                /* push member value and return */
+                members[v].push (L, &info);
+                return 1;
+        } else if (!lua_istable (L, 2))
+                /* creates a table if none is given */
+                lua_newtable (L);
+        /* stores all members in table on top of the stack */
+        for (i = 0; members[i].name; i++) {
+                lua_pushstring (L, members[i].name);
+                members[i].push (L, &info);
+                lua_rawset (L, -3);
+        }
+        return 1;
+}
+
+
+/*
+** Get file information using stat.
+*/
+static int file_info (lua_State *L) {
+        return _file_info_ (L, STAT_FUNC);
+}
+
+
+/*
+** Get symbolic link information using lstat.
+*/
+static int link_info (lua_State *L) {
+        return _file_info_ (L, LSTAT_FUNC);
+}
+
+
+/*
+** Assumes the table is on top of the stack.
+*/
+static void set_info (lua_State *L) {
+        lua_pushliteral (L, "_COPYRIGHT");
+        lua_pushliteral (L, "Copyright (C) 2003-2012 Kepler Project");
+        lua_settable (L, -3);
+        lua_pushliteral (L, "_DESCRIPTION");
+        lua_pushliteral (L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution");
+        lua_settable (L, -3);
+        lua_pushliteral (L, "_VERSION");
+        lua_pushliteral (L, "LuaFileSystem "LFS_VERSION);
+        lua_settable (L, -3);
+}
+
+
+static const struct luaL_Reg fslib[] = {
+        {"attributes", file_info},
+        {"chdir", change_dir},
+        {"currentdir", get_dir},
+        {"dir", dir_iter_factory},
+        {"link", make_link},
+        {"lock", file_lock},
+        {"mkdir", make_dir},
+        {"rmdir", remove_dir},
+        {"symlinkattributes", link_info},
+        {"setmode", lfs_f_setmode},
+        {"touch", file_utime},
+        {"unlock", file_unlock},
+        {"lock_dir", lfs_lock_dir},
+        {NULL, NULL},
+};
+
+int luaopen_lfs (lua_State *L) {
+        dir_create_meta (L);
+        lock_create_meta (L);
+        luaL_newlib (L, fslib);
+        lua_pushvalue(L, -1);
+        lua_setglobal(L, LFS_LIBNAME);
+        set_info (L);
+        return 1;
+}

+ 17 - 17
src/third_party/lfs.h

@@ -1,17 +1,17 @@
-/*
-** LuaFileSystem
-** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)
-**
-** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $
-*/
-
-/* Define 'chdir' for systems that do not implement it */
-#ifdef NO_CHDIR
-#define chdir(p)	(-1)
-#define chdir_error	"Function 'chdir' not provided by system"
-#else
-#define chdir_error	strerror(errno)
-#endif
-
-
-int luaopen_lfs (lua_State *L);
+/*
+** LuaFileSystem
+** Copyright Kepler Project 2003 (http://www.keplerproject.org/luafilesystem)
+**
+** $Id: lfs.h,v 1.5 2008/02/19 20:08:23 mascarenhas Exp $
+*/
+
+/* Define 'chdir' for systems that do not implement it */
+#ifdef NO_CHDIR
+#define chdir(p)	(-1)
+#define chdir_error	"Function 'chdir' not provided by system"
+#else
+#define chdir_error	strerror(errno)
+#endif
+
+
+int luaopen_lfs (lua_State *L);

File diff suppressed because it is too large
+ 450 - 209
src/third_party/sqlite3.c


File diff suppressed because it is too large
+ 357 - 223
src/third_party/sqlite3.h


+ 182 - 182
test/embed.c

@@ -1,182 +1,182 @@
-// Copyright (c) 2004-2009 Sergey Lyubka
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in
-// all copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-// THE SOFTWARE.
-//
-// Unit test for the civetweb web server. Tests embedded API.
-
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#ifndef _WIN32
-#include <unistd.h>
-#endif
-
-#include "civetweb.h"
-
-#if !defined(LISTENING_PORT)
-#define LISTENING_PORT "23456"
-#endif
-
-static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
-  "Content-Type: text/plain\r\n"
-  "Connection: close\r\n\r\n";
-
-static void test_get_var(struct mg_connection *conn,
-                         const struct mg_request_info *ri) {
-  char *var, *buf;
-  size_t buf_len;
-  const char *cl;
-  int var_len;
-
-  mg_printf(conn, "%s", standard_reply);
-
-  buf_len = 0;
-  var = buf = NULL;
-  cl = mg_get_header(conn, "Content-Length");
-  mg_printf(conn, "cl: %p\n", cl);
-  if ((!strcmp(ri->request_method, "POST") ||
-       !strcmp(ri->request_method, "PUT"))
-      && cl != NULL) {
-    buf_len = atoi(cl);
-    buf = malloc(buf_len);
-    /* Read in two pieces, to test continuation */
-    if (buf_len > 2) {
-      mg_read(conn, buf, 2);
-      mg_read(conn, buf + 2, buf_len - 2);
-    } else {
-      mg_read(conn, buf, buf_len);
-    }
-  } else if (ri->query_string != NULL) {
-    buf_len = strlen(ri->query_string);
-    buf = malloc(buf_len + 1);
-    strcpy(buf, ri->query_string);
-  }
-  var = malloc(buf_len + 1);
-  var_len = mg_get_var(buf, buf_len, "my_var", var, buf_len + 1);
-  mg_printf(conn, "Value: [%s]\n", var);
-  mg_printf(conn, "Value size: [%d]\n", var_len);
-  free(buf);
-  free(var);
-}
-
-static void test_get_header(struct mg_connection *conn,
-                            const struct mg_request_info *ri) {
-  const char *value;
-  int i;
-
-  mg_printf(conn, "%s", standard_reply);
-  printf("HTTP headers: %d\n", ri->num_headers);
-  for (i = 0; i < ri->num_headers; i++) {
-    printf("[%s]: [%s]\n", ri->http_headers[i].name, ri->http_headers[i].value);
-  }
-
-  value = mg_get_header(conn, "Host");
-  if (value != NULL) {
-    mg_printf(conn, "Value: [%s]", value);
-  }
-}
-
-static void test_get_request_info(struct mg_connection *conn,
-                                  const struct mg_request_info *ri) {
-  int i;
-
-  mg_printf(conn, "%s", standard_reply);
-
-  mg_printf(conn, "Method: [%s]\n", ri->request_method);
-  mg_printf(conn, "URI: [%s]\n", ri->uri);
-  mg_printf(conn, "HTTP version: [%s]\n", ri->http_version);
-
-  for (i = 0; i < ri->num_headers; i++) {
-    mg_printf(conn, "HTTP header [%s]: [%s]\n",
-              ri->http_headers[i].name,
-              ri->http_headers[i].value);
-  }
-
-  mg_printf(conn, "Query string: [%s]\n",
-            ri->query_string ? ri->query_string: "");
-  mg_printf(conn, "Remote IP: [%lu]\n", ri->remote_ip);
-  mg_printf(conn, "Remote port: [%d]\n", ri->remote_port);
-  mg_printf(conn, "Remote user: [%s]\n",
-            ri->remote_user ? ri->remote_user : "");
-}
-
-static void test_error(struct mg_connection *conn,
-                       const struct mg_request_info *ri) {
-  int status = (int) ri->ev_data;
-  mg_printf(conn, "HTTP/1.1 %d XX\r\n"
-            "Conntection: close\r\n\r\n", status);
-  mg_printf(conn, "Error: [%d]", status);
-}
-
-static void test_post(struct mg_connection *conn,
-                      const struct mg_request_info *ri) {
-  const char *cl;
-  char *buf;
-  int len;
-
-  mg_printf(conn, "%s", standard_reply);
-  if (strcmp(ri->request_method, "POST") == 0 &&
-      (cl = mg_get_header(conn, "Content-Length")) != NULL) {
-    len = atoi(cl);
-    if ((buf = malloc(len)) != NULL) {
-      mg_write(conn, buf, len);
-      free(buf);
-    }
-  }
-}
-
-static const struct test_config {
-  enum mg_event event;
-  const char *uri;
-  void (*func)(struct mg_connection *, const struct mg_request_info *);
-} test_config[] = {
-  {MG_NEW_REQUEST, "/test_get_header", &test_get_header},
-  {MG_NEW_REQUEST, "/test_get_var", &test_get_var},
-  {MG_NEW_REQUEST, "/test_get_request_info", &test_get_request_info},
-  {MG_NEW_REQUEST, "/test_post", &test_post},
-  {MG_HTTP_ERROR, "", &test_error},
-  {0, NULL, NULL}
-};
-
-static void *callback(enum mg_event event,
-                      struct mg_connection *conn) {
-  const struct mg_request_info *request_info = mg_get_request_info(conn);
-  int i;
-
-  for (i = 0; test_config[i].uri != NULL; i++) {
-    if (event == test_config[i].event &&
-        (event == MG_HTTP_ERROR ||
-         !strcmp(request_info->uri, test_config[i].uri))) {
-      test_config[i].func(conn, request_info);
-      return "processed";
-    }
-  }
-
-  return NULL;
-}
-
-int main(void) {
-  struct mg_context *ctx;
-  const char *options[] = {"listening_ports", LISTENING_PORT, NULL};
-
-  ctx = mg_start(callback, NULL, options);
-  pause();
-  return 0;
-}
+// Copyright (c) 2004-2009 Sergey Lyubka
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+//
+// Unit test for the civetweb web server. Tests embedded API.
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "civetweb.h"
+
+#if !defined(LISTENING_PORT)
+#define LISTENING_PORT "23456"
+#endif
+
+static const char *standard_reply = "HTTP/1.1 200 OK\r\n"
+  "Content-Type: text/plain\r\n"
+  "Connection: close\r\n\r\n";
+
+static void test_get_var(struct mg_connection *conn,
+                         const struct mg_request_info *ri) {
+  char *var, *buf;
+  size_t buf_len;
+  const char *cl;
+  int var_len;
+
+  mg_printf(conn, "%s", standard_reply);
+
+  buf_len = 0;
+  var = buf = NULL;
+  cl = mg_get_header(conn, "Content-Length");
+  mg_printf(conn, "cl: %p\n", cl);
+  if ((!strcmp(ri->request_method, "POST") ||
+       !strcmp(ri->request_method, "PUT"))
+      && cl != NULL) {
+    buf_len = atoi(cl);
+    buf = malloc(buf_len);
+    /* Read in two pieces, to test continuation */
+    if (buf_len > 2) {
+      mg_read(conn, buf, 2);
+      mg_read(conn, buf + 2, buf_len - 2);
+    } else {
+      mg_read(conn, buf, buf_len);
+    }
+  } else if (ri->query_string != NULL) {
+    buf_len = strlen(ri->query_string);
+    buf = malloc(buf_len + 1);
+    strcpy(buf, ri->query_string);
+  }
+  var = malloc(buf_len + 1);
+  var_len = mg_get_var(buf, buf_len, "my_var", var, buf_len + 1);
+  mg_printf(conn, "Value: [%s]\n", var);
+  mg_printf(conn, "Value size: [%d]\n", var_len);
+  free(buf);
+  free(var);
+}
+
+static void test_get_header(struct mg_connection *conn,
+                            const struct mg_request_info *ri) {
+  const char *value;
+  int i;
+
+  mg_printf(conn, "%s", standard_reply);
+  printf("HTTP headers: %d\n", ri->num_headers);
+  for (i = 0; i < ri->num_headers; i++) {
+    printf("[%s]: [%s]\n", ri->http_headers[i].name, ri->http_headers[i].value);
+  }
+
+  value = mg_get_header(conn, "Host");
+  if (value != NULL) {
+    mg_printf(conn, "Value: [%s]", value);
+  }
+}
+
+static void test_get_request_info(struct mg_connection *conn,
+                                  const struct mg_request_info *ri) {
+  int i;
+
+  mg_printf(conn, "%s", standard_reply);
+
+  mg_printf(conn, "Method: [%s]\n", ri->request_method);
+  mg_printf(conn, "URI: [%s]\n", ri->uri);
+  mg_printf(conn, "HTTP version: [%s]\n", ri->http_version);
+
+  for (i = 0; i < ri->num_headers; i++) {
+    mg_printf(conn, "HTTP header [%s]: [%s]\n",
+              ri->http_headers[i].name,
+              ri->http_headers[i].value);
+  }
+
+  mg_printf(conn, "Query string: [%s]\n",
+            ri->query_string ? ri->query_string: "");
+  mg_printf(conn, "Remote IP: [%lu]\n", ri->remote_ip);
+  mg_printf(conn, "Remote port: [%d]\n", ri->remote_port);
+  mg_printf(conn, "Remote user: [%s]\n",
+            ri->remote_user ? ri->remote_user : "");
+}
+
+static void test_error(struct mg_connection *conn,
+                       const struct mg_request_info *ri) {
+  int status = (int) ri->ev_data;
+  mg_printf(conn, "HTTP/1.1 %d XX\r\n"
+            "Conntection: close\r\n\r\n", status);
+  mg_printf(conn, "Error: [%d]", status);
+}
+
+static void test_post(struct mg_connection *conn,
+                      const struct mg_request_info *ri) {
+  const char *cl;
+  char *buf;
+  int len;
+
+  mg_printf(conn, "%s", standard_reply);
+  if (strcmp(ri->request_method, "POST") == 0 &&
+      (cl = mg_get_header(conn, "Content-Length")) != NULL) {
+    len = atoi(cl);
+    if ((buf = malloc(len)) != NULL) {
+      mg_write(conn, buf, len);
+      free(buf);
+    }
+  }
+}
+
+static const struct test_config {
+  enum mg_event event;
+  const char *uri;
+  void (*func)(struct mg_connection *, const struct mg_request_info *);
+} test_config[] = {
+  {MG_NEW_REQUEST, "/test_get_header", &test_get_header},
+  {MG_NEW_REQUEST, "/test_get_var", &test_get_var},
+  {MG_NEW_REQUEST, "/test_get_request_info", &test_get_request_info},
+  {MG_NEW_REQUEST, "/test_post", &test_post},
+  {MG_HTTP_ERROR, "", &test_error},
+  {0, NULL, NULL}
+};
+
+static void *callback(enum mg_event event,
+                      struct mg_connection *conn) {
+  const struct mg_request_info *request_info = mg_get_request_info(conn);
+  int i;
+
+  for (i = 0; test_config[i].uri != NULL; i++) {
+    if (event == test_config[i].event &&
+        (event == MG_HTTP_ERROR ||
+         !strcmp(request_info->uri, test_config[i].uri))) {
+      test_config[i].func(conn, request_info);
+      return "processed";
+    }
+  }
+
+  return NULL;
+}
+
+int main(void) {
+  struct mg_context *ctx;
+  const char *options[] = {"listening_ports", LISTENING_PORT, NULL};
+
+  ctx = mg_start(callback, NULL, options);
+  pause();
+  return 0;
+}

+ 83 - 0
test/form.html

@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+
+<head>
+  <meta charset="UTF-8">
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <title>Example page for HTML form handling</title>
+</head>
+
+<body>
+  <form action="/handle_form.lua" method="POST" enctype="multipart/form-data">
+    See <a href="http://www.w3schools.com/html/html_form_input_types.asp">HTML form tutorial</a>.<br />
+
+    <fieldset>
+      <legend>Text inputs:</legend>
+      A text: <input type="text" name="textin"><br />
+      A password: <input type="password" name="passwordin"><br />
+    </fieldset>
+
+    <fieldset>
+      <legend>Radio set 1:</legend>
+      <input type="radio" name="radio1" value="val1" checked>val1<br />
+      <input type="radio" name="radio1" value="val2">val2<br />
+      <input type="radio" name="radio1" value="val3">val3<br />
+    </fieldset>
+
+    <fieldset>
+      <legend>Radio set 2:</legend>
+      <input type="radio" name="radio2" value="val1" checked>val1<br />
+      <input type="radio" name="radio2" value="val2">val2<br />
+      <input type="radio" name="radio2" value="val3">val3<br />
+    </fieldset>
+
+    <fieldset>
+      <legend>Checkboxes:</legend>
+      <input type="checkbox" name="check1" value="val1" checked>val1<br />
+      <input type="checkbox" name="check2" value="val2">val2<br />
+      <input type="checkbox" name="check3" value="val3">val3<br />
+    </fieldset>
+
+    <fieldset>
+      <legend>HTML5 inputs:</legend>
+      A number: <input type="number" name="numberin" min="1" max="5"><br />
+      A date: <input type="date" name="datein"><br />
+      A color: <input type="color" name="colorin"><br />
+      A range: <input type="range" name="rangein" min="1" max="5"><br />
+      A month: <input type="month" name="monthin"><br />
+      A week: <input type="week" name="weekin"><br />
+      A time: <input type="time" name="timein"><br />
+      A datetime: <input type="datetime" name="datetimen"><br />
+      A datetime-local: <input type="datetime-local" name="datetimelocalin"><br />
+      An email: <input type="email" name="emailin"><br />
+      A search: <input type="search" name="searchin"><br />
+      A tel: <input type="tel" name="telin"><br />
+      An url: <input type="url" name="urlin"><br />
+    </fieldset>
+
+    <fieldset>
+      <legend>Files:</legend>
+      A file: <input type="file" name="filein"><br />
+      Multiple files: <input type="file" name="filesin" multiple><br>
+    </fieldset>
+
+    <fieldset>
+      <legend>Dropdown:</legend>
+      <select name="selectin">
+        <option value="opt1">opt1</option>
+        <option value="opt2">opt2</option>
+        <option value="opt3">opt3</option>
+      </select>
+    </fieldset>
+
+    <fieldset>
+      <legend>Text area:</legend>
+      <textarea name="message" rows="10" cols="30">Text area default text.</textarea>
+    </fieldset>
+
+    <input type="submit" value="Submit">
+
+  </form>
+</body>
+
+</html>

+ 97 - 0
test/handle_form.lua

@@ -0,0 +1,97 @@
+-- Some basic checks
+if mg.request_info.request_method ~= "POST" or mg.request_info.content_type:lower():sub(1,19) ~= 'multipart/form-data' then
+  mg.write("HTTP/1.0 400 OK\r\n")
+  mg.write("Connection: close\r\n")
+  mg.write("Content-Type: text/plain; charset=utf-8\r\n")
+  mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
+  mg.write("\r\n")
+  mg.write("Bad request\r\n\r\n")
+  return
+end
+
+-- HTTP headers
+mg.write("HTTP/1.0 200 OK\r\n")
+mg.write("Connection: close\r\n")
+mg.write("Content-Type: text/plain; charset=utf-8\r\n")
+mg.write("Cache-Control: max-age=0, must-revalidate\r\n")
+mg.write("\r\n")
+
+-- Which form sent the data?
+mg.write("Read POST data from " .. mg.request_info.http_headers.Referer .. ":\r\n\r\n")
+
+-- Count some data fields
+local fields = 0
+local datasize = 0
+
+-- Read the entire body data (POST content) into "bdata" variable.
+-- Use a string builder pattern for performance reasons
+stringtab = {}
+bdata = ""
+repeat
+  local add_data = mg.read()
+  if add_data then
+    stringtab[#stringtab+1] = add_data
+  end
+until (add_data == nil);
+bdata = table.concat(stringtab)
+stringtab = nil
+
+-- Get the boundary string.
+bs = "--" .. ((mg.request_info.content_type):upper():match("BOUNDARY=(.*)"));
+
+-- The POST data has to start with the boundary string.
+-- Check this and remove the starting boundary.
+if bdata:sub(1, #bs) ~= bs then
+  error "invalid format of POST data"
+end
+bdata = bdata:sub(#bs)
+
+-- The boundary now starts with CR LF.
+bs = "\r\n" .. bs
+
+-- Now loop through all the parts
+while #bdata>4 do
+   -- Find the header of new part.
+   part_header_end = bdata:find("\r\n\r\n", 1, true)
+
+   -- Parse the header.
+   local form_field_name, file_name
+   h = bdata:sub(1, part_header_end+2)
+   for key,val in h:gmatch("([^%:\r\n]*)%s*%:%s*([^\r\n]*)\r\n") do
+      if key:upper() == "CONTENT-DISPOSITION" then
+          form_field_name = val:match('name=%"([^%"]*)%"')
+          file_name = val:match('filename=%"([^%"]*)%"')
+      end
+   end
+
+   -- Remove the header from "bdata".
+   bdata = bdata:sub(part_header_end+4)
+
+   -- Find the end of the body by locating the boundary string.
+   local part_body_end = bdata:find(bs, 1, true)
+
+   -- Isolate the content, and drop it from "bdata".
+   local form_field_value = bdata:sub(1,part_body_end-1)
+   bdata = bdata:sub(part_body_end+#bs)
+
+   -- Now the data (file content or field value) is isolated: We know form_field_name and form_field_value.
+   -- Here the script should do something useful with the data. This example just sends it back to the client.
+   if form_field_name then
+     mg.write("Field name: " .. form_field_name .. "\r\n")
+   end
+   if file_name then
+     mg.write("File name: " .. file_name .. "\r\n")
+   end
+   local len = #form_field_value
+   if len<50 then
+     mg.write("Field value: " .. form_field_value .. "\r\n")
+   else
+     mg.write("Field value: " .. form_field_value:sub(1, 40) .. " .. (" .. len .. " bytes)\r\n")
+   end
+   mg.write("\r\n")
+   fields = fields + 1
+   datasize = datasize + len
+
+end
+
+mg.write("Got " .. fields .. " input fields with " .. datasize .. " bytes total\r\n");

+ 1 - 1
test/testclient.c

@@ -141,7 +141,7 @@ int main(int argc, char *argv[])
             if (result[j]==i) cnt++;
             if (result[j]==i) cnt++;
         }
         }
         if (cnt>0) {
         if (cnt>0) {
-            printf("%5i\t%7u\n", i, cnt);
+            printf("%5i\t%7i\n", i, cnt);
         }
         }
     }
     }
 
 

+ 10 - 3
test/unit_test.c

@@ -579,16 +579,23 @@ static void test_mg_websocket_client_connect(int use_ssl) {
 
 
 static int alloc_printf(char **buf, size_t size, char *fmt, ...) {
 static int alloc_printf(char **buf, size_t size, char *fmt, ...) {
     va_list ap;
     va_list ap;
+    int ret = 0;
     va_start(ap, fmt);
     va_start(ap, fmt);
-    return alloc_vprintf(buf, size, fmt, ap);
+    ret = alloc_vprintf(buf, size, fmt, ap);
+    va_end(ap);
+    return ret:
 }
 }
 
 
 static void test_mg_upload(void) {
 static void test_mg_upload(void) {
     static const char *boundary = "OOO___MY_BOUNDARY___OOO";
     static const char *boundary = "OOO___MY_BOUNDARY___OOO";
     struct mg_context *ctx;
     struct mg_context *ctx;
+#if 0
     struct mg_connection *conn;
     struct mg_connection *conn;
-    char ebuf[100], buf[20], *file_data, *file2_data, *post_data;
-    int file_len, file2_len, post_data_len;
+    char ebuf[100], buf[20], *file2_data;
+    int file2_len;
+#endif
+    char *file_data, *post_data;
+    int file_len post_data_len;
 
 
     ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
     ASSERT((ctx = mg_start(&CALLBACKS, NULL, OPTIONS)) != NULL);
 
 

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