[njs] Nodejs style file methods.

Dmitry Volyntsev xeioex at nginx.com
Fri Nov 17 16:06:05 UTC 2017


details:   http://hg.nginx.org/njs/rev/5c6aa60224cb
branches:  
changeset: 426:5c6aa60224cb
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Fri Nov 17 18:55:07 2017 +0300
description:
Nodejs style file methods.

diffstat:

 Makefile                        |    27 +
 njs/njs_builtin.c               |    84 ++-
 njs/njs_builtin.h               |     1 +
 njs/njs_fs.c                    |  1095 +++++++++++++++++++++++++++++++++++++++
 njs/njs_fs.h                    |    13 +
 njs/njs_generator.c             |     1 +
 njs/njs_lexer_keyword.c         |     1 +
 njs/njs_module.c                |    85 +++
 njs/njs_module.h                |    22 +
 njs/njs_object_hash.h           |    56 +
 njs/njs_parser.c                |     1 +
 njs/njs_parser.h                |     1 +
 njs/njs_string.c                |    46 +
 njs/njs_string.h                |     1 +
 njs/njs_vm.h                    |    20 +-
 njs/njscript.c                  |     3 +
 njs/test/njs_expect_test.exp    |   221 +++++++
 njs/test/njs_interactive_test.c |     5 +
 njs/test/njs_unit_test.c        |   136 ++++
 19 files changed, 1817 insertions(+), 2 deletions(-)

diffs (truncated from 2098 to 1000 lines):

diff -r 17909969892f -r 5c6aa60224cb Makefile
--- a/Makefile	Fri Nov 17 18:55:07 2017 +0300
+++ b/Makefile	Fri Nov 17 18:55:07 2017 +0300
@@ -22,6 +22,8 @@ NXT_BUILDDIR =	build
 	$(NXT_BUILDDIR)/njs_date.o \
 	$(NXT_BUILDDIR)/njs_error.o \
 	$(NXT_BUILDDIR)/njs_math.o \
+	$(NXT_BUILDDIR)/njs_module.o \
+	$(NXT_BUILDDIR)/njs_fs.o \
 	$(NXT_BUILDDIR)/njs_extern.o \
 	$(NXT_BUILDDIR)/njs_variable.o \
 	$(NXT_BUILDDIR)/njs_builtin.o \
@@ -56,6 +58,8 @@ NXT_BUILDDIR =	build
 		$(NXT_BUILDDIR)/njs_date.o \
 		$(NXT_BUILDDIR)/njs_error.o \
 		$(NXT_BUILDDIR)/njs_math.o \
+		$(NXT_BUILDDIR)/njs_module.o \
+		$(NXT_BUILDDIR)/njs_fs.o \
 		$(NXT_BUILDDIR)/njs_extern.o \
 		$(NXT_BUILDDIR)/njs_variable.o \
 		$(NXT_BUILDDIR)/njs_builtin.o \
@@ -299,6 +303,28 @@ dist:
 		-I$(NXT_LIB) -Injs \
 		njs/njs_math.c
 
+$(NXT_BUILDDIR)/njs_module.o: \
+	$(NXT_BUILDDIR)/libnxt.a \
+	njs/njscript.h \
+	njs/njs_vm.h \
+	njs/njs_module.h \
+	njs/njs_module.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_module.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) -Injs \
+		njs/njs_module.c
+
+$(NXT_BUILDDIR)/njs_fs.o: \
+	$(NXT_BUILDDIR)/libnxt.a \
+	njs/njscript.h \
+	njs/njs_vm.h \
+	njs/njs_fs.h \
+	njs/njs_fs.c \
+
+	$(NXT_CC) -c -o $(NXT_BUILDDIR)/njs_fs.o $(NXT_CFLAGS) \
+		-I$(NXT_LIB) -Injs \
+		njs/njs_fs.c
+
 $(NXT_BUILDDIR)/njs_extern.o: \
 	$(NXT_BUILDDIR)/libnxt.a \
 	njs/njscript.h \
@@ -332,6 +358,7 @@ dist:
 	njs/njs_string.h \
 	njs/njs_object.h \
 	njs/njs_array.h \
