summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--lib.c59
-rw-r--r--lib/fs.c94
-rw-r--r--tests/custom/03_stdlib/40_proto2
3 files changed, 151 insertions, 4 deletions
diff --git a/lib.c b/lib.c
index 5a6c934..e814f49 100644
--- a/lib.c
+++ b/lib.c
@@ -894,7 +894,10 @@ uc_getenv(uc_vm_t *vm, size_t nargs)
* 2. The current index
* 3. The array being filtered
*
- * Returns a new array containing only retainted items, in the same order as
+ * (Note that the `map` function behaves similarly to `filter` with respect
+ * to its `fn` parameters.)
+ *
+ * Returns a new array containing only retained items, in the same order as
* the input array.
*
* @function module:core#filter
@@ -989,7 +992,7 @@ uc_hex(uc_vm_t *vm, size_t nargs)
}
/**
- * Converts the given value to an integer.
+ * Converts the given value to an integer, using an optional base.
*
* Returns `NaN` if the value is not convertible.
*
@@ -998,7 +1001,23 @@ uc_hex(uc_vm_t *vm, size_t nargs)
* @param {*} x
* The value to be converted to an integer.
*
+ * @param {int} [base]
+ * The base into which the value is to be converted, the default is 10.
+ * Note that the base parameter is ignored if the `x` value is already numeric.
+ *
* @returns {number}
+ *
+ * @example
+ * int("123") // Returns 123
+ * int("123", 10) // 123
+ * int("10 or more") // 10
+ * int("12.3") // 12
+ * int("123", 7) // 66
+ * int("abc", 16) // 2748
+ * int("xyz", 36) // 44027
+ * int(10.10, "2") // 10, the invalid base is ignored
+ * int("xyz", 16) // NaN, bad value
+ * int("1010", "2") // NaN, bad base
*/
static uc_value_t *
uc_int(uc_vm_t *vm, size_t nargs)
@@ -1138,6 +1157,16 @@ uc_lc(uc_vm_t *vm, size_t nargs)
* Transform the array passed as the first argument by invoking the function
* specified in the second argument for each array item.
*
+ * The mapping function is invoked with three arguments (see examples, below,
+ * for some possibly counterintuitive usage):
+ *
+ * 1. The array value
+ * 2. The current index
+ * 3. The array being filtered
+ *
+ * (Note that the `filter` function behaves similarly to `map` with respect
+ * to its `fn` parameters.)
+ *
* Returns a new array of the same length as the input array containing the
* transformed values.
*
@@ -1159,6 +1188,28 @@ uc_lc(uc_vm_t *vm, size_t nargs)
* // map to type names:
* a = map(["foo", 1, true, null, 2.2], type);
* // a = ["string", "int", "bool", null, "double"]
+ *
+ * // attempt to naively use built-in 'int' to map an array:
+ * a = map(["x", "2", "11", "7"], int)
+ * // a = [NaN, NaN, 3, NaN]
+ * //
+ * // This is a direct result of 'int' being provided the second, index parameter
+ * // for its base value in the conversion.
+ * //
+ * // The resulting calls to 'int' are as follows:
+ * // int("x", 0, [...]) - convert "x" to base 0, 'int' ignores the third value
+ * // int("2", 1, [...]) - convert "2" to base 1, digit out of range, so NaN
+ * // int("11", 2, [...]) - convert "11" to base 2, produced unexpected 3
+ * // int("7", 3, [...]) - convert "7" to base 3, digit out of range, NaN again
+ *
+ * // remedy this by using an arrow function to ensure the proper base value
+ * // (in this case, the default of 10) is passed to 'int':
+ * a = map(["x", "2", "1", "7"], (x) => int(x))
+ * // a = [NaN, 2, 1, 7]
+ *
+ * // convert base-2 values:
+ * a = map(["22", "1010", "0001", "0101"], (x) => int(x, 2))
+ * // a = [NaN, 10, 1, 5]
*/
static uc_value_t *
uc_map(uc_vm_t *vm, size_t nargs)
@@ -2864,7 +2915,7 @@ uc_require(uc_vm_t *vm, size_t nargs)
* Convert the given IP address string to an array of byte values.
*
* IPv4 addresses result in arrays of 4 integers while IPv6 ones in arrays
- * containing 16 intergers. The resulting array can be turned back into IP
+ * containing 16 integers. The resulting array can be turned back into IP
* address strings using the inverse `arrtoip()` function.
*
* Returns an array containing the address byte values.
@@ -5341,7 +5392,7 @@ uc_gc(uc_vm_t *vm, size_t nargs)
* @typedef {Object} module:core.ParseConfig
*
* @property {boolean} lstrip_blocks
- * Whether to strip whitespace preceeding template directives.
+ * Whether to strip whitespace preceding template directives.
* See {@link tutorial-02-syntax.html#whitespace-handling|Whitespace handling}.
*
* @property {boolean} trim_blocks
diff --git a/lib/fs.c b/lib/fs.c
index 4d9695e..d0d97ac 100644
--- a/lib/fs.c
+++ b/lib/fs.c
@@ -53,6 +53,7 @@
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
+#include <sys/file.h>
#include <grp.h>
#include <pwd.h>
#include <glob.h>
@@ -739,6 +740,97 @@ uc_fs_seek(uc_vm_t *vm, size_t nargs)
}
/**
+ * Truncate file to a given size
+ *
+ * Returns `true` if the file was successfully truncated.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:fs.file#truncate
+ *
+ * @param {number} [offset=0]
+ * The offset in bytes.
+ *
+ * @returns {?boolean}
+ */
+static uc_value_t *
+uc_fs_truncate(uc_vm_t *vm, size_t nargs)
+{
+ FILE *fp = uc_fn_thisval("fs.file");
+ uc_value_t *ofs = uc_fn_arg(0);
+ off_t offset;
+
+ if (!fp)
+ err_return(EBADF);
+
+ if (!ofs)
+ offset = 0;
+ else if (ucv_type(ofs) != UC_INTEGER)
+ err_return(EINVAL);
+ else
+ offset = (off_t)ucv_int64_get(ofs);
+
+ if (ftruncate(fileno(fp), offset) < 0)
+ err_return(errno);
+
+ return ucv_boolean_new(true);
+}
+
+/**
+ * Locks or unlocks a file.
+ *
+ * The mode argument specifies lock/unlock operation flags.
+ *
+ * | Flag | Description |
+ * |---------|------------------------------|
+ * | "s" | shared lock |
+ * | "x" | exclusive lock |
+ * | "n" | don't block when locking |
+ * | "u" | unlock |
+ *
+ * Returns `true` if the file was successfully locked/unlocked.
+ *
+ * Returns `null` if an error occurred.
+ *
+ * @function module:fs.file#lock
+ *
+ * @param {string} [op]
+ * The lock operation flags
+ *
+ * @returns {?boolean}
+ */
+static uc_value_t *
+uc_fs_lock(uc_vm_t *vm, size_t nargs)
+{
+ FILE *fp = uc_fn_thisval("fs.file");
+ uc_value_t *mode = uc_fn_arg(0);
+ int i, op = 0;
+ char *m;
+
+ if (!fp)
+ err_return(EBADF);
+
+ if (ucv_type(mode) != UC_STRING)
+ err_return(EINVAL);
+
+ m = ucv_string_get(mode);
+ for (i = 0; m[i]; i++) {
+ switch (m[i]) {
+ case 's': op |= LOCK_SH; break;
+ case 'x': op |= LOCK_EX; break;
+ case 'n': op |= LOCK_NB; break;
+ case 'u': op |= LOCK_UN; break;
+ default: err_return(EINVAL);
+ }
+ }
+
+ if (flock(fileno(fp), op) < 0)
+ err_return(errno);
+
+ return ucv_boolean_new(true);
+}
+
+/**
* Obtain current read position.
*
* Obtains the current, absolute read position of the open file.
@@ -2571,6 +2663,8 @@ static const uc_function_list_t file_fns[] = {
{ "fileno", uc_fs_fileno },
{ "error", uc_fs_error },
{ "isatty", uc_fs_isatty },
+ { "truncate", uc_fs_truncate },
+ { "lock", uc_fs_lock },
};
static const uc_function_list_t dir_fns[] = {
diff --git a/tests/custom/03_stdlib/40_proto b/tests/custom/03_stdlib/40_proto
index 2a12966..d96d124 100644
--- a/tests/custom/03_stdlib/40_proto
+++ b/tests/custom/03_stdlib/40_proto
@@ -38,6 +38,8 @@ When invoked with two arguments, returns the given value.
Hello, World!
[
{
+ "lock": "function lock(...) { [native code] }",
+ "truncate": "function truncate(...) { [native code] }",
"isatty": "function isatty(...) { [native code] }",
"error": "function error(...) { [native code] }",
"fileno": "function fileno(...) { [native code] }",