+	njs/njs_module.h \
 	njs/njs_function.h \
 	njs/njs_regexp.h \
 	njs/njs_parser.h \
diff -r 17909969892f -r 5c6aa60224cb njs/njs_builtin.c
--- a/njs/njs_builtin.c	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_builtin.c	Fri Nov 17 18:55:07 2017 +0300
@@ -30,6 +30,8 @@
 #include <njs_date.h>
 #include <njs_error.h>
 #include <njs_math.h>
+#include <njs_module.h>
+#include <njs_fs.h>
 #include <string.h>
 #include <stdio.h>
 
@@ -54,6 +56,11 @@ const njs_object_init_t    *njs_object_i
 };
 
 
+const njs_object_init_t    *njs_module_init[] = {
+    &njs_fs_object_init          /* fs                 */
+};
+
+
 const njs_object_init_t  *njs_prototype_init[] = {
     &njs_object_prototype_init,
     &njs_array_prototype_init,
@@ -111,8 +118,10 @@ njs_builtin_objects_create(njs_vm_t *vm)
 {
     nxt_int_t               ret;
     nxt_uint_t              i;
+    njs_module_t            *module;
     njs_object_t            *objects;
     njs_function_t          *functions, *constructors;
+    nxt_lvlhsh_query_t      lhq;
     njs_object_prototype_t  *prototypes;
 
     static const njs_object_prototype_t  prototype_values[] = {
@@ -193,6 +202,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         NULL,                         /* encodeURIComponent */
         NULL,                         /* decodeURI          */
         NULL,                         /* decodeURIComponent */
+        NULL,                         /* require */
     };
 
     static const njs_function_init_t  native_functions[] = {
@@ -208,6 +218,7 @@ njs_builtin_objects_create(njs_vm_t *vm)
         { njs_string_encode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_string_decode_uri,           { NJS_SKIP_ARG, NJS_STRING_ARG } },
         { njs_string_decode_uri_component, { NJS_SKIP_ARG, NJS_STRING_ARG } },
+        { njs_module_require,              { NJS_SKIP_ARG, NJS_STRING_ARG } },
     };
 
     static const njs_object_prop_t    null_proto_property = {
@@ -249,6 +260,37 @@ njs_builtin_objects_create(njs_vm_t *vm)
         objects[i].shared = 1;
     }
 
+    lhq.replace = 0;
+    lhq.proto = &njs_modules_hash_proto;
+    lhq.pool = vm->mem_cache_pool;
+
+    for (i = NJS_MODULE_FS; i < NJS_MODULE_MAX; i++) {
+        module = nxt_mem_cache_zalloc(vm->mem_cache_pool, sizeof(njs_module_t));
+        if (nxt_slow_path(module == NULL)) {
+            return NJS_ERROR;
+        }
+
+        module->name = njs_module_init[i]->name;
+
+        ret = njs_object_hash_create(vm, &module->object.shared_hash,
+                                     njs_module_init[i]->properties,
+                                     njs_module_init[i]->items);
+        if (nxt_slow_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+
+        module->object.shared = 1;
+
+        lhq.key = module->name;
+        lhq.key_hash = nxt_djb_hash(lhq.key.start, lhq.key.length);
+        lhq.value = module;
+
+        ret = nxt_lvlhsh_insert(&vm->modules_hash, &lhq);
+        if (nxt_fast_path(ret != NXT_OK)) {
+            return NXT_ERROR;
+        }
+    }
+
     functions = vm->shared->functions;
 
     for (i = NJS_FUNCTION_EVAL; i < NJS_FUNCTION_MAX; i++) {
@@ -857,10 +899,11 @@ njs_builtin_match_native_function(njs_vm
     size_t                  len;
     nxt_str_t               string;
     nxt_uint_t              i;
+    njs_module_t            *module;
     njs_object_t            *objects;
     njs_function_t          *constructors;
     njs_object_prop_t       *prop;
-    nxt_lvlhsh_each_t       lhe;
+    nxt_lvlhsh_each_t       lhe, lhe_prop;
     njs_object_prototype_t  *prototypes;
 
     objects = vm->shared->objects;
@@ -978,5 +1021,44 @@ njs_builtin_match_native_function(njs_vm
         }
     }
 
+    nxt_lvlhsh_each_init(&lhe, &njs_modules_hash_proto);
+
+    for ( ;; ) {
+        module = nxt_lvlhsh_each(&vm->modules_hash, &lhe);
+        if (module == NULL) {
+            break;
+        }
+
+        nxt_lvlhsh_each_init(&lhe_prop, &njs_object_hash_proto);
+
+        for ( ;; ) {
+            prop = nxt_lvlhsh_each(&module->object.shared_hash, &lhe_prop);
+            if (prop == NULL) {
+                break;
+            }
+
+            if (!njs_is_function(&prop->value)) {
+                continue;
+            }
+
+            if (function == prop->value.data.u.function) {
+                njs_string_get(&prop->name, &string);
+                len = module->name.length + string.length + sizeof(".");
+
+                buf = nxt_mem_cache_zalloc(vm->mem_cache_pool, len);
+                if (buf == NULL) {
+                    return NXT_ERROR;
+                }
+
+                snprintf(buf, len, "%s.%s", module->name.start, string.start);
+
+                name->length = len;
+                name->start = (u_char *) buf;
+
+                return NXT_OK;
+            }
+        }
+    }
+
     return NXT_DECLINED;
 }
diff -r 17909969892f -r 5c6aa60224cb njs/njs_builtin.h
--- a/njs/njs_builtin.h	Fri Nov 17 18:55:07 2017 +0300
+++ b/njs/njs_builtin.h	Fri Nov 17 18:55:07 2017 +0300
@@ -9,6 +9,7 @@
 
 
 extern const njs_object_init_t  *njs_object_init[];
+extern const njs_object_init_t  *njs_module_init[];
 extern const njs_object_init_t  *njs_prototype_init[];
 extern const njs_object_init_t  *njs_constructor_init[];
 
diff -r 17909969892f -r 5c6aa60224cb njs/njs_fs.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/njs/njs_fs.c	Fri Nov 17 18:55:07 2017 +0300
@@ -0,0 +1,1095 @@
+
+/*
+ * Copyright (C) Dmitry Volyntsev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_auto_config.h>
+#include <nxt_alignment.h>
+#include <nxt_types.h>
+#include <nxt_clang.h>
+#include <nxt_string.h>
+#include <nxt_stub.h>
+#include <nxt_djb_hash.h>
+#include <nxt_array.h>
+#include <nxt_lvlhsh.h>
+#include <nxt_random.h>
+#include <nxt_mem_cache_pool.h>
+#include <njscript.h>
+#include <njs_vm.h>
+#include <njs_string.h>
+#include <njs_object.h>
+#include <njs_object_hash.h>
+#include <njs_function.h>
+#include <njs_error.h>
+#include <njs_fs.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+
+
+typedef struct {
+    union {
+        njs_continuation_t  cont;
+        u_char              padding[NJS_CONTINUATION_SIZE];
+    } u;
+
+    nxt_bool_t              done;
+} njs_fs_cont_t;
+
+
+typedef struct {
+    nxt_str_t               name;
+    int                     value;
+} njs_fs_entry_t;
+
+
+static njs_ret_t njs_fs_read_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_append_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+static njs_ret_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags);
+static njs_ret_t njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags);
+static njs_ret_t njs_fs_done(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused);
+
+static njs_ret_t njs_fs_error(njs_vm_t *vm, const char *syscall,
+    const char *description, njs_value_t *path, int errn, njs_value_t *retval);
+static int njs_fs_flags(nxt_str_t *value);
+static mode_t njs_fs_mode(njs_value_t *value);
+
+
+static const njs_value_t  njs_fs_errno_string = njs_string("errno");
+static const njs_value_t  njs_fs_path_string = njs_string("path");
+static const njs_value_t  njs_fs_syscall_string = njs_string("syscall");
+
+
+static njs_fs_entry_t njs_flags_table[] = {
+    { nxt_string("r"),   O_RDONLY },
+    { nxt_string("r+"),  O_RDWR },
+    { nxt_string("w"),   O_TRUNC  | O_CREAT  | O_WRONLY },
+    { nxt_string("w+"),  O_TRUNC  | O_CREAT  | O_RDWR },
+    { nxt_string("a"),   O_APPEND | O_CREAT  | O_WRONLY },
+    { nxt_string("a+"),  O_APPEND | O_CREAT  | O_RDWR },
+    { nxt_string("rs"),  O_SYNC   | O_RDONLY },
+    { nxt_string("sr"),  O_SYNC   | O_RDONLY },
+    { nxt_string("wx"),  O_TRUNC  | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("xw"),  O_TRUNC  | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("ax"),  O_APPEND | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("xa"),  O_APPEND | O_CREAT  | O_EXCL | O_WRONLY },
+    { nxt_string("rs+"), O_SYNC   | O_RDWR },
+    { nxt_string("sr+"), O_SYNC   | O_RDWR },
+    { nxt_string("wx+"), O_TRUNC  | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("xw+"), O_TRUNC  | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("ax+"), O_APPEND | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_string("xa+"), O_APPEND | O_CREAT  | O_EXCL | O_RDWR },
+    { nxt_null_string, 0 }
+};
+
+
+static njs_ret_t
+njs_fs_read_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *start, *end;
+    ssize_t             n, length;
+    nxt_str_t           flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    struct stat         sb;
+    njs_value_t         *callback, arguments[3];
+    njs_fs_cont_t       *cont;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 3)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (!njs_is_function(&args[2])) {
+        if (njs_is_string(&args[2])) {
+            njs_string_get(&args[2], &encoding);
+
+        } else if (njs_is_object(&args[2])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+
+        if (nxt_slow_path(nargs < 4 || !njs_is_function(&args[3]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[3];
+
+    } else {
+        if (nxt_slow_path(!njs_is_function(&args[2]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[2];
+    }
+
+    if (flag.start == NULL) {
+        flag = nxt_string_value("r");
+    }
+
+    flags = njs_fs_flags(&flag);
+    if (nxt_slow_path(flags == -1)) {
+        njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                 (int) flag.length, flag.start);
+        return NJS_ERROR;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    ret = fstat(fd, &sb);
+    if (nxt_slow_path(ret == -1)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "stat";
+        goto done;
+    }
+
+    if (nxt_slow_path(!S_ISREG(sb.st_mode))) {
+        errn = 0;
+        description = "File is not regular";
+        syscall = "stat";
+        goto done;
+    }
+
+    if (encoding.length != 0) {
+        length = sb.st_size;
+
+    } else {
+        length = 0;
+    }
+
+    start = njs_string_alloc(vm, &arguments[2], sb.st_size, length);
+    if (nxt_slow_path(start == NULL)) {
+        goto memory_error;
+    }
+
+    p = start;
+    end = p + sb.st_size;
+
+    while (p < end) {
+        n = read(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "read";
+            goto done;
+        }
+
+        p += n;
+    }
+
+    if (encoding.length != 0) {
+        length = nxt_utf8_length(start, sb.st_size);
+
+        if (length >= 0) {
+            njs_string_offset_map_init(start, sb.st_size);
+            njs_string_length_set(&arguments[2], length);
+
+        } else {
+            errn = 0;
+            description = "Non-UTF8 file, convertion is not implemented";
+            syscall = NULL;
+            goto done;
+        }
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        ret = njs_fs_error(vm, syscall, description, &args[1], errn,
+                           &arguments[1]);
+
+        if (nxt_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+        arguments[2] = njs_value_void;
+
+    } else {
+        arguments[1] = njs_value_void;
+    }
+
+    arguments[0] = njs_value_void;
+
+    cont = njs_vm_continuation(vm);
+    cont->u.cont.function = njs_fs_done;
+
+    return njs_function_apply(vm, callback->data.u.function,
+                              arguments, 3, (njs_index_t) &vm->retval);
+
+memory_error:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    njs_exception_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_fs_read_file_sync(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *start, *end;
+    ssize_t             n, length;
+    nxt_str_t           flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    struct stat         sb;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 2)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (nargs == 3) {
+        if (njs_is_string(&args[2])) {
+            njs_string_get(&args[2], &encoding);
+
+        } else if (njs_is_object(&args[2])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[2].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+    }
+
+    if (flag.start == NULL) {
+        flag = nxt_string_value("r");
+    }
+
+    flags = njs_fs_flags(&flag);
+    if (nxt_slow_path(flags == -1)) {
+        njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                 (int) flag.length, flag.start);
+        return NJS_ERROR;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    ret = fstat(fd, &sb);
+    if (nxt_slow_path(ret == -1)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "stat";
+        goto done;
+    }
+
+    if (nxt_slow_path(!S_ISREG(sb.st_mode))) {
+        errn = 0;
+        description = "File is not regular";
+        syscall = "stat";
+        goto done;
+    }
+
+    if (encoding.length != 0) {
+        length = sb.st_size;
+
+    } else {
+        length = 0;
+    }
+
+    start = njs_string_alloc(vm, &vm->retval, sb.st_size, length);
+    if (nxt_slow_path(start == NULL)) {
+        goto memory_error;
+    }
+
+    p = start;
+    end = p + sb.st_size;
+
+    while (p < end) {
+        n = read(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "read";
+            goto done;
+        }
+
+        p += n;
+    }
+
+    if (encoding.length != 0) {
+        length = nxt_utf8_length(start, sb.st_size);
+
+        if (length >= 0) {
+            njs_string_offset_map_init(start, sb.st_size);
+            njs_string_length_set(&vm->retval, length);
+
+        } else {
+            errn = 0;
+            description = "Non-UTF8 file, convertion is not implemented";
+            syscall = NULL;
+            goto done;
+        }
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        (void) njs_fs_error(vm, syscall, description, &args[1], errn,
+                            &vm->retval);
+
+        return NJS_ERROR;
+    }
+
+    return NJS_OK;
+
+memory_error:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    njs_exception_memory_error(vm);
+
+    return NJS_ERROR;
+}
+
+
+static njs_ret_t
+njs_fs_append_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    return njs_fs_write_file_internal(vm, args, nargs,
+                                      O_APPEND | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t
+njs_fs_write_file(njs_vm_t *vm, njs_value_t *args, nxt_uint_t nargs,
+    njs_index_t unused)
+{
+    return njs_fs_write_file_internal(vm, args, nargs,
+                                      O_TRUNC | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_append_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_fs_write_file_sync_internal(vm, args, nargs,
+                                           O_APPEND | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_write_file_sync(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, njs_index_t unused)
+{
+    return njs_fs_write_file_sync_internal(vm, args, nargs,
+                                           O_TRUNC | O_CREAT | O_WRONLY);
+}
+
+
+static njs_ret_t njs_fs_write_file_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *end;
+    mode_t              md;
+    ssize_t             n;
+    nxt_str_t           data, flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    njs_value_t         *callback, *mode, arguments[2];
+    njs_fs_cont_t       *cont;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 4)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[2]))) {
+        njs_exception_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    mode = NULL;
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (!njs_is_function(&args[3])) {
+        if (njs_is_string(&args[3])) {
+            njs_string_get(&args[3], &encoding);
+
+        } else if (njs_is_object(&args[3])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &encoding);
+            }
+
+            lhq.key_hash = NJS_MODE_HASH;
+            lhq.key = nxt_string_value("mode");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                mode = &prop->value;
+            }
+
+        } else {
+            njs_exception_type_error(vm, "Unknown options type "
+                                     "(a string or object required)", NULL);
+            return NJS_ERROR;
+        }
+
+        if (nxt_slow_path(nargs < 5 || !njs_is_function(&args[4]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[4];
+
+    } else {
+        if (nxt_slow_path(!njs_is_function(&args[3]))) {
+            njs_exception_type_error(vm, "callback must be a function", NULL);
+            return NJS_ERROR;
+        }
+
+        callback = &args[3];
+    }
+
+    if (flag.start != NULL) {
+        flags = njs_fs_flags(&flag);
+        if (nxt_slow_path(flags == -1)) {
+            njs_exception_type_error(vm, "Unknown file open flags: '%.*s'",
+                                     (int) flag.length, flag.start);
+            return NJS_ERROR;
+        }
+
+    } else {
+        flags = default_flags;
+    }
+
+    if (mode != NULL) {
+        md = njs_fs_mode(mode);
+
+    } else {
+        md = 0666;
+    }
+
+    path = (char *) njs_string_to_c_string(vm, &args[1]);
+    if (nxt_slow_path(path == NULL)) {
+        return NJS_ERROR;
+    }
+
+    if (encoding.length != 0
+        && (encoding.length != 4 || memcmp(encoding.start, "utf8", 4) != 0))
+    {
+        njs_exception_type_error(vm, "Unknown encoding: '%.*s'",
+                                 (int) encoding.length, encoding.start);
+        return NJS_ERROR;
+    }
+
+    description = NULL;
+
+    /* GCC 4 complains about uninitialized errn and syscall. */
+    errn = 0;
+    syscall = NULL;
+
+    fd = open(path, flags, md);
+    if (nxt_slow_path(fd < 0)) {
+        errn = errno;
+        description = strerror(errno);
+        syscall = "open";
+        goto done;
+    }
+
+    njs_string_get(&args[2], &data);
+
+    p = data.start;
+    end = p + data.length;
+
+    while (p < end) {
+        n = write(fd, p, end - p);
+        if (nxt_slow_path(n == -1)) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            errn = errno;
+            description = strerror(errno);
+            syscall = "write";
+            goto done;
+        }
+
+        p += n;
+    }
+
+done:
+
+    if (fd > 0) {
+        close(fd);
+    }
+
+    if (description != 0) {
+        ret = njs_fs_error(vm, syscall, description, &args[1], errn,
+                           &arguments[1]);
+
+        if (nxt_slow_path(ret != NJS_OK)) {
+            return NJS_ERROR;
+        }
+
+    } else {
+        arguments[1] = njs_value_void;
+    }
+
+    arguments[0] = njs_value_void;
+
+    cont = njs_vm_continuation(vm);
+    cont->u.cont.function = njs_fs_done;
+
+    return njs_function_apply(vm, callback->data.u.function,
+                              arguments, 2, (njs_index_t) &vm->retval);
+}
+
+
+static njs_ret_t
+njs_fs_write_file_sync_internal(njs_vm_t *vm, njs_value_t *args,
+    nxt_uint_t nargs, int default_flags)
+{
+    int                 fd, errn, flags;
+    u_char              *p, *end;
+    mode_t              md;
+    ssize_t             n;
+    nxt_str_t           data, flag, encoding;
+    njs_ret_t           ret;
+    const char          *path, *syscall, *description;
+    njs_value_t         *mode;
+    njs_object_prop_t   *prop;
+    nxt_lvlhsh_query_t  lhq;
+
+    if (nxt_slow_path(nargs < 3)) {
+        njs_exception_type_error(vm, "too few arguments", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[1]))) {
+        njs_exception_type_error(vm, "path must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    if (nxt_slow_path(!njs_is_string(&args[2]))) {
+        njs_exception_type_error(vm, "data must be a string", NULL);
+        return NJS_ERROR;
+    }
+
+    mode = NULL;
+    flag.start = NULL;
+    encoding.length = 0;
+    encoding.start = NULL;
+
+    if (nargs == 4) {
+        if (njs_is_string(&args[3])) {
+            njs_string_get(&args[3], &encoding);
+
+        } else if (njs_is_object(&args[3])) {
+            lhq.key_hash = NJS_FLAG_HASH;
+            lhq.key = nxt_string_value("flag");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {
+                prop = lhq.value;
+                njs_string_get(&prop->value, &flag);
+            }
+
+            lhq.key_hash = NJS_ENCODING_HASH;
+            lhq.key = nxt_string_value("encoding");
+            lhq.proto = &njs_object_hash_proto;
+
+            ret = nxt_lvlhsh_find(&args[3].data.u.object->hash, &lhq);
+            if (ret == NXT_OK) {


More information about the nginx-devel mailing list